From a8d4da2e55b682201ba80acc059c907a74d4b414 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 15 Mar 2024 09:57:15 -0400 Subject: [PATCH 01/54] Fix tranq rounds injecting when reflected (#26141) * Fix tranq rounds injecting when reflected * Cleanup using directives * Remove FixtureId from component --- .../Components/SolutionInjectOnCollideComponent.cs | 3 --- .../EntitySystems/SolutionInjectOnCollideSystem.cs | 13 +++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs index cdba55467ae..76bb5294bce 100644 --- a/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs +++ b/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs @@ -25,7 +25,4 @@ public sealed partial class SolutionInjectOnCollideComponent : Component /// [DataField("blockSlots"), ViewVariables(VVAccess.ReadWrite)] public SlotFlags BlockSlots = SlotFlags.MASK; - - [DataField] - public string FixtureId = SharedProjectileSystem.ProjectileFixture; } diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs index 0f696774ae5..fb84aca3e41 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs @@ -3,8 +3,7 @@ using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Inventory; -using JetBrains.Annotations; -using Robust.Shared.Physics.Events; +using Content.Shared.Projectiles; namespace Content.Server.Chemistry.EntitySystems; @@ -17,17 +16,15 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(HandleInjection); + SubscribeLocalEvent(HandleInjection); } - private void HandleInjection(Entity ent, ref StartCollideEvent args) + private void HandleInjection(Entity ent, ref ProjectileHitEvent args) { var component = ent.Comp; - var target = args.OtherEntity; + var target = args.Target; - if (!args.OtherBody.Hard || - args.OurFixtureId != ent.Comp.FixtureId || - !EntityManager.TryGetComponent(target, out var bloodstream) || + if (!TryComp(target, out var bloodstream) || !_solutionContainersSystem.TryGetInjectableSolution(ent.Owner, out var solution, out _)) { return; From cacf0f7c95080be4fb45a8ac93f96447997cf788 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:56:01 +0000 Subject: [PATCH 02/54] fix eggsplosion (#26146) Co-authored-by: deltanedas <@deltanedas:kde.org> --- Resources/Prototypes/Recipes/Construction/Graphs/food/egg.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/food/egg.yml b/Resources/Prototypes/Recipes/Construction/Graphs/food/egg.yml index f5eea53bcc2..3c3bca2585a 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/food/egg.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/food/egg.yml @@ -15,7 +15,8 @@ completed: - !type:DamageEntity damage: - Blunt: 10 + types: + Blunt: 10 steps: # egg explodes some time after the water in it boils and increases pressure, guessing ~110C - minTemperature: 383 From 7cd910c280040ea707e3f9862647fb1a043648a0 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 16 Mar 2024 13:49:07 +0000 Subject: [PATCH 03/54] less fish in maints (#26156) pro Co-authored-by: deltanedas <@deltanedas:kde.org> --- Resources/Prototypes/Catalog/Fills/Lockers/misc.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 81ec76dae6a..dd21ea652aa 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -110,6 +110,9 @@ prob: 0.10 - id: Soap prob: 0.44 + - id: null + prob: 0.67 + orGroup: carp - id: PlushieCarp prob: 0.2 - id: PlushieSlime @@ -203,6 +206,9 @@ prob: 0.10 - id: Soap prob: 0.44 + - id: null + prob: 0.67 + orGroup: carp - id: PlushieCarp prob: 0.2 - id: PlushieSlime From af01779ef16ad42b954334fe5aa536140271b824 Mon Sep 17 00:00:00 2001 From: Nikolai Korolev Date: Fri, 15 Mar 2024 21:49:09 +0000 Subject: [PATCH 04/54] Small performance optimization. Don't do String.Trim() second time (#26161) Don't do Trim 2 times --- Content.Server/Disposal/Tube/DisposalTubeSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index b7d8455d85e..20aa8b6d2c5 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -122,7 +122,7 @@ private void OnUiAction(EntityUid uid, DisposalRouterComponent router, SharedDis if (trimmed == "") continue; - router.Tags.Add(tag.Trim()); + router.Tags.Add(trimmed); } _audioSystem.PlayPvs(router.ClickSound, uid, AudioParams.Default.WithVolume(-2f)); From 09d5eb95b980bc791c9f8eb1c810287088279e50 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Thu, 14 Mar 2024 00:46:41 +0200 Subject: [PATCH 05/54] Add log when a player starts observing. (#26091) --- Content.Server/GameTicking/GameTicker.Spawning.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 52b59703e3a..54587e95be3 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -346,6 +346,7 @@ public void SpawnObserver(ICommonSession player) _metaData.SetEntityName(ghost, name); _ghost.SetCanReturnToBody(ghost, false); _mind.TransferTo(mind.Value, ghost); + _adminLogger.Add(LogType.LateJoin, LogImpact.Low, $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}."); } #region Mob Spawning Helpers From 54eb16c855ab3893d0bfe979e322f788288895d9 Mon Sep 17 00:00:00 2001 From: Gyrandola Date: Wed, 13 Mar 2024 04:52:20 +0100 Subject: [PATCH 06/54] Remove butchering popup when clicking own sprite with a knife (#26051) added target!=user check --- Content.Server/Kitchen/EntitySystems/SharpSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index f270fe76d2c..62d5a061624 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -53,7 +53,7 @@ private void TryStartButcherDoafter(EntityUid knife, EntityUid target, EntityUid if (!TryComp(knife, out var sharp)) return; - if (butcher.Type != ButcheringType.Knife) + if (butcher.Type != ButcheringType.Knife && target != user) { _popupSystem.PopupEntity(Loc.GetString("butcherable-different-tool", ("target", target)), knife, user); return; From 231d28bb959cab9e47abc9eeece8f9fe6a1955d7 Mon Sep 17 00:00:00 2001 From: Gyrandola Date: Wed, 13 Mar 2024 03:03:04 +0100 Subject: [PATCH 07/54] Fix Sky Blue carpet making red tables (#26049) * created and added CarpetSBlue tag * reverting unnecessary de-indentation * Update carpets.yml --- .../Entities/Objects/Misc/carpets.yml | 21 +++++++++++-------- Resources/Prototypes/tags.yml | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Misc/carpets.yml b/Resources/Prototypes/Entities/Objects/Misc/carpets.yml index d8f32667307..dca2d40aedb 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/carpets.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/carpets.yml @@ -15,7 +15,7 @@ - type: Stack stackType: FloorCarpetRed - type: Tag - tags: + tags: - CarpetRed - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: Carpet @@ -34,7 +34,7 @@ - type: Stack stackType: FloorCarpetBlack - type: Tag - tags: + tags: - CarpetBlack - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetBlack @@ -53,7 +53,7 @@ - type: Stack stackType: FloorCarpetBlue - type: Tag - tags: + tags: - CarpetBlue - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetBlue @@ -72,7 +72,7 @@ - type: Stack stackType: FloorCarpetGreen - type: Tag - tags: + tags: - CarpetGreen - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetGreen @@ -91,7 +91,7 @@ - type: Stack stackType: FloorCarpetOrange - type: Tag - tags: + tags: - CarpetOrange - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetOrange @@ -109,6 +109,9 @@ heldPrefix: carpet-skyblue - type: Stack stackType: FloorCarpetSkyBlue + - type: Tag + tags: + - CarpetSBlue - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetSBlue doAfter: 0.5 @@ -126,7 +129,7 @@ - type: Stack stackType: FloorCarpetPurple - type: Tag - tags: + tags: - CarpetPurple - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetPurple @@ -145,7 +148,7 @@ - type: Stack stackType: FloorCarpetPink - type: Tag - tags: + tags: - CarpetPink - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetPink @@ -164,7 +167,7 @@ - type: Stack stackType: FloorCarpetCyan - type: Tag - tags: + tags: - CarpetCyan - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetCyan @@ -183,7 +186,7 @@ - type: Stack stackType: FloorCarpetWhite - type: Tag - tags: + tags: - CarpetWhite - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetWhite diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 9f06df7bcb3..2b11b7991b0 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -271,6 +271,9 @@ - type: Tag id: CarpetPurple +- type: Tag + id: CarpetSBlue + - type: Tag id: CarpetPink From bd0df53fb56c419bf714f57e865f4a1b7f4753c5 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 15 Mar 2024 20:47:13 -0700 Subject: [PATCH 08/54] Round start Ion storms (#26165) * Round start Ion storms * five * Update Resources/Prototypes/GameRules/events.yml --------- Co-authored-by: Plykiya Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Resources/Prototypes/GameRules/events.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index db99266cbaa..6812ca14f70 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -463,7 +463,6 @@ components: - type: StationEvent weight: 10 - earliestStart: 20 reoccurrenceDelay: 20 duration: 1 - type: IonStormRule From 830927da11e664f580c79ffbef0a28a0cfa079eb Mon Sep 17 00:00:00 2001 From: Krunklehorn <42424291+Krunklehorn@users.noreply.github.com> Date: Fri, 15 Mar 2024 23:50:53 -0400 Subject: [PATCH 09/54] Fix StrippableSystem blunders (#26166) * Fixes target hand check when no hands were needed. * Adds missing CanStripX checks. * Whitespace. * Fixed bad math causing instant strips. --- Content.Server/Strip/StrippableSystem.cs | 241 ++++++++++++++++++ .../Strip/Components/StrippableComponent.cs | 14 +- 2 files changed, 245 insertions(+), 10 deletions(-) diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index 50a9cb9b691..99d429ed2c0 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -170,6 +170,88 @@ private void OnActivateInWorld(EntityUid uid, StrippableComponent component, Act StartOpeningStripper(args.User, (uid, component)); } + public override void StartOpeningStripper(EntityUid user, Entity strippable, bool openInCombat = false) + { + base.StartOpeningStripper(user, strippable, openInCombat); + + if (TryComp(user, out var mode) && mode.IsInCombatMode && !openInCombat) + return; + + if (TryComp(user, out var actor)) + { + if (_userInterfaceSystem.SessionHasOpenUi(strippable, StrippingUiKey.Key, actor.PlayerSession)) + return; + _userInterfaceSystem.TryOpen(strippable, StrippingUiKey.Key, actor.PlayerSession); + } + } + + private void OnStripButtonPressed(Entity strippable, ref StrippingSlotButtonPressed args) + { + if (args.Session.AttachedEntity is not { Valid: true } user || + !TryComp(user, out var userHands)) + return; + + if (args.IsHand) + { + StripHand((user, userHands), (strippable.Owner, null), args.Slot, strippable); + return; + } + + if (!TryComp(strippable, out var inventory)) + return; + + var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory); + + if (userHands.ActiveHandEntity != null && !hasEnt) + StartStripInsertInventory((user, userHands), strippable.Owner, userHands.ActiveHandEntity.Value, args.Slot); + else if (userHands.ActiveHandEntity == null && hasEnt) + StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot); + } + + private void StripHand( + Entity user, + Entity target, + string handId, + StrippableComponent? targetStrippable) + { + if (!Resolve(user, ref user.Comp) || + !Resolve(target, ref target.Comp) || + !Resolve(target, ref targetStrippable)) + return; + + if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot)) + return; + + // Is the target a handcuff? + if (TryComp(handSlot.HeldEntity, out var virtualItem) && + TryComp(target.Owner, out var cuffable) && + _cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity)) + { + _cuffableSystem.TryUncuff(target.Owner, user, virtualItem.BlockingEntity, cuffable); + return; + } + + if (user.Comp.ActiveHandEntity != null && handSlot.HeldEntity == null) + StartStripInsertHand(user, target, user.Comp.ActiveHandEntity.Value, handId, targetStrippable); + else if (user.Comp.ActiveHandEntity == null && handSlot.HeldEntity != null) + StartStripRemoveHand(user, target, handSlot.HeldEntity.Value, handId, targetStrippable); + } + + private void OnStripEnsnareMessage(EntityUid uid, EnsnareableComponent component, StrippingEnsnareButtonPressed args) + { + if (args.Session.AttachedEntity is not { Valid: true } user) + return; + + foreach (var entity in component.Container.ContainedEntities) + { + if (!TryComp(entity, out var ensnaring)) + continue; + + _ensnaringSystem.TryFree(uid, user, entity, ensnaring); + return; + } + } + /// /// Places item in user's active hand to an inventory slot. /// @@ -464,11 +546,170 @@ bool Check() if (result != DoAfterStatus.Finished) return; +<<<<<<< HEAD _handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: hands); _handsSystem.PickupOrDrop(user, item, animateUser: !ev.Stealth, animate: !ev.Stealth, handsComp: userHands); // hand update will trigger strippable update _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands"); +======= + if (!CanStripInsertHand(user, target, held, handName)) + return; + + _handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: user.Comp); + _handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: stealth, handsComp: target.Comp); + _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands"); + + // Hand update will trigger strippable update. + } + + /// + /// Checks whether the item is in the target's hand and whether it can be dropped. + /// + private bool CanStripRemoveHand( + EntityUid user, + Entity target, + EntityUid item, + string handName) + { + if (!Resolve(target, ref target.Comp)) + return false; + + if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp)) + { + _popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", target)), user); + return false; + } + + if (HasComp(handSlot.HeldEntity)) + return false; + + if (handSlot.HeldEntity == null) + return false; + + if (handSlot.HeldEntity != item) + return false; + + if (!_handsSystem.CanDropHeld(target, handSlot, false)) + { + _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", target)), user); + return false; + } + + return true; + } + + /// + /// Begins a DoAfter to remove the item from the target's hand and insert it in the user's active hand. + /// + private void StartStripRemoveHand( + Entity user, + Entity target, + EntityUid item, + string handName, + StrippableComponent? targetStrippable = null) + { + if (!Resolve(user, ref user.Comp) || + !Resolve(target, ref target.Comp) || + !Resolve(target, ref targetStrippable)) + return; + + if (!CanStripRemoveHand(user, target, item, handName)) + return; + + var (time, stealth) = GetStripTimeModifiers(user, target, targetStrippable.HandStripDelay); + + if (!stealth) + _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", item)), target, target); + + var prefix = stealth ? "stealthily " : ""; + _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands"); + + var doAfterArgs = new DoAfterArgs(EntityManager, user, time, new StrippableDoAfterEvent(false, false, handName), user, target, item) + { + Hidden = stealth, + AttemptFrequency = AttemptFrequency.EveryTick, + BreakOnDamage = true, + BreakOnTargetMove = true, + BreakOnUserMove = true, + NeedHand = true, + BreakOnHandChange = false, // Allow simultaneously removing multiple items. + DuplicateCondition = DuplicateConditions.SameTool + }; + + _doAfterSystem.TryStartDoAfter(doAfterArgs); + } + + /// + /// Takes the item from the target's hand and inserts it in the user's active hand. + /// + private void StripRemoveHand( + Entity user, + Entity target, + EntityUid item, + string handName, + bool stealth) + { + if (!Resolve(user, ref user.Comp) || + !Resolve(target, ref target.Comp)) + return; + + if (!CanStripRemoveHand(user, target, item, handName)) + return; + + _handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: target.Comp); + _handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: stealth, handsComp: user.Comp); + _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands"); + + // Hand update will trigger strippable update. + } + + private void OnStrippableDoAfterRunning(Entity entity, ref DoAfterAttemptEvent ev) + { + var args = ev.DoAfter.Args; + + DebugTools.Assert(entity.Owner == args.User); + DebugTools.Assert(args.Target != null); + DebugTools.Assert(args.Used != null); + DebugTools.Assert(ev.Event.SlotOrHandName != null); + + if (ev.Event.InventoryOrHand) + { + if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) || + !ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName)) + ev.Cancel(); + } + else + { + if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) || + !ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName)) + ev.Cancel(); + } + } + + private void OnStrippableDoAfterFinished(Entity entity, ref StrippableDoAfterEvent ev) + { + if (ev.Cancelled) + return; + + DebugTools.Assert(entity.Owner == ev.User); + DebugTools.Assert(ev.Target != null); + DebugTools.Assert(ev.Used != null); + DebugTools.Assert(ev.SlotOrHandName != null); + + if (ev.InventoryOrHand) + { + if (ev.InsertOrRemove) + StripInsertInventory((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName); + else StripRemoveInventory(entity.Owner, ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden); + } + else + { + if (ev.InsertOrRemove) + StripInsertHand((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden); + else StripRemoveHand((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden); + } +>>>>>>> 8ecb78ee5a (Fix StrippableSystem blunders (#26166)) } } } diff --git a/Content.Shared/Strip/Components/StrippableComponent.cs b/Content.Shared/Strip/Components/StrippableComponent.cs index fbf99992e3c..bf6b9e2a7b1 100644 --- a/Content.Shared/Strip/Components/StrippableComponent.cs +++ b/Content.Shared/Strip/Components/StrippableComponent.cs @@ -37,19 +37,13 @@ public StrippingSlotButtonPressed(string slot, bool isHand) [NetSerializable, Serializable] public sealed class StrippingEnsnareButtonPressed : BoundUserInterfaceMessage { - public StrippingEnsnareButtonPressed() - { - } - } - - public abstract class BaseBeforeStripEvent : EntityEventArgs, IInventoryRelayEvent - { - public readonly float InitialTime; - public float Time => MathF.Max(InitialTime * Multiplier + Additive, 0f); - public float Additive = 0; + public readonly TimeSpan InitialTime = initialTime; public float Multiplier = 1f; + public TimeSpan Additive = TimeSpan.Zero; public bool Stealth; + public TimeSpan Time => TimeSpan.FromSeconds(MathF.Max(InitialTime.Seconds * Multiplier + Additive.Seconds, 0f)); + public SlotFlags TargetSlots { get; } = SlotFlags.GLOVES; public BaseBeforeStripEvent(float initialTime, bool stealth = false) From 0ef2cec6e0fc501958538ea99b8f11a2f0f88f64 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:06:17 +1100 Subject: [PATCH 10/54] Fix store refunds (#26173) --- Content.Server/Store/Systems/StoreSystem.Ui.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index a7490fd27ff..25b1cd1f6c0 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -323,7 +323,7 @@ private void OnRequestRefund(EntityUid uid, StoreComponent component, StoreReque if (!component.RefundAllowed || component.BoughtEntities.Count == 0) return; - for (var i = component.BoughtEntities.Count; i >= 0; i--) + for (var i = component.BoughtEntities.Count - 1; i >= 0; i--) { var purchase = component.BoughtEntities[i]; @@ -332,7 +332,7 @@ private void OnRequestRefund(EntityUid uid, StoreComponent component, StoreReque component.BoughtEntities.RemoveAt(i); - if (_actions.TryGetActionData(purchase, out var actionComponent)) + if (_actions.TryGetActionData(purchase, out var actionComponent, logError: false)) { _actionContainer.RemoveAction(purchase, actionComponent); } @@ -340,6 +340,8 @@ private void OnRequestRefund(EntityUid uid, StoreComponent component, StoreReque EntityManager.DeleteEntity(purchase); } + component.BoughtEntities.Clear(); + foreach (var (currency, value) in component.BalanceSpent) { component.Balance[currency] += value; From f13458abcb91277c2392354efcb5bdfae0493111 Mon Sep 17 00:00:00 2001 From: LordCarve <27449516+LordCarve@users.noreply.github.com> Date: Sat, 16 Mar 2024 18:31:21 +0100 Subject: [PATCH 11/54] Fix anomaly decay logs (#26180) Fix anomaly decay logs (#26122) --- Content.Shared/Anomaly/SharedAnomalySystem.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index 6cdcdf305bb..78b198be801 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -133,7 +133,7 @@ public void StartSupercriticalEvent(EntityUid uid) if (HasComp(uid)) return; - AdminLog.Add(LogType.Anomaly, LogImpact.High, $"Anomaly {ToPrettyString(uid)} began to go supercritical."); + AdminLog.Add(LogType.Anomaly, LogImpact.Extreme, $"Anomaly {ToPrettyString(uid)} began to go supercritical."); if (_net.IsServer) _sawmill.Info($"Anomaly is going supercritical. Entity: {ToPrettyString(uid)}"); @@ -180,7 +180,8 @@ public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool s // Logging before resolve, in case the anomaly has deleted itself. if (_net.IsServer) _sawmill.Info($"Ending anomaly. Entity: {ToPrettyString(uid)}"); - AdminLog.Add(LogType.Anomaly, LogImpact.Extreme, $"Anomaly {ToPrettyString(uid)} went supercritical."); + AdminLog.Add(LogType.Anomaly, supercritical ? LogImpact.High : LogImpact.Low, + $"Anomaly {ToPrettyString(uid)} {(supercritical ? "went supercritical" : "decayed")}."); if (!Resolve(uid, ref component)) return; From 235af01f2e86911637d91d0c6ed7be83c049242c Mon Sep 17 00:00:00 2001 From: LordCarve <27449516+LordCarve@users.noreply.github.com> Date: Sat, 16 Mar 2024 18:31:21 +0100 Subject: [PATCH 12/54] Fix anomaly decay logs (#26180) Fix anomaly decay logs (#26122) From d1ab9b7308e4a9c9e8f7ff9bde6d41bd4330bff1 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Sat, 16 Mar 2024 15:23:53 -0400 Subject: [PATCH 13/54] Small species melee weapon change (#26183) * inital * Update animals.yml --- Resources/Prototypes/Entities/Mobs/NPCs/animals.yml | 7 ++++--- Resources/Prototypes/Entities/Mobs/Species/arachnid.yml | 2 +- Resources/Prototypes/Entities/Mobs/Species/reptilian.yml | 2 +- Resources/Prototypes/Entities/Mobs/Species/vox.yml | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 7af568b40c9..e617e9eaf28 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1359,12 +1359,13 @@ baseSprintSpeed: 5 - type: MeleeWeapon soundHit: - path: /Audio/Weapons/pierce.ogg + collection: AlienClaw angle: 30 - animation: WeaponArcPunch + animation: WeaponArcClaw damage: types: - Piercing: 9 + Slash: 5 + Piercing: 4 - type: Temperature heatDamageThreshold: 360 coldDamageThreshold: 285 diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index d6a60872666..c135ac2b829 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -51,7 +51,7 @@ - type: MeleeWeapon animation: WeaponArcBite soundHit: - collection: AlienClaw + path: /Audio/Effects/bite.ogg damage: types: Piercing: 5 diff --git a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml index c893fab4491..09e86b19968 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml @@ -41,7 +41,7 @@ damageModifierSet: Scale - type: MeleeWeapon soundHit: - path: /Audio/Weapons/pierce.ogg + collection: AlienClaw angle: 30 animation: WeaponArcClaw damage: diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index 184711ec1e5..5e40254e3a8 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -41,7 +41,7 @@ bloodReagent: AmmoniaBlood - type: MeleeWeapon soundHit: - path: /Audio/Weapons/pierce.ogg + collection: AlienClaw angle: 30 animation: WeaponArcClaw damage: From cd7ecf57344cb6390e2e42e4949f655e9e306a3a Mon Sep 17 00:00:00 2001 From: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:55:32 -0500 Subject: [PATCH 14/54] Change collision mask of canisters to allow passing through cargo flaps (#26193) Co-authored-by: Jeff --- .../Entities/Structures/Storage/Canisters/gas_canisters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 897b7be6ffb..cd44f5f585a 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -74,7 +74,7 @@ bounds: "-0.25,-0.25,0.25,0.25" density: 190 mask: - - MachineMask + - SmallMobMask layer: - MachineLayer - type: AtmosDevice From 88f20e7f9f4acf4661ccd33a4c9d3ac38ee40c1a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 17 Mar 2024 12:10:59 +1100 Subject: [PATCH 15/54] Fix presetidcard extended access throwing (#26195) --- Content.Server/Access/Systems/PresetIdCardSystem.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Server/Access/Systems/PresetIdCardSystem.cs b/Content.Server/Access/Systems/PresetIdCardSystem.cs index 4073b4baa28..696b7a1dcfd 100644 --- a/Content.Server/Access/Systems/PresetIdCardSystem.cs +++ b/Content.Server/Access/Systems/PresetIdCardSystem.cs @@ -50,8 +50,10 @@ private void OnMapInit(EntityUid uid, PresetIdCardComponent id, MapInitEvent arg var station = _stationSystem.GetOwningStation(uid); var extended = false; - if (station != null) - extended = Comp(station.Value).ExtendedAccess; + + // Station not guaranteed to have jobs (e.g. nukie outpost). + if (TryComp(station, out StationJobsComponent? stationJobs)) + extended = stationJobs.ExtendedAccess; SetupIdAccess(uid, id, extended); SetupIdName(uid, id); From 8b20f8b77b688be00db407f9bcf0919d3501f2fa Mon Sep 17 00:00:00 2001 From: Adrian16199 <144424013+Adrian16199@users.noreply.github.com> Date: Sun, 17 Mar 2024 03:22:11 +0100 Subject: [PATCH 16/54] Gives glue tag to crazy glue (#26154) --- Resources/Prototypes/Entities/Objects/Fun/toys.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index ccb67b5ae1d..991d2302a95 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1414,6 +1414,9 @@ Quantity: 100 - type: TrashOnSolutionEmpty solution: drink + - type: Tag + tags: + - DrinkSpaceGlue - type: entity parent: BaseItem From 3f2987f3953917b5259afe64fb9e43be2e5cfcc0 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Sun, 17 Mar 2024 05:27:22 +0300 Subject: [PATCH 17/54] SlowContactsSystem to SpeedModifierContactsSystem mini rework (#26110) * rework * update logic --- .../TileReactions/SpillTileReaction.cs | 4 +- .../Fluids/EntitySystems/PuddleSystem.cs | 8 ++-- ....cs => SpeedModifiedByContactComponent.cs} | 2 +- ...t.cs => SpeedModifierContactsComponent.cs} | 4 +- .../Systems/FrictionContactsSystem.cs | 2 +- ...stem.cs => SpeedModifierContactsSystem.cs} | 46 +++++++++++-------- .../Entities/Objects/Misc/kudzu.yml | 6 +-- .../Entities/Objects/Misc/spider_web.yml | 2 +- Resources/Prototypes/Entities/Tiles/water.yml | 2 +- 9 files changed, 42 insertions(+), 34 deletions(-) rename Content.Shared/Movement/Components/{SlowedByContactComponent.cs => SpeedModifiedByContactComponent.cs} (80%) rename Content.Shared/Movement/Components/{SlowContactsComponent.cs => SpeedModifierContactsComponent.cs} (83%) rename Content.Shared/Movement/Systems/{SlowContactsSystem.cs => SpeedModifierContactsSystem.cs} (59%) diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs index 49fdaa5c7ee..8f8b0626a28 100644 --- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs @@ -45,9 +45,9 @@ public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 var step = entityManager.EnsureComponent(puddleUid); entityManager.EntitySysManager.GetEntitySystem().SetRequiredTriggerSpeed(puddleUid, _requiredSlipSpeed, step); - var slow = entityManager.EnsureComponent(puddleUid); + var slow = entityManager.EnsureComponent(puddleUid); var speedModifier = 1 - reagent.Viscosity; - entityManager.EntitySysManager.GetEntitySystem().ChangeModifiers(puddleUid, speedModifier, slow); + entityManager.EntitySysManager.GetEntitySystem().ChangeModifiers(puddleUid, speedModifier, slow); return reactVolume; } diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index c0e8847ae98..46214c7ed8c 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -54,7 +54,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly SharedPopupSystem _popups = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly StepTriggerSystem _stepTrigger = default!; - [Dependency] private readonly SlowContactsSystem _slowContacts = default!; + [Dependency] private readonly SpeedModifierContactsSystem _speedModContacts = default!; [Dependency] private readonly TileFrictionController _tile = default!; [ValidatePrototypeId] @@ -435,13 +435,13 @@ private void UpdateSlow(EntityUid uid, Solution solution) if (maxViscosity > 0) { - var comp = EnsureComp(uid); + var comp = EnsureComp(uid); var speed = 1 - maxViscosity; - _slowContacts.ChangeModifiers(uid, speed, comp); + _speedModContacts.ChangeModifiers(uid, speed, comp); } else { - RemComp(uid); + RemComp(uid); } } diff --git a/Content.Shared/Movement/Components/SlowedByContactComponent.cs b/Content.Shared/Movement/Components/SpeedModifiedByContactComponent.cs similarity index 80% rename from Content.Shared/Movement/Components/SlowedByContactComponent.cs rename to Content.Shared/Movement/Components/SpeedModifiedByContactComponent.cs index eafefefee18..4f791e13cab 100644 --- a/Content.Shared/Movement/Components/SlowedByContactComponent.cs +++ b/Content.Shared/Movement/Components/SpeedModifiedByContactComponent.cs @@ -7,6 +7,6 @@ namespace Content.Shared.Movement.Components; /// Exists just to listen to a single event. What a life. /// [NetworkedComponent, RegisterComponent] // must be networked to properly predict adding & removal -public sealed partial class SlowedByContactComponent : Component +public sealed partial class SpeedModifiedByContactComponent : Component { } diff --git a/Content.Shared/Movement/Components/SlowContactsComponent.cs b/Content.Shared/Movement/Components/SpeedModifierContactsComponent.cs similarity index 83% rename from Content.Shared/Movement/Components/SlowContactsComponent.cs rename to Content.Shared/Movement/Components/SpeedModifierContactsComponent.cs index 00cbc55d198..73bb0690fdc 100644 --- a/Content.Shared/Movement/Components/SlowContactsComponent.cs +++ b/Content.Shared/Movement/Components/SpeedModifierContactsComponent.cs @@ -6,8 +6,8 @@ namespace Content.Shared.Movement.Components; [NetworkedComponent, RegisterComponent] [AutoGenerateComponentState] -[Access(typeof(SlowContactsSystem))] -public sealed partial class SlowContactsComponent : Component +[Access(typeof(SpeedModifierContactsSystem))] +public sealed partial class SpeedModifierContactsComponent : Component { [DataField("walkSpeedModifier"), ViewVariables(VVAccess.ReadWrite)] [AutoNetworkedField] diff --git a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs index b104c549e69..b086bc0e05f 100644 --- a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs +++ b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs @@ -10,7 +10,7 @@ public sealed class FrictionContactsSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; - // Comment copied from "original" SlowContactsSystem.cs + // Comment copied from "original" SlowContactsSystem.cs (now SpeedModifierContactsSystem.cs) // TODO full-game-save // Either these need to be processed before a map is saved, or slowed/slowing entities need to update on init. private HashSet _toUpdate = new(); diff --git a/Content.Shared/Movement/Systems/SlowContactsSystem.cs b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs similarity index 59% rename from Content.Shared/Movement/Systems/SlowContactsSystem.cs rename to Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs index 1ee145075f6..f9f6b82bb18 100644 --- a/Content.Shared/Movement/Systems/SlowContactsSystem.cs +++ b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs @@ -5,7 +5,7 @@ namespace Content.Shared.Movement.Systems; -public sealed class SlowContactsSystem : EntitySystem +public sealed class SpeedModifierContactsSystem : EntitySystem { [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; @@ -18,10 +18,10 @@ public sealed class SlowContactsSystem : EntitySystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnEntityEnter); - SubscribeLocalEvent(OnEntityExit); - SubscribeLocalEvent(MovementSpeedCheck); - SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnEntityEnter); + SubscribeLocalEvent(OnEntityExit); + SubscribeLocalEvent(MovementSpeedCheck); + SubscribeLocalEvent(OnShutdown); UpdatesAfter.Add(typeof(SharedPhysicsSystem)); } @@ -39,18 +39,18 @@ public override void Update(float frameTime) foreach (var ent in _toRemove) { - RemComp(ent); + RemComp(ent); } _toUpdate.Clear(); } - public void ChangeModifiers(EntityUid uid, float speed, SlowContactsComponent? component = null) + public void ChangeModifiers(EntityUid uid, float speed, SpeedModifierContactsComponent? component = null) { ChangeModifiers(uid, speed, speed, component); } - public void ChangeModifiers(EntityUid uid, float walkSpeed, float sprintSpeed, SlowContactsComponent? component = null) + public void ChangeModifiers(EntityUid uid, float walkSpeed, float sprintSpeed, SpeedModifierContactsComponent? component = null) { if (!Resolve(uid, ref component)) { @@ -62,7 +62,7 @@ public void ChangeModifiers(EntityUid uid, float walkSpeed, float sprintSpeed, S _toUpdate.UnionWith(_physics.GetContactingEntities(uid)); } - private void OnShutdown(EntityUid uid, SlowContactsComponent component, ComponentShutdown args) + private void OnShutdown(EntityUid uid, SpeedModifierContactsComponent component, ComponentShutdown args) { if (!TryComp(uid, out PhysicsComponent? phys)) return; @@ -71,48 +71,56 @@ private void OnShutdown(EntityUid uid, SlowContactsComponent component, Componen _toUpdate.UnionWith(_physics.GetContactingEntities(uid, phys)); } - private void MovementSpeedCheck(EntityUid uid, SlowedByContactComponent component, RefreshMovementSpeedModifiersEvent args) + private void MovementSpeedCheck(EntityUid uid, SpeedModifiedByContactComponent component, RefreshMovementSpeedModifiersEvent args) { if (!EntityManager.TryGetComponent(uid, out var physicsComponent)) return; - var walkSpeed = 1.0f; - var sprintSpeed = 1.0f; + var walkSpeed = 0.0f; + var sprintSpeed = 0.0f; bool remove = true; + var entries = 0; foreach (var ent in _physics.GetContactingEntities(uid, physicsComponent)) { - if (!TryComp(ent, out var slowContactsComponent)) + if (!TryComp(ent, out var slowContactsComponent)) continue; if (slowContactsComponent.IgnoreWhitelist != null && slowContactsComponent.IgnoreWhitelist.IsValid(uid)) continue; - walkSpeed = Math.Min(walkSpeed, slowContactsComponent.WalkSpeedModifier); - sprintSpeed = Math.Min(sprintSpeed, slowContactsComponent.SprintSpeedModifier); + walkSpeed += slowContactsComponent.WalkSpeedModifier; + sprintSpeed += slowContactsComponent.SprintSpeedModifier; remove = false; + entries++; } - args.ModifySpeed(walkSpeed, sprintSpeed); + if (entries > 0) + { + walkSpeed /= entries; + sprintSpeed /= entries; + + args.ModifySpeed(walkSpeed, sprintSpeed); + } // no longer colliding with anything if (remove) _toRemove.Add(uid); } - private void OnEntityExit(EntityUid uid, SlowContactsComponent component, ref EndCollideEvent args) + private void OnEntityExit(EntityUid uid, SpeedModifierContactsComponent component, ref EndCollideEvent args) { var otherUid = args.OtherEntity; _toUpdate.Add(otherUid); } - private void OnEntityEnter(EntityUid uid, SlowContactsComponent component, ref StartCollideEvent args) + private void OnEntityEnter(EntityUid uid, SpeedModifierContactsComponent component, ref StartCollideEvent args) { var otherUid = args.OtherEntity; if (!HasComp(otherUid)) return; - EnsureComp(otherUid); + EnsureComp(otherUid); _toUpdate.Add(otherUid); } } diff --git a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml index 5de1f0b7f93..657201912e8 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml @@ -82,7 +82,7 @@ - type: AtmosExposed growthTickChance: 0.3 spreadChance: 0.4 - - type: SlowContacts + - type: SpeedModifierContacts walkSpeedModifier: 0.2 sprintSpeedModifier: 0.2 ignoreWhitelist: @@ -123,7 +123,7 @@ - type: Kudzu spriteVariants: 5 spreadChance: 0.01 - - type: SlowContacts + - type: SpeedModifierContacts walkSpeedModifier: 0.8 sprintSpeedModifier: 0.8 ignoreWhitelist: @@ -237,7 +237,7 @@ Heat: 3 growthTickChance: 0.3 - type: AtmosExposed - - type: SlowContacts + - type: SpeedModifierContacts walkSpeedModifier: 0.3 sprintSpeedModifier: 0.3 ignoreWhitelist: diff --git a/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml b/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml index af65ac95a8a..e1f4d086d45 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml @@ -72,7 +72,7 @@ Flammable: [Touch] Extinguish: [Touch] - type: SpiderWebObject - - type: SlowContacts + - type: SpeedModifierContacts walkSpeedModifier: 0.5 sprintSpeedModifier: 0.5 ignoreWhitelist: diff --git a/Resources/Prototypes/Entities/Tiles/water.yml b/Resources/Prototypes/Entities/Tiles/water.yml index e2c0421aef3..2fd1c8547de 100644 --- a/Resources/Prototypes/Entities/Tiles/water.yml +++ b/Resources/Prototypes/Entities/Tiles/water.yml @@ -32,7 +32,7 @@ Quantity: 100 - type: DrainableSolution solution: pool - - type: SlowContacts + - type: SpeedModifierContacts walkSpeedModifier: 0.5 sprintSpeedModifier: 0.5 - type: Physics From 451badb4da2c75d85572058edc6cc5e3a6095437 Mon Sep 17 00:00:00 2001 From: Ilya246 <57039557+Ilya246@users.noreply.github.com> Date: Sun, 17 Mar 2024 06:31:41 +0400 Subject: [PATCH 18/54] Add decoy syndicate bomb to uplink (#26034) bombening --- Resources/Locale/en-US/store/uplink-catalog.ftl | 5 ++++- Resources/Prototypes/Catalog/uplink_catalog.yml | 10 ++++++++++ .../Entities/Structures/Machines/bombs.yml | 12 ++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 500bb801c4a..e73f253e85c 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -64,7 +64,10 @@ uplink-exploding-pen-name = Exploding pen uplink-exploding-pen-desc = A class IV explosive device contained within a standard pen. Comes with a 4 second fuse. uplink-exploding-syndicate-bomb-name = Syndicate Bomb -uplink-exploding-syndicate-bomb-desc = A big, anchored bomb that can create a huge explosion if not defused in time. Useful as a distraction. Has an adjustable timer with a minimum setting of 120 seconds. +uplink-exploding-syndicate-bomb-desc = A big, anchored bomb that can create a huge explosion if not defused in time. Useful as a distraction. Has an adjustable timer with a minimum setting of 180 seconds. + +uplink-exploding-syndicate-bomb-fake-name = Decoy Syndicate Bomb +uplink-exploding-syndicate-bomb-fake-desc = A training bomb carefully made to look just like the real thing. In all ways similar to a syndicate bomb, but only creates a tiny explosion. uplink-cluster-grenade-name = Cluster Grenade uplink-cluster-grenade-desc = Three explosive grenades bundled together, the grenades get launched after the 3.5 second timer runs out. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index f18deb5c0dc..3c160923451 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -254,6 +254,16 @@ tags: - NukeOpsUplink +- type: listing + id: UplinkSyndicateBombFake + name: uplink-exploding-syndicate-bomb-fake-name + description: uplink-exploding-syndicate-bomb-fake-desc + productEntity: SyndicateBombFake + cost: + Telecrystal: 4 + categories: + - UplinkExplosives + - type: listing id: UplinkClusterGrenade name: uplink-cluster-grenade-name diff --git a/Resources/Prototypes/Entities/Structures/Machines/bombs.yml b/Resources/Prototypes/Entities/Structures/Machines/bombs.yml index 580253f1189..89cadab6b1f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/bombs.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/bombs.yml @@ -127,6 +127,18 @@ intensitySlope: 3 maxIntensity: 400 +- type: entity + parent: SyndicateBomb + id: SyndicateBombFake + suffix: fake + components: + - type: Explosive + explosionType: Default + totalIntensity: 5.0 + intensitySlope: 5 + maxIntensity: 4 + canCreateVacuum: false + - type: entity parent: SyndicateBomb id: DebugHardBomb From 604704b9b45521f848641e3ecaf6a86f93e38189 Mon Sep 17 00:00:00 2001 From: LordCarve <27449516+LordCarve@users.noreply.github.com> Date: Sun, 17 Mar 2024 08:31:09 +0100 Subject: [PATCH 19/54] Obsolete `Logger` cleanup for `EntitySystem`s part 2 (#26159) * Kill the static InRangeUnOccluded * Adjusted 4 more EntitySystems that were missed. --- Content.Client/CardboardBox/CardboardBoxSystem.cs | 5 +++-- Content.Client/Popups/PopupOverlay.cs | 5 ++++- Content.Client/Popups/PopupSystem.cs | 3 +++ .../UserInterface/Systems/Chat/ChatUIController.cs | 2 +- Content.Client/Verbs/VerbSystem.cs | 8 ++++---- .../Administration/Systems/AdminVerbSystem.cs | 3 ++- .../EntitySystems/AtmosphereSystem.Monstermos.cs | 4 ++-- .../Chat/TypingIndicator/TypingIndicatorSystem.cs | 2 +- .../GameTicking/Rules/PiratesRuleSystem.cs | 4 ++-- Content.Server/NPC/Systems/NPCUtilitySystem.cs | 5 +++-- .../Pointing/EntitySystems/PointingSystem.cs | 3 ++- Content.Server/Remotes/DoorRemoteSystem.cs | 3 ++- Content.Server/Tabletop/TabletopSystem.Session.cs | 2 +- Content.Shared/Examine/ExamineSystemShared.cs | 12 ++++++------ 14 files changed, 36 insertions(+), 25 deletions(-) diff --git a/Content.Client/CardboardBox/CardboardBoxSystem.cs b/Content.Client/CardboardBox/CardboardBoxSystem.cs index 50f9de239d5..90a21d8e41b 100644 --- a/Content.Client/CardboardBox/CardboardBoxSystem.cs +++ b/Content.Client/CardboardBox/CardboardBoxSystem.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Content.Shared.CardboardBox; using Content.Shared.CardboardBox.Components; using Content.Shared.Examine; @@ -11,6 +11,7 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem { [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; public override void Initialize() { @@ -55,7 +56,7 @@ private void OnBoxEffect(PlayBoxEffectMessage msg) foreach (var mob in mobMoverEntities) { var mapPos = _transform.GetMapCoordinates(mob); - if (!ExamineSystemShared.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null)) + if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null)) continue; var ent = Spawn(box.Effect, mapPos); diff --git a/Content.Client/Popups/PopupOverlay.cs b/Content.Client/Popups/PopupOverlay.cs index 1305d8bb947..3bfa7278734 100644 --- a/Content.Client/Popups/PopupOverlay.cs +++ b/Content.Client/Popups/PopupOverlay.cs @@ -21,6 +21,7 @@ public sealed class PopupOverlay : Overlay private readonly IUserInterfaceManager _uiManager; private readonly PopupSystem _popup; private readonly PopupUIController _controller; + private readonly ExamineSystemShared _examine; private readonly ShaderInstance _shader; @@ -33,12 +34,14 @@ public PopupOverlay( IPrototypeManager protoManager, IUserInterfaceManager uiManager, PopupUIController controller, + ExamineSystemShared examine, PopupSystem popup) { _configManager = configManager; _entManager = entManager; _playerMgr = playerMgr; _uiManager = uiManager; + _examine = examine; _popup = popup; _controller = controller; @@ -81,7 +84,7 @@ private void DrawWorld(DrawingHandleScreen worldHandle, OverlayDrawArgs args, fl var distance = (mapPos.Position - args.WorldBounds.Center).Length(); // Should handle fade here too wyci. - if (!args.WorldBounds.Contains(mapPos.Position) || !ExamineSystemShared.InRangeUnOccluded(viewPos, mapPos, distance, + if (!args.WorldBounds.Contains(mapPos.Position) || !_examine.InRangeUnOccluded(viewPos, mapPos, distance, e => e == popup.InitialPos.EntityId || e == ourEntity, entMan: _entManager)) continue; diff --git a/Content.Client/Popups/PopupSystem.cs b/Content.Client/Popups/PopupSystem.cs index cf96c41241a..46c1e0cdf62 100644 --- a/Content.Client/Popups/PopupSystem.cs +++ b/Content.Client/Popups/PopupSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Examine; using Content.Shared.GameTicking; using Content.Shared.Popups; using Robust.Client.Graphics; @@ -26,6 +27,7 @@ public sealed class PopupSystem : SharedPopupSystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IUserInterfaceManager _uiManager = default!; [Dependency] private readonly IReplayRecordingManager _replayRecording = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; public IReadOnlyList WorldLabels => _aliveWorldLabels; public IReadOnlyList CursorLabels => _aliveCursorLabels; @@ -51,6 +53,7 @@ public override void Initialize() _prototype, _uiManager, _uiManager.GetUIController(), + _examine, this)); } diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index ff4972d9d08..0ad78f974e5 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -627,7 +627,7 @@ private void UpdateQueuedSpeechBubbles(FrameEventArgs delta) var otherPos = EntityManager.GetComponent(ent).MapPosition; - if (occluded && !ExamineSystemShared.InRangeUnOccluded( + if (occluded && !_examine.InRangeUnOccluded( playerPos, otherPos, 0f, (ent, player), predicate)) diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 329a8977a68..77f46a3fc97 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -20,7 +20,7 @@ namespace Content.Client.Verbs public sealed class VerbSystem : SharedVerbSystem { [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly ExamineSystem _examineSystem = default!; + [Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; @@ -77,7 +77,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true bool Predicate(EntityUid e) => e == player || entitiesUnderMouse.Contains(e); // first check the general location. - if (!_examineSystem.CanExamine(player.Value, targetPos, Predicate)) + if (!_examine.CanExamine(player.Value, targetPos, Predicate)) return false; TryComp(player.Value, out ExaminerComponent? examiner); @@ -86,7 +86,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true entities = new(); foreach (var ent in _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize)) { - if (_examineSystem.CanExamine(player.Value, targetPos, Predicate, ent, examiner)) + if (_examine.CanExamine(player.Value, targetPos, Predicate, ent, examiner)) entities.Add(ent); } } @@ -147,7 +147,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true { var entity = entities[i]; - if (!ExamineSystemShared.InRangeUnOccluded( + if (!_examine.InRangeUnOccluded( playerPos, xformQuery.GetComponent(entity).MapPosition, ExamineSystemShared.ExamineRange, diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index e678abb0c45..a3ed91f92ec 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -66,6 +66,7 @@ public sealed partial class AdminVerbSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly StationSystem _stations = default!; [Dependency] private readonly StationSpawningSystem _spawning = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; private readonly Dictionary _openSolutionUis = new(); @@ -416,7 +417,7 @@ private void AddDebugVerbs(GetVerbsEvent args) Act = () => { - var message = ExamineSystemShared.InRangeUnOccluded(args.User, args.Target) + var message = _examine.InRangeUnOccluded(args.User, args.Target) ? Loc.GetString("in-range-unoccluded-verb-on-activate-not-occluded") : Loc.GetString("in-range-unoccluded-verb-on-activate-occluded"); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs index aceda3cd332..737976a8297 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs @@ -635,14 +635,14 @@ private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, flo // Turns out: no they don't. Temporary debug checks to figure out which caller is causing problems: if (tile == null) { - Logger.Error($"Encountered null-tile in {nameof(AdjustEqMovement)}. Trace: {Environment.StackTrace}"); + Log.Error($"Encountered null-tile in {nameof(AdjustEqMovement)}. Trace: {Environment.StackTrace}"); return; } var adj = tile.AdjacentTiles[direction.ToIndex()]; if (adj == null) { var nonNull = tile.AdjacentTiles.Where(x => x != null).Count(); - Logger.Error($"Encountered null adjacent tile in {nameof(AdjustEqMovement)}. Dir: {direction}, Tile: {tile.Tile}, non-null adj count: {nonNull}, Trace: {Environment.StackTrace}"); + Log.Error($"Encountered null adjacent tile in {nameof(AdjustEqMovement)}. Dir: {direction}, Tile: {tile.Tile}, non-null adj count: {nonNull}, Trace: {Environment.StackTrace}"); return; } diff --git a/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs b/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs index 761e9456bcd..c923738930a 100644 --- a/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs +++ b/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs @@ -39,7 +39,7 @@ private void OnClientTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs var uid = args.SenderSession.AttachedEntity; if (!Exists(uid)) { - Logger.Warning($"Client {args.SenderSession} sent TypingChangedEvent without an attached entity."); + Log.Warning($"Client {args.SenderSession} sent TypingChangedEvent without an attached entity."); return; } diff --git a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs index f83d719c219..98926536b9d 100644 --- a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs @@ -201,7 +201,7 @@ private void OnPlayerSpawningEvent(RulePlayerSpawningEvent ev) if (!gridId.HasValue) { - Logger.ErrorS("pirates", $"Gridid was null when loading \"{map}\", aborting."); + Log.Error($"Gridid was null when loading \"{map}\", aborting."); foreach (var session in ops) { ev.PlayerPool.Add(session); @@ -230,7 +230,7 @@ private void OnPlayerSpawningEvent(RulePlayerSpawningEvent ev) if (spawns.Count == 0) { spawns.Add(Transform(pirates.PirateShip).Coordinates); - Logger.WarningS("pirates", $"Fell back to default spawn for pirates!"); + Log.Warning($"Fell back to default spawn for pirates!"); } for (var i = 0; i < ops.Length; i++) diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index fc483f68c3b..33941be9292 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -43,6 +43,7 @@ public sealed class NPCUtilitySystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SolutionContainerSystem _solutions = default!; [Dependency] private readonly WeldableSystem _weldable = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; private EntityQuery _puddleQuery; private EntityQuery _xformQuery; @@ -296,7 +297,7 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon { var radius = blackboard.GetValueOrDefault(NPCBlackboard.VisionRadius, EntityManager); - return ExamineSystemShared.InRangeUnOccluded(owner, targetUid, radius + 0.5f, null) ? 1f : 0f; + return _examine.InRangeUnOccluded(owner, targetUid, radius + 0.5f, null) ? 1f : 0f; } case TargetInLOSOrCurrentCon: { @@ -313,7 +314,7 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon return 1f; } - return ExamineSystemShared.InRangeUnOccluded(owner, targetUid, radius + bufferRange, null) ? 1f : 0f; + return _examine.InRangeUnOccluded(owner, targetUid, radius + bufferRange, null) ? 1f : 0f; } case TargetIsAliveCon: { diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index a7c455e6a5d..06d2d6f58b5 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -37,6 +37,7 @@ internal sealed class PointingSystem : SharedPointingSystem [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly SharedMindSystem _minds = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; private static readonly TimeSpan PointDelay = TimeSpan.FromSeconds(0.5f); @@ -100,7 +101,7 @@ public bool InRange(EntityUid pointer, EntityCoordinates coordinates) } else { - return ExamineSystemShared.InRangeUnOccluded(pointer, coordinates, 15, predicate: e => e == pointer); + return _examine.InRangeUnOccluded(pointer, coordinates, 15, predicate: e => e == pointer); } } diff --git a/Content.Server/Remotes/DoorRemoteSystem.cs b/Content.Server/Remotes/DoorRemoteSystem.cs index 9be7e5e96b4..cb3243cb837 100644 --- a/Content.Server/Remotes/DoorRemoteSystem.cs +++ b/Content.Server/Remotes/DoorRemoteSystem.cs @@ -22,6 +22,7 @@ public sealed class DoorRemoteSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; // I'm so sorry [Dependency] private readonly SharedAirlockSystem _sharedAirlockSystem = default!; public override void Initialize() @@ -67,7 +68,7 @@ private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, Befo || !TryComp(args.Target, out var doorComp) // If it isn't a door we don't use it // Only able to control doors if they are within your vision and within your max range. // Not affected by mobs or machines anymore. - || !ExamineSystemShared.InRangeUnOccluded(args.User, args.Target.Value, SharedInteractionSystem.MaxRaycastRange, null)) + || !_examine.InRangeUnOccluded(args.User, args.Target.Value, SharedInteractionSystem.MaxRaycastRange, null)) { return; } diff --git a/Content.Server/Tabletop/TabletopSystem.Session.cs b/Content.Server/Tabletop/TabletopSystem.Session.cs index f16f3f01e98..c2bb8426fc2 100644 --- a/Content.Server/Tabletop/TabletopSystem.Session.cs +++ b/Content.Server/Tabletop/TabletopSystem.Session.cs @@ -31,7 +31,7 @@ public TabletopSession EnsureSession(TabletopGameComponent tabletop) // Since this is the first time opening this session, set up the game tabletop.Setup.SetupTabletop(session, EntityManager); - Logger.Info($"Created tabletop session number {tabletop} at position {session.Position}."); + Log.Info($"Created tabletop session number {tabletop} at position {session.Position}."); return session; } diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 5a3fb872293..f792862be14 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -144,7 +144,7 @@ public bool IsOccluded(EntityUid uid) return TryComp(uid, out var eye) && eye.DrawFov; } - public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, Ignored? predicate, bool ignoreInsideBlocker = true, IEntityManager? entMan = null) + public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, Ignored? predicate, bool ignoreInsideBlocker = true, IEntityManager? entMan = null) { // No, rider. This is better. // ReSharper disable once ConvertToLocalFunction @@ -154,7 +154,7 @@ public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other return InRangeUnOccluded(origin, other, range, predicate, wrapped, ignoreInsideBlocker, entMan); } - public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, + public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, TState state, Func predicate, bool ignoreInsideBlocker = true, IEntityManager? entMan = null) { if (other.MapId != origin.MapId || @@ -171,7 +171,7 @@ public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinat if (length > MaxRaycastRange) { - Logger.Warning("InRangeUnOccluded check performed over extreme range. Limiting CollisionRay size."); + Log.Warning("InRangeUnOccluded check performed over extreme range. Limiting CollisionRay size."); length = MaxRaycastRange; } @@ -207,7 +207,7 @@ public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinat return true; } - public static bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) + public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); var originPos = entMan.GetComponent(origin).MapPosition; @@ -216,7 +216,7 @@ public static bool InRangeUnOccluded(EntityUid origin, EntityUid other, float ra return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); } - public static bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) + public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); var originPos = entMan.GetComponent(origin).MapPosition; @@ -225,7 +225,7 @@ public static bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); } - public static bool InRangeUnOccluded(EntityUid origin, MapCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) + public bool InRangeUnOccluded(EntityUid origin, MapCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); var originPos = entMan.GetComponent(origin).MapPosition; From 16a4bc2361b6910f79fa79c813873bdc4871f427 Mon Sep 17 00:00:00 2001 From: Whisper <121047731+QuietlyWhisper@users.noreply.github.com> Date: Sun, 17 Mar 2024 07:39:11 -0400 Subject: [PATCH 20/54] Update blood related tips! (#25809) --- Resources/Prototypes/Datasets/tips.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Datasets/tips.yml b/Resources/Prototypes/Datasets/tips.yml index 3caa2dcfbf8..54d0378ecd9 100644 --- a/Resources/Prototypes/Datasets/tips.yml +++ b/Resources/Prototypes/Datasets/tips.yml @@ -125,8 +125,8 @@ - "In a pinch, you can throw drinks or other reagent containers behind you to create a spill that can slip people chasing you." - "Some weapons, such as knives & shivs, have a fast attack speed." - "The jaws of life can be used to open powered doors." - - "You can drink blood to heal back some of your blood volume, albeit very inefficiently." - - "If you're not a lizard, don't drink blood! It makes you sick and you begin to take damage." + - "If you're not a human, you can drink blood to heal back some of your blood volume, albeit very inefficiently." + - "If you're a human, don't drink blood! It makes you sick and you'll begin to take damage." - "There is a chemical metabolism limit that limits the amount of reagents of a certain type you can digest at once. Certain species have higher metabolism limits, such as slimes." - "Welding without proper eye protection can cause eye damage, which must be cured with oculine." - "Zombies are very vulnerable to heat damage, making welding tools and laser guns extremely effective against them." From 077a307f28aad7754573e8ee35a139d13aec1753 Mon Sep 17 00:00:00 2001 From: LordCarve <27449516+LordCarve@users.noreply.github.com> Date: Sun, 17 Mar 2024 21:30:27 +0100 Subject: [PATCH 21/54] Unify `Content`'s `EntitySystem` logging (#26216) Use Log with generated sawmill name rather than explicitly named one for all non rule-based Content EntitySystems. --- .../Chemistry/EntitySystems/VaporSystem.cs | 6 ++---- .../DeviceNetwork/Systems/DeviceListSystem.cs | 7 ++----- .../Forensics/Systems/ForensicScannerSystem.cs | 10 +++------- Content.Server/Mapping/MappingSystem.cs | 14 ++++++-------- Content.Server/Mech/Systems/MechSystem.cs | 6 +----- .../Movement/Systems/LagCompensationSystem.cs | 8 +++----- Content.Server/NPC/Systems/NpcFactionSystem.cs | 16 +++++++--------- .../Shuttles/Systems/EmergencyShuttleSystem.cs | 9 +++------ .../StationEvents/EventManagerSystem.cs | 18 +++++++----------- .../VendingMachines/VendingMachineSystem.cs | 7 ++----- Content.Shared/Anomaly/SharedAnomalySystem.cs | 12 ++++-------- .../DeviceLinking/SharedDeviceLinkSystem.cs | 6 ++---- .../Nutrition/EntitySystems/ThirstSystem.cs | 6 +----- 13 files changed, 43 insertions(+), 82 deletions(-) diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index 2c23b8f0390..d945c1f818b 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -31,12 +31,10 @@ internal sealed class VaporSystem : EntitySystem private const float ReactTime = 0.125f; - private ISawmill _sawmill = default!; - public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("vapor"); + SubscribeLocalEvent(HandleCollide); } @@ -128,7 +126,7 @@ private void Update(float frameTime, Entity ent, Entity reagentQuantity.Quantity) { - _sawmill.Error($"Tried to tile react more than we have for reagent {reagentQuantity}. Found {reaction} and we only have {reagentQuantity.Quantity}"); + Log.Error($"Tried to tile react more than we have for reagent {reagentQuantity}. Found {reaction} and we only have {reagentQuantity.Quantity}"); reaction = reagentQuantity.Quantity; } diff --git a/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs index 978dbc0ed93..a8d40882c4d 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Server.DeviceNetwork.Components; using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork.Components; @@ -12,8 +12,6 @@ namespace Content.Server.DeviceNetwork.Systems; [UsedImplicitly] public sealed class DeviceListSystem : SharedDeviceListSystem { - private ISawmill _sawmill = default!; - [Dependency] private readonly NetworkConfiguratorSystem _configurator = default!; public override void Initialize() @@ -23,7 +21,6 @@ public override void Initialize() SubscribeLocalEvent(OnBeforeBroadcast); SubscribeLocalEvent(OnBeforePacketSent); SubscribeLocalEvent(OnMapSave); - _sawmill = Logger.GetSawmill("devicelist"); } private void OnShutdown(EntityUid uid, DeviceListComponent component, ComponentShutdown args) @@ -154,7 +151,7 @@ private void OnMapSave(BeforeSaveEvent ev) // TODO full game saves. // when full saves are supported, this should instead add data to the BeforeSaveEvent informing the // saving system that this map (or null-space entity) also needs to be included in the save. - _sawmill.Error( + Log.Error( $"Saving a device list ({ToPrettyString(uid)}) that has a reference to an entity on another map ({ToPrettyString(ent)}). Removing entity from list."); } diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index be44f9e203b..0d3168fd4e0 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -28,14 +28,10 @@ public sealed class ForensicScannerSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; - private ISawmill _sawmill = default!; - public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("forensics.scanner"); - SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnAfterInteractUsing); SubscribeLocalEvent(OnBeforeActivatableUIOpen); @@ -57,7 +53,7 @@ private void UpdateUserInterface(EntityUid uid, ForensicScannerComponent compone component.PrintReadyAt); if (!_uiSystem.TrySetUiState(uid, ForensicScannerUiKey.Key, state)) - _sawmill.Warning($"{ToPrettyString(uid)} was unable to set UI state."); + Log.Warning($"{ToPrettyString(uid)} was unable to set UI state."); } private void OnDoAfter(EntityUid uid, ForensicScannerComponent component, DoAfterEvent args) @@ -180,7 +176,7 @@ private void OnPrint(EntityUid uid, ForensicScannerComponent component, Forensic { if (!args.Session.AttachedEntity.HasValue) { - _sawmill.Warning($"{ToPrettyString(uid)} got OnPrint without Session.AttachedEntity"); + Log.Warning($"{ToPrettyString(uid)} got OnPrint without Session.AttachedEntity"); return; } @@ -200,7 +196,7 @@ private void OnPrint(EntityUid uid, ForensicScannerComponent component, Forensic if (!HasComp(printed)) { - _sawmill.Error("Printed paper did not have PaperComponent."); + Log.Error("Printed paper did not have PaperComponent."); return; } diff --git a/Content.Server/Mapping/MappingSystem.cs b/Content.Server/Mapping/MappingSystem.cs index 0678dab7ce2..28bb3afbe10 100644 --- a/Content.Server/Mapping/MappingSystem.cs +++ b/Content.Server/Mapping/MappingSystem.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using Content.Server.Administration; using Content.Shared.Administration; using Content.Shared.CCVar; @@ -32,7 +32,6 @@ public sealed class MappingSystem : EntitySystem /// private Dictionary _currentlyAutosaving = new(); - private ISawmill _sawmill = default!; private bool _autosaveEnabled; public override void Initialize() @@ -44,7 +43,6 @@ public override void Initialize() "autosave ", ToggleAutosaveCommand); - _sawmill = Logger.GetSawmill("autosave"); Subs.CVar(_cfg, CCVars.AutosaveEnabled, SetAutosaveEnabled, true); } @@ -69,7 +67,7 @@ public override void Update(float frameTime) if (!_mapManager.MapExists(map) || _mapManager.IsMapInitialized(map)) { - _sawmill.Warning($"Can't autosave map {map}; it doesn't exist, or is initialized. Removing from autosave."); + Log.Warning($"Can't autosave map {map}; it doesn't exist, or is initialized. Removing from autosave."); _currentlyAutosaving.Remove(map); return; } @@ -79,7 +77,7 @@ public override void Update(float frameTime) var path = Path.Combine(saveDir, $"{DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss")}-AUTO.yml"); _currentlyAutosaving[map] = (CalculateNextTime(), name); - _sawmill.Info($"Autosaving map {name} ({map}) to {path}. Next save in {ReadableTimeLeft(map)} seconds."); + Log.Info($"Autosaving map {name} ({map}) to {path}. Next save in {ReadableTimeLeft(map)} seconds."); _map.SaveMap(map, path); } } @@ -105,17 +103,17 @@ public void ToggleAutosave(MapId map, string? path=null) { if (!_mapManager.MapExists(map) || _mapManager.IsMapInitialized(map)) { - _sawmill.Warning("Tried to enable autosaving on non-existant or already initialized map!"); + Log.Warning("Tried to enable autosaving on non-existant or already initialized map!"); _currentlyAutosaving.Remove(map); return; } - _sawmill.Info($"Started autosaving map {path} ({map}). Next save in {ReadableTimeLeft(map)} seconds."); + Log.Info($"Started autosaving map {path} ({map}). Next save in {ReadableTimeLeft(map)} seconds."); } else { _currentlyAutosaving.Remove(map); - _sawmill.Info($"Stopped autosaving on map {map}"); + Log.Info($"Stopped autosaving on map {map}"); } } diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index eef9bcb8ecd..1012b9727df 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -39,15 +39,11 @@ public sealed partial class MechSystem : SharedMechSystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - private ISawmill _sawmill = default!; - /// public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("mech"); - SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnInsertBattery); SubscribeLocalEvent(OnMapInit); @@ -343,7 +339,7 @@ public override bool TryChangeEnergy(EntityUid uid, FixedPoint2 delta, MechCompo _battery.SetCharge(battery!.Value, batteryComp.CurrentCharge + delta.Float(), batteryComp); if (batteryComp.CurrentCharge != component.Energy) //if there's a discrepency, we have to resync them { - _sawmill.Debug($"Battery charge was not equal to mech charge. Battery {batteryComp.CurrentCharge}. Mech {component.Energy}"); + Log.Debug($"Battery charge was not equal to mech charge. Battery {batteryComp.CurrentCharge}. Mech {component.Energy}"); component.Energy = batteryComp.CurrentCharge; Dirty(component); } diff --git a/Content.Server/Movement/Systems/LagCompensationSystem.cs b/Content.Server/Movement/Systems/LagCompensationSystem.cs index 0576fe8f255..8496a8a9b90 100644 --- a/Content.Server/Movement/Systems/LagCompensationSystem.cs +++ b/Content.Server/Movement/Systems/LagCompensationSystem.cs @@ -18,12 +18,10 @@ public sealed class LagCompensationSystem : EntitySystem // Max ping I've had is 350ms from aus to spain. public static readonly TimeSpan BufferTime = TimeSpan.FromMilliseconds(750); - private ISawmill _sawmill = Logger.GetSawmill("lagcomp"); - public override void Initialize() { base.Initialize(); - _sawmill.Level = LogLevel.Info; + Log.Level = LogLevel.Info; SubscribeLocalEvent(OnLagMove); } @@ -87,13 +85,13 @@ private void OnLagMove(EntityUid uid, LagCompensationComponent component, ref Mo if (coordinates == default) { - _sawmill.Debug($"No long comp coords found, using {xform.Coordinates}"); + Log.Debug($"No long comp coords found, using {xform.Coordinates}"); coordinates = xform.Coordinates; angle = xform.LocalRotation; } else { - _sawmill.Debug($"Actual coords is {xform.Coordinates} and got {coordinates}"); + Log.Debug($"Actual coords is {xform.Coordinates} and got {coordinates}"); } return (coordinates, angle); diff --git a/Content.Server/NPC/Systems/NpcFactionSystem.cs b/Content.Server/NPC/Systems/NpcFactionSystem.cs index 3458025f193..a96067c5cf3 100644 --- a/Content.Server/NPC/Systems/NpcFactionSystem.cs +++ b/Content.Server/NPC/Systems/NpcFactionSystem.cs @@ -15,8 +15,6 @@ public sealed partial class NpcFactionSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; - private ISawmill _sawmill = default!; - /// /// To avoid prototype mutability we store an intermediary data class that gets used instead. /// @@ -25,7 +23,7 @@ public sealed partial class NpcFactionSystem : EntitySystem public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("faction"); + SubscribeLocalEvent(OnFactionStartup); SubscribeLocalEvent(OnProtoReload); @@ -70,7 +68,7 @@ public void AddFaction(EntityUid uid, string faction, bool dirty = true) { if (!_protoManager.HasIndex(faction)) { - _sawmill.Error($"Unable to find faction {faction}"); + Log.Error($"Unable to find faction {faction}"); return; } @@ -91,7 +89,7 @@ public void RemoveFaction(EntityUid uid, string faction, bool dirty = true) { if (!_protoManager.HasIndex(faction)) { - _sawmill.Error($"Unable to find faction {faction}"); + Log.Error($"Unable to find faction {faction}"); return; } @@ -214,13 +212,13 @@ public void MakeFriendly(string source, string target) { if (!_factions.TryGetValue(source, out var sourceFaction)) { - _sawmill.Error($"Unable to find faction {source}"); + Log.Error($"Unable to find faction {source}"); return; } if (!_factions.ContainsKey(target)) { - _sawmill.Error($"Unable to find faction {target}"); + Log.Error($"Unable to find faction {target}"); return; } @@ -256,13 +254,13 @@ public void MakeHostile(string source, string target) { if (!_factions.TryGetValue(source, out var sourceFaction)) { - _sawmill.Error($"Unable to find faction {source}"); + Log.Error($"Unable to find faction {source}"); return; } if (!_factions.ContainsKey(target)) { - _sawmill.Error($"Unable to find faction {target}"); + Log.Error($"Unable to find faction {target}"); return; } diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index a7df41d8877..0a83d64b48d 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -63,8 +63,6 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - private ISawmill _sawmill = default!; - private const float ShuttleSpawnBuffer = 1f; private bool _emergencyShuttleEnabled; @@ -74,7 +72,6 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem public override void Initialize() { - _sawmill = Logger.GetSawmill("shuttle.emergency"); _emergencyShuttleEnabled = _configManager.GetCVar(CCVars.EmergencyShuttleEnabled); // Don't immediately invoke as roundstart will just handle it. Subs.CVar(_configManager, CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled); @@ -384,7 +381,7 @@ private void AddCentcomm(StationCentcommComponent component) { if (component.MapEntity != null || component.Entity != null) { - _sawmill.Warning("Attempted to re-add an existing centcomm map."); + Log.Warning("Attempted to re-add an existing centcomm map."); return; } @@ -409,7 +406,7 @@ private void AddCentcomm(StationCentcommComponent component) if (string.IsNullOrEmpty(component.Map.ToString())) { - _sawmill.Warning("No CentComm map found, skipping setup."); + Log.Warning("No CentComm map found, skipping setup."); return; } @@ -484,7 +481,7 @@ private void AddEmergencyShuttle(EntityUid uid, StationEmergencyShuttleComponent if (shuttle == null) { - _sawmill.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(uid)}"); + Log.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(uid)}"); return; } diff --git a/Content.Server/StationEvents/EventManagerSystem.cs b/Content.Server/StationEvents/EventManagerSystem.cs index 8942bdaf500..261e8ca46dd 100644 --- a/Content.Server/StationEvents/EventManagerSystem.cs +++ b/Content.Server/StationEvents/EventManagerSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Server.GameTicking; using Content.Server.StationEvents.Components; using Content.Shared.CCVar; @@ -19,8 +19,6 @@ public sealed class EventManagerSystem : EntitySystem [Dependency] public readonly GameTicker GameTicker = default!; [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; //Nyano - Summary: pulls in the glimmer system. - private ISawmill _sawmill = default!; - public bool EventsEnabled { get; private set; } private void SetEnabled(bool value) => EventsEnabled = value; @@ -28,8 +26,6 @@ public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("events"); - Subs.CVar(_configurationManager, CCVars.EventsEnabled, SetEnabled, true); } @@ -43,13 +39,13 @@ public string RunRandomEvent() if (randomEvent == null) { var errStr = Loc.GetString("station-event-system-run-random-event-no-valid-events"); - _sawmill.Error(errStr); + Log.Error(errStr); return errStr; } var ent = GameTicker.AddGameRule(randomEvent); var str = Loc.GetString("station-event-system-run-event",("eventName", ToPrettyString(ent))); - _sawmill.Info(str); + Log.Info(str); return str; } @@ -59,7 +55,7 @@ public string RunRandomEvent() public string? PickRandomEvent() { var availableEvents = AvailableEvents(); - _sawmill.Info($"Picking from {availableEvents.Count} total available events"); + Log.Info($"Picking from {availableEvents.Count} total available events"); return FindEvent(availableEvents); } @@ -71,7 +67,7 @@ public string RunRandomEvent() { if (availableEvents.Count == 0) { - _sawmill.Warning("No events were available to run!"); + Log.Warning("No events were available to run!"); return null; } @@ -94,7 +90,7 @@ public string RunRandomEvent() } } - _sawmill.Error("Event was not found after weighted pick process!"); + Log.Error("Event was not found after weighted pick process!"); return null; } @@ -118,7 +114,7 @@ private Dictionary AvailableEvents(bool { if (CanRun(proto, stationEvent, playerCount, currentTime)) { - _sawmill.Debug($"Adding event {proto.ID} to possibilities"); + Log.Debug($"Adding event {proto.ID} to possibilities"); result.Add(proto, stationEvent); } } diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index 9443b0b3948..b5fa611a396 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -41,13 +41,10 @@ public sealed class VendingMachineSystem : SharedVendingMachineSystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AdvertiseSystem _advertise = default!; - private ISawmill _sawmill = default!; - public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("vending"); SubscribeLocalEvent(OnComponentMapInit); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnBreak); @@ -86,7 +83,7 @@ private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, re { if (!PrototypeManager.TryIndex(entry.ID, out var proto)) { - _sawmill.Error($"Unable to find entity prototype {entry.ID} on {ToPrettyString(uid)} vending."); + Log.Error($"Unable to find entity prototype {entry.ID} on {ToPrettyString(uid)} vending."); continue; } @@ -193,7 +190,7 @@ private void OnDoAfter(EntityUid uid, VendingMachineComponent component, DoAfter if (!TryComp(args.Args.Used, out var restockComponent)) { - _sawmill.Error($"{ToPrettyString(args.Args.User)} tried to restock {ToPrettyString(uid)} with {ToPrettyString(args.Args.Used.Value)} which did not have a VendingMachineRestockComponent."); + Log.Error($"{ToPrettyString(args.Args.User)} tried to restock {ToPrettyString(uid)} with {ToPrettyString(args.Args.Used.Value)} which did not have a VendingMachineRestockComponent."); return; } diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index 78b198be801..c335cd7b858 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -35,8 +35,6 @@ public abstract class SharedAnomalySystem : EntitySystem [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly IRobustRandom _random = default!; - private ISawmill _sawmill = default!; - public override void Initialize() { base.Initialize(); @@ -45,8 +43,6 @@ public override void Initialize() SubscribeLocalEvent(OnAttacked); SubscribeLocalEvent(OnAnomalyThrowStart); SubscribeLocalEvent(OnAnomalyThrowEnd); - - _sawmill = Logger.GetSawmill("anomaly"); } private void OnInteractHand(EntityUid uid, AnomalyComponent component, InteractHandEvent args) @@ -98,7 +94,7 @@ public void DoAnomalyPulse(EntityUid uid, AnomalyComponent? component = null) component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation; if (_net.IsServer) - _sawmill.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}"); + Log.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}"); // if we are above the growth threshold, then grow before the pulse if (component.Stability > component.GrowthThreshold) @@ -135,7 +131,7 @@ public void StartSupercriticalEvent(EntityUid uid) AdminLog.Add(LogType.Anomaly, LogImpact.Extreme, $"Anomaly {ToPrettyString(uid)} began to go supercritical."); if (_net.IsServer) - _sawmill.Info($"Anomaly is going supercritical. Entity: {ToPrettyString(uid)}"); + Log.Info($"Anomaly is going supercritical. Entity: {ToPrettyString(uid)}"); var super = AddComp(uid); super.EndTime = Timing.CurTime + super.SupercriticalDuration; @@ -161,7 +157,7 @@ public void DoAnomalySupercriticalEvent(EntityUid uid, AnomalyComponent? compone Audio.PlayPvs(component.SupercriticalSound, uid); if (_net.IsServer) - _sawmill.Info($"Raising supercritical event. Entity: {ToPrettyString(uid)}"); + Log.Info($"Raising supercritical event. Entity: {ToPrettyString(uid)}"); var ev = new AnomalySupercriticalEvent(uid); RaiseLocalEvent(uid, ref ev, true); @@ -179,7 +175,7 @@ public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool s { // Logging before resolve, in case the anomaly has deleted itself. if (_net.IsServer) - _sawmill.Info($"Ending anomaly. Entity: {ToPrettyString(uid)}"); + Log.Info($"Ending anomaly. Entity: {ToPrettyString(uid)}"); AdminLog.Add(LogType.Anomaly, supercritical ? LogImpact.High : LogImpact.Low, $"Anomaly {ToPrettyString(uid)} {(supercritical ? "went supercritical" : "decayed")}."); diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index 2ac525d154d..83aa7085897 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -13,7 +13,6 @@ public abstract class SharedDeviceLinkSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - private ISawmill _sawmill = default!; public const string InvokedPort = "link_port"; @@ -25,7 +24,6 @@ public override void Initialize() SubscribeLocalEvent(OnSinkStartup); SubscribeLocalEvent(OnSourceRemoved); SubscribeLocalEvent(OnSinkRemoved); - _sawmill = Logger.GetSawmill("devicelink"); } #region Link Validation @@ -386,12 +384,12 @@ public void RemoveSinkFromSource( if (sourceComponent == null) { - _sawmill.Error($"Attempted to remove link between {ToPrettyString(sourceUid)} and {ToPrettyString(sinkUid)}, but the source component was missing."); + Log.Error($"Attempted to remove link between {ToPrettyString(sourceUid)} and {ToPrettyString(sinkUid)}, but the source component was missing."); sinkComponent!.LinkedSources.Remove(sourceUid); } else { - _sawmill.Error($"Attempted to remove link between {ToPrettyString(sourceUid)} and {ToPrettyString(sinkUid)}, but the sink component was missing."); + Log.Error($"Attempted to remove link between {ToPrettyString(sourceUid)} and {ToPrettyString(sinkUid)}, but the sink component was missing."); sourceComponent.LinkedPorts.Remove(sourceUid); } } diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs index b070cee2831..29218f57198 100644 --- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs @@ -18,14 +18,10 @@ public sealed class ThirstSystem : EntitySystem [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly SharedJetpackSystem _jetpack = default!; - private ISawmill _sawmill = default!; - public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("thirst"); - SubscribeLocalEvent(OnRefreshMovespeed); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnRejuvenate); @@ -156,7 +152,7 @@ private void UpdateEffects(EntityUid uid, ThirstComponent component) return; default: - _sawmill.Error($"No thirst threshold found for {component.CurrentThirstThreshold}"); + Log.Error($"No thirst threshold found for {component.CurrentThirstThreshold}"); throw new ArgumentOutOfRangeException($"No thirst threshold found for {component.CurrentThirstThreshold}"); } } From 57a57af73649c5955907d7b9af10fee9bac2479b Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 17 Mar 2024 22:37:28 +0100 Subject: [PATCH 22/54] Fix barotrauma calculations The math for our pressure damage (barotrauma) system is directly taken from TG. The constants are the same and the math is almost the same. However there are two errors. 1. Pressure damage started being applied within the WARNING bounds, rather than the HAZARD bounds. This means you started taking low pressure damage at 50 kPa instead of the intended 20 kPa, and also the HUD icon didn't show "danger" like it should even if you were already taking damage. 2. The calculations for high pressure damage were wrong. These are supposed to be linearly scaled, but the function was wrong so the scaling didn't actually work properly (especially when considering the fixed bounds above). This appears to be the case because the function was taken from an incorrect comment in the original source, rather than the real math. Both of these issues are now fixed to match the TG behavior. Note that this somewhat nerfs pressure damage in non-extreme circumstances. e.g. a room at 40 kPa now gives NO pressure damage, whereas previously it would do full space damage. The description of the pressure alerts is wrong for "low" severity, but I can't be arsed to fix that right now. Alerts don't have a way to change the description depending on severity... --- .../Atmos/EntitySystems/BarotraumaSystem.cs | 103 +++++++++--------- Content.Shared/Atmos/Atmospherics.cs | 2 +- 2 files changed, 50 insertions(+), 55 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 023ac287634..2b1d6b526b2 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -224,69 +224,64 @@ public override void Update(float frameTime) pressure = MathF.Max(mixture.Pressure, 1f); } - switch (pressure) + pressure = pressure switch { - // Low pressure. - case <= Atmospherics.WarningLowPressure: - pressure = GetFeltLowPressure(uid, barotrauma, pressure); + // Adjust pressure based on equipment. Works differently depending on if it's "high" or "low". + <= Atmospherics.WarningLowPressure => GetFeltLowPressure(uid, barotrauma, pressure), + >= Atmospherics.WarningHighPressure => GetFeltHighPressure(uid, barotrauma, pressure), + _ => pressure + }; - if (pressure > Atmospherics.WarningLowPressure) - goto default; - - // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. - _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false); - - if (!barotrauma.TakingDamage) - { - barotrauma.TakingDamage = true; - _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking low pressure damage"); - } - - if (pressure <= Atmospherics.HazardLowPressure) - { - _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 2); - break; - } - - _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 1); - break; + if (pressure <= Atmospherics.HazardLowPressure) + { + // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. + _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false); - // High pressure. - case >= Atmospherics.WarningHighPressure: - pressure = GetFeltHighPressure(uid, barotrauma, pressure); + if (!barotrauma.TakingDamage) + { + barotrauma.TakingDamage = true; + _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking low pressure damage"); + } - if (pressure < Atmospherics.WarningHighPressure) - goto default; + _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 2); + } + else if (pressure >= Atmospherics.HazardHighPressure) + { + var damageScale = MathF.Min(((pressure / Atmospherics.HazardHighPressure) - 1) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage); - var damageScale = MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage); + // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. + _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false); - // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. - _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false); + if (!barotrauma.TakingDamage) + { + barotrauma.TakingDamage = true; + _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking high pressure damage"); + } - if (!barotrauma.TakingDamage) - { - barotrauma.TakingDamage = true; - _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking high pressure damage"); - } + _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 2); + } + else + { + // Within safe pressure limits + if (barotrauma.TakingDamage) + { + barotrauma.TakingDamage = false; + _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} stopped taking pressure damage"); + } - if (pressure >= Atmospherics.HazardHighPressure) - { - _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 2); + // Set correct alert. + switch (pressure) + { + case <= Atmospherics.WarningLowPressure: + _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 1); break; - } - - _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 1); - break; - - // Normal pressure. - default: - if (barotrauma.TakingDamage) - { - barotrauma.TakingDamage = false; - _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} stopped taking pressure damage"); - } - _alertsSystem.ClearAlertCategory(uid, AlertCategory.Pressure); - break; + case >= Atmospherics.WarningHighPressure: + _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 1); + break; + default: + _alertsSystem.ClearAlertCategory(uid, AlertCategory.Pressure); + break; + } } } } diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index 7460e08e46f..c56edd205b7 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -276,7 +276,7 @@ static Atmospherics() public const float HazardLowPressure = 20f; /// - /// The amount of pressure damage someone takes is equal to (pressure / HAZARD_HIGH_PRESSURE)*PRESSURE_DAMAGE_COEFFICIENT, + /// The amount of pressure damage someone takes is equal to ((pressure / HAZARD_HIGH_PRESSURE) - 1)*PRESSURE_DAMAGE_COEFFICIENT, /// with the maximum of MaxHighPressureDamage. /// public const float PressureDamageCoefficient = 4; From 0979471d8e36f649d99cd0a5450f04cdd62ebe5c Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 17 Mar 2024 22:38:48 +0100 Subject: [PATCH 23/54] Increase air alarm low pressure thresholds. If a room's pressure drops it can become unbreathable long before people start taking pressure damage, and so also long before the air alarm starts reporting a danger state. I've increased the values so that the air alarm should show danger if the pressure becomes so low as to be unbreathable. --- Resources/Prototypes/Atmospherics/thresholds.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/Atmospherics/thresholds.yml b/Resources/Prototypes/Atmospherics/thresholds.yml index 9b09d64a10c..bd7df23841f 100644 --- a/Resources/Prototypes/Atmospherics/thresholds.yml +++ b/Resources/Prototypes/Atmospherics/thresholds.yml @@ -12,13 +12,14 @@ - type: alarmThreshold id: stationPressure upperBound: !type:AlarmThresholdSetting - threshold: 550 # as defined in Atmospherics.cs + threshold: 550 # HazardHighPressure from Atmospherics.cs lowerBound: !type:AlarmThresholdSetting - threshold: 20 # as defined in Atmospherics.cs + # Actual low pressure damage threshold is at 20 kPa, but below ~85 kPa you can't breathe due to lack of oxygen. + threshold: 85 upperWarnAround: !type:AlarmThresholdSetting - threshold: 0.7 + threshold: 0.7 # 385 kPa, WarningHighPressure from Atmospherics.cs lowerWarnAround: !type:AlarmThresholdSetting - threshold: 2.5 + threshold: 1.05 # ~90 kPa # a reminder that all of these are percentages (where 1 is 100%), # so 0.01 is 1%, From ab0d3e969caf30ff755b3b7f9ae90b4c0e8607a5 Mon Sep 17 00:00:00 2001 From: potato1234_x <79580518+potato1234x@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:15:45 +1000 Subject: [PATCH 24/54] Make puddle smoothing better (#26171) puddles --- Resources/Textures/Fluids/puddle.rsi/meta.json | 2 +- .../Textures/Fluids/puddle.rsi/splat10.png | Bin 861 -> 687 bytes .../Textures/Fluids/puddle.rsi/splat11.png | Bin 298 -> 273 bytes .../Textures/Fluids/puddle.rsi/splat12.png | Bin 670 -> 706 bytes .../Textures/Fluids/puddle.rsi/splat13.png | Bin 284 -> 276 bytes .../Textures/Fluids/puddle.rsi/splat14.png | Bin 269 -> 260 bytes Resources/Textures/Fluids/puddle.rsi/splat3.png | Bin 670 -> 715 bytes Resources/Textures/Fluids/puddle.rsi/splat5.png | Bin 904 -> 716 bytes Resources/Textures/Fluids/puddle.rsi/splat6.png | Bin 866 -> 701 bytes Resources/Textures/Fluids/puddle.rsi/splat7.png | Bin 344 -> 326 bytes Resources/Textures/Fluids/puddle.rsi/splat9.png | Bin 860 -> 675 bytes 11 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Fluids/puddle.rsi/meta.json b/Resources/Textures/Fluids/puddle.rsi/meta.json index b47a24b44d9..5e0b79b3143 100644 --- a/Resources/Textures/Fluids/puddle.rsi/meta.json +++ b/Resources/Textures/Fluids/puddle.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Made by Alekshhh on github", + "copyright": "Made by Alekshhh and modified by potato1234x on github", "states": [ { "name": "splata" diff --git a/Resources/Textures/Fluids/puddle.rsi/splat10.png b/Resources/Textures/Fluids/puddle.rsi/splat10.png index 2b2abe9c32f6e92e0881a6ac1fa63c6b3a60290f..96bd6b8cc21daaef5e596346ca1d449ea52ebaa7 100644 GIT binary patch delta 674 zcmV;T0$u&x2CoH>8Gi!+002a!ipBr{0fT8nLr_UWLm*IcZ)Rz1WdHyu&y|rmNCQC_ zg})?Tcwk`=5sNLNm4c{uRQ>M7w<;Z8>`D15JU zqrfkUGd>4ZX4px@hJ?eyTBd}J5;hkdOSn%slCo^c4<-woQoN?v%C49{_f#*eU$Cd* zN|)KjC}T`8iGPN{3QJ_k(7P{|8{lV{U-E?nH zA*ntJl~11c)(+KM@U`iApNF3Jbp)YDxUh@<FuCD0l=l}o!)~uDT00004bW%=J|Ns90Kq{&900009 za7bBm001r`001r`0jlENk^lezs!2paR5(wS(%TZkAPfZ1CH?<@c5@K|q;cFAg`6&x z5g6m?f`0*N2@4>CSQQ~4!MiX(63aV$*h^x4MW;##>Q{{;Ot4HO_GdD9*0+@jVxQ;P z=gb0Yv~?tqNXiB(asVaVilOELDv-np?9#HS8jMJY8+a%hU?uS? z`G~4SxBVu3>UWp2gL7*0uQ(<-e5wcR`33ZMbQ{1bS}*{706a7Xs6nR=c>n+a07*qo IM6N<$g5^3gga7~l delta 849 zcmV-X1Frn91>FXa8Gi-<0047(dh`GQ0fcEoLr_UWLm+T+Z)Rz1WdHyuk$sUpNW(xJ z#a~mkq7?^=h&W`ZP8LK(9Hojyuu$3xtvZ-o`UOoIk`xz5!L{Jv$70pN#aUMeS3wZ` z0C9D3Qgo3L|Cbb6#CUMrk9YSTckck9US_Joi9Y!xG|1K!1dcDoQBBLWEY06cb6>k9zor9e;vcGPz1%thv24_i_3Fq^Yar8-L*75Ev^^_PWQryW4yF_e`_D z9|`|*eRh!1e*gdg24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2j>G81{VQ?PCL5*000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-} z0003sNq~5q7-K}#(g-2QDv6$|7UQ}TKNUY=PdAuEM{QfI}E~%$MaXD00rwj zT^vI!{FhF26mn4Ha9aGX{yG1%_|P2_Tq`FZmUfnQlQ?kdckCDLger%9%mEBN7PV|; zFWP#{Km7_2cM!kPed3YcuYa8U2`{(bXYl>WcR*nBQ{PvK417144UE&SwF}6e5xKSb zy@12bgADusGi-6;KGNaJmh0J^R@>G6pz$l?CV9q%4@F{@0G-0%>FVdQ&MBb@04tSZ Au>b%7 delta 282 zcmV+#0pne}4EcN3Hb?j*2mcobzFQe|QG}Mi7A3n&~pk0Sm$%V2p7K z;SNAW$Pg)|3k1XghUmRtAfDa;K|F(Wg0-y;*#$U&EXpkaT7OUgD#onf<_|1G@$<`esXPKC%aDgXcg07*qoM6N<$f>;4{dH?_b diff --git a/Resources/Textures/Fluids/puddle.rsi/splat12.png b/Resources/Textures/Fluids/puddle.rsi/splat12.png index a8a2e7f90c967fc30e42177244b14797fff2ca6e..cd0c9b32c7a9de389d74c8e4333e4cb45633768a 100644 GIT binary patch delta 693 zcmV;m0!sa!1;Pc88Gi!+002a!ipBr{0fT8nLr_UWLm*IcZ)Rz1WdHyu&y|rmNCQC_ zg})?Tcwk`=5sNLNm4c{uRQ>M7w<;Z8>`D15JU zqrfkUGd>4ZX4px@hJ?eyTBd}J5;hkdOSn%slCo^c4<-woQoN?v%C49{_f#*eU$Cd* zN|)KjC}T`8iGPN{3QJ_k(7P{|8{lV{U-E?nH zA*ntJl~11c)(+KM@U`iApNF3Jbp)YDxUh@<FuCD0l=l}o!)~uDT00004bW%=J|Ns90Kq{&900009 za7bBm001r`001r`0jlENk^lezy-7qtR5(xVQ%e$sAPg&T|2vycE5i6#ytNsDG)+Or z5@W#@0)GKC(?AS2bkPP(LsP0tg|0`e($O8G*hF&~9P-GjvmJ;|1LVqh0nAW? zbE}SFvI1ib9!=kFaJIgQJlCJFL3?GDGZ=pup1%^<4QRzxIvt^n zP6lOoi9Y!xG|1K!1dcDoQBBLWEY06cb6>k9zor9e;vcGPz1%thv24_i_3Fq^Yar8-L*75Ev^^_PWQryW4yF_e`_D z9|`|*eRh!1e*gdg24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2j>G81`Z?5F4IQ<000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-} z0001XNq6$|7UQ}TKNUY=PdAuEM{QfI|Ravq8eTeK*2^& z7sn6_|F@F@`5F{>oO8?nTi@axt z+?kWVJG19szdAWwA!#b3?JH41)ev*Z6GCez&+8F;>c8J-)`Dp+r#HN*)7#{6@!DKN zpMfv#cjboFyt=akR{ E0CF&Al>h($ delta 268 zcmV+n0rUQp0-OSn8Gi-<0047(dh`GQ0OLtSK~z|U?UT(3!!Qhm$>jKjW*vYZU6>g00v+H24DaN@c#hb`_pd& zh%q|&NQjw|FU#_%n7ov-6UZn4*IGM8wr#rzaRB_+TDy|Uj(=(Qa0c-+@w*BM0*C36 zCPZ;z_hV~0fD}ezbWRiJMIncTz{qj8Yql5@7t##k1f+G8Ajs%k_j2)F z(M*bK3rsN{|7yEs72Kq_9LuXc`{{Y{O0?wYmRvgil2S^;p7j$1T*8`3I{;IdpkBxp SLHzovP6b4+LSTX#C3jT- diff --git a/Resources/Textures/Fluids/puddle.rsi/splat14.png b/Resources/Textures/Fluids/puddle.rsi/splat14.png index fa2c45ae753e37081341ec65a829eede063d0167..811c4f6557f53aafd60d6454c7a70d6667be0f78 100644 GIT binary patch delta 243 zcmeBWYGIlnSllz?EDmyaVpw-h<|UBBlJ4m1$iT3%pZiZDE0E6<;1l9{ z>eQ)q>(;$^@d7AtZOznmAax}{e!>6$|7UQ}TKNUY=PdAuEM{QfI}E~%$MaXD00j#@ zT^vI!{NMI_^EDW7xG=u2Pk-(lEOxARa?ZXhr_$L>l;+E?Vp=X7c)KBtm90loLp5N| z!n{Wh0y`gieVBHQDK1E${H1B`VGA#o>3Lyi-UrUUHSLY}hjtFFz4Mye8|>b1yL>Xn m{LsPj(gN0JTxS@Y&WKMGWSRbCImc(9?F^o-elF{r5}E*1s9rt* delta 252 zcmZo+>SdZBS>@pN$viD-R0=`=61B9F^-jZ}qGH<(icG*Sx|@El3p ztuV_Y&ay^KF?Fhpje3jy=bRhc>aQ2ds2+>h^ZjYxzg6X&&dFzf%Q*e$uzbGDG@h}c z@Z3(%OHuhd)}PJP?wHI`vt-Jp%6s2qO)oPn+UNXes;2ofw$hlz2Sp@{CQnT8JGQqi z+(}k}@kDm0Z6JTkKNjD1Hsc9Z&NkEMiBx@3VEXZOZ`+(ZQMYT(O}wd7##FFsHPe%` yXTq=KBz)VpI^X{C<8#kWGwh$hz|nw&4sh$us%@+cel7;|AcLo?pUXO@geCw?Zfy7f diff --git a/Resources/Textures/Fluids/puddle.rsi/splat3.png b/Resources/Textures/Fluids/puddle.rsi/splat3.png index dbffc99bb73553f45256ee32531a840ecf54d3cc..73bd6804783db4b0e4e08160602f060c40d551ec 100644 GIT binary patch delta 702 zcmV;v0zv(r1uRQ>M7w<;Z8>`D15JU zqrfkUGd>4ZX4px@hJ?eyTBd}J5;hkdOSn%slCo^c4<-woQoN?v%C49{_f#*eU$Cd* zN|)KjC}T`8iGPN{3QJ_k(7P{|8{lV{U-E?nH zA*ntJl~11c)(+KM@U`iApNF3Jbp)YDxUh@<FuCD0l=l}o!)~uDT00004bW%=J|Ns90Kq{&900009 za7bBm001r`001r`0jlENk^lez#z{m$R5(wSlG_r*AP7TI{{Nph3E*@sy0g5rnA32n zX5$*u{C`H+e?UXpF!Lp}RsfuRQnVRokFef0TFkU&b`HRzi_S~R*tbuZ@~ke(&_ zNZ{4miUcs-!2?^o17j9&D1sq>IB3rVI8+nyFVo>joPf*;^esMswh5hS696EtKVA`c znVZtG^v;tNk!c11OtTW k__Q*5AfOFwYpAekA3Z7rk5Q~tUH||907*qoM6N<$foi9Y!xG|1K!1dcDoQBBLWEY06cb6>k9zor9e;vcGPz1%thv24_i_3Fq^Yar8-L*75Ev^^_PWQryW4yF_e`_D z9|`|*eRh!1e*gdg24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2j>G81`Zf}B|2jO000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-} z0001XNqpe ra!|7cvD$SZTvf1dLfQ?K{~f<7`!8n~0D0;F015yANkvXXu0mjfA)6JR diff --git a/Resources/Textures/Fluids/puddle.rsi/splat5.png b/Resources/Textures/Fluids/puddle.rsi/splat5.png index ca044c828558046c2fe867cf6b4eb3c4cc5a8841..97e00030222e5e59b90c92ba31819bf644d2d9f4 100644 GIT binary patch delta 703 zcmV;w0zmzU2h0VK8Gi!+002a!ipBr{0fT8nLr_UWLm*IcZ)Rz1WdHyu&y|rmNCQC_ zg})?Tcwk`=5sNLNm4c{uRQ>M7w<;Z8>`D15JU zqrfkUGd>4ZX4px@hJ?eyTBd}J5;hkdOSn%slCo^c4<-woQoN?v%C49{_f#*eU$Cd* zN|)KjC}T`8iGPN{3QJ_k(7P{|8{lV{U-E?nH zA*ntJl~11c)(+KM@U`iApNF3Jbp)YDxUh@<FuCD0l=l}o!)~uDT00004bW%=J|Ns90Kq{&900009 za7bBm001r`001r`0jlENk^lez$4Nv%R5(wKk_i&UAP7UL-2cu$VNvS#o9QT;C(v3E zc^*75tbY!QWmJk|aZx#~JT@TXy1rnJ3lmpUw%9mIwfp$nS zGQ`HL@4!hP0X?=9#*sMxGg;8u)bpQmu+gfD+5I#n!Vkh1}CCud*R`js8 znE*q?M)<}0*a2G+Fv9;8( delta 892 zcmV-?1B3j`1&9Zb8Gi-<0047(dh`GQ0fcEoLr_UWLm+T+Z)Rz1WdHyuk$sUpNW(xJ z#a~mkq7?^=h&W`ZP8LK(9Hojyuu$3xtvZ-o`UOoIk`xz5!L{Jv$70pN#aUMeS3wZ` z0C9D3Qgo3L|Cbb6#CUMrk9YSTckck9US_Joi9Y!xG|1K!1dcDoQBBLWEY06cb6>k9zor9e;vcGPz1%thv24_i_3Fq^Yar8-L*75Ev^^_PWQryW4yF_e`_D z9|`|*eRh!1e*gdg24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2j>G81{WmAO7Hvt000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-} z0004CNq;3=58d6x3XRU&TR2Kz2ocv8f!$ zke8R_83q-b=Q$uESZkq&{lLT41OO2)2kdhdaA4&yjN2mv_fyMMP>?h)r)Se6BPPC&Ke~|%W%x(e@5u}v&F%YZaFbpg{yOI@F zb0ATgrisNn!fgPKl+juXQc75su#;=r*Voxk4cffk|4-JfD0 zZgn<7OhAs7Rq{!L=FY1O!~t@dq&}n<#+WJ#hF&-iaW SQ`JoX0000uRQ>M7w<;Z8>`D15JU zqrfkUGd>4ZX4px@hJ?eyTBd}J5;hkdOSn%slCo^c4<-woQoN?v%C49{_f#*eU$Cd* zN|)KjC}T`8iGPN{3QJ_k(7P{|8{lV{U-E?nH zA*ntJl~11c)(+KM@U`iApNF3Jbp)YDxUh@<wj>2F9D<5%~HU)+oTMDJzOXSR_%aCGMp^M4EW{+5D)pQklGn5aEn&CLkqxH zt`*?nQDgVF3=DMzqy%7BG9mT(695jLeNR6CbMl1fpIy+WH9j0&3RwT%DFNdp(;vXo z`i<}|UMjo-JfjwY*d=!0diWy&izR$;J~IdKR4JY!z>DshBlip$Fa;nj@RcrqDYFkP Wk_NlGNfsLb0000y#f8Gi-<0047(dh`GQ0fcEoLr_UWLm+T+Z)Rz1WdHyuk$sUpNW(xJ z#a~mkq7?^=h&W`ZP8LK(9Hojyuu$3xtvZ-o`UOoIk`xz5!L{Jv$70pN#aUMeS3wZ` z0C9D3Qgo3L|Cbb6#CUMrk9YSTckck9US_Joi9Y!xG|1K!1dcDoQBBLWEY06cb6>k9zor9e;vcGPz1%thv24_i_3Fq^Yar8-L*75Ev^^_PWQryW4yF_e`_D z9|`|*eRh!1e*gdg24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2j>G81{NbvHA@}<000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-} z0003xNqX%>)Abk3=gfPXy0TIiNRdBzgT!N3?}GaLYx)`8AB zC80f#Ap-6yG0lkhRM+6%`^5jaJ$pjejD*d^d!O=sm++w#V5d8d5y5Ky|#J1Vu@56l{gm9hyneay+$S;^#Q9T2to9!zC ztWULScJ|G;VQ&@Wcp?H3VcWKMFQV3$&Xb7zp(58>W|g)ixb#iG&8lb4)4XMN7u2>+ g|1&sft^5Mya~60+0>$|bgD~Uq{1quc!Ofm7 zjv*HQTPH>e9ai9R?%nx+t~RH+&c>Pl>O&@Tvh0swIy1|1UeL9*4`YR|S^wY9WU`V~ zLHQXIzZnY;@sJ%ofNMvc|E0c-qnX6Gu|Zme6?HJ zvHI`I_{Kf9UwI>im`eCsdD0mc?F(RIIXI*83WLd!OL{vlI~|>JAeV0e*FmjvLEn2q z0=_}|8<+IP=}1Y1q%vw;D`oik(?;sR+UC56{SCj_cXqI^vhI*70eXPJ)78&qol`;+ E0893IyZ`_I delta 328 zcmV-O0k{6f0@wnO8Gi-<0047(dh`GQ0Ut?3K~z|U#h1YigD?z5ovK4{K#akGF&LCF zI4}m{z!p>=)yN4+plWRX1&JW}CH_ANjxrjCVXNz!mSxF(_Lp30V49{4PtD@cD>v9Q zjcV~r01I6Lzyna%bt(W!575^KXu~|uyB9PJd8`16!S!;mZGT%gj^pkHtu&lgfTM@v z;X6`Dh3A1(^!mQfwyGGwo!Ie8r~p(|<#Gkk7TKB-ddG@{@6ejkg;_}xrl0^*(uf`a zietk;?kO;%RtmhC$xVlgY9B@iz`CxRb#dYpaQ>w%PC^P}C}+<;Fa?OgJO*H;D1g=M zPXML&`P%^U+(Eb2X(zPGKPzcMt9Ez*nir2-08)GjeGI@#(Iqc^=Kl;}332&jVs;;R a3gQE6XQ7h~q#OhQ0000uRQ>M7w<;Z8>`D15JU zqrfkUGd>4ZX4px@hJ?eyTBd}J5;hkdOSn%slCo^c4<-woQoN?v%C49{_f#*eU$Cd* zN|)KjC}T`8iGPN{3QJ_k(7P{|8{lV{U-E?nH zA*ntJl~11c)(+KM@U`iApNF3Jbp)YDxUh@<g00004bW%=J|Ns90Kq{&900009 za7bBm001r`001r`0jlENk^lezo=HSOR5(wil3NylAP5Bo_P^(NN}9+=_rpBRq>zpx zU?Y$|zkfj;fv6I60pW#^{45y12ogZ$r4YOrKCxlfzCB0N9mjc9X(`}CYw$EOfIzhDF|9}ln}asU7T07*qoM6N<$g3tji0{{R3 delta 848 zcmV-W1F!s}1>6RZ8Gi-<0047(dh`GQ0fcEoLr_UWLm+T+Z)Rz1WdHyuk$sUpNW(xJ z#a~mkq7?^=h&W`ZP8LK(9Hojyuu$3xtvZ-o`UOoIk`xz5!L{Jv$70pN#aUMeS3wZ` z0C9D3Qgo3L|Cbb6#CUMrk9YSTckck9US_Joi9Y!xG|1K!1dcDoQBBLWEY06cb6>k9zor9e;vcGPz1%thv24_i_3Fq^Yar8-L*75Ev^^_PWQryW4yF_e`_D z9|`|*eRh!1e*gdg24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2j>G81{W0WliyGP000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-} z0003rNqO@OgK`WP9D{V>Eu2*iDj+zD$z-}wBebty z9?Dv~z>R{4z|8B@7^8CQJ7^@%xy3nma8{)vfQXxKW-j(#-p6rNVm#a*Mx~1>5#vMz z%)ExQHVq|?_4@_k#?<>aLPXEiz!i{9ico-Qt$$${2Gs-rB7zjSF-9RGc<&GE`lmeJ z7n^iM(-f3+PWNQqi3noDr&$EN_opEG7S+G+{ibawAW~8I-6DK_5+Y(Y_szQCRRAT2 zQ}?FvBmf~LH6-M$3#!fnVL{9cTI*HO6;`CR&enp84G1EFX_~%f#$r6>{f3l=bUGZL z-bwpQOh@$Oi~|59e_Cso(q4|WUCEsw3t$hVrus_^9{_~hy+*;UGSBl8-eB&&w*BQV aG5G*K)wRw>9b~Nl0000 Date: Sun, 17 Mar 2024 20:37:00 -0500 Subject: [PATCH 25/54] Criminal Record Icons below Job Icons (#26203) SS13 Criminal Record Icon Location REAL Adds a new Offset DataField to the StatusIcon Prototype. It effects status icon location on a per-pixel level. Not sure what else it could be used for, but hey, nothing wrong with generalizing. Also moves the mindshield icon priority to one, to fix an unreported bug with them covering non-job icons. --- .../StatusIcon/StatusIconOverlay.cs | 4 +- .../StatusIcon/StatusIconPrototype.cs | 6 +++ .../Prototypes/StatusEffects/security.yml | 41 +++++++++++++++++++ Resources/Prototypes/StatusIcon/antag.yml | 2 +- 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 Resources/Prototypes/StatusEffects/security.yml diff --git a/Content.Client/StatusIcon/StatusIconOverlay.cs b/Content.Client/StatusIcon/StatusIconOverlay.cs index 1cfb4c2a558..156212c55d3 100644 --- a/Content.Client/StatusIcon/StatusIconOverlay.cs +++ b/Content.Client/StatusIcon/StatusIconOverlay.cs @@ -93,7 +93,7 @@ protected override void Draw(in OverlayDrawArgs args) accOffsetL += texture.Height; countL++; } - yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetL / EyeManager.PixelsPerMeter; + yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) (accOffsetL - proto.Offset) / EyeManager.PixelsPerMeter; xOffset = -(bounds.Width + sprite.Offset.X) / 2f; } @@ -106,7 +106,7 @@ protected override void Draw(in OverlayDrawArgs args) accOffsetR += texture.Height; countR++; } - yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetR / EyeManager.PixelsPerMeter; + yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) (accOffsetR - proto.Offset) / EyeManager.PixelsPerMeter; xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float) texture.Width / EyeManager.PixelsPerMeter; } diff --git a/Content.Shared/StatusIcon/StatusIconPrototype.cs b/Content.Shared/StatusIcon/StatusIconPrototype.cs index a313f55c057..428ca950823 100644 --- a/Content.Shared/StatusIcon/StatusIconPrototype.cs +++ b/Content.Shared/StatusIcon/StatusIconPrototype.cs @@ -40,6 +40,12 @@ public int CompareTo(StatusIconData? other) /// [DataField] public StatusIconLayer Layer = StatusIconLayer.Base; + + /// + /// Offset of the status icon, up and down only. + /// + [DataField] + public int Offset = 0; } /// diff --git a/Resources/Prototypes/StatusEffects/security.yml b/Resources/Prototypes/StatusEffects/security.yml new file mode 100644 index 00000000000..51d087104d5 --- /dev/null +++ b/Resources/Prototypes/StatusEffects/security.yml @@ -0,0 +1,41 @@ +- type: statusIcon + id: SecurityIcon + abstract: true + priority: 2 + offset: 1 + locationPreference: Right + +- type: statusIcon + parent: SecurityIcon + id: SecurityIconDischarged + icon: + sprite: /Textures/Interface/Misc/security_icons.rsi + state: hud_discharged + +- type: statusIcon + parent: SecurityIcon + id: SecurityIconIncarcerated + icon: + sprite: /Textures/Interface/Misc/security_icons.rsi + state: hud_incarcerated + +- type: statusIcon + parent: SecurityIcon + id: SecurityIconParoled + icon: + sprite: /Textures/Interface/Misc/security_icons.rsi + state: hud_paroled + +- type: statusIcon + parent: SecurityIcon + id: SecurityIconSuspected + icon: + sprite: /Textures/Interface/Misc/security_icons.rsi + state: hud_suspected + +- type: statusIcon + parent: SecurityIcon + id: SecurityIconWanted + icon: + sprite: /Textures/Interface/Misc/security_icons.rsi + state: hud_wanted diff --git a/Resources/Prototypes/StatusIcon/antag.yml b/Resources/Prototypes/StatusIcon/antag.yml index 3b64517530b..757ec0354b0 100644 --- a/Resources/Prototypes/StatusIcon/antag.yml +++ b/Resources/Prototypes/StatusIcon/antag.yml @@ -21,7 +21,7 @@ - type: statusIcon id: MindShieldIcon - priority: 2 + priority: 1 locationPreference: Right layer: Mod icon: From a25aeda8e0349902957a583e8c0bd98f285e0842 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Mon, 18 Mar 2024 02:25:36 -0400 Subject: [PATCH 26/54] make romerol work on the dead (#26222) * a * review * g --- Content.Server/Zombies/ZombieSystem.cs | 12 +++++++++--- Resources/Prototypes/Reagents/toxins.yml | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index e515c377be8..bef57eceb39 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -55,7 +55,7 @@ public override void Initialize() SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnEmote, before: - new []{typeof(VocalSystem), typeof(BodyEmotesSystem)}); + new[] { typeof(VocalSystem), typeof(BodyEmotesSystem) }); SubscribeLocalEvent(OnMeleeHit); SubscribeLocalEvent(OnMobState); @@ -70,6 +70,12 @@ public override void Initialize() private void OnPendingMapInit(EntityUid uid, PendingZombieComponent component, MapInitEvent args) { + if (_mobState.IsDead(uid)) + { + ZombifyEntity(uid); + return; + } + component.NextTick = _timing.CurTime + TimeSpan.FromSeconds(1f); } @@ -196,7 +202,7 @@ private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component) var min = component.MinZombieInfectionChance; //gets a value between the max and min based on how many items the entity is wearing - var chance = (max-min) * ((total - items)/total) + min; + var chance = (max - min) * ((total - items) / total) + min; return chance; } @@ -261,7 +267,7 @@ public bool UnZombify(EntityUid source, EntityUid target, ZombieComponent? zombi _humanoidAppearance.SetBaseLayerColor(target, layer, info.Color); _humanoidAppearance.SetBaseLayerId(target, layer, info.Id); } - if(TryComp(target, out var appcomp)) + if (TryComp(target, out var appcomp)) { appcomp.EyeColor = zombiecomp.BeforeZombifiedEyeColor; } diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index c0491716d77..a5b9a3e9bcb 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -460,6 +460,7 @@ physicalDesc: reagent-physical-desc-necrotic flavor: bitter color: "#7e916e" + worksOnTheDead: true metabolisms: Medicine: effects: From 44663520aff9fd6b7052e886300eab9a5eea8e9c Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Mon, 18 Mar 2024 07:23:25 +0000 Subject: [PATCH 27/54] Move faction exception and everything it needs to shared (#25154) * move faction prototype to shared * move faction exception and member stuff to shared * fix breaking changes for random stuff * move pettable friend stuff to shared * mostly fix * final fixy * dragonops * final fixy II * use querys and fix warpspeed fish (probably) * fixer * Rrrr! --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: metalgearsloth --- Content.Client/Entry/EntryPoint.cs | 2 - .../Dragon/Components/DragonComponent.cs | 8 + Content.Server/Dragon/DragonSystem.cs | 10 + .../Components/PettableFriendComponent.cs | 23 --- .../Friends/Systems/PettableFriendSystem.cs | 50 ----- .../Rules/Components/NukeopsRuleComponent.cs | 2 +- .../Rules/Components/TraitorRuleComponent.cs | 2 +- .../GameTicking/Rules/NukeopsRuleSystem.cs | 6 +- .../GameTicking/Rules/PiratesRuleSystem.cs | 4 +- .../Rules/RevolutionaryRuleSystem.cs | 4 +- .../GameTicking/Rules/TraitorRuleSystem.cs | 2 +- .../FactionExceptionTrackerComponent.cs | 16 -- .../NPC/Components/NpcFactionPrototype.cs | 22 --- Content.Server/NPC/FactionData.cs | 13 -- .../NPC/Systems/NPCRetaliationSystem.cs | 32 ++-- .../NPC/Systems/NPCSteeringSystem.Context.cs | 2 +- .../NPC/Systems/NPCSteeringSystem.cs | 2 + .../NPC/Systems/NPCUtilitySystem.cs | 1 + .../NPC/Systems/NpcFactionSystem.Exception.cs | 130 ------------- .../Zombies/ZombieSystem.Transform.cs | 8 +- .../Components/PettableFriendComponent.cs | 24 +++ .../Friends/Systems/PettableFriendSystem.cs | 62 ++++++ .../Components/FactionExceptionComponent.cs | 11 +- .../FactionExceptionTrackerComponent.cs | 17 ++ .../Components/NpcFactionMemberComponent.cs | 28 +++ .../NPC/Prototypes/NpcFactionPrototype.cs | 32 ++++ .../NPC/Systems/NpcFactionSystem.Exception.cs | 135 +++++++++++++ .../NPC/Systems/NpcFactionSystem.cs | 180 +++++++++--------- 28 files changed, 449 insertions(+), 379 deletions(-) delete mode 100644 Content.Server/Friends/Components/PettableFriendComponent.cs delete mode 100644 Content.Server/Friends/Systems/PettableFriendSystem.cs delete mode 100644 Content.Server/NPC/Components/FactionExceptionTrackerComponent.cs delete mode 100644 Content.Server/NPC/Components/NpcFactionPrototype.cs delete mode 100644 Content.Server/NPC/FactionData.cs delete mode 100644 Content.Server/NPC/Systems/NpcFactionSystem.Exception.cs create mode 100644 Content.Shared/Friends/Components/PettableFriendComponent.cs create mode 100644 Content.Shared/Friends/Systems/PettableFriendSystem.cs rename {Content.Server => Content.Shared}/NPC/Components/FactionExceptionComponent.cs (72%) create mode 100644 Content.Shared/NPC/Components/FactionExceptionTrackerComponent.cs create mode 100644 Content.Shared/NPC/Components/NpcFactionMemberComponent.cs create mode 100644 Content.Shared/NPC/Prototypes/NpcFactionPrototype.cs create mode 100644 Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs rename {Content.Server => Content.Shared}/NPC/Systems/NpcFactionSystem.cs (51%) diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 3acc4ab1800..6e9d8c4021e 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -100,12 +100,10 @@ public override void Init() _prototypeManager.RegisterIgnore("seed"); // Seeds prototypes are server-only. _prototypeManager.RegisterIgnore("objective"); _prototypeManager.RegisterIgnore("holiday"); - _prototypeManager.RegisterIgnore("aiFaction"); _prototypeManager.RegisterIgnore("htnCompound"); _prototypeManager.RegisterIgnore("htnPrimitive"); _prototypeManager.RegisterIgnore("gameMap"); _prototypeManager.RegisterIgnore("gameMapPool"); - _prototypeManager.RegisterIgnore("npcFaction"); _prototypeManager.RegisterIgnore("lobbyBackground"); _prototypeManager.RegisterIgnore("advertisementsPack"); _prototypeManager.RegisterIgnore("gamePreset"); diff --git a/Content.Server/Dragon/Components/DragonComponent.cs b/Content.Server/Dragon/Components/DragonComponent.cs index f403979a007..80461e156af 100644 --- a/Content.Server/Dragon/Components/DragonComponent.cs +++ b/Content.Server/Dragon/Components/DragonComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.NPC.Prototypes; using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -57,5 +58,12 @@ public sealed partial class DragonComponent : Component { Params = AudioParams.Default.WithVolume(3f), }; + + /// + /// NPC faction to re-add after being zombified. + /// Prevents zombie dragon from being attacked by its own carp. + /// + [DataField] + public ProtoId Faction = "Dragon"; } } diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index 93d6bc8db0b..36df6fb6f29 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -10,6 +10,8 @@ using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Movement.Systems; +using Content.Shared.NPC.Systems; +using Content.Shared.Zombies; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.GameStates; @@ -24,6 +26,7 @@ public sealed partial class DragonSystem : EntitySystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDef = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; + [Dependency] private readonly NpcFactionSystem _faction = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly RoleSystem _role = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; @@ -55,6 +58,7 @@ public override void Initialize() SubscribeLocalEvent(OnDragonMove); SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnCreated); + SubscribeLocalEvent(OnZombified); } public override void Update(float frameTime) @@ -202,6 +206,12 @@ private void OnCreated(EntityUid uid, DragonComponent comp, ref GenericAntagCrea }, mind); } + private void OnZombified(Entity ent, ref EntityZombifiedEvent args) + { + // prevent carp attacking zombie dragon + _faction.AddFaction(ent.Owner, ent.Comp.Faction); + } + private void Roar(EntityUid uid, DragonComponent comp) { if (comp.SoundRoar != null) diff --git a/Content.Server/Friends/Components/PettableFriendComponent.cs b/Content.Server/Friends/Components/PettableFriendComponent.cs deleted file mode 100644 index fe68029a663..00000000000 --- a/Content.Server/Friends/Components/PettableFriendComponent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Server.Friends.Systems; - -namespace Content.Server.Friends.Components; - -/// -/// Pet something to become friends with it (use in hand, press Z) -/// Uses FactionExceptionComponent behind the scenes -/// -[RegisterComponent, Access(typeof(PettableFriendSystem))] -public sealed partial class PettableFriendComponent : Component -{ - /// - /// Localized popup sent when petting for the first time - /// - [DataField("successString", required: true), ViewVariables(VVAccess.ReadWrite)] - public string SuccessString = string.Empty; - - /// - /// Localized popup sent when petting multiple times - /// - [DataField("failureString", required: true), ViewVariables(VVAccess.ReadWrite)] - public string FailureString = string.Empty; -} diff --git a/Content.Server/Friends/Systems/PettableFriendSystem.cs b/Content.Server/Friends/Systems/PettableFriendSystem.cs deleted file mode 100644 index 8f70c843e70..00000000000 --- a/Content.Server/Friends/Systems/PettableFriendSystem.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Content.Server.Friends.Components; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; -using Content.Shared.Chemistry.Components; -using Content.Shared.Interaction.Events; -using Content.Shared.Popups; - -namespace Content.Server.Friends.Systems; - -public sealed class PettableFriendSystem : EntitySystem -{ - [Dependency] private readonly NpcFactionSystem _factionException = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnRehydrated); - } - - private void OnUseInHand(EntityUid uid, PettableFriendComponent comp, UseInHandEvent args) - { - var user = args.User; - if (args.Handled || !TryComp(uid, out var factionException)) - return; - - if (_factionException.IsIgnored(uid, user, factionException)) - { - _popup.PopupEntity(Loc.GetString(comp.FailureString, ("target", uid)), user, user); - return; - } - - // you have made a new friend :) - _popup.PopupEntity(Loc.GetString(comp.SuccessString, ("target", uid)), user, user); - _factionException.IgnoreEntity(uid, user, factionException); - args.Handled = true; - } - - private void OnRehydrated(EntityUid uid, PettableFriendComponent _, ref GotRehydratedEvent args) - { - // can only pet before hydrating, after that the fish cannot be negotiated with - if (!TryComp(uid, out var comp)) - return; - - var targetComp = AddComp(args.Target); - _factionException.IgnoreEntities(args.Target, comp.Ignored, targetComp); - } -} diff --git a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs index 8efd61b4694..c66a9d12a1c 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs @@ -1,8 +1,8 @@ using Content.Server.Maps; -using Content.Server.NPC.Components; using Content.Server.RoundEnd; using Content.Server.StationEvents.Events; using Content.Shared.Dataset; +using Content.Shared.NPC.Prototypes; using Content.Shared.Roles; using Robust.Shared.Map; using Robust.Shared.Prototypes; diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index 62619db76a2..e904d8a7c2a 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -1,5 +1,5 @@ -using Content.Server.NPC.Components; using Content.Shared.Dataset; +using Content.Shared.NPC.Prototypes; using Content.Shared.Random; using Content.Shared.Roles; using Robust.Shared.Audio; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index b9255bcbe4f..ea5ab9c2abe 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -7,8 +7,6 @@ using Content.Server.Ghost.Roles.Events; using Content.Server.Humanoid; using Content.Server.Mind; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; using Content.Server.Nuke; using Content.Server.NukeOps; using Content.Server.Popups; @@ -30,6 +28,8 @@ using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; using Content.Shared.Nuke; using Content.Shared.NukeOps; using Content.Shared.Preferences; @@ -474,7 +474,7 @@ private void OnRoundStart(EntityUid uid, NukeopsRuleComponent? component = null) var eligibleQuery = EntityQueryEnumerator(); while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member)) { - if (!_npcFaction.IsFactionHostile(component.Faction, eligibleUid, member)) + if (!_npcFaction.IsFactionHostile(component.Faction, (eligibleUid, member))) continue; eligible.Add((eligibleUid, eligibleComp, member)); diff --git a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs index 98926536b9d..900554d8241 100644 --- a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs @@ -4,8 +4,6 @@ using Content.Server.Cargo.Systems; using Content.Server.Chat.Managers; using Content.Server.GameTicking.Rules.Components; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; using Content.Server.Preferences.Managers; using Content.Server.Spawners.Components; using Content.Server.Station.Components; @@ -14,6 +12,8 @@ using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Mind; +using Content.Shared.NPC.Prototypes; +using Content.Shared.NPC.Systems; using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Server.GameObjects; diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index d20775c7343..5caa223c9c1 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -4,8 +4,6 @@ using Content.Server.Flash; using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; using Content.Server.Popups; using Content.Server.Revolutionary; using Content.Server.Revolutionary.Components; @@ -23,6 +21,8 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.NPC.Prototypes; +using Content.Shared.NPC.Systems; using Content.Shared.Revolutionary.Components; using Content.Shared.Roles; using Content.Shared.Stunnable; diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index fc9f0a9a9ff..769d7e0a5b5 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Antag; using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; -using Content.Server.NPC.Systems; using Content.Server.Objectives; using Content.Server.PDA.Ringer; using Content.Server.Roles; @@ -10,6 +9,7 @@ using Content.Shared.Dataset; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; +using Content.Shared.NPC.Systems; using Content.Shared.Objectives.Components; using Content.Shared.PDA; using Content.Shared.Roles; diff --git a/Content.Server/NPC/Components/FactionExceptionTrackerComponent.cs b/Content.Server/NPC/Components/FactionExceptionTrackerComponent.cs deleted file mode 100644 index 804a61b456d..00000000000 --- a/Content.Server/NPC/Components/FactionExceptionTrackerComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Server.NPC.Systems; - -namespace Content.Server.NPC.Components; - -/// -/// This is used for tracking entities stored in -/// -[RegisterComponent, Access(typeof(NpcFactionSystem))] -public sealed partial class FactionExceptionTrackerComponent : Component -{ - /// - /// entities with that are tracking this entity. - /// - [DataField("entities")] - public HashSet Entities = new(); -} diff --git a/Content.Server/NPC/Components/NpcFactionPrototype.cs b/Content.Server/NPC/Components/NpcFactionPrototype.cs deleted file mode 100644 index fe5774710af..00000000000 --- a/Content.Server/NPC/Components/NpcFactionPrototype.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Server.NPC.Components -{ - /// - /// Contains data about this faction's relations with other factions. - /// - [Prototype("npcFaction")] - public sealed partial class NpcFactionPrototype : IPrototype - { - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - - [ViewVariables(VVAccess.ReadWrite), DataField("friendly", customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List Friendly = new(); - - [ViewVariables(VVAccess.ReadWrite), DataField("hostile", customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List Hostile = new(); - } -} diff --git a/Content.Server/NPC/FactionData.cs b/Content.Server/NPC/FactionData.cs deleted file mode 100644 index b74150acc9d..00000000000 --- a/Content.Server/NPC/FactionData.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Content.Server.NPC; - -/// -/// Cached data for the faction prototype. Can be modified at runtime. -/// -public sealed class FactionData -{ - [ViewVariables] - public HashSet Friendly = new(); - - [ViewVariables] - public HashSet Hostile = new(); -} diff --git a/Content.Server/NPC/Systems/NPCRetaliationSystem.cs b/Content.Server/NPC/Systems/NPCRetaliationSystem.cs index cde8decefc4..d6b2000f329 100644 --- a/Content.Server/NPC/Systems/NPCRetaliationSystem.cs +++ b/Content.Server/NPC/Systems/NPCRetaliationSystem.cs @@ -1,7 +1,9 @@ -using Content.Server.NPC.Components; +using Content.Server.NPC.Components; using Content.Shared.CombatMode; using Content.Shared.Damage; using Content.Shared.Mobs.Components; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; using Robust.Shared.Collections; using Robust.Shared.Timing; @@ -22,37 +24,35 @@ public override void Initialize() SubscribeLocalEvent(OnDisarmed); } - private void OnDamageChanged(EntityUid uid, NPCRetaliationComponent component, DamageChangedEvent args) + private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) { if (!args.DamageIncreased) return; - if (args.Origin is not { } origin) + if (args.Origin is not {} origin) return; - TryRetaliate(uid, origin, component); + TryRetaliate(ent, origin); } - private void OnDisarmed(EntityUid uid, NPCRetaliationComponent component, DisarmedEvent args) + private void OnDisarmed(Entity ent, ref DisarmedEvent args) { - TryRetaliate(uid, args.Source, component); + TryRetaliate(ent, args.Source); } - public bool TryRetaliate(EntityUid uid, EntityUid target, NPCRetaliationComponent? component = null) + public bool TryRetaliate(Entity ent, EntityUid target) { - if (!Resolve(uid, ref component)) - return false; - // don't retaliate against inanimate objects. if (!HasComp(target)) return false; - if (_npcFaction.IsEntityFriendly(uid, target)) + // don't retaliate against the same faction + if (_npcFaction.IsEntityFriendly(ent.Owner, target)) return false; - _npcFaction.AggroEntity(uid, target); - if (component.AttackMemoryLength is { } memoryLength) - component.AttackMemories[target] = _timing.CurTime + memoryLength; + _npcFaction.AggroEntity(ent.Owner, target); + if (ent.Comp.AttackMemoryLength is {} memoryLength) + ent.Comp.AttackMemories[target] = _timing.CurTime + memoryLength; return true; } @@ -64,12 +64,14 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var retaliationComponent, out var factionException)) { + // TODO: can probably reuse this allocation and clear it foreach (var entity in new ValueList(retaliationComponent.AttackMemories.Keys)) { if (!TerminatingOrDeleted(entity) && _timing.CurTime < retaliationComponent.AttackMemories[entity]) continue; - _npcFaction.DeAggroEntity(uid, entity, factionException); + _npcFaction.DeAggroEntity((uid, factionException), entity); + // TODO: should probably remove the AttackMemory, thats the whole point of the ValueList right?? } } } diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs index 7ac6768e359..69792683ada 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs @@ -559,7 +559,7 @@ private void Separation( (mask & otherBody.CollisionLayer) == 0x0 && (layer & otherBody.CollisionMask) == 0x0 || !_factionQuery.TryGetComponent(ent, out var otherFaction) || - !_npcFaction.IsEntityFriendly(uid, ent, ourFaction, otherFaction) || + !_npcFaction.IsEntityFriendly((uid, ourFaction), (ent, otherFaction)) || // Use <= 0 so we ignore stationary friends in case. Vector2.Dot(otherBody.LinearVelocity, ourVelocity) <= 0f) { diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index c00375d6488..f04dc56bc4d 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -13,6 +13,8 @@ using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Content.Shared.NPC; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; using Content.Shared.NPC.Events; using Content.Shared.Physics; using Content.Shared.Weapons.Melee; diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 33941be9292..6793161105e 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; +using Content.Shared.NPC.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Tools.Systems; using Content.Shared.Weapons.Melee; diff --git a/Content.Server/NPC/Systems/NpcFactionSystem.Exception.cs b/Content.Server/NPC/Systems/NpcFactionSystem.Exception.cs deleted file mode 100644 index acef9005ead..00000000000 --- a/Content.Server/NPC/Systems/NpcFactionSystem.Exception.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Linq; -using Content.Server.NPC.Components; - -namespace Content.Server.NPC.Systems; - -/// -/// Prevents an NPC from attacking some entities from an enemy faction. -/// -public sealed partial class NpcFactionSystem -{ - private EntityQuery _exceptionQuery; - private EntityQuery _trackerQuery; - - public void InitializeException() - { - _exceptionQuery = GetEntityQuery(); - _trackerQuery = GetEntityQuery(); - - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnTrackerShutdown); - } - - private void OnShutdown(EntityUid uid, FactionExceptionComponent component, ComponentShutdown args) - { - foreach (var ent in component.Hostiles) - { - if (!_trackerQuery.TryGetComponent(ent, out var trackerComponent)) - continue; - trackerComponent.Entities.Remove(uid); - } - - foreach (var ent in component.Ignored) - { - if (!_trackerQuery.TryGetComponent(ent, out var trackerComponent)) - continue; - trackerComponent.Entities.Remove(uid); - } - } - - private void OnTrackerShutdown(EntityUid uid, FactionExceptionTrackerComponent component, ComponentShutdown args) - { - foreach (var ent in component.Entities) - { - if (!_exceptionQuery.TryGetComponent(ent, out var exceptionComponent)) - continue; - exceptionComponent.Ignored.Remove(uid); - exceptionComponent.Hostiles.Remove(uid); - } - } - - /// - /// Returns whether the entity from an enemy faction won't be attacked - /// - public bool IsIgnored(EntityUid uid, EntityUid target, FactionExceptionComponent? comp = null) - { - if (!Resolve(uid, ref comp, false)) - return false; - - return comp.Ignored.Contains(target); - } - - /// - /// Returns the specific hostile entities for a given entity. - /// - public IEnumerable GetHostiles(EntityUid uid, FactionExceptionComponent? comp = null) - { - if (!Resolve(uid, ref comp, false)) - return Array.Empty(); - - return comp.Hostiles; - } - - /// - /// Prevents an entity from an enemy faction from being attacked - /// - public void IgnoreEntity(EntityUid uid, EntityUid target, FactionExceptionComponent? comp = null) - { - comp ??= EnsureComp(uid); - comp.Ignored.Add(target); - EnsureComp(target).Entities.Add(uid); - } - - /// - /// Prevents a list of entities from an enemy faction from being attacked - /// - public void IgnoreEntities(EntityUid uid, IEnumerable ignored, FactionExceptionComponent? comp = null) - { - comp ??= EnsureComp(uid); - foreach (var ignore in ignored) - { - IgnoreEntity(uid, ignore, comp); - } - } - - /// - /// Makes an entity always be considered hostile. - /// - public void AggroEntity(EntityUid uid, EntityUid target, FactionExceptionComponent? comp = null) - { - comp ??= EnsureComp(uid); - comp.Hostiles.Add(target); - EnsureComp(target).Entities.Add(uid); - } - - /// - /// Makes an entity no longer be considered hostile, if it was. - /// Doesn't apply to regular faction hostilities. - /// - public void DeAggroEntity(EntityUid uid, EntityUid target, FactionExceptionComponent? comp = null) - { - if (!Resolve(uid, ref comp, false)) - return; - if (!comp.Hostiles.Remove(target) || !_trackerQuery.TryGetComponent(target, out var tracker)) - return; - tracker.Entities.Remove(uid); - } - - /// - /// Makes a list of entities no longer be considered hostile, if it was. - /// Doesn't apply to regular faction hostilities. - /// - public void AggroEntities(EntityUid uid, IEnumerable entities, FactionExceptionComponent? comp = null) - { - comp ??= EnsureComp(uid); - foreach (var ent in entities) - { - AggroEntity(uid, ent, comp); - } - } -} diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 1b4f2f1c5b2..d6540815e9e 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -27,6 +27,8 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Systems; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; using Content.Shared.Nutrition.AnimalHusbandry; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; @@ -228,11 +230,7 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) _damageable.SetAllDamage(target, damageablecomp, 0); _mobState.ChangeMobState(target, MobState.Alive); - var factionComp = EnsureComp(target); - foreach (var id in new List(factionComp.Factions)) - { - _faction.RemoveFaction(target, id); - } + _faction.ClearFactions(target, dirty: false); _faction.AddFaction(target, "Zombie"); //gives it the funny "Zombie ___" name. diff --git a/Content.Shared/Friends/Components/PettableFriendComponent.cs b/Content.Shared/Friends/Components/PettableFriendComponent.cs new file mode 100644 index 00000000000..d05e1769aec --- /dev/null +++ b/Content.Shared/Friends/Components/PettableFriendComponent.cs @@ -0,0 +1,24 @@ +using Content.Shared.Friends.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Friends.Components; + +/// +/// Pet something to become friends with it (use in hand, press Z) +/// Requires this entity to have FactionExceptionComponent to work. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(PettableFriendSystem))] +public sealed partial class PettableFriendComponent : Component +{ + /// + /// Localized popup sent when petting for the first time + /// + [DataField(required: true)] + public LocId SuccessString = string.Empty; + + /// + /// Localized popup sent when petting multiple times + /// + [DataField(required: true)] + public LocId FailureString = string.Empty; +} diff --git a/Content.Shared/Friends/Systems/PettableFriendSystem.cs b/Content.Shared/Friends/Systems/PettableFriendSystem.cs new file mode 100644 index 00000000000..00a4ddd155c --- /dev/null +++ b/Content.Shared/Friends/Systems/PettableFriendSystem.cs @@ -0,0 +1,62 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Friends.Components; +using Content.Shared.Interaction.Events; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; +using Content.Shared.Popups; +using Content.Shared.Timing; + +namespace Content.Shared.Friends.Systems; + +public sealed class PettableFriendSystem : EntitySystem +{ + [Dependency] private readonly NpcFactionSystem _factionException = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; + + private EntityQuery _exceptionQuery; + private EntityQuery _useDelayQuery; + + public override void Initialize() + { + base.Initialize(); + + _exceptionQuery = GetEntityQuery(); + _useDelayQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnRehydrated); + } + + private void OnUseInHand(Entity ent, ref UseInHandEvent args) + { + var (uid, comp) = ent; + var user = args.User; + if (args.Handled || !_exceptionQuery.TryGetComponent(uid, out var exceptionComp)) + return; + + if (_useDelayQuery.TryGetComponent(uid, out var useDelay) && !_useDelay.TryResetDelay((uid, useDelay), true)) + return; + + var exception = (uid, exceptionComp); + if (_factionException.IsIgnored(exception, user)) + { + _popup.PopupClient(Loc.GetString(comp.FailureString, ("target", uid)), user, user); + return; + } + + // you have made a new friend :) + _popup.PopupClient(Loc.GetString(comp.SuccessString, ("target", uid)), user, user); + _factionException.IgnoreEntity(exception, user); + args.Handled = true; + } + + private void OnRehydrated(Entity ent, ref GotRehydratedEvent args) + { + // can only pet before hydrating, after that the fish cannot be negotiated with + if (!TryComp(ent, out var comp)) + return; + + _factionException.IgnoreEntities(args.Target, comp.Ignored); + } +} diff --git a/Content.Server/NPC/Components/FactionExceptionComponent.cs b/Content.Shared/NPC/Components/FactionExceptionComponent.cs similarity index 72% rename from Content.Server/NPC/Components/FactionExceptionComponent.cs rename to Content.Shared/NPC/Components/FactionExceptionComponent.cs index 6abd503537c..54de0404c2f 100644 --- a/Content.Server/NPC/Components/FactionExceptionComponent.cs +++ b/Content.Shared/NPC/Components/FactionExceptionComponent.cs @@ -1,23 +1,24 @@ -using Content.Server.NPC.Systems; +using Content.Shared.NPC.Systems; +using Robust.Shared.GameStates; -namespace Content.Server.NPC.Components; +namespace Content.Shared.NPC.Components; /// /// Prevents an NPC from attacking ignored entities from enemy factions. /// Can be added to if pettable, see PettableFriendComponent. /// -[RegisterComponent, Access(typeof(NpcFactionSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem))] public sealed partial class FactionExceptionComponent : Component { /// /// Collection of entities that this NPC will refuse to attack /// - [DataField("ignored")] + [DataField] public HashSet Ignored = new(); /// /// Collection of entities that this NPC will attack, regardless of faction. /// - [DataField("hostiles")] + [DataField] public HashSet Hostiles = new(); } diff --git a/Content.Shared/NPC/Components/FactionExceptionTrackerComponent.cs b/Content.Shared/NPC/Components/FactionExceptionTrackerComponent.cs new file mode 100644 index 00000000000..f6eded7371b --- /dev/null +++ b/Content.Shared/NPC/Components/FactionExceptionTrackerComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.NPC.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.NPC.Components; + +/// +/// This is used for tracking entities stored in . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem))] +public sealed partial class FactionExceptionTrackerComponent : Component +{ + /// + /// Entities with that are tracking this entity. + /// + [DataField] + public HashSet Entities = new(); +} diff --git a/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs b/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs new file mode 100644 index 00000000000..91521e98545 --- /dev/null +++ b/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs @@ -0,0 +1,28 @@ +using Content.Shared.NPC.Prototypes; +using Content.Shared.NPC.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.NPC.Components; + +[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem))] +public sealed partial class NpcFactionMemberComponent : Component +{ + /// + /// Factions this entity is a part of. + /// + [DataField] + public HashSet> Factions = new(); + + /// + /// Cached friendly factions. + /// + [ViewVariables] + public readonly HashSet> FriendlyFactions = new(); + + /// + /// Cached hostile factions. + /// + [ViewVariables] + public readonly HashSet> HostileFactions = new(); +} diff --git a/Content.Shared/NPC/Prototypes/NpcFactionPrototype.cs b/Content.Shared/NPC/Prototypes/NpcFactionPrototype.cs new file mode 100644 index 00000000000..1dcdd751c88 --- /dev/null +++ b/Content.Shared/NPC/Prototypes/NpcFactionPrototype.cs @@ -0,0 +1,32 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.NPC.Prototypes; + +/// +/// Contains data about this faction's relations with other factions. +/// +[Prototype("npcFaction")] +public sealed partial class NpcFactionPrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + [DataField] + public List> Friendly = new(); + + [DataField] + public List> Hostile = new(); +} + +/// +/// Cached data for the faction prototype. Is modified at runtime, whereas the prototype is not. +/// +public record struct FactionData +{ + [ViewVariables] + public HashSet> Friendly; + + [ViewVariables] + public HashSet> Hostile; +} diff --git a/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs b/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs new file mode 100644 index 00000000000..e69f0c2f7ad --- /dev/null +++ b/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs @@ -0,0 +1,135 @@ +using Content.Shared.NPC.Components; +using System.Linq; + +namespace Content.Shared.NPC.Systems; + +/// +/// Prevents an NPC from attacking some entities from an enemy faction. +/// Also makes it attack some entities even if they are in neutral factions (retaliation). +/// +public sealed partial class NpcFactionSystem +{ + private EntityQuery _exceptionQuery; + private EntityQuery _trackerQuery; + + public void InitializeException() + { + _exceptionQuery = GetEntityQuery(); + _trackerQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnTrackerShutdown); + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + foreach (var uid in ent.Comp.Hostiles) + { + if (_trackerQuery.TryGetComponent(uid, out var tracker)) + tracker.Entities.Remove(ent); + } + + foreach (var uid in ent.Comp.Ignored) + { + if (_trackerQuery.TryGetComponent(uid, out var tracker)) + tracker.Entities.Remove(ent); + } + } + + private void OnTrackerShutdown(Entity ent, ref ComponentShutdown args) + { + foreach (var uid in ent.Comp.Entities) + { + if (!_exceptionQuery.TryGetComponent(uid, out var exception)) + continue; + + exception.Ignored.Remove(ent); + exception.Hostiles.Remove(ent); + } + } + + /// + /// Returns whether the entity from an enemy faction won't be attacked + /// + public bool IsIgnored(Entity ent, EntityUid target) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + return ent.Comp.Ignored.Contains(target); + } + + /// + /// Returns the specific hostile entities for a given entity. + /// + public IEnumerable GetHostiles(Entity ent) + { + if (!Resolve(ent, ref ent.Comp, false)) + return Array.Empty(); + + // evil c# + return ent.Comp!.Hostiles; + } + + /// + /// Prevents an entity from an enemy faction from being attacked + /// + public void IgnoreEntity(Entity ent, Entity target) + { + ent.Comp ??= EnsureComp(ent); + ent.Comp.Ignored.Add(target); + target.Comp ??= EnsureComp(target); + target.Comp.Entities.Add(ent); + } + + /// + /// Prevents a list of entities from an enemy faction from being attacked + /// + public void IgnoreEntities(Entity ent, IEnumerable ignored) + { + ent.Comp ??= EnsureComp(ent); + foreach (var ignore in ignored) + { + IgnoreEntity(ent, ignore); + } + } + + /// + /// Makes an entity always be considered hostile. + /// + public void AggroEntity(Entity ent, Entity target) + { + ent.Comp ??= EnsureComp(ent); + ent.Comp.Hostiles.Add(target); + target.Comp ??= EnsureComp(target); + target.Comp.Entities.Add(ent); + } + + /// + /// Makes an entity no longer be considered hostile, if it was. + /// Doesn't apply to regular faction hostilities. + /// + public void DeAggroEntity(Entity ent, EntityUid target) + { + if (!Resolve(ent, ref ent.Comp, false)) + return; + + if (!ent.Comp.Hostiles.Remove(target) || !_trackerQuery.TryGetComponent(target, out var tracker)) + return; + + tracker.Entities.Remove(ent); + } + + /// + /// Makes a list of entities no longer be considered hostile, if it was. + /// Doesn't apply to regular faction hostilities. + /// + public void AggroEntities(Entity ent, IEnumerable entities) + { + ent.Comp ??= EnsureComp(ent); + foreach (var uid in entities) + { + AggroEntity(ent, uid); + } + } +} diff --git a/Content.Server/NPC/Systems/NpcFactionSystem.cs b/Content.Shared/NPC/Systems/NpcFactionSystem.cs similarity index 51% rename from Content.Server/NPC/Systems/NpcFactionSystem.cs rename to Content.Shared/NPC/Systems/NpcFactionSystem.cs index a96067c5cf3..f0bb1945eb4 100644 --- a/Content.Server/NPC/Systems/NpcFactionSystem.cs +++ b/Content.Shared/NPC/Systems/NpcFactionSystem.cs @@ -1,10 +1,10 @@ +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Prototypes; +using Robust.Shared.Prototypes; using System.Collections.Frozen; using System.Linq; -using Content.Server.NPC.Components; -using JetBrains.Annotations; -using Robust.Shared.Prototypes; -namespace Content.Server.NPC.Systems; +namespace Content.Shared.NPC.Systems; /// /// Outlines faction relationships with each other. @@ -13,7 +13,8 @@ namespace Content.Server.NPC.Systems; public sealed partial class NpcFactionSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; /// /// To avoid prototype mutability we store an intermediary data class that gets used instead. @@ -37,122 +38,126 @@ private void OnProtoReload(PrototypesReloadedEventArgs obj) RefreshFactions(); } - private void OnFactionStartup(EntityUid uid, NpcFactionMemberComponent memberComponent, ComponentStartup args) + private void OnFactionStartup(Entity ent, ref ComponentStartup args) { - RefreshFactions(memberComponent); + RefreshFactions(ent); } /// /// Refreshes the cached factions for this component. /// - private void RefreshFactions(NpcFactionMemberComponent memberComponent) + private void RefreshFactions(Entity ent) { - memberComponent.FriendlyFactions.Clear(); - memberComponent.HostileFactions.Clear(); + ent.Comp.FriendlyFactions.Clear(); + ent.Comp.HostileFactions.Clear(); - foreach (var faction in memberComponent.Factions) + foreach (var faction in ent.Comp.Factions) { - // YAML Linter already yells about this + // YAML Linter already yells about this, don't need to log an error here if (!_factions.TryGetValue(faction, out var factionData)) continue; - memberComponent.FriendlyFactions.UnionWith(factionData.Friendly); - memberComponent.HostileFactions.UnionWith(factionData.Hostile); + ent.Comp.FriendlyFactions.UnionWith(factionData.Friendly); + ent.Comp.HostileFactions.UnionWith(factionData.Hostile); } } + /// + /// Returns whether an entity is a member of a faction. + /// + public bool IsMember(Entity ent, string faction) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + return ent.Comp.Factions.Contains(faction); + } + /// /// Adds this entity to the particular faction. /// - public void AddFaction(EntityUid uid, string faction, bool dirty = true) + public void AddFaction(Entity ent, string faction, bool dirty = true) { - if (!_protoManager.HasIndex(faction)) + if (!_proto.HasIndex(faction)) { Log.Error($"Unable to find faction {faction}"); return; } - var comp = EnsureComp(uid); - if (!comp.Factions.Add(faction)) + ent.Comp ??= EnsureComp(ent); + if (!ent.Comp.Factions.Add(faction)) return; if (dirty) - { - RefreshFactions(comp); - } + RefreshFactions((ent, ent.Comp)); } /// /// Removes this entity from the particular faction. /// - public void RemoveFaction(EntityUid uid, string faction, bool dirty = true) + public void RemoveFaction(Entity ent, string faction, bool dirty = true) { - if (!_protoManager.HasIndex(faction)) + if (!_proto.HasIndex(faction)) { Log.Error($"Unable to find faction {faction}"); return; } - if (!TryComp(uid, out var component)) + if (!Resolve(ent, ref ent.Comp, false)) return; - if (!component.Factions.Remove(faction)) + if (!ent.Comp.Factions.Remove(faction)) return; if (dirty) - { - RefreshFactions(component); - } + RefreshFactions((ent, ent.Comp)); } /// /// Remove this entity from all factions. /// - public void ClearFactions(EntityUid uid, bool dirty = true) + public void ClearFactions(Entity ent, bool dirty = true) { - if (!TryComp(uid, out var component)) + if (!Resolve(ent, ref ent.Comp, false)) return; - component.Factions.Clear(); + ent.Comp.Factions.Clear(); if (dirty) - RefreshFactions(component); + RefreshFactions((ent, ent.Comp)); } - public IEnumerable GetNearbyHostiles(EntityUid entity, float range, NpcFactionMemberComponent? component = null) + public IEnumerable GetNearbyHostiles(Entity ent, float range) { - if (!Resolve(entity, ref component, false)) + if (!Resolve(ent, ref ent.Comp1, false)) return Array.Empty(); - var hostiles = GetNearbyFactions(entity, range, component.HostileFactions); - if (TryComp(entity, out var factionException)) - { - // ignore anything from enemy faction that we are explicitly friendly towards - return hostiles - .Union(GetHostiles(entity, factionException)) - .Where(target => !IsIgnored(entity, target, factionException)); - } - - return hostiles; + var hostiles = GetNearbyFactions(ent, range, ent.Comp1.HostileFactions) + // ignore mobs that have both hostile faction and the same faction, + // otherwise having multiple factions is strictly negative + .Where(target => !IsEntityFriendly((ent, ent.Comp1), target)); + if (!Resolve(ent, ref ent.Comp2, false)) + return hostiles; + + // ignore anything from enemy faction that we are explicitly friendly towards + var faction = (ent.Owner, ent.Comp2); + return hostiles + .Union(GetHostiles(faction)) + .Where(target => !IsIgnored(faction, target)); } - [PublicAPI] - public IEnumerable GetNearbyFriendlies(EntityUid entity, float range, NpcFactionMemberComponent? component = null) + public IEnumerable GetNearbyFriendlies(Entity ent, float range) { - if (!Resolve(entity, ref component, false)) + if (!Resolve(ent, ref ent.Comp, false)) return Array.Empty(); - return GetNearbyFactions(entity, range, component.FriendlyFactions); + return GetNearbyFactions(ent, range, ent.Comp.FriendlyFactions); } - private IEnumerable GetNearbyFactions(EntityUid entity, float range, HashSet factions) + private IEnumerable GetNearbyFactions(EntityUid entity, float range, HashSet> factions) { - var xformQuery = GetEntityQuery(); - - if (!xformQuery.TryGetComponent(entity, out var entityXform)) - yield break; - - foreach (var ent in _lookup.GetEntitiesInRange(entityXform.MapPosition, range)) + var xform = Transform(entity); + foreach (var ent in _lookup.GetEntitiesInRange(_xform.GetMapCoordinates((entity, xform)), range)) { if (ent.Owner == entity) continue; @@ -164,12 +169,15 @@ private IEnumerable GetNearbyFactions(EntityUid entity, float range, } } - public bool IsEntityFriendly(EntityUid uidA, EntityUid uidB, NpcFactionMemberComponent? factionA = null, NpcFactionMemberComponent? factionB = null) + /// + /// 1-way and purely faction based, ignores faction exception. + /// + public bool IsEntityFriendly(Entity ent, Entity other) { - if (!Resolve(uidA, ref factionA, false) || !Resolve(uidB, ref factionB, false)) + if (!Resolve(ent, ref ent.Comp, false) || !Resolve(other, ref other.Comp, false)) return false; - return factionA.Factions.Overlaps(factionB.Factions) || factionA.FriendlyFactions.Overlaps(factionB.Factions); + return ent.Comp.Factions.Overlaps(other.Comp.Factions) || ent.Comp.FriendlyFactions.Overlaps(other.Comp.Factions); } public bool IsFactionFriendly(string target, string with) @@ -177,13 +185,13 @@ public bool IsFactionFriendly(string target, string with) return _factions[target].Friendly.Contains(with) && _factions[with].Friendly.Contains(target); } - public bool IsFactionFriendly(string target, EntityUid with, NpcFactionMemberComponent? factionWith = null) + public bool IsFactionFriendly(string target, Entity with) { - if (!Resolve(with, ref factionWith, false)) + if (!Resolve(with, ref with.Comp, false)) return false; - return factionWith.Factions.All(x => IsFactionFriendly(target, x)) || - factionWith.FriendlyFactions.Contains(target); + return with.Comp.Factions.All(x => IsFactionFriendly(target, x)) || + with.Comp.FriendlyFactions.Contains(target); } public bool IsFactionHostile(string target, string with) @@ -191,13 +199,13 @@ public bool IsFactionHostile(string target, string with) return _factions[target].Hostile.Contains(with) && _factions[with].Hostile.Contains(target); } - public bool IsFactionHostile(string target, EntityUid with, NpcFactionMemberComponent? factionWith = null) + public bool IsFactionHostile(string target, Entity with) { - if (!Resolve(with, ref factionWith, false)) + if (!Resolve(with, ref with.Comp, false)) return false; - return factionWith.Factions.All(x => IsFactionHostile(target, x)) || - factionWith.HostileFactions.Contains(target); + return with.Comp.Factions.All(x => IsFactionHostile(target, x)) || + with.Comp.HostileFactions.Contains(target); } public bool IsFactionNeutral(string target, string with) @@ -227,26 +235,6 @@ public void MakeFriendly(string source, string target) RefreshFactions(); } - private void RefreshFactions() - { - - _factions = _protoManager.EnumeratePrototypes().ToFrozenDictionary( - faction => faction.ID, - faction => new FactionData - { - Friendly = faction.Friendly.ToHashSet(), - Hostile = faction.Hostile.ToHashSet() - - }); - - foreach (var comp in EntityQuery(true)) - { - comp.FriendlyFactions.Clear(); - comp.HostileFactions.Clear(); - RefreshFactions(comp); - } - } - /// /// Makes the source faction hostile to the target faction, 1-way. /// @@ -268,5 +256,23 @@ public void MakeHostile(string source, string target) sourceFaction.Hostile.Add(target); RefreshFactions(); } -} + private void RefreshFactions() + { + _factions = _proto.EnumeratePrototypes().ToFrozenDictionary( + faction => faction.ID, + faction => new FactionData + { + Friendly = faction.Friendly.ToHashSet(), + Hostile = faction.Hostile.ToHashSet() + }); + + var query = AllEntityQuery(); + while (query.MoveNext(out var uid, out var comp)) + { + comp.FriendlyFactions.Clear(); + comp.HostileFactions.Clear(); + RefreshFactions((uid, comp)); + } + } +} From c11cacd869c67a073e6a73ebe35ea22ed6c35ef6 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:31:36 +1100 Subject: [PATCH 28/54] Add reckless replay load button (#26212) * Add reckless replay load button * A * More descriptive button --- Content.Client/Entry/EntryPoint.cs | 2 + .../Replay/ContentReplayPlaybackManager.cs | 62 ++++++++++++++++++- Content.Replay/Menu/ReplayMainMenu.cs | 15 +++-- Resources/Locale/en-US/replays/replays.ftl | 3 +- 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 6e9d8c4021e..7dde4557cd8 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -70,6 +70,7 @@ public sealed class EntryPoint : GameClient [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly IReplayLoadManager _replayLoad = default!; [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!; public override void Init() { @@ -191,6 +192,7 @@ private void SwitchToDefaultState(bool disconnected = false) _resourceManager, ReplayConstants.ReplayZipFolder.ToRootedPath()); + _replayMan.LastLoad = (null, ReplayConstants.ReplayZipFolder.ToRootedPath()); _replayLoad.LoadAndStartReplay(reader); } else if (_gameController.LaunchState.FromLauncher) diff --git a/Content.Client/Replay/ContentReplayPlaybackManager.cs b/Content.Client/Replay/ContentReplayPlaybackManager.cs index bc979575f58..f90731bfa75 100644 --- a/Content.Client/Replay/ContentReplayPlaybackManager.cs +++ b/Content.Client/Replay/ContentReplayPlaybackManager.cs @@ -1,8 +1,10 @@ +using System.IO.Compression; using Content.Client.Administration.Managers; using Content.Client.Launcher; using Content.Client.MainMenu; using Content.Client.Replay.Spectator; using Content.Client.Replay.UI.Loading; +using Content.Client.Stylesheets; using Content.Client.UserInterface.Systems.Chat; using Content.Shared.Chat; using Content.Shared.Effects; @@ -24,7 +26,13 @@ using Robust.Client.State; using Robust.Client.Timing; using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared; +using Robust.Shared.Configuration; +using Robust.Shared.ContentPack; using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Utility; namespace Content.Client.Replay; @@ -41,6 +49,8 @@ public sealed class ContentReplayPlaybackManager [Dependency] private readonly IClientAdminManager _adminMan = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IBaseClient _client = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IResourceManager _resMan = default!; /// /// UI state to return to when stopping a replay or loading fails. @@ -50,6 +60,13 @@ public sealed class ContentReplayPlaybackManager public bool IsScreenshotMode = false; private bool _initialized; + + /// + /// Most recently loaded file, for re-attempting the load with error tolerance. + /// Required because the zip reader auto-disposes and I'm too lazy to change it so that + /// can re-open it. + /// + public (ResPath? Zip, ResPath Folder)? LastLoad; public void Initialize() { @@ -73,11 +90,50 @@ private void LoadOverride(IReplayFileReader fileReader) private void OnFinishedLoading(Exception? exception) { - if (exception != null) + if (exception == null) + { + LastLoad = null; + return; + } + + ReturnToDefaultState(); + + // Show a popup window with the error message + var text = Loc.GetString("replay-loading-failed", ("reason", exception)); + var box = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Vertical, + Children = {new Label {Text = text}} + }; + + var popup = new DefaultWindow { Title = "Error!" }; + popup.Contents.AddChild(box); + + // Add button for attempting to re-load the replay while ignoring some errors. + if (!_cfg.GetCVar(CVars.ReplayIgnoreErrors) && LastLoad is {} last) { - ReturnToDefaultState(); - _uiMan.Popup(Loc.GetString("replay-loading-failed", ("reason", exception))); + var button = new Button + { + Text = Loc.GetString("replay-loading-retry"), + StyleClasses = { StyleBase.ButtonCaution } + }; + + button.OnPressed += _ => + { + _cfg.SetCVar(CVars.ReplayIgnoreErrors, true); + popup.Dispose(); + + IReplayFileReader reader = last.Zip == null + ? new ReplayFileReaderResources(_resMan, last.Folder) + : new ReplayFileReaderZip(new(_resMan.UserData.OpenRead(last.Zip.Value)), last.Folder); + + _loadMan.LoadAndStartReplay(reader); + }; + + box.AddChild(button); } + + popup.OpenCentered(); } public void ReturnToDefaultState() diff --git a/Content.Replay/Menu/ReplayMainMenu.cs b/Content.Replay/Menu/ReplayMainMenu.cs index 8bd99f82fb2..85c39c59dac 100644 --- a/Content.Replay/Menu/ReplayMainMenu.cs +++ b/Content.Replay/Menu/ReplayMainMenu.cs @@ -1,6 +1,7 @@ using System.IO.Compression; using System.Linq; using Content.Client.Message; +using Content.Client.Replay; using Content.Client.UserInterface.Systems.EscapeMenu; using Robust.Client; using Robust.Client.Replays.Loading; @@ -31,6 +32,7 @@ public sealed class ReplayMainScreen : State [Dependency] private readonly IGameController _controllerProxy = default!; [Dependency] private readonly IClientRobustSerializer _serializer = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; + [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!; private ReplayMainMenuControl _mainMenuControl = default!; private SelectReplayWindow? _selectWindow; @@ -207,12 +209,13 @@ private void OnFolderPressed(BaseButton.ButtonEventArgs obj) private void OnLoadPressed(BaseButton.ButtonEventArgs obj) { - if (_selected.HasValue) - { - var fileReader = new ReplayFileReaderZip( - new ZipArchive(_resMan.UserData.OpenRead(_selected.Value)), ReplayZipFolder); - _loadMan.LoadAndStartReplay(fileReader); - } + if (!_selected.HasValue) + return; + + _replayMan.LastLoad = (_selected.Value, ReplayZipFolder); + var fileReader = new ReplayFileReaderZip( + new ZipArchive(_resMan.UserData.OpenRead(_selected.Value)), ReplayZipFolder); + _loadMan.LoadAndStartReplay(fileReader); } private void RefreshReplays() diff --git a/Resources/Locale/en-US/replays/replays.ftl b/Resources/Locale/en-US/replays/replays.ftl index 560285cbb1b..7a7e551b3e9 100644 --- a/Resources/Locale/en-US/replays/replays.ftl +++ b/Resources/Locale/en-US/replays/replays.ftl @@ -6,8 +6,9 @@ replay-loading-processing = Processing Files replay-loading-spawning = Spawning Entities replay-loading-initializing = Initializing Entities replay-loading-starting= Starting Entities -replay-loading-failed = Failed to load replay: +replay-loading-failed = Failed to load replay. Error: {$reason} +replay-loading-retry = Try load with more exception tolerance - MAY CAUSE BUGS! # Main Menu replay-menu-subtext = Replay Client From f69e1eb38a285322b2db134d0a598a998dd37b03 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 18 Mar 2024 09:37:49 -0400 Subject: [PATCH 29/54] Fix borg charger error (#26230) Fix borg chargers not working --- .../Power/EntitySystems/ChargerSystem.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index b6b718455d8..db16dfa008e 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -6,7 +6,6 @@ using JetBrains.Annotations; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Content.Shared.Storage.Components; using Robust.Server.Containers; @@ -179,7 +178,7 @@ private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component) if (container.ContainedEntities.Count == 0) return CellChargerStatus.Empty; - if (!SearchForBattery(container.ContainedEntities.First(), out var heldBattery)) + if (!SearchForBattery(container.ContainedEntities[0], out _, out var heldBattery)) return CellChargerStatus.Off; if (Math.Abs(heldBattery.MaxCharge - heldBattery.CurrentCharge) < 0.01) @@ -199,27 +198,28 @@ private void TransferPower(EntityUid uid, EntityUid targetEntity, ChargerCompone if (component.Whitelist?.IsValid(targetEntity, EntityManager) == false) return; - if (!SearchForBattery(targetEntity, out var heldBattery)) + if (!SearchForBattery(targetEntity, out var batteryUid, out var heldBattery)) return; - _battery.SetCharge(targetEntity, heldBattery.CurrentCharge + component.ChargeRate * frameTime, heldBattery); + _battery.SetCharge(batteryUid.Value, heldBattery.CurrentCharge + component.ChargeRate * frameTime, heldBattery); // Just so the sprite won't be set to 99.99999% visibility if (heldBattery.MaxCharge - heldBattery.CurrentCharge < 0.01) { - _battery.SetCharge(targetEntity, heldBattery.MaxCharge, heldBattery); + _battery.SetCharge(batteryUid.Value, heldBattery.MaxCharge, heldBattery); } UpdateStatus(uid, component); } - private bool SearchForBattery(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? component) + private bool SearchForBattery(EntityUid uid, [NotNullWhen(true)] out EntityUid? batteryUid, [NotNullWhen(true)] out BatteryComponent? component) { // try get a battery directly on the inserted entity if (!TryComp(uid, out component)) { // or by checking for a power cell slot on the inserted entity - return _powerCell.TryGetBatteryFromSlot(uid, out component); + return _powerCell.TryGetBatteryFromSlot(uid, out batteryUid, out component); } + batteryUid = uid; return true; } } From fc09c4ca11d11f2038e155b8b22c678cedb5d2d9 Mon Sep 17 00:00:00 2001 From: DoutorWhite <68350815+DoutorWhite@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:56:12 -0300 Subject: [PATCH 30/54] Improves Medical Hud interface and functionality (#26027) * Improvements to the medical hud interface * Adds an icon for SSD players * Indentation fix * Update sprites, changes direction and adds new status icon. * Adds decomposing icon * Code cleaning * Changes Crit Icon sprite * Simplifies the decision structure * Changes copyright line * Changes Critical Icon sprite * Makes the Critical Icon border darker * Changes sprites to /tg/ station * Updates copyright * Makes Dead Icon animation slight faster * Code cleaning * Remove some unused imports * Code cleaning and rename icon * Minor code cleaning --- .../Overlays/ShowHealthIconsSystem.cs | 25 ++++++++++------ .../Damage/Components/DamageableComponent.cs | 16 ++++++++++- Resources/Prototypes/StatusEffects/health.yml | 27 +++++++++++++++++- .../Misc/health_icons.rsi/Critical.png | Bin 0 -> 125 bytes .../Interface/Misc/health_icons.rsi/Dead.png | Bin 0 -> 214 bytes .../Interface/Misc/health_icons.rsi/Fine.png | Bin 1120 -> 139 bytes .../Misc/health_icons.rsi/Rotting.png | Bin 0 -> 135 bytes .../Interface/Misc/health_icons.rsi/meta.json | 23 +++++++++++++-- 8 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 Resources/Textures/Interface/Misc/health_icons.rsi/Critical.png create mode 100644 Resources/Textures/Interface/Misc/health_icons.rsi/Dead.png create mode 100644 Resources/Textures/Interface/Misc/health_icons.rsi/Rotting.png diff --git a/Content.Client/Overlays/ShowHealthIconsSystem.cs b/Content.Client/Overlays/ShowHealthIconsSystem.cs index 6ed9d6a41db..a546cf4d828 100644 --- a/Content.Client/Overlays/ShowHealthIconsSystem.cs +++ b/Content.Client/Overlays/ShowHealthIconsSystem.cs @@ -1,5 +1,7 @@ +using Content.Shared.Atmos.Rotting; using Content.Shared.Damage; using Content.Shared.Inventory.Events; +using Content.Shared.Mobs.Components; using Content.Shared.Overlays; using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; @@ -17,9 +19,6 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem DamageContainers = new(); - [ValidatePrototypeId] - private const string HealthIconFine = "HealthIconFine"; - public override void Initialize() { base.Initialize(); @@ -45,18 +44,20 @@ protected override void DeactivateInternal() DamageContainers.Clear(); } - private void OnGetStatusIconsEvent(EntityUid uid, DamageableComponent damageableComponent, ref GetStatusIconsEvent args) + private void OnGetStatusIconsEvent(Entity entity, ref GetStatusIconsEvent args) { if (!IsActive || args.InContainer) return; - var healthIcons = DecideHealthIcons(damageableComponent); + var healthIcons = DecideHealthIcons(entity); args.StatusIcons.AddRange(healthIcons); } - private IReadOnlyList DecideHealthIcons(DamageableComponent damageableComponent) + private IReadOnlyList DecideHealthIcons(Entity entity) { + var damageableComponent = entity.Comp; + if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID)) { @@ -66,10 +67,16 @@ private IReadOnlyList DecideHealthIcons(DamageableComponent var result = new List(); // Here you could check health status, diseases, mind status, etc. and pick a good icon, or multiple depending on whatever. - if (damageableComponent?.DamageContainerID == "Biological" && - _prototypeMan.TryIndex(HealthIconFine, out var healthyIcon)) + if (damageableComponent?.DamageContainerID == "Biological") { - result.Add(healthyIcon); + if (TryComp(entity, out var state)) + { + // Since there is no MobState for a rotting mob, we have to deal with this case first. + if (HasComp(entity) && _prototypeMan.TryIndex(damageableComponent.RottingIcon, out var rottingIcon)) + result.Add(rottingIcon); + else if (damageableComponent.HealthIcons.TryGetValue(state.CurrentState, out var value) && _prototypeMan.TryIndex(value, out var icon)) + result.Add(icon); + } } return result; diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index f8ba661a665..be66d51e3bc 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -1,6 +1,9 @@ using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; +using Content.Shared.Mobs; +using Content.Shared.StatusIcon; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; @@ -62,7 +65,18 @@ public sealed partial class DamageableComponent : Component public FixedPoint2 TotalDamage; [DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List RadiationDamageTypeIDs = new() {"Radiation"}; + public List RadiationDamageTypeIDs = new() { "Radiation" }; + + [DataField] + public Dictionary> HealthIcons = new() + { + { MobState.Alive, "HealthIconFine" }, + { MobState.Critical, "HealthIconCritical" }, + { MobState.Dead, "HealthIconDead" }, + }; + + [DataField] + public ProtoId RottingIcon = "HealthIconRotting"; } [Serializable, NetSerializable] diff --git a/Resources/Prototypes/StatusEffects/health.yml b/Resources/Prototypes/StatusEffects/health.yml index 0a1df020c05..12c16e57f05 100644 --- a/Resources/Prototypes/StatusEffects/health.yml +++ b/Resources/Prototypes/StatusEffects/health.yml @@ -4,4 +4,29 @@ icon: sprite: /Textures/Interface/Misc/health_icons.rsi state: Fine - locationPreference: Right \ No newline at end of file + locationPreference: Right + +- type: statusIcon + id: HealthIconCritical + priority: 1 + icon: + sprite: /Textures/Interface/Misc/health_icons.rsi + state: Critical + locationPreference: Right + +- type: statusIcon + id: HealthIconDead + priority: 1 + icon: + sprite: /Textures/Interface/Misc/health_icons.rsi + state: Dead + locationPreference: Right + +- type: statusIcon + id: HealthIconRotting + priority: 1 + icon: + sprite: /Textures/Interface/Misc/health_icons.rsi + state: Rotting + locationPreference: Right + diff --git a/Resources/Textures/Interface/Misc/health_icons.rsi/Critical.png b/Resources/Textures/Interface/Misc/health_icons.rsi/Critical.png new file mode 100644 index 0000000000000000000000000000000000000000..779874eaaeb3c64047fc7f01bc618725d36d2e7b GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqE}kxqArezt`*({nC~z?IhaZ}G z=j7Z1HJzG&89UT`g_;<4tQ4ML#j;?>N@Iq%dv-aiswuB@JlEN=PImjkukq#1-&{|z Y1hPvq8@V+r1I=UbboFyt=akR{06Re@u>b%7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/health_icons.rsi/Dead.png b/Resources/Textures/Interface/Misc/health_icons.rsi/Dead.png new file mode 100644 index 0000000000000000000000000000000000000000..beceebd05cf3cb07497ad0a16f286074c94c1a12 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^20+Zg!3HGvYuC5~skxpmjv*Gk$tej52?hoqew=S$ zjoGoGxp2d2V=0M>KlKHVnanELSb3S*fS~Q5&iNLd4~d7E*KiefK4vB>{Ma|bSQ(T LtDnm{r-UW|I{QlO literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/health_icons.rsi/Fine.png b/Resources/Textures/Interface/Misc/health_icons.rsi/Fine.png index 8d07f93496e64642ba31441fa4f9881f5ca27c42..45725aa73c819040ef7f9209ce2df68bff32c47b 100644 GIT binary patch delta 111 zcmV-#0FeLS2#W!bBWM6pNklx zuRG~JIs=e2m=F=xB@K56;J6+D?sHGiXtQLc*JP4<1C|XfH_PrvFB=U0Z@#wF7|rZT Ry&?br002ovPDHLkV1fYBDqa8p delta 1100 zcmV-S1he~#0pJLbBYy*qdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+O3yslIti8 zh5u_6vjj^*JeGqERLu@%`E$U;d318SZ`+|_up+}jpOCw${`YUAfAEn-BRLDvC+`=0 zTyjY=265Rxd(Vp-*GF5wf3nl@gTSQdb-(p>`e)ef{y=ckzJEU3DQt6|I48QTJSWt? zvS2X%;+#ljxAVNGG@r}vLHhizW!h}-UhiE%Fp6P56_To~3$C%RqW!Cy(Fa^v1x~C9 zW2b)D<$dhv7`_DW7`?=9$4(yeCBDS2wGvya z`|h&yh?J+fsejv6#t4opLaF01j~4JXjKgRNEnQj zX~#sZ_0(lv;t?z8lAbk?(aH5Iya0sTjH!+Ijz$l8vwu8*RS>ih=7T0M*rDQ7I2F?Gh%86G*Wvg+zqTm2f=xTZC4NT1DZ zw)rh=aZ6h+QncXs7heLESW?NAD^jVtYSq_JV@)+Ty81k~6%oWh+L z2)fE*jSqHra)0DT=KhMCd?H63-4*1hqnqTua(hFq6DCzp3cZQWYxs1U3c|4uJm zAZF&#c`-u^T_EP3M<>M$Ep&mHr#$+dn1?*3vvuf}$8@$1-9*s^#c?nG7Zk@Kims{H zy{D4^000SaNLh0L01m_e01m_fl`9S#00007bV*G` z2j>S25iSD9jze_-002`-L_t&-(}j=04S*mJ1m}n?SQ15G2|TtK5-fotu(5@FKU;{YL>opGFfn^~8v>a(U&hhEXg8khrrPvMef+**P SP(L960000|*$lNmf+{an^LB{Ts5NEt6f literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/health_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/health_icons.rsi/meta.json index 9bc63271451..fb584d6b7f3 100644 --- a/Resources/Textures/Interface/Misc/health_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/health_icons.rsi/meta.json @@ -5,10 +5,29 @@ "y": 8 }, "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/blob/master/icons/mob/huds/hud.dmi", + "copyright": "Taken from /tg/station at commit https://github.com/tgstation/tgstation/commit/20ae083f140ac5b4da7e8bc40f95349001b6c086", "states": [ { "name": "Fine" + }, + { + "name": "Critical" + }, + { + "name": "Dead", + "delays": [ + [ + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125 + ] + ] + }, + { + "name": "Rotting" } ] -} \ No newline at end of file +} From 66e87e4749febf6ca18f18e4de871a92843c0478 Mon Sep 17 00:00:00 2001 From: Ilya246 <57039557+Ilya246@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:57:59 +0400 Subject: [PATCH 31/54] fix decoy bomb uplink category (#26205) move --- .../Prototypes/Catalog/uplink_catalog.yml | 74 ++++++++++++++++--- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 3c160923451..8a39d57d35f 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -254,16 +254,6 @@ tags: - NukeOpsUplink -- type: listing - id: UplinkSyndicateBombFake - name: uplink-exploding-syndicate-bomb-fake-name - description: uplink-exploding-syndicate-bomb-fake-desc - productEntity: SyndicateBombFake - cost: - Telecrystal: 4 - categories: - - UplinkExplosives - - type: listing id: UplinkClusterGrenade name: uplink-cluster-grenade-name @@ -398,6 +388,70 @@ categories: - UplinkUtility +- type: listing + id: UplinkDecoyDisk + name: uplink-decoy-disk-name + description: uplink-decoy-disk-desc + productEntity: NukeDiskFake + cost: + Telecrystal: 1 + categories: + - UplinkDeception + +- type: listing + id: UplinkUltrabrightLantern + name: uplink-ultrabright-lantern-name + description: uplink-ultrabright-lantern-desc + productEntity: LanternFlash + cost: + Telecrystal: 2 + categories: + - UplinkDeception + +- type: listing + id: UplinkBribe + name: uplink-bribe-name + description: uplink-bribe-desc + productEntity: BriefcaseSyndieLobbyingBundleFilled + cost: + Telecrystal: 4 + categories: + - UplinkDeception + +# - type: listing +# id: UplinkGigacancerScanner +# name: Ultragigacancer Health Analyzer +# description: Works like a normal health analyzer, other than giving everyone it scans ultragigacancer. +# productEntity: HandheldHealthAnalyzerGigacancer +# cost: +# Telecrystal: 5 +# categories: +# - UplinkDeception + +- type: listing + id: UplinkDecoyKit + name: uplink-decoy-kit-name + description: uplink-decoy-kit-desc + icon: { sprite: /Textures/Objects/Tools/Decoys/operative_decoy.rsi, state: folded } + productEntity: ClothingBackpackDuffelSyndicateDecoyKitFilled + cost: + Telecrystal: 6 + categories: + - UplinkDeception + +- type: listing + id: UplinkSyndicateBombFake + name: uplink-exploding-syndicate-bomb-fake-name + description: uplink-exploding-syndicate-bomb-fake-desc + productEntity: SyndicateBombFake + cost: + Telecrystal: 4 + categories: + - UplinkDeception + +# Disruption +>>>>>>> 21c527d6d0 (fix decoy bomb uplink category (#26205)) + - type: listing id: UplinkEmag name: uplink-emag-name From 7a2259a8ad8724a1546e7f785c4736e82ffb8ec0 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 18 Mar 2024 17:46:31 +0100 Subject: [PATCH 32/54] Fix barotrauma pressure protection (#26236) Oops In #26217 I re-organized the logic for the calculation. Part of that was moving the logic for GetFeltLowPressure and GetFeltHighPressure to be done before we actually check the hazard thresholds. What I didn't realize is that, with how our pressure protection is set up, these functions can return values so extreme they rebound into the other category. For example, according to the math, when you're wearing a hardsuit in a low-pressure environment you have "felt" pressure of 1000 kPa. Yeah that's not right. Now these functions clamp their result to OneAtmosphere, in the appropriate direction (101.3 kPa). Fixes #26234 --- Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 2b1d6b526b2..98a5ffa70a8 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -149,7 +149,8 @@ public float GetFeltLowPressure(EntityUid uid, BarotraumaComponent barotrauma, f return Atmospherics.OneAtmosphere; } - return (environmentPressure + barotrauma.LowPressureModifier) * (barotrauma.LowPressureMultiplier); + var modified = (environmentPressure + barotrauma.LowPressureModifier) * (barotrauma.LowPressureMultiplier); + return Math.Min(modified, Atmospherics.OneAtmosphere); } /// @@ -162,7 +163,8 @@ public float GetFeltHighPressure(EntityUid uid, BarotraumaComponent barotrauma, return Atmospherics.OneAtmosphere; } - return (environmentPressure + barotrauma.HighPressureModifier) * (barotrauma.HighPressureMultiplier); + var modified = (environmentPressure + barotrauma.HighPressureModifier) * (barotrauma.HighPressureMultiplier); + return Math.Max(modified, Atmospherics.OneAtmosphere); } public bool TryGetPressureProtectionValues( From 1b21d5155c919c63a22ccd305e0b193acd358f92 Mon Sep 17 00:00:00 2001 From: potato1234_x <79580518+potato1234x@users.noreply.github.com> Date: Tue, 19 Mar 2024 06:53:13 +1000 Subject: [PATCH 33/54] makes closets/lockers better (#24942) * hmngm * fix --- .../Closets/Lockers/base_structurelockers.yml | 10 +++ .../Storage/Closets/Lockers/lockers.yml | 12 ++++ .../Storage/Closets/base_structureclosets.yml | 5 ++ .../Crafting/Graphs/storage/tallbox.yml | 63 ++++++++++++++++++ .../Prototypes/Recipes/Crafting/tallbox.yml | 27 ++++++++ .../Structures/Storage/closet.rsi/meta.json | 3 + .../Storage/closet.rsi/secure_icon.png | Bin 0 -> 353 bytes .../Storage/wall_locker.rsi/generic_icon.png | Bin 0 -> 321 bytes .../Storage/wall_locker.rsi/meta.json | 1 + 9 files changed, 121 insertions(+) create mode 100644 Resources/Textures/Structures/Storage/closet.rsi/secure_icon.png create mode 100644 Resources/Textures/Structures/Storage/wall_locker.rsi/generic_icon.png diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml index 7ab24f6b5ca..783bec3ba0d 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml @@ -44,6 +44,11 @@ SheetSteel1: min: 1 max: 2 + - type: Construction + graph: ClosetSteel + node: done + containers: + - entity_storage - type: entity id: LockerBaseSecure @@ -61,3 +66,8 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] + - type: Construction + graph: ClosetSteelSecure + node: done + containers: + - entity_storage diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml index dbbd9b16ae9..67379ec990c 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml @@ -18,6 +18,18 @@ openSound: path: /Audio/Effects/woodenclosetopen.ogg +# Basic +- type: entity + id: LockerSteel + parent: LockerBaseSecure + name: secure closet + components: + - type: Appearance + - type: EntityStorageVisuals + stateBaseClosed: secure + stateDoorOpen: secure_open + stateDoorClosed: secure_door + # Cargo - type: entity id: LockerQuarterMaster diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml index 66647d168f6..0ef99b9f47e 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml @@ -171,6 +171,11 @@ SheetSteel1: min: 1 max: 1 + - type: Construction + graph: ClosetWall + node: done + containers: + - entity_storage #Wall locker - type: entity diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml index 5b0cc1f9a2e..e72c56ff44c 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml @@ -25,3 +25,66 @@ amount: 4 - !type:EmptyAllContainers - !type:DeleteEntity + +- type: constructionGraph + id: ClosetSteelSecure + start: start + graph: + - node: start + edges: + - to: done + steps: + - material: Steel + amount: 4 + - material: Cable + amount: 2 + doAfter: 5 + - node: done + entity: LockerSteel + edges: + - to: start + steps: + - tool: Screwing + doAfter: 5 + conditions: + - !type:StorageWelded + welded: false + - !type:Locked + locked: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 4 + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 2 + - !type:EmptyAllContainers + - !type:DeleteEntity + +- type: constructionGraph + id: ClosetWall + start: start + graph: + - node: start + edges: + - to: done + steps: + - material: Steel + amount: 4 + doAfter: 5 + - node: done + entity: ClosetWall + edges: + - to: start + steps: + - tool: Screwing + doAfter: 5 + conditions: + - !type:StorageWelded + welded: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 4 + - !type:EmptyAllContainers + - !type:DeleteEntity diff --git a/Resources/Prototypes/Recipes/Crafting/tallbox.yml b/Resources/Prototypes/Recipes/Crafting/tallbox.yml index 09469093f77..21a7ec8225d 100644 --- a/Resources/Prototypes/Recipes/Crafting/tallbox.yml +++ b/Resources/Prototypes/Recipes/Crafting/tallbox.yml @@ -8,3 +8,30 @@ description: A tall steel box that cannot be locked. icon: { sprite: Structures/Storage/closet.rsi, state: generic_icon } objectType: Structure + +- type: construction + id: ClosetSteelSecure + name: secure closet + graph: ClosetSteelSecure + startNode: start + targetNode: done + category: construction-category-storage + description: A tall steel box that can be locked. + icon: { sprite: Structures/Storage/closet.rsi, state: secure_icon } + objectType: Structure + +- type: construction + id: ClosetWall + name: wall closet + graph: ClosetWall + startNode: start + targetNode: done + category: construction-category-storage + description: A standard-issue Nanotrasen storage unit, now on walls. + icon: { sprite: Structures/Storage/wall_locker.rsi, state: generic_icon } + objectType: Structure + placementMode: SnapgridCenter + canRotate: true + canBuildInImpassable: true + conditions: + - !type:WallmountCondition \ No newline at end of file diff --git a/Resources/Textures/Structures/Storage/closet.rsi/meta.json b/Resources/Textures/Structures/Storage/closet.rsi/meta.json index 98bbe8085a2..cf0c204447b 100644 --- a/Resources/Textures/Structures/Storage/closet.rsi/meta.json +++ b/Resources/Textures/Structures/Storage/closet.rsi/meta.json @@ -461,6 +461,9 @@ { "name": "secure_door" }, + { + "name": "secure_icon" + }, { "name": "secure_open" }, diff --git a/Resources/Textures/Structures/Storage/closet.rsi/secure_icon.png b/Resources/Textures/Structures/Storage/closet.rsi/secure_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03b98c64db4675cb0a0ab2deaf827806e0570227 GIT binary patch literal 353 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCik_0(?STH8eCb($fnH3i9*w8ygy|tgQ6)^fEFs8X6iB5)v{J5?aH<0|Eka z?Ct;A+H!DkI5;@u7#REq+M}T0=m<29fzML%GmsK13Gxg6j}0&|UOMmusDiV=BeEE% z{xAqL9?xHq0u*fXba4!^@V|P=ldnO6$04vFL4ZAA$N$jKoDFKMWp4J|K9z}%DGP;| zcvu%N{yq7l+R=*=-^v|sFOA~*!gTep0JCDK7ng)x!zE2uCeG#VUwG@8J}P<`#2k=V zY|43z+voF^8$t!ES2Kh&-i{PovE$1OR=57YI6J== T6Ir$ZUBuw&>gTe~DWM4f$)SGw literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Storage/wall_locker.rsi/generic_icon.png b/Resources/Textures/Structures/Storage/wall_locker.rsi/generic_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..63e9dcb0fad5ebda57c939e651170d21ee2cda01 GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijK0(?STt*or{_4M-d^BWr)5)%@Ni;F8OE0vX%g+)XqB_%V`(*ptm9336Q zLqcR^WPqBi1?PzZDS?t8zu^C61tRzs5v%n*=n1O-sFbFdq&tH)O6s+@faSXBW zzdGqISAznN+voV`XTSI7Do@k2WWVe3-0ddc{0|RnO-Siiqpnb;x_+>@wuf|GNmYDgTX_FI95T!oZdN&=e{-Y7F>ba(hioL0TEs6jC6 t>Kq02n9yRURE-iF){p*0HTqME7~Hk=bPF$bgaTc{;OXk;vd$@?2>=haZx#Rm literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json b/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json index 3408841645d..b6ed63c7a3e 100644 --- a/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json +++ b/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json @@ -18,6 +18,7 @@ { "name": "fire_open" }, { "name": "generic" }, { "name": "generic_door" }, + { "name": "generic_icon" }, { "name": "generic_open" }, { "name": "gray_door" }, { "name": "green_door" }, From 47f9fcdc929bc73b0292c0d8152140e6f354913c Mon Sep 17 00:00:00 2001 From: brainfood1183 <113240905+brainfood1183@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:29:48 +0000 Subject: [PATCH 34/54] Spray Paint (Review Ready) (#23003) * Spray Paint (Draft) * paint colors, paints in maints loot, cargo crate of paints. * fix * remove paint (sort of) * moved paintcleaner into own system * Moved paint to server (had to unfortunately) * doafter now breaks when moving away. * cant paint mobstatecomp * loads of fixes * fixes * fixes * nopaintshadercomp * fixes * fix * use locale for paint remove string * remove nopaintshadercomponent and use blacklist * remove enabled.true from visualizer * paint doafter event. * add verbs for paint and remove paint and icon for paint verb. * fixes * no longer replaces shader when shader exists. * replace forloop with foreach, check shader before adding and removing. * paint doafter now separate so no copy paste code * Entities in sprayed targets item slots are also now correctly sprayed. * fix * fix * fix airlock psray painter now removes painted before painting door. * spray paints now use openablecomponent. * fix * fix damn accesstypes. * fix * fix --- Content.Client/Paint/PaintVisualizerSystem.cs | 120 +++++++ Content.Server/Paint/PaintSystem.cs | 180 +++++++++++ .../EntitySystems/SpawnItemsOnUseSystem.cs | 6 +- Content.Shared/Paint/PaintComponent.cs | 60 ++++ Content.Shared/Paint/PaintDoAfterEvent.cs | 9 + Content.Shared/Paint/PaintRemoverComponent.cs | 24 ++ .../Paint/PaintRemoverDoAfterEvent.cs | 9 + Content.Shared/Paint/PaintRemoverSystem.cs | 96 ++++++ Content.Shared/Paint/PaintedComponent.cs | 41 +++ Content.Shared/Paint/SharedPaintSystem.cs | 11 + .../SprayPainter/SharedSprayPainterSystem.cs | 3 + Resources/Locale/en-US/paint/paint.ftl | 8 + .../Prototypes/Catalog/Cargo/cargo_fun.yml | 10 + .../Prototypes/Catalog/Fills/Crates/fun.yml | 48 ++- .../Prototypes/Catalog/Fills/Lockers/misc.yml | 4 + .../Markers/Spawners/Random/crates.yml | 1 + .../Markers/Spawners/Random/maintenance.yml | 5 + .../Prototypes/Entities/Mobs/NPCs/carp.yml | 1 + .../Entities/Mobs/NPCs/revenant.yml | 3 + .../Entities/Mobs/Player/guardian.yml | 2 + .../Entities/Objects/Fun/spray_paint.yml | 292 ++++++++++++++++++ .../Objects/Materials/Sheets/glass.yml | 1 + .../Objects/Materials/Sheets/metal.yml | 1 + .../Objects/Materials/Sheets/other.yml | 3 + .../Entities/Objects/Materials/materials.yml | 1 + .../Entities/Objects/Misc/tiles.yml | 3 + .../Objects/Specific/Janitorial/soap.yml | 1 + .../Objects/Weapons/Melee/e_sword.yml | 4 + .../Structures/Decoration/bonfire.yml | 3 + .../Structures/Holographic/projections.yml | 3 + .../Entities/Structures/hydro_tray.yml | 3 + Resources/Prototypes/tags.yml | 3 + .../Textures/Interface/VerbIcons/paint.svg | 39 +++ .../Interface/VerbIcons/paint.svg.192dpi.png | Bin 0 -> 15653 bytes .../VerbIcons/paint.svg.192dpi.png.yml | 2 + .../Fun/spraycans.rsi/clown-inhand-left.png | Bin 0 -> 20773 bytes .../Fun/spraycans.rsi/clown-inhand-right.png | Bin 0 -> 20783 bytes .../Objects/Fun/spraycans.rsi/meta.json | 19 +- .../Fun/spraycans.rsi/spray-inhand-left.png | Bin 0 -> 21199 bytes .../Fun/spraycans.rsi/spray-inhand-right.png | Bin 0 -> 21213 bytes .../Objects/Fun/spraycans.rsi/spray_cap.png | Bin 246 -> 0 bytes 41 files changed, 1008 insertions(+), 11 deletions(-) create mode 100644 Content.Client/Paint/PaintVisualizerSystem.cs create mode 100644 Content.Server/Paint/PaintSystem.cs create mode 100644 Content.Shared/Paint/PaintComponent.cs create mode 100644 Content.Shared/Paint/PaintDoAfterEvent.cs create mode 100644 Content.Shared/Paint/PaintRemoverComponent.cs create mode 100644 Content.Shared/Paint/PaintRemoverDoAfterEvent.cs create mode 100644 Content.Shared/Paint/PaintRemoverSystem.cs create mode 100644 Content.Shared/Paint/PaintedComponent.cs create mode 100644 Content.Shared/Paint/SharedPaintSystem.cs create mode 100644 Resources/Locale/en-US/paint/paint.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml create mode 100644 Resources/Textures/Interface/VerbIcons/paint.svg create mode 100644 Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png create mode 100644 Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png.yml create mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-right.png delete mode 100644 Resources/Textures/Objects/Fun/spraycans.rsi/spray_cap.png diff --git a/Content.Client/Paint/PaintVisualizerSystem.cs b/Content.Client/Paint/PaintVisualizerSystem.cs new file mode 100644 index 00000000000..6c99b2d35f0 --- /dev/null +++ b/Content.Client/Paint/PaintVisualizerSystem.cs @@ -0,0 +1,120 @@ +using System.Linq; +using Robust.Client.GameObjects; +using static Robust.Client.GameObjects.SpriteComponent; +using Content.Shared.Clothing; +using Content.Shared.Hands; +using Content.Shared.Paint; +using Robust.Client.Graphics; +using Robust.Shared.Prototypes; + +namespace Content.Client.Paint +{ + public sealed class PaintedVisualizerSystem : VisualizerSystem + { + /// + /// Visualizer for Paint which applies a shader and colors the entity. + /// + + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + + public ShaderInstance? Shader; // in Robust.Client.Graphics so cannot move to shared component. + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHeldVisualsUpdated); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnEquipmentVisualsUpdated); + } + + protected override void OnAppearanceChange(EntityUid uid, PaintedComponent component, ref AppearanceChangeEvent args) + { + // ShaderPrototype sadly in Robust.Client, cannot move to shared component. + Shader = _protoMan.Index(component.ShaderName).Instance(); + + if (args.Sprite == null) + return; + + if (!_appearance.TryGetData(uid, PaintVisuals.Painted, out bool isPainted)) + return; + + var sprite = args.Sprite; + + + foreach (var spriteLayer in sprite.AllLayers) + { + if (spriteLayer is not Layer layer) + continue; + + if (layer.Shader == null) // If shader isn't null we dont want to replace the original shader. + { + layer.Shader = Shader; + layer.Color = component.Color; + } + } + } + + private void OnHeldVisualsUpdated(EntityUid uid, PaintedComponent component, HeldVisualsUpdatedEvent args) + { + if (args.RevealedLayers.Count == 0) + return; + + if (!TryComp(args.User, out SpriteComponent? sprite)) + return; + + foreach (var revealed in args.RevealedLayers) + { + if (!sprite.LayerMapTryGet(revealed, out var layer) || sprite[layer] is not Layer notlayer) + continue; + + sprite.LayerSetShader(layer, component.ShaderName); + sprite.LayerSetColor(layer, component.Color); + } + } + + private void OnEquipmentVisualsUpdated(EntityUid uid, PaintedComponent component, EquipmentVisualsUpdatedEvent args) + { + if (args.RevealedLayers.Count == 0) + return; + + if (!TryComp(args.Equipee, out SpriteComponent? sprite)) + return; + + foreach (var revealed in args.RevealedLayers) + { + if (!sprite.LayerMapTryGet(revealed, out var layer) || sprite[layer] is not Layer notlayer) + continue; + + sprite.LayerSetShader(layer, component.ShaderName); + sprite.LayerSetColor(layer, component.Color); + } + } + + private void OnShutdown(EntityUid uid, PaintedComponent component, ref ComponentShutdown args) + { + if (!TryComp(uid, out SpriteComponent? sprite)) + return; + + component.BeforeColor = sprite.Color; + Shader = _protoMan.Index(component.ShaderName).Instance(); + + if (!Terminating(uid)) + { + foreach (var spriteLayer in sprite.AllLayers) + { + if (spriteLayer is not Layer layer) + continue; + + if (layer.Shader == Shader) // If shader isn't same as one in component we need to ignore it. + { + layer.Shader = null; + if (layer.Color == component.Color) // If color isn't the same as one in component we don't want to change it. + layer.Color = component.BeforeColor; + } + } + } + } + } +} diff --git a/Content.Server/Paint/PaintSystem.cs b/Content.Server/Paint/PaintSystem.cs new file mode 100644 index 00000000000..c6718aced68 --- /dev/null +++ b/Content.Server/Paint/PaintSystem.cs @@ -0,0 +1,180 @@ +using Content.Shared.Popups; +using Content.Shared.Paint; +using Content.Shared.Sprite; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Server.Chemistry.Containers.EntitySystems; +using Robust.Shared.Audio.Systems; +using Content.Shared.Humanoid; +using Robust.Shared.Utility; +using Content.Shared.Verbs; +using Content.Shared.SubFloor; +using Content.Server.Nutrition.Components; +using Content.Shared.Inventory; +using Content.Server.Nutrition.EntitySystems; + +namespace Content.Server.Paint; + +/// +/// Colors target and consumes reagent on each color success. +/// +public sealed class PaintSystem : SharedPaintSystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly OpenableSystem _openable = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnPaint); + SubscribeLocalEvent>(OnPaintVerb); + } + + private void OnInteract(EntityUid uid, PaintComponent component, AfterInteractEvent args) + { + if (!args.CanReach) + return; + + if (args.Target is not { Valid: true } target) + return; + + PrepPaint(uid, component, target, args.User); + } + + private void OnPaintVerb(EntityUid uid, PaintComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var paintText = Loc.GetString("paint-verb"); + + var verb = new UtilityVerb() + { + Act = () => + { + PrepPaint(uid, component, args.Target, args.User); + }, + + Text = paintText, + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/paint.svg.192dpi.png")) + }; + args.Verbs.Add(verb); + } + private void PrepPaint(EntityUid uid, PaintComponent component, EntityUid target, EntityUid user) + { + + var doAfterEventArgs = new DoAfterArgs(EntityManager, user, component.Delay, new PaintDoAfterEvent(), uid, target: target, used: uid) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + NeedHand = true, + BreakOnHandChange = true + }; + + if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs)) + return; + } + + private void OnPaint(Entity entity, ref PaintDoAfterEvent args) + { + if (args.Target == null || args.Used == null) + return; + + if (args.Handled || args.Cancelled) + return; + + if (args.Target is not { Valid: true } target) + return; + + if (!_openable.IsOpen(entity)) + { + _popup.PopupEntity(Loc.GetString("paint-closed", ("used", args.Used)), args.User, args.User, PopupType.Medium); + return; + } + + if (HasComp(target) || HasComp(target)) + { + _popup.PopupEntity(Loc.GetString("paint-failure-painted", ("target", args.Target)), args.User, args.User, PopupType.Medium); + return; + } + + if (!entity.Comp.Blacklist?.IsValid(target, EntityManager) != true || HasComp(target) || HasComp(target)) + { + _popup.PopupEntity(Loc.GetString("paint-failure", ("target", args.Target)), args.User, args.User, PopupType.Medium); + return; + } + + + if (TryPaint(entity, target)) + { + EnsureComp(target, out PaintedComponent? paint); + EnsureComp(target); + + paint.Color = entity.Comp.Color; // set the target color to the color specified in the spray paint yml. + _audio.PlayPvs(entity.Comp.Spray, entity); + paint.Enabled = true; + + if (HasComp(target)) // Paint any clothing the target is wearing. + { + if (_inventory.TryGetSlots(target, out var slotDefinitions)) + { + foreach (var slot in slotDefinitions) + { + if (!_inventory.TryGetSlotEntity(target, slot.Name, out var slotEnt)) + continue; + + if (slotEnt == null) + return; + + if (HasComp(slotEnt.Value) || !entity.Comp.Blacklist?.IsValid(slotEnt.Value, EntityManager) != true + || HasComp(slotEnt.Value) || HasComp(slotEnt.Value)) + return; + + EnsureComp(slotEnt.Value, out PaintedComponent? slotpaint); + EnsureComp(slotEnt.Value); + slotpaint.Color = entity.Comp.Color; + _appearanceSystem.SetData(slotEnt.Value, PaintVisuals.Painted, true); + Dirty(slotEnt.Value, slotpaint); + } + } + } + + _popup.PopupEntity(Loc.GetString("paint-success", ("target", args.Target)), args.User, args.User, PopupType.Medium); + _appearanceSystem.SetData(target, PaintVisuals.Painted, true); + Dirty(target, paint); + args.Handled = true; + return; + } + + if (!TryPaint(entity, target)) + { + _popup.PopupEntity(Loc.GetString("paint-empty", ("used", args.Used)), args.User, args.User, PopupType.Medium); + return; + } + } + + private bool TryPaint(Entity reagent, EntityUid target) + { + if (HasComp(target) || HasComp(target)) + return false; + + if (_solutionContainer.TryGetSolution(reagent.Owner, reagent.Comp.Solution, out _, out var solution)) + { + var quantity = solution.RemoveReagent(reagent.Comp.Reagent, reagent.Comp.ConsumptionUnit); + if (quantity > 0)// checks quantity of solution is more than 0. + return true; + + if (quantity < 1) + return false; + } + return false; + } +} diff --git a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs index c49bfdec931..0b4b13d6e4b 100644 --- a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs +++ b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs @@ -80,11 +80,6 @@ private void OnUseInHand(EntityUid uid, SpawnItemsOnUseComponent component, UseI _adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(args.User)} used {ToPrettyString(uid)} which spawned {ToPrettyString(entityToPlaceInHands.Value)}"); } - if (component.Sound != null) - { - _audio.PlayPvs(component.Sound, uid); - } - component.Uses--; // Delete entity only if component was successfully used @@ -97,6 +92,7 @@ private void OnUseInHand(EntityUid uid, SpawnItemsOnUseComponent component, UseI if (entityToPlaceInHands != null) { _hands.PickupOrDrop(args.User, entityToPlaceInHands.Value); + _audio.PlayPvs(component.Sound, entityToPlaceInHands.Value); } } } diff --git a/Content.Shared/Paint/PaintComponent.cs b/Content.Shared/Paint/PaintComponent.cs new file mode 100644 index 00000000000..ad09f4ca730 --- /dev/null +++ b/Content.Shared/Paint/PaintComponent.cs @@ -0,0 +1,60 @@ +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.Audio; +using Content.Shared.Whitelist; +using Robust.Shared.Prototypes; +using Robust.Shared.GameStates; + +namespace Content.Shared.Paint; + +/// +/// Entity when used on another entity will paint target entity. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedPaintSystem))] +public sealed partial class PaintComponent : Component +{ + /// + /// Noise made when paint applied. + /// + [DataField] + public SoundSpecifier Spray = new SoundPathSpecifier("/Audio/Effects/spray2.ogg"); + + /// + /// Solution on the entity that contains the paint. + /// + [DataField] + public string Solution = "drink"; + + /// + /// How long the doafter will take. + /// + [DataField] + public int Delay = 2; + + /// + /// Reagent that will be used as paint. + /// + [DataField, AutoNetworkedField] + public ProtoId Reagent = "SpaceGlue"; + + /// + /// Color that the painting entity will instruct the painted entity to be. + /// + [DataField, AutoNetworkedField] + public Color Color = Color.FromHex("#c62121"); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? Blacklist; + /// + /// Reagent consumption per use. + /// + [DataField] + public FixedPoint2 ConsumptionUnit = FixedPoint2.New(5); + + /// + /// Duration per unit + /// + [DataField] + public TimeSpan DurationPerUnit = TimeSpan.FromSeconds(6); +} diff --git a/Content.Shared/Paint/PaintDoAfterEvent.cs b/Content.Shared/Paint/PaintDoAfterEvent.cs new file mode 100644 index 00000000000..0851f1541b4 --- /dev/null +++ b/Content.Shared/Paint/PaintDoAfterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Paint; + +[Serializable, NetSerializable] +public sealed partial class PaintDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Paint/PaintRemoverComponent.cs b/Content.Shared/Paint/PaintRemoverComponent.cs new file mode 100644 index 00000000000..54d0ed7a71b --- /dev/null +++ b/Content.Shared/Paint/PaintRemoverComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Audio; + +namespace Content.Shared.Paint; + +/// +/// Removes paint from an entity that was painted with spray paint. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(PaintRemoverSystem))] +public sealed partial class PaintRemoverComponent : Component +{ + /// + /// Sound when target is cleaned. + /// + [DataField] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg"); + + /// + /// DoAfter wait time. + /// + [DataField] + public float CleanDelay = 2f; +} diff --git a/Content.Shared/Paint/PaintRemoverDoAfterEvent.cs b/Content.Shared/Paint/PaintRemoverDoAfterEvent.cs new file mode 100644 index 00000000000..940b1aa513c --- /dev/null +++ b/Content.Shared/Paint/PaintRemoverDoAfterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Paint; + +[Serializable, NetSerializable] +public sealed partial class PaintRemoverDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Paint/PaintRemoverSystem.cs b/Content.Shared/Paint/PaintRemoverSystem.cs new file mode 100644 index 00000000000..ac1cc624cfe --- /dev/null +++ b/Content.Shared/Paint/PaintRemoverSystem.cs @@ -0,0 +1,96 @@ +using Content.Shared.Popups; +using Content.Shared.Interaction; +using Content.Shared.DoAfter; +using Content.Shared.Verbs; +using Content.Shared.Sprite; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Timing; + +namespace Content.Shared.Paint; + +/// +/// Removes paint from an entity. +/// +public sealed class PaintRemoverSystem : SharedPaintSystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent>(OnPaintRemoveVerb); + } + + // When entity is painted, remove paint from that entity. + private void OnInteract(EntityUid uid, PaintRemoverComponent component, AfterInteractEvent args) + { + if (args.Handled) + return; + + if (!args.CanReach || args.Target is not { Valid: true } target || !HasComp(target)) + return; + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + }); + args.Handled = true; + } + + private void OnDoAfter(EntityUid uid, PaintRemoverComponent component, DoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Args.Target == null) + return; + + if (args.Target is not { Valid: true } target) + return; + + if (!TryComp(target, out PaintedComponent? paint)) + return; + + paint.Enabled = false; + _audio.PlayPredicted(component.Sound, target, args.User); + _popup.PopupClient(Loc.GetString("paint-removed", ("target", target)), args.User, args.User, PopupType.Medium); + _appearanceSystem.SetData(target, PaintVisuals.Painted, false); + RemComp(target); + Dirty(target, paint); + + args.Handled = true; + } + + private void OnPaintRemoveVerb(EntityUid uid, PaintRemoverComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var paintremovalText = Loc.GetString("paint-remove-verb"); + + var verb = new UtilityVerb() + { + Act = () => { + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + }); + }, + + Text = paintremovalText + }; + + args.Verbs.Add(verb); + } +} diff --git a/Content.Shared/Paint/PaintedComponent.cs b/Content.Shared/Paint/PaintedComponent.cs new file mode 100644 index 00000000000..a6ee7377e11 --- /dev/null +++ b/Content.Shared/Paint/PaintedComponent.cs @@ -0,0 +1,41 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Paint; + +/// +/// Component applied to target entity when painted. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PaintedComponent : Component +{ + /// + /// Color of the paint. + /// + [DataField, AutoNetworkedField] + public Color Color = Color.FromHex("#2cdbd5"); + + /// + /// Used to remove the color when component removed. + /// + [DataField, AutoNetworkedField] + public Color BeforeColor; + + /// + /// If paint is enabled. + /// + [DataField, AutoNetworkedField] + public bool Enabled; + + /// + /// Name of the shader. + /// + [DataField, AutoNetworkedField] + public string ShaderName = "Greyscale"; +} + +[Serializable, NetSerializable] +public enum PaintVisuals : byte +{ + Painted, +} diff --git a/Content.Shared/Paint/SharedPaintSystem.cs b/Content.Shared/Paint/SharedPaintSystem.cs new file mode 100644 index 00000000000..10185817b86 --- /dev/null +++ b/Content.Shared/Paint/SharedPaintSystem.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Paint; + +/// +/// Colors target and consumes reagent on each color success. +/// +public abstract class SharedPaintSystem : EntitySystem +{ + public virtual void UpdateAppearance(EntityUid uid, PaintedComponent? component = null) + { + } +} diff --git a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs index 529e321f8da..fa04a50f8b0 100644 --- a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs +++ b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Doors.Components; using Content.Shared.Interaction; using Content.Shared.Popups; +using Content.Shared.Paint; using Content.Shared.SprayPainter.Components; using Content.Shared.SprayPainter.Prototypes; using Robust.Shared.Audio.Systems; @@ -129,6 +130,8 @@ private void OnAirlockInteract(Entity ent, ref Intera return; } + RemComp(ent); + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used) { BreakOnTargetMove = true, diff --git a/Resources/Locale/en-US/paint/paint.ftl b/Resources/Locale/en-US/paint/paint.ftl new file mode 100644 index 00000000000..200b1f6e3f3 --- /dev/null +++ b/Resources/Locale/en-US/paint/paint.ftl @@ -0,0 +1,8 @@ +paint-success = {THE($target)} has been covered in paint! +paint-failure = Can't cover {THE($target)} in paint! +paint-failure-painted = {THE($target)} is already covered in paint! +paint-empty = {THE($used)} is empty! +paint-removed = You clean off the paint! +paint-closed = You must open {THE($used)} first! +paint-verb = Paint +paint-remove-verb = Remove Paint diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml b/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml index 2d5acf5c90f..d84fedd543f 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml @@ -68,6 +68,16 @@ category: Fun group: market +- type: cargoProduct + id: FunSprayPaints + icon: + sprite: Objects/Fun/spraycans.rsi + state: death2_cap + product: CrateFunSprayPaints + cost: 2000 + category: Fun + group: market + - type: cargoProduct id: FunParty icon: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml index 1db0c3121ed..cc5e3b1d174 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml @@ -292,14 +292,21 @@ contents: - id: SnapPopBox - id: CrazyGlue - amount: 2 - id: PlasticBanana + - id: FunnyPaint + orGroup: Paint + prob: 0.5 + - id: FunnyPaintYellow + orGroup: Paint + prob: 0.5 - id: WhoopieCushion - id: ToyHammer - id: MrChips - orGroup: GiftPool + prob: 0.5 + orGroup: Dummy - id: MrDips - orGroup: Giftpool + prob: 0.5 + orGroup: Dummy - id: RevolverCapGun - id: BalloonNT - id: ClothingShoesClownLarge @@ -332,6 +339,41 @@ amount: 15 prob: 0.05 +- type: entity + id: CrateFunSprayPaints + name: spray paint crate + description: a crate filled with spray paint. + parent: CratePlastic + suffix: Spray Paint + components: + - type: StorageFill + contents: + - id: SprayPaintBlue + amount: 2 + prob: 0.33 + - id: SprayPaintRed + amount: 2 + prob: 0.33 + - id: SprayPaintOrange + amount: 2 + prob: 0.33 + - id: SprayPaintBlack + amount: 2 + prob: 0.33 + - id: SprayPaintGreen + amount: 2 + prob: 0.33 + - id: SprayPaintPurple + amount: 2 + prob: 0.33 + - id: SprayPaintWhite + amount: 2 + prob: 0.33 + - id: DeathPaint + amount: 2 + - id: DeathPaintTwo + amount: 2 + - type: entity name: dartboard box set description: A box with everything you need for a fun game of darts. diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index dd21ea652aa..3a02478b87a 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -135,6 +135,10 @@ prob: 0.25 - id: StrangePill prob: 0.20 + - id: DeathPaint + prob: 0.05 + - id: DeathPaintTwo + prob: 0.05 - id: DrinkMopwataBottleRandom prob: 0.20 - id: ModularReceiver diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml index ae7e5bcf762..883182aae8d 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/crates.yml @@ -44,6 +44,7 @@ - CrateMaterialPlastic - CrateMaterialWood - CrateMaterialPlasteel + - CrateFunSprayPaints - CrateFunArtSupplies - CrateEngineeringCableLV - CrateEngineeringCableMV diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 879c1689291..10fb034d0d5 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -174,7 +174,12 @@ - MaterialCloth10 - MaterialWoodPlank10 - ResearchDisk + - DeathPaint - Plunger + - SprayPaintBlue + - SprayPaintRed + - SprayPaintGreen + - SprayPaintOrange - TechnologyDisk - PowerCellMedium - PowerCellSmall diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index 73082674736..3e6c603626b 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -70,6 +70,7 @@ tags: - Carp - DoorBumpOpener + - NoPaint - type: ReplacementAccent accent: genericAggressive - type: Speech diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index 28355bb459e..ec1ed3a58f6 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -97,3 +97,6 @@ - RevenantTheme - type: Speech speechVerb: Ghost + - type: Tag + tags: + - NoPaint diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index d892b31fac3..c37826666ac 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -104,6 +104,7 @@ - type: Tag tags: - CannotSuicide + - NoPaint # From the uplink injector - type: entity @@ -212,6 +213,7 @@ tags: - CannotSuicide - FootstepSound + - NoPaint - type: Inventory templateId: holoclown - type: Hands diff --git a/Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml b/Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml new file mode 100644 index 00000000000..1b417f6cde0 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Fun/spray_paint.yml @@ -0,0 +1,292 @@ +# Base Paints +- type: entity + parent: BaseItem + id: PaintBase + name: spray paint + description: A tin of spray paint. + noSpawn: true + components: + - type: Appearance + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + state: clown_cap + layers: + - state: clown_cap + map: ["enum.OpenableVisuals.Layer"] + - type: Paint + consumptionUnit: 10 + blacklist: + tags: + - NoPaint + - type: Item + sprite: Objects/Fun/spraycans.rsi + heldPrefix: spray + - type: SolutionContainerManager + solutions: + drink: + maxVol: 50 + reagents: + - ReagentId: SpaceGlue + Quantity: 50 + - type: TrashOnSolutionEmpty + solution: drink + - type: Sealable + - type: Openable + sound: + path: /Audio/Effects/pop_high.ogg + closeable: true + closeSound: + path: /Audio/Effects/pop_high.ogg + +# Paints + +# funnypaint +- type: entity + parent: PaintBase + id: FunnyPaint + name: funny paint + description: A tin of funny paint, manufactured by Honk! Co. + components: + - type: Paint + color: "#fa74df" + - type: Item + sprite: Objects/Fun/spraycans.rsi + heldPrefix: clown + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "clown"} + False: {state: "clown_cap"} + +- type: entity + parent: PaintBase + id: FunnyPaintYellow + name: funny paint + description: A tin of funny paint, manufactured by Honk! Co. + components: + - type: Paint + color: "#d5e028" + - type: Item + sprite: Objects/Fun/spraycans.rsi + heldPrefix: clown + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + state: clown2_cap + layers: + - state: clown2_cap + map: ["enum.OpenableVisuals.Layer"] + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "clown2"} + False: {state: "clown2_cap"} + +#death paint +- type: entity + parent: PaintBase + id: DeathPaint + components: + - type: Paint + color: "#ff20c8" + - type: Item + sprite: Objects/Fun/spraycans.rsi + heldPrefix: spray + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + state: death_cap + layers: + - state: death_cap + map: ["enum.OpenableVisuals.Layer"] + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "death"} + False: {state: "death_cap"} + +- type: entity + parent: PaintBase + id: DeathPaintTwo + components: + - type: Paint + color: "#ff2020" + - type: Item + sprite: Objects/Fun/spraycans.rsi + heldPrefix: spray + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + state: death2_cap + layers: + - state: death2_cap + map: ["enum.OpenableVisuals.Layer"] + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "death2"} + False: {state: "death2_cap"} + +#Sprays + +#Blue +- type: entity + parent: PaintBase + id: SprayPaintBlue + suffix: Blue + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#5890f7" + - type: Paint + color: "#5890f7" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#5890f7"} + False: {state: "spray_cap_colors" , color: "#5890f7"} + +#Red +- type: entity + parent: PaintBase + id: SprayPaintRed + suffix: Red + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#ff3b3b" + - type: Paint + color: "#ff3b3b" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#ff3b3b"} + False: {state: "spray_cap_colors" , color: "#ff3b3b"} + +#Green +- type: entity + parent: PaintBase + id: SprayPaintGreen + suffix: Green + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#73f170" + - type: Paint + color: "#73f170" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#73f170"} + False: {state: "spray_cap_colors" , color: "#73f170"} + +#Black +- type: entity + parent: PaintBase + id: SprayPaintBlack + suffix: Black + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#3a3a3a" + - type: Paint + color: "#3a3a3a" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#3a3a3a"} + False: {state: "spray_cap_colors" , color: "#3a3a3a"} + +#Orange +- type: entity + parent: PaintBase + id: SprayPaintOrange + suffix: Orange + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#f6a44b" + - type: Paint + color: "#f6a44b" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#f6a44b"} + False: {state: "spray_cap_colors" , color: "#f6a44b"} + +#Purple +- type: entity + parent: PaintBase + id: SprayPaintPurple + suffix: Purple + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#c063f5" + - type: Paint + color: "#c063f5" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#c063f5"} + False: {state: "spray_cap_colors" , color: "#c063f5"} + +#White +- type: entity + parent: PaintBase + id: SprayPaintWhite + suffix: White + components: + - type: Sprite + sprite: Objects/Fun/spraycans.rsi + layers: + - state: spray + map: ["Base"] + - state: spray_cap_colors + map: ["enum.OpenableVisuals.Layer"] + color: "#f2f2f2" + - type: Paint + color: "#f2f2f2" + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray_colors" , color: "#f2f2f2"} + False: {state: "spray_cap_colors" , color: "#f2f2f2"} diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index 59d8ed19220..2e0eec7a658 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -15,6 +15,7 @@ - type: Tag tags: - Sheet + - NoPaint - type: Material - type: Damageable damageContainer: Inorganic diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml index 82b9f62837a..3a887848bf5 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml @@ -15,6 +15,7 @@ tags: - Sheet - Metal + - NoPaint - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index dfb51336289..9dc87a9117d 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -12,6 +12,7 @@ - type: Tag tags: - Sheet + - NoPaint - type: Damageable damageContainer: Inorganic - type: Destructible @@ -110,6 +111,7 @@ - type: Tag tags: - Sheet + - NoPaint - type: entity parent: SheetPlasma @@ -132,6 +134,7 @@ tags: - Plastic - Sheet + - NoPaint - type: Material - type: PhysicalComposition materialComposition: diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml index e71a163b5d0..96fddefa3ec 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml @@ -12,6 +12,7 @@ - type: Tag tags: - RawMaterial + - NoPaint - type: Damageable damageContainer: Inorganic - type: Destructible diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 8e29c3c27e6..5b57d930b03 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -15,6 +15,9 @@ Blunt: 5 - type: Stack count: 1 + - type: Tag + tags: + - NoPaint - type: Damageable damageContainer: Inorganic - type: Destructible diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml index 5678de6bafc..56786057d57 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml @@ -62,6 +62,7 @@ solution: soap - type: DeleteOnSolutionEmpty solution: soap + - type: PaintRemover - type: FlavorProfile flavors: - clean diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index bc376df5eab..ccbbcc2de84 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -78,6 +78,9 @@ malus: 0 - type: Reflect enabled: false + - type: Tag + tags: + - NoPaint - type: IgnitionSource temperature: 700 @@ -156,6 +159,7 @@ - type: Tag tags: - Write + - NoPaint - type: DisarmMalus malus: 0 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml index 7777153bbac..f82fe8b51bb 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml @@ -31,6 +31,9 @@ sound: path: /Audio/Ambience/Objects/fireplace.ogg - type: AlwaysHot + - type: Tag + tags: + - NoPaint - type: entity id: LegionnaireBonfire diff --git a/Resources/Prototypes/Entities/Structures/Holographic/projections.yml b/Resources/Prototypes/Entities/Structures/Holographic/projections.yml index d2a5853fcb0..b8717cf6cc9 100644 --- a/Resources/Prototypes/Entities/Structures/Holographic/projections.yml +++ b/Resources/Prototypes/Entities/Structures/Holographic/projections.yml @@ -25,6 +25,9 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] + - type: Tag + tags: + - NoPaint - type: entity id: HoloFan diff --git a/Resources/Prototypes/Entities/Structures/hydro_tray.yml b/Resources/Prototypes/Entities/Structures/hydro_tray.yml index 1ab1fd5b2fd..43b8bd197a5 100644 --- a/Resources/Prototypes/Entities/Structures/hydro_tray.yml +++ b/Resources/Prototypes/Entities/Structures/hydro_tray.yml @@ -92,6 +92,9 @@ - type: GuideHelp guides: - Botany + - type: Tag + tags: + - NoPaint - type: entity parent: hydroponicsTray diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 2b11b7991b0..b148b5f32e4 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -874,6 +874,9 @@ - type: Tag id: NoBlockAnchoring +- type: Tag + id: NoPaint + - type: Tag id: NozzleBackTank diff --git a/Resources/Textures/Interface/VerbIcons/paint.svg b/Resources/Textures/Interface/VerbIcons/paint.svg new file mode 100644 index 00000000000..78a56e3570a --- /dev/null +++ b/Resources/Textures/Interface/VerbIcons/paint.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png b/Resources/Textures/Interface/VerbIcons/paint.svg.192dpi.png new file mode 100644 index 0000000000000000000000000000000000000000..b7bd88245f2ab069dbf5a39850a866af0c5dc174 GIT binary patch literal 15653 zcmeI3Ym6J!6@c$%At6fwD^Z{%uuL393Tejkve)auHrd!ob^#~b&7$l^6=XbfZ4a?$ zoSE^icLQO0mqGyXgEvtlDvC%zAkn5$c`8a@KR`fGMTH1e8(?XniJ&}$1l*bN^RD+2 zK_vcM%O3mObI&>Ve&?R!`7^f-ZQQW9Ye^RXVDaEUY8ZX5aX+WcN7pCx9q*v8g{6Tp z8-NRLbw3?<9X$>KxOkO5GU|+`FPBxb7*sSf1A~=f3C#u|c1fkAsN12#WMED=;(`DD z@x=h6Yw^IAaGFn-k}$6iOj&UA)W#8YYP%ZM0+%GZVig%VC_+bJD#e0f%awS*^DCp@ z?l2c%yeZE1c%a`6$c(0kn51bzCLHW#RlYaEM4~|<9EpS?s~M3O!W=Jg63>c~9OC7W z$W&i}L>KxMv$U){oLXNEhopEQ?>Hry<0d91f)i5Ev~rvfjYc_Mg|JMlok4OD+s_f;&_0~vOe9iqrploBTdd9Iz2rq*pr zWvk#VO;b5ofJJCHHnJ1i+LiLAW7>Ihj*xZroPkkJhV@+DvFq{MuG#YDv|R%7kWQ1W zi^aBugabg z<-_PbBJ|3l^fO+NdA`YmKMiw`HZ?svS#uC#dC_&i^Kv*OhoW13^QK9-IjTHOag^_J z&^)clg{I0`(<&-XLN6*g$d!y-jB5hvH6m++G<$_}5;(-dQ z!K_j)IDtem$umRcOhH$flvz}CgGoV6R~4^5tLI5|^Q4bMRC+}d9SOJEOP-D9nQ&9k zGQAYXIJcw=dg~he(blDTdz-b5SiQ{Tq**X6L>nfg7}svNiAcTl%>G|h4MqM=|}ffstSqQ-{9s>Eth6fBd;s;r=9#7L-5vzZbuw8|-Se054j$BE zT+3)#&A8*Rs9&9t~OehxY&!OXSB8|`Y+G>Wo zZ(n&QB@4DP{=;%`VkPxGa?qwn|9Ba2S^Lq|JnFKj3vsoBTN^mBr1`0At*Ba+&aS|i z`?oBk>P3gi>)Ht+#?2aQME2*8x>Z;xdsQYxMIOC4!M#>(oAtQu*mu}A>v7wL=@o|V zB*bbsY`DqFy?gNg3pS%$WtivVor!L$??281k9Tx33&mg2&IkVT4%%m>p_To@3tNO6(*U|Na`140%lE;1iXOL1X9 z$Rfo>=7VV|E({1+q`1g@FfGM}0U?VN7nu*HrMNI4WRc<`^TD(f7Y2kZQe0#{n3m$g zfRIIsi_8bpQd}4ivPf}}`CwX#3j;zHDK0V}OiOWLK*%D+MdpKPDJ~2MS){ngd@wD= zg#jUp6c?Efrlq(rAY_r^BJ;tt6c+}BEK*!#KA4u`!hn!PBCf8+1F+CQPrXi{$6Mb! zmwOC78q25y!)XAfRsb-4BLH81gT7|~DDwb(xD5dLegMuj@44#GdH_1ggQ@`d;nH-J03Zr|Ct&;EP&wwn%Znb)2De;!;OJaeCP>1qFb`z&qb zg)8@-^X`%LZ){q!ck-1BpXgZqbmESl&P`|DK7V@ADIfoI-Z{G~r|l^uJHXQD`dFJrr zYaZS8?4R>VcFlroKUneXvSrhEzG~gOYt5<*A7mc6YOA>XZxXv~Xw}p^pRE1e-k*Ri zrS~5^-uH*Y_pQ)xy78qiuK-iITc3L1-+S`XlSgL0{?D<$Ja+2N$%Dtaf!HnY9TInZ z)bZ|<2bV7FeDvz;xy2v6_Sfs5(R#+um`?6F@bIGFb{so;PtTRY>VL&{pML1-rJp4h p2EWO5KELuJFpt6C3+_AuIgtw=ow{Om$WDZhO1a zb2ELndlv}NAi?0LfS8CM6uby02@z2t!6<475|9%`OpFHo5=^2ZA<_8dd_CPW)jK`UfrR*3;3-k>tqICz>+em=Y=u^wicy2c#`)`lhXpkVkb= z3-oH#o~i%+i67T%k(#OB?kwS@&b(fW4xZ}j!>4Xpk*AJIU)67F?VH|ef`JX4h}zc1 zdb`)$nyL5gHldwfZq#dik>u!1eKzf|c4X;LZ9eYmnlt4hiCwSe`BTL4JlngmMlo?3 zm^Bzilr?SKv}rB>s<-w*Yr3n#=JLXUygT@7roNUWoo1u4xw$#D$)@7&YJ>Q`-@vp% zDFO-9JK0Xe7Hapd&5R7}Ea;x>Mx7*z+qKlL2;$?(Oue3VH2lh+YojylsNKui0ma6a z=rqU_Zj4h>a;Vce-d*nxP05B{*BiQ>^gxe{)$6RqN!(kDcjMt zV`FUBy=4ChK#(IHtJzyQ+0l(<-HVTRrQUx69_89`Pv1+Hwed~~Qy@OpP!ff^*h%l~ z3PEp0yQrmiGPVAk%3M7S(3MR3Z@U6_gEJzL~Wph4WkGn7;-D1;? zahJzxEyVJ8x+-SdQun|Gy@G4NLH%_EWs=0`Zm98oLx&Yj1By?Q6xt0IUYu4|3C*5EOU{o`o_$bS8D9B(~#lSHcd=av*}b zHUjS|&0L>prY8iVm=pn15^)jAY}CS*0&#~?N{PhqFq4{gsV|5Qfwjt)%wElFC173n zo{xN@`wJabV*r1`Dr5nrj_Z45!cl988r&6qAi3$i0-}8>Yz0%=w;i0%kUd(BTIfJY zm?6M=V0w>Yh*q|uf`{o0*=r!0Q4-j|I&|_GB7{egaI`JB;vpfEZUs8juH)MRF;C<{ z6?OrPd@g(k0xs7=O*lhBN+{znLu?m-_k*gyA@B*Yb{Lb?3nn)0P}ry-Ji!3-h5qJ8 zy&6Golx>auCWIcrh3g{bxFNCw8#YAD8RA4~0lOu)`Gq$DNS>w?Y_^VT3(0+rT;>Ic zxi)OEQaDJ#ULtKGWT?Xnavc~80vt#c5+p+HBjwm3Y}Elc$qm35w>2kDX=9;7ly74f z(SQr&2-uZi_vFZ>u2fiZ>G^(nV}e5tC<$^w4ThJ_QO_nQWSE8$Q@{!N(zqlgyShF3 zDP~sZOP$g@bZM!%*zP3)GONXfR!S-Y>^$(L!y))PfdoDt_z*S~jUX%Gfu^pKX_(%j zZqldR^q_YzJwAnEl3{qf5cU>Nq?21iM*-{yI8bT&VpbtSaW9iW}bES3$3P*Y4O>TY1yG+iEq1VhBefj&IF>J?ktC3hx+b) zY_93{`O22eR9|X$BfU6GHbqy@u0m>GrlQKMZdV&MKYo?$i6eUA#KpORN#E6i`-nOa zbv)O`h)d-l7ec&ay8-c(H*j;oi=&U2K1=~HRF4c{l2(uDv4{<2U*YcV%G)`XC}_*z zH)E4)qP?nBt1%eP;KJyL-pU^19)~I}q$r@ijXVq+JXa7FIqW_j3qt9Ku>E9UCh!VPP5=~@EJSb&0vSqZLk(;b4?!{Ja^LZB zh~aFcT9m`YAUhyik1iq!xoD>`PjUh$Oo&RTxOf1Gyf0kX1*PXO?gU|&o|R4Hv>m6O znU()X_VL!*{)Qo905;DkM+2N0bs@g=qsj$>XBuCJ!FJF|!NzDu|HZ9-?;`2j=b0~h zQI;$7szsSc(ro%}ukr?!6T_lJtHMWmuGGRk%t(G9nc&!=gm1!c_v25vgz)7A0C0t`d-pNQKL=DAB5Lm4IYK zDqMy|iB^TH1SBI;;W8{rv?^RBAQ_Pgmtj$&RpBZD$%s_A42u%23Rek8Mx?@JSd?g0 zxJp1WA{8#fqC~60RRWR`sc;zgD_hUK^6Yo7`035>`J%WcxNPn4*+Y+Aj;{Rq((B*&*6ict(+}VI`5S-o zm9xvM)?qR`_0*qUSbpx?&wTjz_jiBshwHl=*KJ(_cD8-pe3~-vdk> zqc6I@zT~T~K5{DjrT@sW-~RQgZ@!8b?@n%b<-Pp2yWiM+_O8R%9=hxG7scn!T>8M7 z$IpE7nJ2D!v|j(tmskG$Lw?QNHp|=pIQ#L39=h+H|N6{R_xa$nh^vpY+|IxEAzV`g$3;%rbx_e(fc4DabzVAN??Q`cYweJ7HYcK!i>Yw#TzqtRF Lg-7S^{K9_#hzGN8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-right.png b/Resources/Textures/Objects/Fun/spraycans.rsi/clown-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..27b68e2cfe5d2b364b379707b887bdfcb115eeb8 GIT binary patch literal 20783 zcmeI4Z-^vE6~OzF5cJ|fFa!@j#9@LEHMLWJ`d^K^lkDx@c}Lvj*lfsMz~ri{s(0Jl zou16h-R_++UepMRD42Xu6Uc!V@effWLBU*%ND`2!1VM;?2+{cAA^|~<12K5Mp6;3I zot?ew%&nS0FYIn@_j|9Zf3ND*d)3Q)asRC!+p+!q+bzr5v2X9}Jp8^n`F_`x@bi1o zy|2KpcenN)>{!-S*CgNDtZzQL)3R=RJX$!=Juvt2x{OyQgo*<_v9{6zY0H|tX{{yX zVco3+dNFEFRsZ(%v(-wZrmA;3b9k;bqnDz+$J_e$1-*{QEhReN^p&`vJbs+FEd_wZD8I_a=-U~Ye9CT{DBGvOkMU9aNt3F3I3?cG?R zm^d}eY78UF>Nc+1w32;Q8{45Z*;ZkFes)jR9sD&_UFvpQ^;&Ipb#-EuO~mcR8sVJR zFs)IFKmv7+HM?RBH9Oa*M*4PUbw{?NRyT^9mBg+H;v?OuYBlL-@RdE+N^8(jvy-s{ zinTS-s*wp?8>OV=K&N%2z1$m`k~O`oS9G)60X;HOueB6+fJ}-QLZ2L^qua!Hr^><3dBbm>PF!v zc9J`rLeOi`CThu@RIN9sQdds`wE9e2i*DRrh~wo(I$ihoeNma2=}phd4R=OO6|Z(a zG+7%~F%!pF)3c(h8x+G_Bp6}3K%6>r>csm9CUuN+MpDTk8*{OW!ef0K3=xW`y+GVL zV|9n!m8$22hSgyWD$x}eGmsa{nNYGG#_bi+ZA2?#QP*0{#mSnvn3Eh#%KA*a9JgUa zy1^!Eqb`rsnvLa=WK~QzrS55bYC8ATxpgreBi7>^6Y@dM(!6eI1~_STNtQ%tdd2rw<3jI{V*=qyYK>hAthKkbqvl$%FaWE8=0Rp!6M_Oy!Lv}tLuXt!N@6>X?@HK# zSq4N<*GAx7rJ2i_W_nB@ib)YLC6O;enT}f6QXt=9lu{xwJj}SJUCIT~A+T22lG&Ld<$`1({cPr4Lb{%dD z#5|D&RoDeE;=bSx1YBPWHRcQnDWS}V8DhHtycbjj4uMaIwZoXCUNE+4hr&h$;Ryzq zE%diO>ct3hqjYQRH6io}U$`z}jvFF7uwg^QK0`iHTEK4U+y2Ej0Z5)E6l}JRYYXXf zja=phh`Bawu~Ill!CoS5B4nt;i*oH73j!QS6%r&u&5?5K5Vq<7oa6>z%(t~qoWjOJ zhbV4i7tz2M$PutB!S3lJm%376=}V9E;>LszIiMuS1vMC6GDkg|ppao2N=yMK zg5Iw0U4PZ2puG~ZCE6XG6i8FmeuXrsIn#VI%Q}07R$a)X_-x3e?9i~p*IhNk8fZ?Y z0?|fy7DKQ>ee*t6mvr+&WlL(RC$+heP8@buMO#lVLTX>6qDrl-7ao@-;|OXVOJLcC+U0r8aAcXQ5*!;hFeOb#$qj|^dwRFBEAhz(_5 z?(XKw>p2!EXp7)CW0Omwxu{j6)*nv)!tjXR&K~nUAF8;JqJVN6c^Ec$UqMdn!|vm; zAe20W?I#5@hR4_f#`VN$q;y($5bxsVQQdwk5f9?3fb&ER=K^6nUa1U@BP!jvB3iBG zNQxws){Zt+b~-mG;``pq+4vt$P^H_^TL~(;QOZ81$~)|tFFB0D34o%Kg$U9T2RWe) zHLy)Q1jU%^bBE&)!`Vo=Cb5wvm1jRq3-4Y_Ha6%7Su19@h zhi(Y9*H}*5aO#;_`F~^|Z?Eld5F+|uvy5^$z?o6!;!7{8TqJlV@pTYv`<>)$40rTj z-0HV3lD>1E`H~l9u`;h*lvyNAr|kZOD)dRUPhQPz4iStYjEdk@yX<6 zU>&M^s5E=2U<1x;H(bkE>)kt$RO!n{dR4ebF?`iX@-tJ--yT*ycwyE2?O|1;c}Fwq zHsIAmZ!klbb6Uw8%k;(C?eLafH(bYIaE>*6d01nAw5FF2%tr8Ht&^NHxNs`h(;2=o zQt{wsin$w^V!CW{@6&sZNw31&diY0y+TcG0ay~QZ%a*sy6K%O^W3cSRuqev`SnBAQ_Pomtj$$RpKfD$%vG=42uG-5?298Mx?}LSQKcLxC%frA|)=vqCl&} zRREF^DRCJV1zIJp0+5VIiOaAk&?<2ifMi5UT!uw~R*9+U4^cBdG6fF zQ`3LndFQteJ#gcRTb91de(|LB-~$!)=1sp^`OAI$vFc+#d-|zIzhPgOd=%iiueJBy zaV_hx!`DD*&Ke(IsiPCeXx;|o7|r>D>E+xE#9U*7TjGl%Z3e7O4O>ijL&o{2tv!z({*qu+k@>UH+l|MBp1zdjwE zc=R7HzINu>%f1sW|NDvSPCjw@Q=i!O{og!u|F+f7eColg-#mWszO!Gxr}n~YX9v0` ie?JNB^XD(OKJepnS6;F36_MQ9xBJ%FAM85x+5Z4lF0`ir literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json b/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json index 0f883ee2801..f34820cec45 100644 --- a/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/spraycans.rsi/meta.json @@ -58,9 +58,6 @@ { "name": "spray" }, - { - "name": "spray_cap" - }, { "name": "spray_cap_colors" }, @@ -70,6 +67,22 @@ { "name": "equipped-BELT", "directions": 4 + }, + { + "name": "clown-inhand-right", + "directions": 4 + }, + { + "name": "clown-inhand-left", + "directions": 4 + }, + { + "name": "spray-inhand-right", + "directions": 4 + }, + { + "name": "spray-inhand-left", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-left.png b/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..ad3ad959de4138922ea09c84316ad9369fedf403 GIT binary patch literal 21199 zcmeI4eT*bU6~G%61ILFZ5K;bdTr`SUJN3~YRm1GS-tL`u35)0667CKYuDYsvuVr_7 zn3=oXJBb1O!)S=2Mx!SZC4MAEi5mFBaK=P}e+c}c5s4o$l3-MVL5+!GqWF4xW~ygy z_O6-RC?q|}?$&m{_p18$>f^mPllj=b8{d1$jw^RKj&sT0J&Vim_xkkbg4e_EZ^pO( z7XDn=+H)H5wvKAci@W>X!GCizN4nisty(^2iX)s2&8CABNV z6sw!ERVA~yI!Q^Zp-$^)d#yJ#t*U0ttea-H1A1hlUh7EGO*%)CExH`u-m+ux$7;) zVJE$_B?Pk>Z=sgn$<%stDsy!mpw$-IMs}0-N|LNKvgx{S;ET$_LT`Fj-gR@_)X7HY z>Uwot#ZDY+%`D2UX;2Jvkzj=B3h`>ptC8RuOllbCjAW8yHkJ|{N5=;?7$Ou=u|oVB zp*6w}W$HPhadlXOT6X2x4CKXfCbX(WNqb#(8}YhaHPu#gwO+LsbCSbJSzAcfk~WOU zG+4bl>GDLa#Y7!VSH*l&nGU$1lXDF?sJHHS%GJ@UgBxpcgY4!zcM6Z~a+%;e7sx!_ zIq!SEhv(;oPj~ZrHC^E#XnXJby?;I1wsfLmU9+RcK9`=N24NF&M2Im+a_S=?IK@n0 z#)LbqX{Ds0U`zQN<_0kug&2M*-(Vt`%dt>Xnq~nDa3lmWzKSx_TzMW+t}vWN+NU&} z(zGk7#*s8gxIm>hY929)A`(hXv1>4%7Kq0~!hMR730!x3M;&OUSx6+9R$iaA0X{0= zfuuB&jL<282(664J_Xh$*xpeKqgfP!)zFBrZ`u%o!a&2bP$nX8S~n`J^x zp;j(3A=1B#?)T*)<$2$}XMFp=>+;Y!2; z+22&*6u?MuDLmM4xsiIx84^)K8HX9-`T)GQsY)CHp9mX|F-3!LYSSKtg9^4M7+`;) zzx+|J#t<82M`N!ETaV<@_Yw2_2)UsP2O{PSaiWcc(~`UV?1umZPqP{hThDi;;=&-G z1tDU-3rDPy9@21@D3?eTndq!s2gbq>he}5T$;b$#JvV}*Is_;AAsFMX;lwL!Eb@pJ zF7^=(xkR3XQwdH_j(qAXjTKjc5XFs24l$r2$OknTUOGnum!ODY8YxTxC&WvWf|Ts) zXz-Jdtj-iVrE%!eQf+Uu)0Gfe?Okl7q+-Ah@r&{`48`|!fUtB!Bp6jFfOs+T!yr)F z??soR>Nb4pn;!HIrnd;8VE5#a2ABALZw7*Yg@!nX7&r6{_&WlM**J5?PV4A>?HCeM zxt5a|cs)?y4CQGQ1rbaf=6Wb}!Syh$m`j*zL?8!UOfYj=S9{_#1U)_)EA~Rr{pR-3 zt2PAfrI0Pz?wB+|nwuG9NW+{ni#PjGXD`vJNtGs_4V9)H2A259RXePqW<3*#Pj_cA z1gEKQ-N(ie(>zn!l9}pBZEd8JMBNS9HuI|x+84>FGOMF%@c> zU_KX~z!8SpNVzD7fk9RvTZ_*j2svk`u|RPG6(&R#WL!LiKwd~6PC*rTjC)}erM0rD zoR(HS-L&e-+!~|hPP&ad=~*_ml2&EFnZJ$Po-{Oxg7t9es1?l$24qTXP}F6Xq;x0$ndL-)f+D@?=pJO;(b@yp{H z`{GTrc3?4vcSAdAama_Dsi!l3W1`~0trT-N`ij}I$$bc-=a}p&e8d7i-BBHWyd&o` zyHjp^%RbSzo3;kaPArQ8trAxONLHl8WmyzxmADE(vLYoe%c4N5#8m*26)AC976n=* zt^$y(NQuj`D9|c#6@X+#N?evjfmVsD03<6?;<79Xv`SnBAX$+Tmt|3)RpKfD$%>S? zEQ)qoTJ!ro;&O~wa+`w70IV>eQY<#zOZ+3er5Al&;RnfmpqCu zUi|6)d*4Fux$?-Tp*^Ug1S?#}!7{0M*M3s<-6 zm)Aji^mTaopZYrfLI}hLeU!MjguK)l5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-right.png b/Resources/Textures/Objects/Fun/spraycans.rsi/spray-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..353e47c56fa71b948a6443d4a3a850de2edcb5c0 GIT binary patch literal 21213 zcmeI4eTXDS6~H@+A31Y=Bpw$PbeM2*hpCB-F8?c9PG4T>RXf(VA-UxpxvANYqNMu;M(e~3XeL{A}qm_NjeC>s1DVmMz<&rJ2q z&E9q97EPcRcDJ_sy;s%0SM}<>>R}%~cK1j3?s@+n%d++^9bGsMzwb)E-~Lwk`OWBo z7vR^;jiYB;mi3O?lJA?WC%$&OW!-x=S~=Z5z5Ma2j5nu*iUU2>*=&HcWlbOMG=yB& z?NXpuqxwww?@vEdE=6jle9BqI%Z+)x79HJg>J!^{ugL9n$yNDqZO?S43I;ZHTa-GR z8}(MTGgI!`twKAwTq&2jBJK5=@@&#!>Gblk(tO<1C1=V-61!f><5R@(JlngoL@{wH zm{k}?lvQn9wP~sMRj%!U)^t;a)#D3`z3$+znetk@-KbV7TU%RGTWl(Bu2u-=yn<;mSBAB?mfpzpEc z-P|18b*p{kJRs;H9jn<|xzNy+-a+m_~v4+}FxPzVK z&W;dtC)zLZj-i29;=w>lw(3WlShp4ddpfXxE}mv8pSL`s#GWT+B!gCS`R#-iVtpB3)zC zm2sEHYAwX_T(T-=>r%JC1+9#0z(L)0KPOg)ul8?jL^ZPL&&_d%9kChr=jO>Qott$X z*TJ*1+@*{DbR}8gAZU8;`Mq~N4O=Rfk*b18nh+Ft3Z8{B9y*h{Q4-s6d{@E_%z8is zb!`ORRhqe+X{ILxqL>r`Qxf?ilUnQ+t^qK5B^E|8w--2$Sy6t;pX&20zwX2>qBMlE!p zB+L+C-8a2UF+?j{QQ?Ql4B2fUno$ziz&dn#GeiiFBH?IT_{t9nnRF}Ap>`c^3&cFp z3#zaSV8ngF9SFF-7HYy75>i5$4>QDe0eCm43LFBT5Nn4qNxfiV(+-7=3c?c%u(!}( z`>0nV$c@sivD<{uBYfeyh&gVE?7)T%5&I1JL}>xLrEmM!-vl6eno_XYI<76G&oy$H z7a-=^u*FK@AO(Agw26?R4zJ6#Z!8FKAXP|^2sKB_u|wFZ18|ZXfHB|JK5=pz3mu}k zja@_oUm!=ot^~WMk6h|Xg{3b&&hr}+KIDLsAQ#kNc*z{~Y=T0DX(%xToRBY#OH$IS z!^uxJv$|I5l;oky%hjcNt1Te2T3Vgs5eq@WjubH#27dO|-i zeAT3&-4e1Pnk}6aNHgVrg*2!+(|og+b#@D_s*p+XS(8cGp<#&+T{XiRXildB(bewE zhu|vpo%`5Y)AehWEvc!l)Xqj)aoFAxO+C8`sePV`Dz!SSHfnzSD%llBw8Z%va|4sG zaD>khbs*|^u8ol|m4jRe@s8~V#8Y11%^5F_K4S7P8Ng6IGK5J|JtoH@Hk5styE`in zbIeiD=D}~qCf7uLRjXR1Kb-!B(Gk6#J?48pRB<6i0p&LGFvLM$K~C($?&Gl_lsts( zCj~Qs$Jh$S)x>F}a9Vp1@8bG--Fz((58|qT^F$5j0%0@WEDeq$O6|BL8jX!eiX@a) z&ev7%bZ$_@_q~^~@jslPN;jj|5>zsylzmL4H`p`Za2SOX07WGW5gda!$O&zzfoS-sZo{Uu;Rz%WWjHK5Io=JQi1lzv%GBN568A;zb&wRs+GUS+JsK1cs!Ams_ z@f6OrC=^MA6YjZ^1_}=PibeUlilq5!^NF1{^!_Nd^8cvgcCC;|%ZuUCykG8LUCQ^* zEz-PfG07&AQx}M7*Qwm0B7;|y)l3o4KTFBG^Npwv#m=5})q4-N<8ey9iUx`Zn|ge6&K>T*qN>_&9ob zRO49G(Ho~1B6u6xN)CryI7;p6jNTZlcxETX%#EI6x@DgU2mBu+I7>$V9|+Tk)u`M$^prU6u1nF9IXOZ4oF6%z-3tEXcf3}Kr$i)F2f>6 ztH6~5k`XCz85TKO1+E;Bj7WjYu*lIWaOHqxL<(GnMUGa1D+eSaQs6Qyao!y-qkz?B1%5h-vP7CBl4 zt{jkzNP)|+$k8fr<$z>F3S5Roj#hyy2P7j>;4&<7v_V9D?uM|u2 z=<#LC+CE@e4?bjBuUvuOf3d7{*s?C)Z&}sHEo*=L7@gIqE8+?`J?wf_LBpPSDtRY^kPl@^vvbI-OBgh z{6SD3{_5Ozavy84M{a)X_&=Y2;?7_G=HjoGp1rhn_)R~H=tx2X7U8A;nEvG=NTxeNJV7FiDb@}tVsWL9Qxep_<$e?f*|}G z&bh+e{o=ud@ZRr?zG+8zGDBw!FAJcx-keiP)H~D{7-J4;N1z(WIb#@xoyI^lur_6l zEA$QkK;QQr04&QwFXX8Pu7GMlDFpzCF_urQ6{>-Gp5La`xN5D?I}k!(M>pKQ0}`NP whMaT7TD#GvX~H;;&F@hGYKZljF&6;f1$#d@3sMJ{VE_OC07*qoM6N<$f-h%ecmMzZ From 87460eb3c62265764ffab2c3a61886580f7fbfe1 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 18 Mar 2024 22:31:34 +0100 Subject: [PATCH 35/54] Include bans in adminremarks command (#26240) Fixes #20645 --- Content.Server/Database/ServerDbBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index ed22c66360a..c6d53119888 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -1368,6 +1368,8 @@ public async Task> GetVisibleAdminRemarks(Guid player) .Include(note => note.Player) .ToListAsync()).Select(MakeAdminNoteRecord)); notesCol.AddRange(await GetMessagesImpl(db, player)); + notesCol.AddRange(await GetServerBansAsNotesForUser(db, player)); + notesCol.AddRange(await GetGroupedServerRoleBansAsNotesForUser(db, player)); return notesCol; } From 7c2e22164eea52f3fc6e2484db245ecef06d3623 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:34:34 -0700 Subject: [PATCH 36/54] Adds craftable ducky slippers (#26138) * Craftable ducky slippers * fix node --------- Co-authored-by: Plykiya --- .../Entities/Clothing/Shoes/misc.yml | 3 +++ .../Prototypes/Entities/Objects/Fun/toys.yml | 3 +++ .../Graphs/clothing/ducky_slippers.yml | 22 +++++++++++++++++++ .../Recipes/Construction/clothing.yml | 11 ++++++++++ Resources/Prototypes/tags.yml | 7 ++++++ 5 files changed, 46 insertions(+) create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/clothing/ducky_slippers.yml diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index ee1708caef6..d1f6e083f40 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -47,6 +47,9 @@ collection: FootstepDuck params: variation: 0.07 + - type: Construction + graph: ClothingShoeSlippersDuck + node: shoes - type: entity parent: ClothingShoesBaseButcherable diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 991d2302a95..2baa541beb9 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -593,6 +593,9 @@ requiresSpecialDigestion: true useSound: path: /Audio/Items/Toys/mousesqueek.ogg + - type: Tag + tags: + - ToyRubberDuck - type: entity parent: BasePlushie diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/clothing/ducky_slippers.yml b/Resources/Prototypes/Recipes/Construction/Graphs/clothing/ducky_slippers.yml new file mode 100644 index 00000000000..e017096fa90 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/clothing/ducky_slippers.yml @@ -0,0 +1,22 @@ +- type: constructionGraph + id: ClothingShoeSlippersDuck + start: start + graph: + - node: start + edges: + - to: shoes + steps: + - tag: ToyRubberDuck + name: a rubber ducky + icon: + sprite: Objects/Fun/ducky.rsi + state: icon + doAfter: 1 + - tag: ToyRubberDuck + name: a rubber ducky + icon: + sprite: Objects/Fun/ducky.rsi + state: icon + doAfter: 1 + - node: shoes + entity: ClothingShoeSlippersDuck diff --git a/Resources/Prototypes/Recipes/Construction/clothing.yml b/Resources/Prototypes/Recipes/Construction/clothing.yml index 4fe2c474bb4..f1eb270af73 100644 --- a/Resources/Prototypes/Recipes/Construction/clothing.yml +++ b/Resources/Prototypes/Recipes/Construction/clothing.yml @@ -85,3 +85,14 @@ description: Two huds joined by arms icon: { sprite: Clothing/Eyes/Hud/medsec.rsi, state: icon } objectType: Item + +- type: construction + name: ducky slippers + id: ClothingShoeSlippersDuck + graph: ClothingShoeSlippersDuck + startNode: start + targetNode: shoes + category: construction-category-clothing + description: Comfy, yet haunted by the ghosts of ducks you fed bread to as a child. + icon: { sprite: Clothing/Shoes/Misc/duck-slippers.rsi, state: icon } + objectType: Item diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index b148b5f32e4..41f61eeba16 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1185,6 +1185,13 @@ id: Torch - type: Tag + id: ToyRubberDuck + +- type: Tag + id: ToySidearm + +- type: Tag +>>>>>>> 6694f92171 (Adds craftable ducky slippers (#26138)) id: Trash - type: Tag From 12d932bb829409f6f22f77eeb396a775c5b525a3 Mon Sep 17 00:00:00 2001 From: shamp <140359015+shampunj@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:47:39 +0300 Subject: [PATCH 37/54] Zombies can very slowly regen heat and shock damage (#25925) Update ZombieComponent.cs --- Content.Shared/Zombies/ZombieComponent.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Zombies/ZombieComponent.cs b/Content.Shared/Zombies/ZombieComponent.cs index 5ae441b1327..cdf00f56b8a 100644 --- a/Content.Shared/Zombies/ZombieComponent.cs +++ b/Content.Shared/Zombies/ZombieComponent.cs @@ -110,7 +110,9 @@ public sealed partial class ZombieComponent : Component, IAntagStatusIconCompone { { "Blunt", -0.4 }, { "Slash", -0.2 }, - { "Piercing", -0.2 } + { "Piercing", -0.2 }, + { "Heat", -0.02 }, + { "Shock", -0.02 } } }; From db3b3193d30644e07000b200f93e3e9aad5e64b5 Mon Sep 17 00:00:00 2001 From: Killerqu00 <47712032+Killerqu00@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:57:36 +0100 Subject: [PATCH 38/54] Initial Infected can see each other (#25934) initial infected icon --- Content.Client/Antag/AntagStatusIconSystem.cs | 1 + Content.Client/Zombies/ZombieSystem.cs | 14 +++++++++++++- .../Zombies/InitialInfectedComponent.cs | 16 ++++++++++++++++ Resources/Prototypes/StatusIcon/antag.yml | 7 +++++++ .../Misc/job_icons.rsi/InitialInfected.png | Bin 0 -> 4268 bytes .../Interface/Misc/job_icons.rsi/meta.json | 3 +++ 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Content.Shared/Zombies/InitialInfectedComponent.cs create mode 100644 Resources/Textures/Interface/Misc/job_icons.rsi/InitialInfected.png diff --git a/Content.Client/Antag/AntagStatusIconSystem.cs b/Content.Client/Antag/AntagStatusIconSystem.cs index 5d87837893c..804ae21ad4a 100644 --- a/Content.Client/Antag/AntagStatusIconSystem.cs +++ b/Content.Client/Antag/AntagStatusIconSystem.cs @@ -22,6 +22,7 @@ public override void Initialize() SubscribeLocalEvent(GetRevIcon); SubscribeLocalEvent(GetIcon); SubscribeLocalEvent(GetIcon); + SubscribeLocalEvent(GetIcon); } /// diff --git a/Content.Client/Zombies/ZombieSystem.cs b/Content.Client/Zombies/ZombieSystem.cs index bd89e978c70..7c1fb38e744 100644 --- a/Content.Client/Zombies/ZombieSystem.cs +++ b/Content.Client/Zombies/ZombieSystem.cs @@ -15,6 +15,7 @@ public override void Initialize() SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnCanDisplayStatusIcons); + SubscribeLocalEvent(OnCanDisplayStatusIcons); } private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args) @@ -36,7 +37,18 @@ private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartu /// private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args) { - if (HasComp(args.User) || HasComp(args.User)) + if (HasComp(args.User) || HasComp(args.User) || HasComp(args.User)) + return; + + if (component.IconVisibleToGhost && HasComp(args.User)) + return; + + args.Cancelled = true; + } + + private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, CanDisplayStatusIconsEvent args) + { + if (HasComp(args.User) && !HasComp(args.User)) return; if (component.IconVisibleToGhost && HasComp(args.User)) diff --git a/Content.Shared/Zombies/InitialInfectedComponent.cs b/Content.Shared/Zombies/InitialInfectedComponent.cs new file mode 100644 index 00000000000..3200dd7f5ee --- /dev/null +++ b/Content.Shared/Zombies/InitialInfectedComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Antag; +using Content.Shared.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Zombies; + +[RegisterComponent, NetworkedComponent] +public sealed partial class InitialInfectedComponent : Component, IAntagStatusIconComponent +{ + [DataField("initialInfectedStatusIcon")] + public ProtoId StatusIcon { get; set; } = "InitialInfectedFaction"; + + [DataField] + public bool IconVisibleToGhost { get; set; } = true; +} diff --git a/Resources/Prototypes/StatusIcon/antag.yml b/Resources/Prototypes/StatusIcon/antag.yml index 757ec0354b0..87576f93b77 100644 --- a/Resources/Prototypes/StatusIcon/antag.yml +++ b/Resources/Prototypes/StatusIcon/antag.yml @@ -5,6 +5,13 @@ sprite: /Textures/Interface/Misc/job_icons.rsi state: Zombie +- type: statusIcon + id: InitialInfectedFaction + priority: 11 + icon: + sprite: /Textures/Interface/Misc/job_icons.rsi + state: InitialInfected + - type: statusIcon id: RevolutionaryFaction priority: 11 diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/InitialInfected.png b/Resources/Textures/Interface/Misc/job_icons.rsi/InitialInfected.png new file mode 100644 index 0000000000000000000000000000000000000000..3684fc448892d31bcbec4e9ed4eae0ae169c16e0 GIT binary patch literal 4268 zcmeH~eQ*=#(X-y~v0l`}Ak}_7bn|;emcc-D$CbTI{jio{>V0p6pBwb0e>F%~k zi!V%poAdMlWq7LJ>#+r093c73=m z(mSE!^J^`8-o>`etL@shwA@y<<+bxS{e9ztoqw)q`YdbZrS5vzMtW zr-|j2i&`>4x50C)?#L78Ccfs(eRTc3#@qwO8I#VReWO!y>^~6x-tdim+4Sy>*S=c$ zdCR7r>k~iDSRb4`BL}~l_?qp}( z>5jSXhK3m{j8h)TUpi++dt+l{X6n(*se!aS*R1(pJnG)lMSGI?t;8sRB!8vO)IvqeB|N^rRM#e({i8QmA7%~FG-+u6!zx1E1n|){fUAOFeE4z5Y;xf|>)0j{8K4&n}_gkwf zI;5h$4|1BW&R0%;*tPaaA;r3Pb9T{pIamAsm0#(3*}c2q^nmZ(oEH|ZnpJNN4h)?8 z>+-y{|GVYjKl$tTPv3EEUiXU-)x!hCF?(ybN=ns7D zXU4;?Y#lISokvwA2`qy*MW zWi1L^(o$J1w5%35(Nwg+P#EJ8AOtml$3nrd%Euh0IG0EFx|uZLaTjg1!&Kw(;w7>I zaf{hvrU+L|YGOrCL28#RZ?gvRl~{6Z0rX9yowlPVxR$QX!_w3#A9 zA#&s!s^)4!kYqwX`G#7J7AaW;Rk=|SpsNXnwcL@2Qn$xDM9@nLNTEo)C?wvfV4^VO z7im<2aUW42VGxE8R7LsGqx?0=KP=X$-1L!nIU^H6;fDC5ya#KKJ0mZThj+?CqaM`l zbeQyfc~KT5k&oZn?LOK^iGUC($PyOO&JutYZG^x98^dxoWD!Q7++kG%VFBtW1UE~F z!?Ki}5qyyFK^r724Db=0U7!ipXSLfIV6$6%wh@SX6bW@D2#(B3M~MhU2^I_FU;$yZ z(;Q);0VE1IiYBa#jim&dvDs~09Hq|#U+i@|OpKWtws?a;^UF#IwYn4*<)}J5R4s+z z5)J6G(RPaCXr_R*FcinKiR9wMrBG2(%jr~_GABnwfnS7}0JV-30s%-y!htwfpBNsM zgQNv|ry_FPj;i5H6bLj~sg~uS!=$ru9XT}YK?_O*8gK#)qG%Lj;VG7<3aV)vZ?W?< za~DPP)ChlBl>E&<=dCXvyf888Ws-{GH^)thHMImbB%TtFK`FkNa6G;&cpxN7P(c$E z<9Q-hVo0b5;Q&PYN3vlD?b1&)g9QRB?dJr71EQ6%P>hcNtj|tx0_U^&DS=}||7f}@ z`?V-gU~vHHh_pft8rKTHBRPljNBc+Xp*}%KGJ;}>VaY5eGFdFC?-+yC7Lq^GqcH9; zVv9lVC2VNxLOUTjv=t`x(l?!7@spf~Uoiqgj|Ul(zTmD*M626W7tlL#Pm#ZN;%2qAyci}SzJB)tqq(0l<_K+p8Dp0)}|c({aHoW-($NB zQmcB(%Q_y~aQ%Y^W?-SLhxX=lKj%5V$F Date: Tue, 19 Mar 2024 00:35:46 +0200 Subject: [PATCH 39/54] Fix SCRAM implant not working while cuffed. Incidentally fix freedom implant working while dead/crit (#25978) * Fix SCRAM implant not being usable while in cuffs. Also fix freedom implant from working while dead/crit as a side effect * Move check up to apply to all actions and do thing I forgor to do before * Change check into an ActionBlocker check that also checks whether the user is sleeping. * Make checking for Consciousness the default for actions Went through and chose what I believe to be sensible defaults for actions that had CheckCanInteract. * Fix typos my beloved I had an unbelievable skill issue * Fix major skill issue --- Content.Client/Actions/ActionsSystem.cs | 1 + Content.Server/Bed/Sleep/SleepingSystem.cs | 8 +++ .../ActionBlocker/ActionBlockerSystem.cs | 21 ++++++ Content.Shared/Actions/BaseActionComponent.cs | 11 ++- Content.Shared/Actions/SharedActionsSystem.cs | 4 ++ .../Events/InteractionAttemptEvent.cs | 8 +++ .../Systems/MobStateSystem.Subscribers.cs | 2 +- Resources/Prototypes/Actions/crit.yml | 5 +- Resources/Prototypes/Actions/diona.yml | 1 + Resources/Prototypes/Actions/types.yml | 9 +++ .../Entities/Mobs/Player/guardian.yml | 1 + .../Prototypes/Entities/Objects/Fun/pai.yml | 2 + Resources/mapping_actions.yml | 72 +++++++++++++++++++ 13 files changed, 142 insertions(+), 3 deletions(-) diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 31d092b25d1..b992e772563 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -95,6 +95,7 @@ private void BaseHandleState(EntityUid uid, BaseActionComponent component, Ba component.Container = EnsureEntity(state.Container, uid); component.EntityIcon = EnsureEntity(state.EntityIcon, uid); component.CheckCanInteract = state.CheckCanInteract; + component.CheckConsciousness = state.CheckConsciousness; component.ClientExclusive = state.ClientExclusive; component.Priority = state.Priority; component.AttachedEntity = EnsureEntity(state.AttachedEntity, uid); diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs index 5d1def7cec1..685b1087d70 100644 --- a/Content.Server/Bed/Sleep/SleepingSystem.cs +++ b/Content.Server/Bed/Sleep/SleepingSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Examine; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Slippery; @@ -45,6 +46,7 @@ public override void Initialize() SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnSlip); + SubscribeLocalEvent(OnConsciousAttempt); SubscribeLocalEvent(OnInit); } @@ -173,6 +175,12 @@ private void OnSlip(EntityUid uid, SleepingComponent component, SlipAttemptEvent args.Cancel(); } + private void OnConsciousAttempt(EntityUid uid, SleepingComponent component, ConsciousAttemptEvent args) + { + args.Cancel(); + } + + private void OnInit(EntityUid uid, ForcedSleepingComponent component, ComponentInit args) { TrySleeping(uid); diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 6dff8161eef..a914a8f267d 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Bed.Sleep; using Content.Shared.Body.Events; using Content.Shared.DragDrop; using Content.Shared.Emoting; @@ -5,6 +6,8 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Item; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; using Content.Shared.Speech; @@ -67,6 +70,9 @@ public bool UpdateCanMove(EntityUid uid, InputMoverComponent? component = null) /// public bool CanInteract(EntityUid user, EntityUid? target) { + if (!CanConsciouslyPerformAction(user)) + return false; + var ev = new InteractionAttemptEvent(user, target); RaiseLocalEvent(user, ev); @@ -98,6 +104,21 @@ public bool CanUseHeldEntity(EntityUid user) return !ev.Cancelled; } + + /// + /// Whether a user conscious to perform an action. + /// + /// + /// This should be used when you want a much more permissive check than + /// + public bool CanConsciouslyPerformAction(EntityUid user) + { + var ev = new ConsciousAttemptEvent(user); + RaiseLocalEvent(user, ev); + + return !ev.Cancelled; + } + public bool CanThrow(EntityUid user, EntityUid itemUid) { var ev = new ThrowAttemptEvent(user, itemUid); diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs index cce7b912c76..6d9242acc1d 100644 --- a/Content.Shared/Actions/BaseActionComponent.cs +++ b/Content.Shared/Actions/BaseActionComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Audio; +using Content.Shared.Mobs; +using Robust.Shared.Audio; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -118,6 +119,12 @@ public EntityUid? EntityIcon /// [DataField("checkCanInteract")] public bool CheckCanInteract = true; + /// + /// Whether to check if the user is conscious or not. Can be used instead of + /// for a more permissive check. + /// + [DataField] public bool CheckConsciousness = true; + /// /// If true, this will cause the action to only execute locally without ever notifying the server. /// @@ -177,6 +184,7 @@ public abstract class BaseActionComponentState : ComponentState public NetEntity? Container; public NetEntity? EntityIcon; public bool CheckCanInteract; + public bool CheckConsciousness; public bool ClientExclusive; public int Priority; public NetEntity? AttachedEntity; @@ -204,6 +212,7 @@ protected BaseActionComponentState(BaseActionComponent component, IEntityManager MaxCharges = component.MaxCharges; RenewCharges = component.RenewCharges; CheckCanInteract = component.CheckCanInteract; + CheckConsciousness = component.CheckConsciousness; ClientExclusive = component.ClientExclusive; Priority = component.Priority; AutoPopulate = component.AutoPopulate; diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index a3bfa071308..e909f0a8a30 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Interaction; using Content.Shared.Inventory.Events; using Content.Shared.Mind; +using Content.Shared.Mobs.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -370,6 +371,9 @@ private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArg BaseActionEvent? performEvent = null; + if (action.CheckConsciousness && !_actionBlockerSystem.CanConsciouslyPerformAction(user)) + return; + // Validate request by checking action blockers and the like: switch (action) { diff --git a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs index 46dfd20c34d..0024811c369 100644 --- a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs +++ b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs @@ -15,6 +15,14 @@ public InteractionAttemptEvent(EntityUid uid, EntityUid? target) public EntityUid? Target { get; } } + /// + /// Raised to determine whether an entity is conscious to perform an action. + /// + public sealed class ConsciousAttemptEvent(EntityUid Uid) : CancellableEntityEventArgs + { + public EntityUid Uid { get; } = Uid; + } + /// /// Event raised directed at the target entity of an interaction to see if the user is allowed to perform some /// generic interaction. diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index 51991332539..0c2fcc05794 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -28,7 +28,7 @@ private void SubscribeEvents() SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); - SubscribeLocalEvent(CheckAct); + SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(OnSpeakAttempt); SubscribeLocalEvent(OnEquipAttempt); diff --git a/Resources/Prototypes/Actions/crit.yml b/Resources/Prototypes/Actions/crit.yml index bdd09d697ec..705ee6ee6b3 100644 --- a/Resources/Prototypes/Actions/crit.yml +++ b/Resources/Prototypes/Actions/crit.yml @@ -1,4 +1,4 @@ -# Actions added to mobs in crit. +# Actions added to mobs in crit. - type: entity id: ActionCritSuccumb name: Succumb @@ -8,6 +8,7 @@ - type: InstantAction itemIconStyle: NoItem checkCanInteract: false + checkConsciousness: false icon: sprite: Mobs/Ghosts/ghost_human.rsi state: icon @@ -22,6 +23,7 @@ - type: InstantAction itemIconStyle: NoItem checkCanInteract: false + checkConsciousness: false icon: sprite: Interface/Actions/actions_crit.rsi state: fakedeath @@ -37,6 +39,7 @@ - type: InstantAction itemIconStyle: NoItem checkCanInteract: false + checkConsciousness: false icon: sprite: Interface/Actions/actions_crit.rsi state: lastwords diff --git a/Resources/Prototypes/Actions/diona.yml b/Resources/Prototypes/Actions/diona.yml index 73bc1ba5290..2d188987afc 100644 --- a/Resources/Prototypes/Actions/diona.yml +++ b/Resources/Prototypes/Actions/diona.yml @@ -8,6 +8,7 @@ icon: Mobs/Species/Diona/organs.rsi/brain.png event: !type:GibActionEvent {} checkCanInteract: false + checkConsciousness: false - type: entity id: DionaReformAction diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 2d5ec9a6784..84f0839b254 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -29,6 +29,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false icon: Interface/Actions/zombie-turn.png event: !type:ZombifySelfActionEvent @@ -66,6 +67,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false itemIconStyle: BigAction priority: -20 icon: @@ -82,6 +84,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false itemIconStyle: BigAction priority: -20 icon: @@ -143,6 +146,7 @@ noSpawn: true components: - type: InstantAction + checkCanInteract: false charges: 2 useDelay: 5 itemIconStyle: BigAction @@ -186,6 +190,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false icon: Interface/Actions/harmOff.png iconOn: Interface/Actions/harm.png event: !type:ToggleCombatActionEvent @@ -256,6 +261,7 @@ - type: InstantAction clientExclusive: true checkCanInteract: false + checkConsciousness: false temporary: true icon: { sprite: Objects/Tools/multitool.rsi, state: icon } event: !type:ClearAllOverlaysEvent @@ -279,6 +285,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false icon: { sprite: Clothing/Head/Hats/pyjamasyndicatered.rsi, state: icon } event: !type:SleepActionEvent @@ -291,6 +298,7 @@ - type: InstantAction icon: { sprite: Clothing/Head/Hats/pyjamasyndicatered.rsi, state: icon } checkCanInteract: false + checkConsciousness: false event: !type:WakeActionEvent - type: entity @@ -328,6 +336,7 @@ event: !type:ToggleEyesActionEvent useDelay: 1 # so u cant give yourself and observers eyestrain by rapidly spamming the action checkCanInteract: false + checkConsciousness: false - type: entity id: ActionToggleWagging diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index c37826666ac..9f0d54ee64a 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -255,3 +255,4 @@ event: !type:GuardianToggleActionEvent useDelay: 2 checkCanInteract: false + checkConsciousness: false diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 36e20c22a9c..537562f4618 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -138,6 +138,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false icon: Interface/Actions/pai-midi.png event: !type:OpenUiActionEvent key: enum.InstrumentUiKey.Key @@ -150,6 +151,7 @@ components: - type: InstantAction checkCanInteract: false + checkConsciousness: false icon: { sprite: Interface/Actions/pai-map.rsi, state: icon } event: !type:OpenUiActionEvent key: enum.StationMapUiKey.Key diff --git a/Resources/mapping_actions.yml b/Resources/mapping_actions.yml index 0dda6b38a52..9498c3062a3 100644 --- a/Resources/mapping_actions.yml +++ b/Resources/mapping_actions.yml @@ -3,6 +3,7 @@ entity: ReinforcedWindow keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -17,6 +18,7 @@ entity: WallSolid keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -31,6 +33,7 @@ entity: WallReinforced keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -45,6 +48,7 @@ entity: Window keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -59,6 +63,7 @@ entity: Firelock keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -73,6 +78,7 @@ entity: Grille keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -86,6 +92,7 @@ icon: Interface/VerbIcons/delete.svg.192dpi.png keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -104,6 +111,7 @@ entity: GasPipeStraight keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -118,6 +126,7 @@ entity: GasPipeBend keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -132,6 +141,7 @@ entity: GasPipeTJunction keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -146,6 +156,7 @@ entity: GasPipeFourway keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -160,6 +171,7 @@ entity: GasVentScrubber keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -174,6 +186,7 @@ entity: GasVentPump keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -188,6 +201,7 @@ entity: AirAlarm keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -202,6 +216,7 @@ entity: FireAlarm keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -216,6 +231,7 @@ entity: APCBasic keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -230,6 +246,7 @@ entity: CableApcExtension keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -244,6 +261,7 @@ entity: CableMV keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -258,6 +276,7 @@ entity: CableHV keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -272,6 +291,7 @@ entity: SubstationBasic keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -286,6 +306,7 @@ entity: Poweredlight keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -300,6 +321,7 @@ entity: EmergencyLight keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -314,6 +336,7 @@ entity: SMESBasic keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -328,6 +351,7 @@ entity: TableWood keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -342,6 +366,7 @@ entity: Table keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -356,6 +381,7 @@ entity: ChairWood keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -370,6 +396,7 @@ entity: Chair keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -384,6 +411,7 @@ entity: ChairOfficeLight keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -398,6 +426,7 @@ entity: StoolBar keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -412,6 +441,7 @@ entity: Stool keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -426,6 +456,7 @@ entity: Rack keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -440,6 +471,7 @@ entity: ChairOfficeDark keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -454,6 +486,7 @@ entity: LampGold keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -468,6 +501,7 @@ entity: DisposalPipe keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -482,6 +516,7 @@ entity: DisposalBend keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -496,6 +531,7 @@ entity: DisposalJunction keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -510,6 +546,7 @@ entity: DisposalJunctionFlipped keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -524,6 +561,7 @@ entity: DisposalRouter keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -538,6 +576,7 @@ entity: DisposalRouterFlipped keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -552,6 +591,7 @@ entity: DisposalUnit keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -566,6 +606,7 @@ entity: DisposalTrunk keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -580,6 +621,7 @@ entity: SignDisposalSpace keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -594,6 +636,7 @@ entity: Windoor keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -608,6 +651,7 @@ entity: WindowDirectional keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -622,6 +666,7 @@ entity: WindowReinforcedDirectional keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -636,6 +681,7 @@ entity: PlasmaWindowDirectional keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -650,6 +696,7 @@ entity: Railing keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -664,6 +711,7 @@ entity: RailingCorner keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -678,6 +726,7 @@ entity: RailingCornerSmall keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -693,6 +742,7 @@ name: RailingRound keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -706,6 +756,7 @@ entity: AirlockMaintLocked keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -720,6 +771,7 @@ entity: AirlockGlass keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -734,6 +786,7 @@ entity: AirlockServiceLocked keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -748,6 +801,7 @@ entity: AirlockSecurityLocked keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -762,6 +816,7 @@ entity: AirlockCommand keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -776,6 +831,7 @@ entity: AirlockScience keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -790,6 +846,7 @@ entity: AirlockMedical keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -804,6 +861,7 @@ entity: AirlockEngineering keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -818,6 +876,7 @@ entity: AirlockCargo keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -837,6 +896,7 @@ state: bot_left iconColor: '#EFB34196' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -857,6 +917,7 @@ state: delivery iconColor: '#EFB34196' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -877,6 +938,7 @@ state: warn_full iconColor: '#EFB34196' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -897,6 +959,7 @@ state: halftile_overlay iconColor: '#EFB34196' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -917,6 +980,7 @@ state: halftile_overlay iconColor: '#334E6DC8' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -937,6 +1001,7 @@ state: halftile_overlay iconColor: '#52B4E996' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -957,6 +1022,7 @@ state: halftile_overlay iconColor: '#9FED5896' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -977,6 +1043,7 @@ state: halftile_overlay iconColor: '#DE3A3A96' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -997,6 +1064,7 @@ state: halftile_overlay iconColor: '#D381C996' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -1017,6 +1085,7 @@ state: halftile_overlay iconColor: '#A4610696' checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -1031,6 +1100,7 @@ icon: /Textures/Tiles/steel.png keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -1044,6 +1114,7 @@ icon: /Textures/Tiles/plating.png keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True @@ -1059,6 +1130,7 @@ icon: /Textures/Tiles/cropped_parallax.png keywords: [] checkCanInteract: False + checkConsciousness: False clientExclusive: True autoPopulate: False temporary: True From e9322cd2eb0d480a207ecc2aa097ff85c74ad310 Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:22:21 -0500 Subject: [PATCH 40/54] Fix artifact report info (#26252) * Update ArtifactReports.xml Fix conflicting info * Small tweak to wording --- Resources/ServerInfo/Guidebook/Science/ArtifactReports.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ServerInfo/Guidebook/Science/ArtifactReports.xml b/Resources/ServerInfo/Guidebook/Science/ArtifactReports.xml index 3d7e5131cab..a377c980e55 100644 --- a/Resources/ServerInfo/Guidebook/Science/ArtifactReports.xml +++ b/Resources/ServerInfo/Guidebook/Science/ArtifactReports.xml @@ -14,7 +14,7 @@ A large portion of Xenoarchaeology gameplay revolves around the interpretation o - [color=#a4885c]Edges:[/color] the amount of nodes that are connected to the current node. Using this, you can calculate the total number of nodes as well as organize a map of their connections. -- [color=#a4885c]Current value:[/color] the amount of research points an artifact is currently worth. This is an important factor in choosing when to stop research and destroy the artifact in exchange for points. +- [color=#a4885c]Current value:[/color] the amount of research points an artifact is currently worth. Extracting will set this to zero and traversing new nodes will increase it. Reports are a helpful tool in manipulating an artifact, especially in the later stages where you are traversing nodes that have already been activated. From 648de561795809a83c11a29583c36a700e9ad018 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:30:56 +1100 Subject: [PATCH 41/54] Pulling rework v2 (#24936) * Pulling rework Fixing up the FOUR systems managing pulling, all the shitcode, and also making it nicer ingame. * More pulling cleanup * stats * More cleanup * First draft * More pulling * weh * Fix puller * Pulling working * Fix merge * Dunked * Self-merge time * Fix hotkey * Fix container changes * oop * Fix multi-pulling * Move alerts cleanup. * pulling fixes --- Content.Client/Alerts/ClientAlertsSystem.cs | 23 +- .../Physics/Controllers/MoverController.cs | 7 +- Content.Client/Pulling/PullingSystem.cs | 21 - .../ReplaySpectatorSystem.Blockers.cs | 2 +- .../Throwing/ThrownItemVisualizerSystem.cs | 2 +- .../Tests/Puller/PullerTest.cs | 4 +- Content.Server/Alert/Click/StopBeingPulled.cs | 8 +- Content.Server/Alert/Click/StopPulling.cs | 12 +- .../Electrocution/ElectrocutionSystem.cs | 7 +- Content.Server/Hands/Systems/HandsSystem.cs | 23 +- .../HTN/Preconditions/PulledPrecondition.cs | 5 +- .../Systems/StealConditionSystem.cs | 15 +- .../Physics/Controllers/PullController.cs | 207 -------- Content.Server/Pulling/PullingSystem.cs | 48 -- .../ArtifactInteractionTriggerSystem.cs | 4 +- .../Zombies/ZombieSystem.Transform.cs | 7 +- .../Administration/AdminFrozenSystem.cs | 15 +- .../Buckle/SharedBuckleSystem.Buckle.cs | 10 +- Content.Shared/Buckle/SharedBuckleSystem.cs | 3 +- .../EntitySystems/AnchorableSystem.cs | 15 +- Content.Shared/Cuffs/SharedCuffableSystem.cs | 14 +- Content.Shared/Follower/FollowerSystem.cs | 2 +- .../Friction/TileFrictionController.cs | 39 +- .../Interaction/SharedInteractionSystem.cs | 10 +- .../Pulling/Components/PullableComponent.cs | 39 ++ .../Pulling/Components/PullerComponent.cs | 41 ++ .../Pulling/Events/AttemptPullEvent.cs | 13 + .../Pulling/Events/AttemptStopPullingEvent.cs | 10 + .../Pulling/Events/BeingPulledAttemptEvent.cs | 0 .../Movement/Pulling/Events/PullMessage.cs | 13 + .../Pulling/Events/PullStartedMessage.cs | 9 + .../Pulling/Events/PullStoppedMessage.cs | 13 + .../Pulling/Events/StartPullAttemptEvent.cs | 0 .../Movement/Pulling/Systems/PullingSystem.cs | 492 ++++++++++++++++++ .../Movement/Systems/SharedMoverController.cs | 8 +- .../Pulling/Components/PullableComponent.cs | 57 -- .../Components/SharedPullerComponent.cs | 23 - .../Pulling/Events/PullAttemptEvent.cs | 11 - Content.Shared/Pulling/Events/PullMessage.cs | 16 - .../Pulling/Events/PullStartedMessage.cs | 12 - .../Pulling/Events/PullStoppedMessage.cs | 11 - Content.Shared/Pulling/PullableMoveMessage.cs | 6 - .../Pulling/PullableStopMovingMessage.cs | 6 - .../Pulling/Systems/SharedPullableSystem.cs | 28 - .../Pulling/Systems/SharedPullerSystem.cs | 90 ---- .../SharedPullingStateManagementSystem.cs | 196 ------- .../Systems/SharedPullingSystem.Actions.cs | 239 --------- .../Pulling/Systems/SharedPullingSystem.cs | 243 --------- .../Systems/DeployableBarrierSystem.cs | 10 +- .../Systems/SharedPortalSystem.cs | 16 +- Content.Shared/Throwing/ThrowingSystem.cs | 33 +- .../Throwing/ThrownItemComponent.cs | 6 + Content.Shared/Throwing/ThrownItemSystem.cs | 7 +- 53 files changed, 796 insertions(+), 1355 deletions(-) delete mode 100644 Content.Client/Pulling/PullingSystem.cs delete mode 100644 Content.Server/Physics/Controllers/PullController.cs delete mode 100644 Content.Server/Pulling/PullingSystem.cs create mode 100644 Content.Shared/Movement/Pulling/Components/PullableComponent.cs create mode 100644 Content.Shared/Movement/Pulling/Components/PullerComponent.cs create mode 100644 Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs create mode 100644 Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs rename Content.Shared/{ => Movement}/Pulling/Events/BeingPulledAttemptEvent.cs (100%) create mode 100644 Content.Shared/Movement/Pulling/Events/PullMessage.cs create mode 100644 Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs create mode 100644 Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs rename Content.Shared/{ => Movement}/Pulling/Events/StartPullAttemptEvent.cs (100%) create mode 100644 Content.Shared/Movement/Pulling/Systems/PullingSystem.cs delete mode 100644 Content.Shared/Pulling/Components/PullableComponent.cs delete mode 100644 Content.Shared/Pulling/Components/SharedPullerComponent.cs delete mode 100644 Content.Shared/Pulling/Events/PullAttemptEvent.cs delete mode 100644 Content.Shared/Pulling/Events/PullMessage.cs delete mode 100644 Content.Shared/Pulling/Events/PullStartedMessage.cs delete mode 100644 Content.Shared/Pulling/Events/PullStoppedMessage.cs delete mode 100644 Content.Shared/Pulling/PullableMoveMessage.cs delete mode 100644 Content.Shared/Pulling/PullableStopMovingMessage.cs delete mode 100644 Content.Shared/Pulling/Systems/SharedPullableSystem.cs delete mode 100644 Content.Shared/Pulling/Systems/SharedPullerSystem.cs delete mode 100644 Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs delete mode 100644 Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs delete mode 100644 Content.Shared/Pulling/Systems/SharedPullingSystem.cs diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index ab296a96195..d1ea1c32bc0 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -4,6 +4,7 @@ using Robust.Client.Player; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; namespace Content.Client.Alerts; @@ -12,6 +13,7 @@ public sealed class ClientAlertsSystem : AlertsSystem { public AlertOrderPrototype? AlertOrder { get; set; } + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -49,24 +51,23 @@ public IReadOnlyDictionary? ActiveAlerts protected override void AfterShowAlert(Entity alerts) { - if (_playerManager.LocalEntity != alerts.Owner) - return; - - SyncAlerts?.Invoke(this, alerts.Comp.Alerts); + UpdateHud(alerts); } - protected override void AfterClearAlert(Entity alertsComponent) + protected override void AfterClearAlert(Entity alerts) { - if (_playerManager.LocalEntity != alertsComponent.Owner) - return; + UpdateHud(alerts); + } - SyncAlerts?.Invoke(this, alertsComponent.Comp.Alerts); + private void ClientAlertsHandleState(Entity alerts, ref AfterAutoHandleStateEvent args) + { + UpdateHud(alerts); } - private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref AfterAutoHandleStateEvent args) + private void UpdateHud(Entity entity) { - if (_playerManager.LocalEntity == uid) - SyncAlerts?.Invoke(this, component.Alerts); + if (_playerManager.LocalEntity == entity.Owner) + SyncAlerts?.Invoke(this, entity.Comp.Alerts); } private void OnPlayerAttached(EntityUid uid, AlertsComponent component, LocalPlayerAttachedEvent args) diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index 7a8a6e39cff..31042854d4a 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -1,6 +1,7 @@ using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Systems; -using Content.Shared.Pulling.Components; +using Robust.Client.GameObjects; using Robust.Client.Physics; using Robust.Client.Player; using Robust.Shared.Physics.Components; @@ -24,7 +25,7 @@ public override void Initialize() SubscribeLocalEvent(OnUpdatePredicted); SubscribeLocalEvent(OnUpdateRelayTargetPredicted); - SubscribeLocalEvent(OnUpdatePullablePredicted); + SubscribeLocalEvent(OnUpdatePullablePredicted); } private void OnUpdatePredicted(EntityUid uid, InputMoverComponent component, ref UpdateIsPredictedEvent args) @@ -40,7 +41,7 @@ private void OnUpdateRelayTargetPredicted(EntityUid uid, MovementRelayTargetComp args.IsPredicted = true; } - private void OnUpdatePullablePredicted(EntityUid uid, SharedPullableComponent component, ref UpdateIsPredictedEvent args) + private void OnUpdatePullablePredicted(EntityUid uid, PullableComponent component, ref UpdateIsPredictedEvent args) { // Enable prediction if an entity is being pulled by the player. // Disable prediction if an entity is being pulled by some non-player entity. diff --git a/Content.Client/Pulling/PullingSystem.cs b/Content.Client/Pulling/PullingSystem.cs deleted file mode 100644 index 556dadd00da..00000000000 --- a/Content.Client/Pulling/PullingSystem.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; -using Robust.Client.Physics; - -namespace Content.Client.Pulling -{ - [UsedImplicitly] - public sealed class PullingSystem : SharedPullingSystem - { - public override void Initialize() - { - base.Initialize(); - - UpdatesAfter.Add(typeof(PhysicsSystem)); - - SubscribeLocalEvent(OnPullableMove); - SubscribeLocalEvent(OnPullableStopMove); - } - } -} diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs index 86d113defb1..2fa862f3df7 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs @@ -3,7 +3,7 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Events; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Throwing; namespace Content.Client.Replay.Spectator; diff --git a/Content.Client/Throwing/ThrownItemVisualizerSystem.cs b/Content.Client/Throwing/ThrownItemVisualizerSystem.cs index bbd36731104..b25b4fbb7de 100644 --- a/Content.Client/Throwing/ThrownItemVisualizerSystem.cs +++ b/Content.Client/Throwing/ThrownItemVisualizerSystem.cs @@ -24,7 +24,7 @@ public override void Initialize() private void OnAutoHandleState(EntityUid uid, ThrownItemComponent component, ref AfterAutoHandleStateEvent args) { - if (!TryComp(uid, out var sprite)) + if (!TryComp(uid, out var sprite) || !component.Animate) return; var animationPlayer = EnsureComp(uid); diff --git a/Content.IntegrationTests/Tests/Puller/PullerTest.cs b/Content.IntegrationTests/Tests/Puller/PullerTest.cs index ba91f54ff77..87d174f7272 100644 --- a/Content.IntegrationTests/Tests/Puller/PullerTest.cs +++ b/Content.IntegrationTests/Tests/Puller/PullerTest.cs @@ -1,6 +1,6 @@ using Content.Shared.Hands.Components; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Prototypes; -using Content.Shared.Pulling.Components; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -29,7 +29,7 @@ await server.WaitAssertion(() => { foreach (var proto in protoManager.EnumeratePrototypes()) { - if (!proto.TryGetComponent(out SharedPullerComponent? puller)) + if (!proto.TryGetComponent(out PullerComponent? puller)) continue; if (!puller.NeedsHands) diff --git a/Content.Server/Alert/Click/StopBeingPulled.cs b/Content.Server/Alert/Click/StopBeingPulled.cs index 2cf076fbeed..b02da38ecfa 100644 --- a/Content.Server/Alert/Click/StopBeingPulled.cs +++ b/Content.Server/Alert/Click/StopBeingPulled.cs @@ -1,7 +1,7 @@ using Content.Shared.ActionBlocker; using Content.Shared.Alert; -using Content.Shared.Pulling.Components; -using Content.Shared.Pulling; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using JetBrains.Annotations; namespace Content.Server.Alert.Click @@ -20,9 +20,9 @@ public void AlertClicked(EntityUid player) if (!entityManager.System().CanInteract(player, null)) return; - if (entityManager.TryGetComponent(player, out SharedPullableComponent? playerPullable)) + if (entityManager.TryGetComponent(player, out PullableComponent? playerPullable)) { - entityManager.System().TryStopPull(playerPullable); + entityManager.System().TryStopPull(player, playerPullable, user: player); } } } diff --git a/Content.Server/Alert/Click/StopPulling.cs b/Content.Server/Alert/Click/StopPulling.cs index 00a41495985..76f9569429f 100644 --- a/Content.Server/Alert/Click/StopPulling.cs +++ b/Content.Server/Alert/Click/StopPulling.cs @@ -1,6 +1,6 @@ using Content.Shared.Alert; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using JetBrains.Annotations; namespace Content.Server.Alert.Click @@ -15,12 +15,12 @@ public sealed partial class StopPulling : IAlertClick public void AlertClicked(EntityUid player) { var entManager = IoCManager.Resolve(); + var ps = entManager.System(); - var ps = entManager.System(); - var playerTarget = ps.GetPulled(player); - if (playerTarget != default && entManager.TryGetComponent(playerTarget, out SharedPullableComponent? playerPullable)) + if (entManager.TryGetComponent(player, out PullerComponent? puller) && + entManager.TryGetComponent(puller.Pulling, out PullableComponent? pullableComp)) { - ps.TryStopPull(playerPullable); + ps.TryStopPull(puller.Pulling.Value, pullableComp, user: player); } } } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index f71f8b69742..d967013f652 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -19,7 +19,6 @@ using Content.Shared.Maps; using Content.Shared.Mobs; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; using Content.Shared.Speech.EntitySystems; using Content.Shared.StatusEffect; using Content.Shared.Stunnable; @@ -32,6 +31,8 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; +using PullerComponent = Content.Shared.Movement.Pulling.Components.PullerComponent; namespace Content.Server.Electrocution; @@ -475,14 +476,14 @@ private void GetChainedElectrocutionTargetsRecurse( all.Add((entity, depth)); visited.Add(entity); - if (TryComp(entity, out var pullable) && + if (TryComp(entity, out var pullable) && pullable.Puller is { Valid: true } pullerId && !visited.Contains(pullerId)) { GetChainedElectrocutionTargetsRecurse(pullerId, depth + 1, visited, all); } - if (TryComp(entity, out var puller) && + if (TryComp(entity, out var puller) && puller.Pulling is { Valid: true } pullingId && !visited.Contains(pullingId)) { diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index a783a9e0968..7cc1b071960 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -1,6 +1,5 @@ using System.Numerics; using Content.Server.Inventory; -using Content.Server.Pulling; using Content.Server.Stack; using Content.Server.Stunnable; using Content.Shared.ActionBlocker; @@ -11,8 +10,9 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; using Content.Shared.Inventory.VirtualItem; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Stacks; using Content.Shared.Throwing; using Robust.Shared.GameStates; @@ -83,9 +83,8 @@ private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent a return; // Break any pulls - if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is EntityUid pulled && - TryComp(pulled, out SharedPullableComponent? pullable)) - _pullingSystem.TryStopPull(pullable); + if (TryComp(uid, out PullerComponent? puller) && TryComp(puller.Pulling, out PullableComponent? pullable)) + _pullingSystem.TryStopPull(puller.Pulling.Value, pullable); if (!_handsSystem.TryDrop(uid, component.ActiveHand!, null, checkActionBlocker: false)) return; @@ -123,13 +122,13 @@ private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args) { - if (args.Puller.Owner != uid) + if (args.PullerUid != uid) return; - if (TryComp(args.Puller.Owner, out var pullerComp) && !pullerComp.NeedsHands) + if (TryComp(args.PullerUid, out var pullerComp) && !pullerComp.NeedsHands) return; - if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.Pulled.Owner, uid)) + if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.PulledUid, uid)) { DebugTools.Assert("Unable to find available hand when starting pulling??"); } @@ -137,7 +136,7 @@ private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStar private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args) { - if (args.Puller.Owner != uid) + if (args.PullerUid != uid) return; // Try find hand that is doing this pull. @@ -146,8 +145,10 @@ private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStop { if (hand.HeldEntity == null || !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem) - || virtualItem.BlockingEntity != args.Pulled.Owner) + || virtualItem.BlockingEntity != args.PulledUid) + { continue; + } QueueDel(hand.HeldEntity.Value); break; diff --git a/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs index 64a72b13cfa..d276be72187 100644 --- a/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs @@ -1,4 +1,5 @@ using Content.Shared.Pulling; +using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem; namespace Content.Server.NPC.HTN.Preconditions; @@ -7,14 +8,14 @@ namespace Content.Server.NPC.HTN.Preconditions; /// public sealed partial class PulledPrecondition : HTNPrecondition { - private SharedPullingSystem _pulling = default!; + private PullingSystem _pulling = default!; [ViewVariables(VVAccess.ReadWrite)] [DataField("isPulled")] public bool IsPulled = true; public override void Initialize(IEntitySystemManager sysManager) { base.Initialize(sysManager); - _pulling = sysManager.GetEntitySystem(); + _pulling = sysManager.GetEntitySystem(); } public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/Objectives/Systems/StealConditionSystem.cs b/Content.Server/Objectives/Systems/StealConditionSystem.cs index 02d4ee010b5..0fe6f0947c8 100644 --- a/Content.Server/Objectives/Systems/StealConditionSystem.cs +++ b/Content.Server/Objectives/Systems/StealConditionSystem.cs @@ -6,11 +6,10 @@ using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Content.Shared.Pulling.Components; -using Content.Shared.Objectives; using Content.Shared.Mind.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Components; +using Content.Shared.Movement.Pulling.Components; namespace Content.Server.Objectives.Systems; @@ -100,19 +99,19 @@ private float GetProgress(MindComponent mind, StealConditionComponent condition) var count = 0; //check pulling object - if (TryComp(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition + if (TryComp(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition { - var pullid = pull.Pulling; - if (pullid != null) + var pulledEntity = pull.Pulling; + if (pulledEntity != null) { // check if this is the item - if (CheckStealTarget(pullid.Value, condition)) count++; + if (CheckStealTarget(pulledEntity.Value, condition)) count++; //we don't check the inventories of sentient entity - if (!TryComp(pullid, out var pullMind)) + if (!HasComp(pulledEntity)) { // if it is a container check its contents - if (_containerQuery.TryGetComponent(pullid, out var containerManager)) + if (_containerQuery.TryGetComponent(pulledEntity, out var containerManager)) stack.Push(containerManager); } } diff --git a/Content.Server/Physics/Controllers/PullController.cs b/Content.Server/Physics/Controllers/PullController.cs deleted file mode 100644 index 8f58f807aae..00000000000 --- a/Content.Server/Physics/Controllers/PullController.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System.Numerics; -using Content.Shared.ActionBlocker; -using Content.Shared.Gravity; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using Content.Shared.Rotatable; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Controllers; - -namespace Content.Server.Physics.Controllers -{ - public sealed class PullController : VirtualController - { - // Parameterization for pulling: - // Speeds. Note that the speed is mass-independent (multiplied by mass). - // Instead, tuning to mass is done via the mass values below. - // Note that setting the speed too high results in overshoots (stabilized by drag, but bad) - private const float AccelModifierHigh = 15f; - private const float AccelModifierLow = 60.0f; - // High/low-mass marks. Curve is constant-lerp-constant, i.e. if you can even pull an item, - // you'll always get at least AccelModifierLow and no more than AccelModifierHigh. - private const float AccelModifierHighMass = 70.0f; // roundstart saltern emergency closet - private const float AccelModifierLowMass = 5.0f; // roundstart saltern emergency crowbar - // Used to control settling (turns off pulling). - private const float MaximumSettleVelocity = 0.1f; - private const float MaximumSettleDistance = 0.1f; - // Settle shutdown control. - // Mustn't be too massive, as that causes severe mispredicts *and can prevent it ever resolving*. - // Exists to bleed off "I pulled my crowbar" overshoots. - // Minimum velocity for shutdown to be necessary. This prevents stuff getting stuck b/c too much shutdown. - private const float SettleMinimumShutdownVelocity = 0.25f; - // Distance in which settle shutdown multiplier is at 0. It then scales upwards linearly with closer distances. - private const float SettleShutdownDistance = 1.0f; - // Velocity change of -LinearVelocity * frameTime * this - private const float SettleShutdownMultiplier = 20.0f; - - // How much you must move for the puller movement check to actually hit. - private const float MinimumMovementDistance = 0.005f; - - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly SharedPullingSystem _pullableSystem = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - - // TODO: Move this stuff to pullingsystem - /// - /// If distance between puller and pulled entity lower that this threshold, - /// pulled entity will not change its rotation. - /// Helps with small distance jittering - /// - private const float ThresholdRotDistance = 1; - - /// - /// If difference between puller and pulled angle lower that this threshold, - /// pulled entity will not change its rotation. - /// Helps with diagonal movement jittering - /// As of further adjustments, should divide cleanly into 90 degrees - /// - private const float ThresholdRotAngle = 22.5f; - - public override void Initialize() - { - UpdatesAfter.Add(typeof(MoverController)); - SubscribeLocalEvent(OnPullerMove); - - base.Initialize(); - } - - private void OnPullerMove(EntityUid uid, SharedPullerComponent component, ref MoveEvent args) - { - if (component.Pulling is not { } pullable || !TryComp(pullable, out var pullableComponent)) - return; - - UpdatePulledRotation(uid, pullable); - - if (args.NewPosition.EntityId == args.OldPosition.EntityId && - (args.NewPosition.Position - args.OldPosition.Position).LengthSquared() < MinimumMovementDistance * MinimumMovementDistance) - return; - - if (TryComp(pullable, out var physics)) - PhysicsSystem.WakeBody(pullable, body: physics); - - _pullableSystem.StopMoveTo(pullableComponent); - } - - private void UpdatePulledRotation(EntityUid puller, EntityUid pulled) - { - // TODO: update once ComponentReference works with directed event bus. - if (!TryComp(pulled, out RotatableComponent? rotatable)) - return; - - if (!rotatable.RotateWhilePulling) - return; - - var xforms = GetEntityQuery(); - var pulledXform = xforms.GetComponent(pulled); - var pullerXform = xforms.GetComponent(puller); - - var pullerData = TransformSystem.GetWorldPositionRotation(pullerXform, xforms); - var pulledData = TransformSystem.GetWorldPositionRotation(pulledXform, xforms); - - var dir = pullerData.WorldPosition - pulledData.WorldPosition; - if (dir.LengthSquared() > ThresholdRotDistance * ThresholdRotDistance) - { - var oldAngle = pulledData.WorldRotation; - var newAngle = Angle.FromWorldVec(dir); - - var diff = newAngle - oldAngle; - if (Math.Abs(diff.Degrees) > ThresholdRotAngle / 2f) - { - // Ok, so this bit is difficult because ideally it would look like it's snapping to sane angles. - // Otherwise PIANO DOOR STUCK! happens. - // But it also needs to work with station rotation / align to the local parent. - // So... - var baseRotation = pulledData.WorldRotation - pulledXform.LocalRotation; - var localRotation = newAngle - baseRotation; - var localRotationSnapped = Angle.FromDegrees(Math.Floor((localRotation.Degrees / ThresholdRotAngle) + 0.5f) * ThresholdRotAngle); - TransformSystem.SetLocalRotation(pulledXform, localRotationSnapped); - } - } - } - - public override void UpdateBeforeSolve(bool prediction, float frameTime) - { - base.UpdateBeforeSolve(prediction, frameTime); - - foreach (var pullable in _pullableSystem.Moving) - { - // There's a 1-frame delay between stopping moving something and it leaving the Moving set. - // This can include if leaving the Moving set due to not being pulled anymore, - // or due to being deleted. - - if (pullable.Deleted) - continue; - - if (pullable.MovingTo == null) - continue; - - if (pullable.Puller is not {Valid: true} puller) - continue; - - var pullableEnt = pullable.Owner; - var pullableXform = Transform(pullableEnt); - var pullerXform = Transform(puller); - - // Now that's over with... - - var pullerPosition = pullerXform.MapPosition; - var movingTo = pullable.MovingTo.Value.ToMap(EntityManager, _transform); - if (movingTo.MapId != pullerPosition.MapId) - { - _pullableSystem.StopMoveTo(pullable); - continue; - } - - if (!TryComp(pullableEnt, out var physics) || - physics.BodyType == BodyType.Static || - movingTo.MapId != pullableXform.MapID) - { - _pullableSystem.StopMoveTo(pullable); - continue; - } - - var movingPosition = movingTo.Position; - var ownerPosition = pullableXform.MapPosition.Position; - - var diff = movingPosition - ownerPosition; - var diffLength = diff.Length(); - - if (diffLength < MaximumSettleDistance && physics.LinearVelocity.Length() < MaximumSettleVelocity) - { - PhysicsSystem.SetLinearVelocity(pullableEnt, Vector2.Zero, body: physics); - _pullableSystem.StopMoveTo(pullable); - continue; - } - - var impulseModifierLerp = Math.Min(1.0f, Math.Max(0.0f, (physics.Mass - AccelModifierLowMass) / (AccelModifierHighMass - AccelModifierLowMass))); - var impulseModifier = MathHelper.Lerp(AccelModifierLow, AccelModifierHigh, impulseModifierLerp); - var multiplier = diffLength < 1 ? impulseModifier * diffLength : impulseModifier; - // Note the implication that the real rules of physics don't apply to pulling control. - var accel = diff.Normalized() * multiplier; - // Now for the part where velocity gets shutdown... - if (diffLength < SettleShutdownDistance && physics.LinearVelocity.Length() >= SettleMinimumShutdownVelocity) - { - // Shutdown velocity increases as we get closer to centre - var scaling = (SettleShutdownDistance - diffLength) / SettleShutdownDistance; - accel -= physics.LinearVelocity * SettleShutdownMultiplier * scaling; - } - - PhysicsSystem.WakeBody(pullableEnt, body: physics); - - var impulse = accel * physics.Mass * frameTime; - PhysicsSystem.ApplyLinearImpulse(pullableEnt, impulse, body: physics); - - // if the puller is weightless or can't move, then we apply the inverse impulse (Newton's third law). - // doing it under gravity produces an unsatisfying wiggling when pulling. - // If player can't move, assume they are on a chair and we need to prevent pull-moving. - if ((_gravity.IsWeightless(puller) && pullerXform.GridUid == null) || !_actionBlockerSystem.CanMove(puller)) - { - PhysicsSystem.WakeBody(puller); - PhysicsSystem.ApplyLinearImpulse(puller, -impulse); - } - } - } - } -} diff --git a/Content.Server/Pulling/PullingSystem.cs b/Content.Server/Pulling/PullingSystem.cs deleted file mode 100644 index 69bb7c93704..00000000000 --- a/Content.Server/Pulling/PullingSystem.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Content.Shared.Input; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Input.Binding; -using Robust.Shared.Player; - -namespace Content.Server.Pulling -{ - [UsedImplicitly] - public sealed class PullingSystem : SharedPullingSystem - { - public override void Initialize() - { - base.Initialize(); - - UpdatesAfter.Add(typeof(PhysicsSystem)); - - SubscribeLocalEvent(OnPullableMove); - SubscribeLocalEvent(OnPullableStopMove); - - CommandBinds.Builder - .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject)) - .Register(); - } - - private void HandleReleasePulledObject(ICommonSession? session) - { - if (session?.AttachedEntity is not {Valid: true} player) - { - return; - } - - if (!TryGetPulled(player, out var pulled)) - { - return; - } - - if (!EntityManager.TryGetComponent(pulled.Value, out SharedPullableComponent? pullable)) - { - return; - } - - TryStopPull(pullable); - } - } -} diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs index 239b6741608..9976d56da0b 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Interaction; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Weapons.Melee.Events; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -22,7 +22,7 @@ private void OnPull(EntityUid uid, ArtifactInteractionTriggerComponent component if (!component.PullActivation) return; - _artifactSystem.TryActivateArtifact(uid, args.Puller.Owner); + _artifactSystem.TryActivateArtifact(uid, args.PullerUid); } private void OnAttack(EntityUid uid, ArtifactInteractionTriggerComponent component, AttackedEvent args) diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index d6540815e9e..da97c018dfd 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -26,6 +26,7 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Systems; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; @@ -33,7 +34,6 @@ using Content.Shared.Nutrition.Components; using Content.Shared.Popups; using Content.Shared.Roles; -using Content.Shared.Pulling.Components; using Content.Shared.Weapons.Melee; using Content.Shared.Zombies; using Content.Shared.Prying.Components; @@ -61,7 +61,6 @@ public sealed partial class ZombieSystem [Dependency] private readonly IChatManager _chatMan = default!; [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; - [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ActionsSystem _actions = default!; // DeltaV - No psionic zombies @@ -277,7 +276,9 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) RemComp(target, handsComp); } - RemComp(target); + // Sloth: What the fuck? + // How long until compregistry lmao. + RemComp(target); // No longer waiting to become a zombie: // Requires deferral because this is (probably) the event which called ZombifyEntity in the first place. diff --git a/Content.Shared/Administration/AdminFrozenSystem.cs b/Content.Shared/Administration/AdminFrozenSystem.cs index 14438cc5912..4ec9600b0bd 100644 --- a/Content.Shared/Administration/AdminFrozenSystem.cs +++ b/Content.Shared/Administration/AdminFrozenSystem.cs @@ -1,13 +1,10 @@ using Content.Shared.ActionBlocker; using Content.Shared.Interaction.Events; using Content.Shared.Item; -using Content.Shared.Movement; using Content.Shared.Movement.Events; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using Content.Shared.Pulling.Events; -using Content.Shared.Stunnable; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Throwing; namespace Content.Shared.Administration; @@ -15,7 +12,7 @@ namespace Content.Shared.Administration; public sealed class AdminFrozenSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; public override void Initialize() { @@ -45,9 +42,9 @@ private void OnPullAttempt(EntityUid uid, AdminFrozenComponent component, PullAt private void OnStartup(EntityUid uid, AdminFrozenComponent component, ComponentStartup args) { - if (TryComp(uid, out var pullable)) + if (TryComp(uid, out var pullable)) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(uid, pullable); } UpdateCanMove(uid, component, args); diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 731b2892aa8..7c85c5a6311 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -11,7 +11,6 @@ using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; using Content.Shared.Standing; using Content.Shared.Storage.Components; using Content.Shared.Stunnable; @@ -20,6 +19,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Utility; +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Buckle; @@ -356,11 +356,11 @@ public bool TryBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid RaiseLocalEvent(ev.BuckledEntity, ref ev); RaiseLocalEvent(ev.StrapEntity, ref ev); - if (TryComp(buckleUid, out var ownerPullable)) + if (TryComp(buckleUid, out var ownerPullable)) { if (ownerPullable.Puller != null) { - _pulling.TryStopPull(ownerPullable); + _pulling.TryStopPull(buckleUid, ownerPullable); } } @@ -369,12 +369,12 @@ public bool TryBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid _physics.ResetDynamics(physics); } - if (!buckleComp.PullStrap && TryComp(strapUid, out var toPullable)) + if (!buckleComp.PullStrap && TryComp(strapUid, out var toPullable)) { if (toPullable.Puller == buckleUid) { // can't pull it and buckle to it at the same time - _pulling.TryStopPull(toPullable); + _pulling.TryStopPull(strapUid, toPullable); } } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.cs b/Content.Shared/Buckle/SharedBuckleSystem.cs index 8f683356637..67218657e52 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Timing; +using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem; namespace Content.Shared.Buckle; @@ -35,7 +36,7 @@ public abstract partial class SharedBuckleSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedJointSystem _joints = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index 70bcfbab43f..f06278c6d73 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -6,16 +6,15 @@ using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Interaction; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Popups; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Tools; using Content.Shared.Tools.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Content.Shared.Tag; -using Robust.Shared.Player; using Robust.Shared.Serialization; using Robust.Shared.Utility; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; @@ -27,7 +26,7 @@ public sealed partial class AnchorableSystem : EntitySystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedToolSystem _tool = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!; @@ -132,9 +131,9 @@ private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryA var rot = xform.LocalRotation; xform.LocalRotation = Math.Round(rot / (Math.PI / 2)) * (Math.PI / 2); - if (TryComp(uid, out var pullable) && pullable.Puller != null) + if (TryComp(uid, out var pullable) && pullable.Puller != null) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(uid, pullable); } // TODO: Anchoring snaps rn anyway! @@ -175,7 +174,7 @@ private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryA public void TryToggleAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid, AnchorableComponent? anchorable = null, TransformComponent? transform = null, - SharedPullableComponent? pullable = null, + PullableComponent? pullable = null, ToolComponent? usingTool = null) { if (!Resolve(uid, ref transform)) @@ -198,7 +197,7 @@ public void TryToggleAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid private void TryAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid, AnchorableComponent? anchorable = null, TransformComponent? transform = null, - SharedPullableComponent? pullable = null, + PullableComponent? pullable = null, ToolComponent? usingTool = null) { if (!Resolve(uid, ref anchorable, ref transform)) diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index fc005fd30fa..5cade56aca1 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -21,9 +21,8 @@ using Content.Shared.Item; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Events; using Content.Shared.Rejuvenate; using Content.Shared.Stunnable; @@ -36,6 +35,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Serialization; +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Cuffs { @@ -70,7 +70,7 @@ public override void Initialize() SubscribeLocalEvent(OnCuffsInsertedIntoContainer); SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(HandleStopPull); + SubscribeLocalEvent(HandleStopPull); SubscribeLocalEvent(HandleMoveAttempt); SubscribeLocalEvent(OnEquipAttempt); SubscribeLocalEvent(OnUnequipAttempt); @@ -182,7 +182,7 @@ public void UpdateCuffState(EntityUid uid, CuffableComponent component) private void OnBeingPulledAttempt(EntityUid uid, CuffableComponent component, BeingPulledAttemptEvent args) { - if (!TryComp(uid, out var pullable)) + if (!TryComp(uid, out var pullable)) return; if (pullable.Puller != null && !component.CanStillInteract) // If we are being pulled already and cuffed, we can't get pulled again. @@ -214,19 +214,19 @@ private void OnPull(EntityUid uid, CuffableComponent component, PullMessage args private void HandleMoveAttempt(EntityUid uid, CuffableComponent component, UpdateCanMoveEvent args) { - if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out SharedPullableComponent? pullable) || !pullable.BeingPulled) + if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out PullableComponent? pullable) || !pullable.BeingPulled) return; args.Cancel(); } - private void HandleStopPull(EntityUid uid, CuffableComponent component, StopPullingEvent args) + private void HandleStopPull(EntityUid uid, CuffableComponent component, AttemptStopPullingEvent args) { if (args.User == null || !Exists(args.User.Value)) return; if (args.User.Value == uid && !component.CanStillInteract) - args.Cancel(); + args.Cancelled = true; } private void AddUncuffVerb(EntityUid uid, CuffableComponent component, GetVerbsEvent args) diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs index 5656778a3f9..fc7cccf9bd6 100644 --- a/Content.Shared/Follower/FollowerSystem.cs +++ b/Content.Shared/Follower/FollowerSystem.cs @@ -4,8 +4,8 @@ using Content.Shared.Ghost; using Content.Shared.Hands; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Systems; -using Content.Shared.Physics.Pull; using Content.Shared.Tag; using Content.Shared.Verbs; using Robust.Shared.Containers; diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index ba4d9fc24f8..3583947ee36 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -2,8 +2,8 @@ using Content.Shared.CCVar; using Content.Shared.Gravity; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Systems; -using Content.Shared.Pulling.Components; using JetBrains.Annotations; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -23,6 +23,12 @@ public sealed class TileFrictionController : VirtualController [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + private EntityQuery _frictionQuery; + private EntityQuery _xformQuery; + private EntityQuery _pullerQuery; + private EntityQuery _pullableQuery; + private EntityQuery _gridQuery; + private float _stopSpeed; private float _frictionModifier; public const float DefaultFriction = 0.3f; @@ -33,18 +39,17 @@ public override void Initialize() Subs.CVar(_configManager, CCVars.TileFrictionModifier, value => _frictionModifier = value, true); Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true); + _frictionQuery = GetEntityQuery(); + _xformQuery = GetEntityQuery(); + _pullerQuery = GetEntityQuery(); + _pullableQuery = GetEntityQuery(); + _gridQuery = GetEntityQuery(); } public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent mapComponent, float frameTime) { base.UpdateBeforeMapSolve(prediction, mapComponent, frameTime); - var frictionQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var pullerQuery = GetEntityQuery(); - var pullableQuery = GetEntityQuery(); - var gridQuery = GetEntityQuery(); - foreach (var body in mapComponent.AwakeBodies) { var uid = body.Owner; @@ -60,16 +65,16 @@ public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent m if (body.LinearVelocity.Equals(Vector2.Zero) && body.AngularVelocity.Equals(0f)) continue; - if (!xformQuery.TryGetComponent(uid, out var xform)) + if (!_xformQuery.TryGetComponent(uid, out var xform)) { Log.Error($"Unable to get transform for {ToPrettyString(uid)} in tilefrictioncontroller"); continue; } - var surfaceFriction = GetTileFriction(uid, body, xform, gridQuery, frictionQuery); + var surfaceFriction = GetTileFriction(uid, body, xform); var bodyModifier = 1f; - if (frictionQuery.TryGetComponent(uid, out var frictionComp)) + if (_frictionQuery.TryGetComponent(uid, out var frictionComp)) { bodyModifier = frictionComp.Modifier; } @@ -82,8 +87,8 @@ public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent m // If we're sandwiched between 2 pullers reduce friction // Might be better to make this dynamic and check how many are in the pull chain? // Either way should be much faster for now. - if (pullerQuery.TryGetComponent(uid, out var puller) && puller.Pulling != null && - pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled) + if (_pullerQuery.TryGetComponent(uid, out var puller) && puller.Pulling != null && + _pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled) { bodyModifier *= 0.2f; } @@ -163,9 +168,7 @@ private void ReduceAngularVelocity(EntityUid uid, bool prediction, PhysicsCompon private float GetTileFriction( EntityUid uid, PhysicsComponent body, - TransformComponent xform, - EntityQuery gridQuery, - EntityQuery frictionQuery) + TransformComponent xform) { // TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events if (_gravity.IsWeightless(uid, body, xform)) @@ -175,9 +178,9 @@ private float GetTileFriction( return 0.0f; // If not on a grid then return the map's friction. - if (!gridQuery.TryGetComponent(xform.GridUid, out var grid)) + if (!_gridQuery.TryGetComponent(xform.GridUid, out var grid)) { - return frictionQuery.TryGetComponent(xform.MapUid, out var friction) + return _frictionQuery.TryGetComponent(xform.MapUid, out var friction) ? friction.Modifier : DefaultFriction; } @@ -197,7 +200,7 @@ private float GetTileFriction( while (anc.MoveNext(out var tileEnt)) { - if (frictionQuery.TryGetComponent(tileEnt, out var friction)) + if (_frictionQuery.TryGetComponent(tileEnt, out var friction)) return friction.Modifier; } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 75063c55503..29cf8d6d12f 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -15,10 +15,10 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Physics; using Content.Shared.Popups; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Tag; using Content.Shared.Timing; using Content.Shared.Verbs; @@ -60,7 +60,7 @@ public abstract partial class SharedInteractionSystem : EntitySystem [Dependency] private readonly SharedVerbSystem _verbSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - [Dependency] private readonly SharedPullingSystem _pullSystem = default!; + [Dependency] private readonly PullingSystem _pullSystem = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TagSystem _tagSystem = default!; @@ -185,10 +185,10 @@ private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coor if (!InRangeUnobstructed(userEntity.Value, uid, popup: true)) return false; - if (!TryComp(uid, out SharedPullableComponent? pull)) + if (!TryComp(uid, out PullableComponent? pull)) return false; - _pullSystem.TogglePull(userEntity.Value, pull); + _pullSystem.TogglePull(uid, userEntity.Value, pull); return false; } diff --git a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs new file mode 100644 index 00000000000..db889e7e3bd --- /dev/null +++ b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Pulling.Components; + +/// +/// Specifies an entity as being pullable by an entity with +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(Systems.PullingSystem))] +public sealed partial class PullableComponent : Component +{ + /// + /// The current entity pulling this component. + /// + [AutoNetworkedField, DataField] + public EntityUid? Puller; + + /// + /// The pull joint. + /// + [AutoNetworkedField, DataField] + public string? PullJointId; + + public bool BeingPulled => Puller != null; + + /// + /// If the physics component has FixedRotation should we keep it upon being pulled + /// + [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)] + [ViewVariables(VVAccess.ReadWrite), DataField("fixedRotation")] + public bool FixedRotationOnPull; + + /// + /// What the pullable's fixedrotation was set to before being pulled. + /// + [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)] + [AutoNetworkedField, DataField] + public bool PrevFixedRotation; +} diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs new file mode 100644 index 00000000000..1fc9b731bd5 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.Movement.Pulling.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Movement.Pulling.Components; + +/// +/// Specifies an entity as being able to pull another entity with +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(PullingSystem))] +public sealed partial class PullerComponent : Component +{ + // My raiding guild + /// + /// Next time the puller can throw what is being pulled. + /// Used to avoid spamming it for infinite spin + velocity. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] + public TimeSpan NextThrow; + + [DataField] + public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(1); + + // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed + public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f; + + public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f; + + /// + /// Entity currently being pulled if applicable. + /// + [AutoNetworkedField, DataField] + public EntityUid? Pulling; + + /// + /// Does this entity need hands to be able to pull something? + /// + [DataField] + public bool NeedsHands = true; +} diff --git a/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs b/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs new file mode 100644 index 00000000000..b0101c46996 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.Physics.Components; + +namespace Content.Shared.Movement.Pulling.Events; + +/// +/// Raised directed on puller and pullable to determine if it can be pulled. +/// +public sealed class PullAttemptEvent : PullMessage +{ + public PullAttemptEvent(EntityUid pullerUid, EntityUid pullableUid) : base(pullerUid, pullableUid) { } + + public bool Cancelled { get; set; } +} diff --git a/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs b/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs new file mode 100644 index 00000000000..cd7edc5f623 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Pulling.Events; + +/// +/// Raised when a request is made to stop pulling an entity. +/// +public record struct AttemptStopPullingEvent(EntityUid? User = null) +{ + public readonly EntityUid? User = User; + public bool Cancelled; +} \ No newline at end of file diff --git a/Content.Shared/Pulling/Events/BeingPulledAttemptEvent.cs b/Content.Shared/Movement/Pulling/Events/BeingPulledAttemptEvent.cs similarity index 100% rename from Content.Shared/Pulling/Events/BeingPulledAttemptEvent.cs rename to Content.Shared/Movement/Pulling/Events/BeingPulledAttemptEvent.cs diff --git a/Content.Shared/Movement/Pulling/Events/PullMessage.cs b/Content.Shared/Movement/Pulling/Events/PullMessage.cs new file mode 100644 index 00000000000..a427e448d5c --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/PullMessage.cs @@ -0,0 +1,13 @@ +namespace Content.Shared.Movement.Pulling.Events; + +public abstract class PullMessage : EntityEventArgs +{ + public readonly EntityUid PullerUid; + public readonly EntityUid PulledUid; + + protected PullMessage(EntityUid pullerUid, EntityUid pulledUid) + { + PullerUid = pullerUid; + PulledUid = pulledUid; + } +} diff --git a/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs new file mode 100644 index 00000000000..29460e1dfc1 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.Movement.Pulling.Events; + +public sealed class PullStartedMessage : PullMessage +{ + public PullStartedMessage(EntityUid pullerUid, EntityUid pullableUid) : + base(pullerUid, pullableUid) + { + } +} diff --git a/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs new file mode 100644 index 00000000000..47aa34562fb --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs @@ -0,0 +1,13 @@ +using Robust.Shared.Physics.Components; + +namespace Content.Shared.Movement.Pulling.Events; + +/// +/// Raised directed on both puller and pullable. +/// +public sealed class PullStoppedMessage : PullMessage +{ + public PullStoppedMessage(EntityUid pullerUid, EntityUid pulledUid) : base(pullerUid, pulledUid) + { + } +} diff --git a/Content.Shared/Pulling/Events/StartPullAttemptEvent.cs b/Content.Shared/Movement/Pulling/Events/StartPullAttemptEvent.cs similarity index 100% rename from Content.Shared/Pulling/Events/StartPullAttemptEvent.cs rename to Content.Shared/Movement/Pulling/Events/StartPullAttemptEvent.cs diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs new file mode 100644 index 00000000000..33794ba169e --- /dev/null +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -0,0 +1,492 @@ +using System.Numerics; +using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; +using Content.Shared.Alert; +using Content.Shared.Buckle.Components; +using Content.Shared.Database; +using Content.Shared.Hands; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Input; +using Content.Shared.Interaction; +using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Systems; +using Content.Shared.Pulling.Events; +using Content.Shared.Throwing; +using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Robust.Shared.Input.Binding; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Timing; + +namespace Content.Shared.Movement.Pulling.Systems; + +/// +/// Allows one entity to pull another behind them via a physics distance joint. +/// +public sealed class PullingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly AlertsSystem _alertsSystem = default!; + [Dependency] private readonly MovementSpeedModifierSystem _modifierSystem = default!; + [Dependency] private readonly SharedJointSystem _joints = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _xformSys = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + + public override void Initialize() + { + base.Initialize(); + + UpdatesAfter.Add(typeof(SharedPhysicsSystem)); + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnPullableMoveInput); + SubscribeLocalEvent(OnPullableCollisionChange); + SubscribeLocalEvent(OnJointRemoved); + SubscribeLocalEvent>(AddPullVerbs); + SubscribeLocalEvent(OnPullableContainerInsert); + + SubscribeLocalEvent(OnPullerContainerInsert); + SubscribeLocalEvent(OnPullerUnpaused); + SubscribeLocalEvent(OnVirtualItemDeleted); + SubscribeLocalEvent(OnRefreshMovespeed); + + CommandBinds.Builder + .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject)) + .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(OnReleasePulledObject, handle: false)) + .Register(); + } + + private void OnPullerContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + if (ent.Comp.Pulling == null) return; + + if (!TryComp(ent.Comp.Pulling.Value, out PullableComponent? pulling)) + return; + + TryStopPull(ent.Comp.Pulling.Value, pulling, ent.Owner); + } + + private void OnPullableContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + TryStopPull(ent.Owner, ent.Comp); + } + + public override void Shutdown() + { + base.Shutdown(); + CommandBinds.Unregister(); + } + + private void OnPullerUnpaused(EntityUid uid, PullerComponent component, ref EntityUnpausedEvent args) + { + component.NextThrow += args.PausedTime; + } + + private void OnVirtualItemDeleted(EntityUid uid, PullerComponent component, VirtualItemDeletedEvent args) + { + // If client deletes the virtual hand then stop the pull. + if (component.Pulling == null) + return; + + if (component.Pulling != args.BlockingEntity) + return; + + if (EntityManager.TryGetComponent(args.BlockingEntity, out PullableComponent? comp)) + { + TryStopPull(args.BlockingEntity, comp, uid); + } + } + + private void AddPullVerbs(EntityUid uid, PullableComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + // Are they trying to pull themselves up by their bootstraps? + if (args.User == args.Target) + return; + + //TODO VERB ICONS add pulling icon + if (component.Puller == args.User) + { + Verb verb = new() + { + Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling"), + Act = () => TryStopPull(uid, component, user: args.User), + DoContactInteraction = false // pulling handle its own contact interaction. + }; + args.Verbs.Add(verb); + } + else if (CanPull(args.User, args.Target)) + { + Verb verb = new() + { + Text = Loc.GetString("pulling-verb-get-data-text"), + Act = () => TryStartPull(args.User, args.Target), + DoContactInteraction = false // pulling handle its own contact interaction. + }; + args.Verbs.Add(verb); + } + } + + private void OnRefreshMovespeed(EntityUid uid, PullerComponent component, RefreshMovementSpeedModifiersEvent args) + { + args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier); + } + + private void OnPullableMoveInput(EntityUid uid, PullableComponent component, ref MoveInputEvent args) + { + // If someone moves then break their pulling. + if (!component.BeingPulled) + return; + + var entity = args.Entity; + + if (!_blocker.CanMove(entity)) + return; + + TryStopPull(uid, component, user: uid); + } + + private void OnPullableCollisionChange(EntityUid uid, PullableComponent component, ref CollisionChangeEvent args) + { + // IDK what this is supposed to be. + if (!_timing.ApplyingState && component.PullJointId != null && !args.CanCollide) + { + _joints.RemoveJoint(uid, component.PullJointId); + } + } + + private void OnJointRemoved(EntityUid uid, PullableComponent component, JointRemovedEvent args) + { + // Just handles the joint getting nuked without going through pulling system (valid behavior). + + // Not relevant / pullable state handle it. + if (component.Puller != args.OtherEntity || + args.Joint.ID != component.PullJointId || + _timing.ApplyingState) + { + return; + } + + if (args.Joint.ID != component.PullJointId || component.Puller == null) + return; + + StopPulling(uid, component); + } + + /// + /// Forces pulling to stop and handles cleanup. + /// + private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) + { + if (!_timing.ApplyingState) + { + if (TryComp(pullableUid, out var pullablePhysics)) + { + _physics.SetFixedRotation(pullableUid, pullableComp.PrevFixedRotation, body: pullablePhysics); + } + } + + // No more joints with puller -> force stop pull. + if (TryComp(pullableComp.Puller, out var pullerComp)) + { + var pullerUid = pullableComp.Puller.Value; + _alertsSystem.ClearAlert(pullerUid, AlertType.Pulling); + pullerComp.Pulling = null; + Dirty(pullableComp.Puller.Value, pullerComp); + + // Messaging + var message = new PullStoppedMessage(pullerUid, pullableUid); + _modifierSystem.RefreshMovementSpeedModifiers(pullerUid); + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(pullerUid):user} stopped pulling {ToPrettyString(pullableUid):target}"); + + RaiseLocalEvent(pullerUid, message); + RaiseLocalEvent(pullableUid, message); + } + + pullableComp.PullJointId = null; + pullableComp.Puller = null; + Dirty(pullableUid, pullableComp); + + _alertsSystem.ClearAlert(pullableUid, AlertType.Pulled); + } + + public bool IsPulled(EntityUid uid, PullableComponent? component = null) + { + return Resolve(uid, ref component, false) && component.BeingPulled; + } + + private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) + { + if (session?.AttachedEntity is not { } player || + !player.IsValid()) + { + return false; + } + + if (!TryComp(player, out var pullerComp)) + return false; + + var pulled = pullerComp.Pulling; + + if (!HasComp(pulled)) + return false; + + if (_containerSystem.IsEntityInContainer(player)) + return false; + + // Cooldown buddy + if (_timing.CurTime < pullerComp.NextThrow) + return false; + + pullerComp.NextThrow = _timing.CurTime + pullerComp.ThrowCooldown; + + // Cap the distance + const float range = 2f; + var fromUserCoords = coords.WithEntityId(player, EntityManager); + var userCoords = new EntityCoordinates(player, Vector2.Zero); + + if (!userCoords.InRange(EntityManager, _xformSys, fromUserCoords, range)) + { + var userDirection = fromUserCoords.Position - userCoords.Position; + fromUserCoords = userCoords.Offset(userDirection.Normalized() * range); + } + + Dirty(player, pullerComp); + _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false); + return false; + } + + public bool IsPulling(EntityUid puller, PullerComponent? component = null) + { + return Resolve(puller, ref component, false) && component.Pulling != null; + } + + private void OnReleasePulledObject(ICommonSession? session) + { + if (session?.AttachedEntity is not {Valid: true} player) + { + return; + } + + if (!TryComp(player, out PullerComponent? pullerComp) || + !TryComp(pullerComp.Pulling, out PullableComponent? pullableComp)) + { + return; + } + + TryStopPull(pullerComp.Pulling.Value, pullableComp, user: player); + } + + public bool CanPull(EntityUid puller, EntityUid pullableUid, PullerComponent? pullerComp = null) + { + if (!Resolve(puller, ref pullerComp, false)) + { + return false; + } + + if (pullerComp.NeedsHands && !_handsSystem.TryGetEmptyHand(puller, out _)) + { + return false; + } + + if (!_blocker.CanInteract(puller, pullableUid)) + { + return false; + } + + if (!EntityManager.TryGetComponent(pullableUid, out var physics)) + { + return false; + } + + if (physics.BodyType == BodyType.Static) + { + return false; + } + + if (puller == pullableUid) + { + return false; + } + + if (!_containerSystem.IsInSameOrNoContainer(puller, pullableUid)) + { + return false; + } + + if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle)) + { + // Prevent people pulling the chair they're on, etc. + if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pullableUid)) + { + return false; + } + } + + var getPulled = new BeingPulledAttemptEvent(puller, pullableUid); + RaiseLocalEvent(pullableUid, getPulled, true); + var startPull = new StartPullAttemptEvent(puller, pullableUid); + RaiseLocalEvent(puller, startPull, true); + return !startPull.Cancelled && !getPulled.Cancelled; + } + + public bool TogglePull(EntityUid pullableUid, EntityUid pullerUid, PullableComponent pullable) + { + if (pullable.Puller == pullerUid) + { + return TryStopPull(pullableUid, pullable); + } + + return TryStartPull(pullerUid, pullableUid, pullableComp: pullable); + } + + public bool TogglePull(EntityUid pullerUid, PullerComponent puller) + { + if (!TryComp(puller.Pulling, out var pullable)) + return false; + + return TogglePull(puller.Pulling.Value, pullerUid, pullable); + } + + public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, EntityUid? user = null, + PullerComponent? pullerComp = null, PullableComponent? pullableComp = null) + { + if (!Resolve(pullerUid, ref pullerComp, false) || + !Resolve(pullableUid, ref pullableComp, false)) + { + return false; + } + + if (pullerComp.Pulling == pullableUid) + return true; + + if (!CanPull(pullerUid, pullableUid)) + return false; + + if (!EntityManager.TryGetComponent(pullerUid, out var pullerPhysics) || + !EntityManager.TryGetComponent(pullableUid, out var pullablePhysics)) + { + return false; + } + + // Ensure that the puller is not currently pulling anything. + var oldPullable = pullerComp.Pulling; + + if (oldPullable != null) + { + // Well couldn't stop the old one. + if (!TryStopPull(oldPullable.Value, pullableComp, user)) + return false; + } + + // Is the pullable currently being pulled by something else? + if (pullableComp.Puller != null) + { + // Uhhh + if (pullableComp.Puller == pullerUid) + return false; + + if (!TryStopPull(pullableUid, pullableComp, pullerUid)) + return false; + } + + var pullAttempt = new PullAttemptEvent(pullerUid, pullableUid); + RaiseLocalEvent(pullerUid, pullAttempt); + + if (pullAttempt.Cancelled) + return false; + + RaiseLocalEvent(pullableUid, pullAttempt); + + if (pullAttempt.Cancelled) + return false; + + // Pulling confirmed + + _interaction.DoContactInteraction(pullableUid, pullerUid); + + // Use net entity so it's consistent across client and server. + pullableComp.PullJointId = $"pull-joint-{GetNetEntity(pullableUid)}"; + + pullerComp.Pulling = pullableUid; + pullableComp.Puller = pullerUid; + + // joint state handling will manage its own state + if (!_timing.ApplyingState) + { + // Joint startup + var union = _physics.GetHardAABB(pullerUid).Union(_physics.GetHardAABB(pullableUid, body: pullablePhysics)); + var length = Math.Max((float) union.Size.X, (float) union.Size.Y) * 0.75f; + + var joint = _joints.CreateDistanceJoint(pullableUid, pullerUid, id: pullableComp.PullJointId); + joint.CollideConnected = false; + // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts. + joint.MaxLength = Math.Max(1.0f, length); + joint.Length = length * 0.75f; + joint.MinLength = 0f; + joint.Stiffness = 1f; + + _physics.SetFixedRotation(pullableUid, pullableComp.FixedRotationOnPull, body: pullablePhysics); + } + + pullableComp.PrevFixedRotation = pullablePhysics.FixedRotation; + + // Messaging + var message = new PullStartedMessage(pullerUid, pullableUid); + _alertsSystem.ShowAlert(pullerUid, AlertType.Pulling); + _alertsSystem.ShowAlert(pullableUid, AlertType.Pulled); + + RaiseLocalEvent(pullerUid, message); + RaiseLocalEvent(pullableUid, message); + + Dirty(pullerUid, pullerComp); + Dirty(pullableUid, pullableComp); + + _adminLogger.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(pullerUid):user} started pulling {ToPrettyString(pullableUid):target}"); + return true; + } + + public bool TryStopPull(EntityUid pullableUid, PullableComponent pullable, EntityUid? user = null) + { + var pullerUidNull = pullable.Puller; + + if (pullerUidNull == null) + return false; + + var msg = new AttemptStopPullingEvent(user); + RaiseLocalEvent(pullableUid, msg, true); + + if (msg.Cancelled) + return false; + + // Stop pulling confirmed! + if (!_timing.ApplyingState) + { + // Joint shutdown + if (pullable.PullJointId != null) + { + _joints.RemoveJoint(pullableUid, pullable.PullJointId); + pullable.PullJointId = null; + } + } + + StopPulling(pullableUid, pullable); + return true; + } +} diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 4d9eedbf7c4..550a53ac9ca 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -9,7 +9,6 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; -using Content.Shared.Pulling.Components; using Content.Shared.Tag; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -24,6 +23,7 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Shared.StepTrigger.Components; // Delta V-NoShoesSilentFootstepsComponent +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Movement.Systems { @@ -55,7 +55,7 @@ public abstract partial class SharedMoverController : VirtualController protected EntityQuery ModifierQuery; protected EntityQuery PhysicsQuery; protected EntityQuery RelayQuery; - protected EntityQuery PullableQuery; + protected EntityQuery PullableQuery; protected EntityQuery XformQuery; protected EntityQuery CanMoveInAirQuery; protected EntityQuery NoRotateQuery; @@ -87,7 +87,7 @@ public override void Initialize() RelayTargetQuery = GetEntityQuery(); PhysicsQuery = GetEntityQuery(); RelayQuery = GetEntityQuery(); - PullableQuery = GetEntityQuery(); + PullableQuery = GetEntityQuery(); XformQuery = GetEntityQuery(); NoRotateQuery = GetEntityQuery(); CanMoveInAirQuery = GetEntityQuery(); @@ -378,7 +378,7 @@ private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformCom !otherCollider.CanCollide || ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 && (otherCollider.CollisionMask & collider.CollisionLayer) == 0) || - (TryComp(otherCollider.Owner, out SharedPullableComponent? pullable) && pullable.BeingPulled)) + (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled)) { continue; } diff --git a/Content.Shared/Pulling/Components/PullableComponent.cs b/Content.Shared/Pulling/Components/PullableComponent.cs deleted file mode 100644 index c5c30688699..00000000000 --- a/Content.Shared/Pulling/Components/PullableComponent.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Map; -using Robust.Shared.Serialization; - -namespace Content.Shared.Pulling.Components -{ - // Before you try to add another type than SharedPullingStateManagementSystem, consider the can of worms you may be opening! - [NetworkedComponent, AutoGenerateComponentState] - [Access(typeof(SharedPullingStateManagementSystem))] - [RegisterComponent] - public sealed partial class SharedPullableComponent : Component - { - /// - /// The current entity pulling this component. - /// - [DataField, AutoNetworkedField] - public EntityUid? Puller { get; set; } - - /// - /// The pull joint. - /// - [DataField, AutoNetworkedField] - public string? PullJointId { get; set; } - - public bool BeingPulled => Puller != null; - - [Access(typeof(SharedPullingStateManagementSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public EntityCoordinates? MovingTo { get; set; } - - /// - /// If the physics component has FixedRotation should we keep it upon being pulled - /// - [Access(typeof(SharedPullingSystem), Other = AccessPermissions.ReadExecute)] - [ViewVariables(VVAccess.ReadWrite), DataField("fixedRotation")] - public bool FixedRotationOnPull { get; set; } - - /// - /// What the pullable's fixedrotation was set to before being pulled. - /// - [Access(typeof(SharedPullingSystem), Other = AccessPermissions.ReadExecute)] - [ViewVariables] - public bool PrevFixedRotation; - } - - /// - /// Raised when a request is made to stop pulling an entity. - /// - public sealed class StopPullingEvent : CancellableEntityEventArgs - { - public EntityUid? User { get; } - - public StopPullingEvent(EntityUid? uid = null) - { - User = uid; - } - } -} diff --git a/Content.Shared/Pulling/Components/SharedPullerComponent.cs b/Content.Shared/Pulling/Components/SharedPullerComponent.cs deleted file mode 100644 index 57a86e7f7a8..00000000000 --- a/Content.Shared/Pulling/Components/SharedPullerComponent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Pulling.Components -{ - [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] - [Access(typeof(SharedPullingStateManagementSystem))] - public sealed partial class SharedPullerComponent : Component - { - // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed - public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f; - - public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f; - - [DataField, AutoNetworkedField] - public EntityUid? Pulling { get; set; } - - /// - /// Does this entity need hands to be able to pull something? - /// - [DataField("needsHands")] - public bool NeedsHands = true; - } -} diff --git a/Content.Shared/Pulling/Events/PullAttemptEvent.cs b/Content.Shared/Pulling/Events/PullAttemptEvent.cs deleted file mode 100644 index 6296dc2f14f..00000000000 --- a/Content.Shared/Pulling/Events/PullAttemptEvent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public sealed class PullAttemptEvent : PullMessage - { - public PullAttemptEvent(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled) { } - - public bool Cancelled { get; set; } - } -} diff --git a/Content.Shared/Pulling/Events/PullMessage.cs b/Content.Shared/Pulling/Events/PullMessage.cs deleted file mode 100644 index b11de7c1702..00000000000 --- a/Content.Shared/Pulling/Events/PullMessage.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public abstract class PullMessage : EntityEventArgs - { - public readonly PhysicsComponent Puller; - public readonly PhysicsComponent Pulled; - - protected PullMessage(PhysicsComponent puller, PhysicsComponent pulled) - { - Puller = puller; - Pulled = pulled; - } - } -} diff --git a/Content.Shared/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Pulling/Events/PullStartedMessage.cs deleted file mode 100644 index 0ede284bb0c..00000000000 --- a/Content.Shared/Pulling/Events/PullStartedMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public sealed class PullStartedMessage : PullMessage - { - public PullStartedMessage(PhysicsComponent puller, PhysicsComponent pulled) : - base(puller, pulled) - { - } - } -} diff --git a/Content.Shared/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Pulling/Events/PullStoppedMessage.cs deleted file mode 100644 index afcbcb70740..00000000000 --- a/Content.Shared/Pulling/Events/PullStoppedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public sealed class PullStoppedMessage : PullMessage - { - public PullStoppedMessage(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled) - { - } - } -} diff --git a/Content.Shared/Pulling/PullableMoveMessage.cs b/Content.Shared/Pulling/PullableMoveMessage.cs deleted file mode 100644 index 46c6b1291d6..00000000000 --- a/Content.Shared/Pulling/PullableMoveMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Pulling -{ - public sealed class PullableMoveMessage : EntityEventArgs - { - } -} diff --git a/Content.Shared/Pulling/PullableStopMovingMessage.cs b/Content.Shared/Pulling/PullableStopMovingMessage.cs deleted file mode 100644 index 0233e32f2b4..00000000000 --- a/Content.Shared/Pulling/PullableStopMovingMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Pulling -{ - public sealed class PullableStopMovingMessage : EntityEventArgs - { - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullableSystem.cs b/Content.Shared/Pulling/Systems/SharedPullableSystem.cs deleted file mode 100644 index 3dab476337b..00000000000 --- a/Content.Shared/Pulling/Systems/SharedPullableSystem.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Content.Shared.ActionBlocker; -using Content.Shared.Mobs.Systems; -using Content.Shared.Pulling.Components; -using Content.Shared.Movement.Events; - -namespace Content.Shared.Pulling.Systems -{ - public sealed class SharedPullableSystem : EntitySystem - { - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedPullingSystem _pullSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnRelayMoveInput); - } - - private void OnRelayMoveInput(EntityUid uid, SharedPullableComponent component, ref MoveInputEvent args) - { - var entity = args.Entity; - if (_mobState.IsIncapacitated(entity) || !_blocker.CanMove(entity)) return; - - _pullSystem.TryStopPull(component); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs deleted file mode 100644 index e388d7a57c6..00000000000 --- a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Content.Shared.Administration.Logs; -using Content.Shared.Alert; -using Content.Shared.Database; -using Content.Shared.Hands; -using Content.Shared.Movement.Systems; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; - -namespace Content.Shared.Pulling.Systems -{ - [UsedImplicitly] - public sealed class SharedPullerSystem : EntitySystem - { - [Dependency] private readonly SharedPullingStateManagementSystem _why = default!; - [Dependency] private readonly SharedPullingSystem _pullSystem = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!; - [Dependency] private readonly AlertsSystem _alertsSystem = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(PullerHandlePullStarted); - SubscribeLocalEvent(PullerHandlePullStopped); - SubscribeLocalEvent(OnVirtualItemDeleted); - SubscribeLocalEvent(OnRefreshMovespeed); - SubscribeLocalEvent(OnPullerShutdown); - } - - private void OnPullerShutdown(EntityUid uid, SharedPullerComponent component, ComponentShutdown args) - { - _why.ForceDisconnectPuller(component); - } - - private void OnVirtualItemDeleted(EntityUid uid, SharedPullerComponent component, VirtualItemDeletedEvent args) - { - if (component.Pulling == null) - return; - - if (component.Pulling == args.BlockingEntity) - { - if (EntityManager.TryGetComponent(args.BlockingEntity, out var comp)) - { - _pullSystem.TryStopPull(comp, uid); - } - } - } - - private void PullerHandlePullStarted( - EntityUid uid, - SharedPullerComponent component, - PullStartedMessage args) - { - if (args.Puller.Owner != uid) - return; - - _alertsSystem.ShowAlert(component.Owner, AlertType.Pulling); - - RefreshMovementSpeed(component); - } - - private void PullerHandlePullStopped( - EntityUid uid, - SharedPullerComponent component, - PullStoppedMessage args) - { - if (args.Puller.Owner != uid) - return; - - var euid = component.Owner; - if (_alertsSystem.IsShowingAlert(euid, AlertType.Pulling)) - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(euid):user} stopped pulling {ToPrettyString(args.Pulled.Owner):target}"); - _alertsSystem.ClearAlert(euid, AlertType.Pulling); - - RefreshMovementSpeed(component); - } - - private void OnRefreshMovespeed(EntityUid uid, SharedPullerComponent component, RefreshMovementSpeedModifiersEvent args) - { - args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier); - } - - private void RefreshMovementSpeed(SharedPullerComponent component) - { - _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(component.Owner); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs deleted file mode 100644 index 38ed8998898..00000000000 --- a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; - -namespace Content.Shared.Pulling -{ - /// - /// This is the core of pulling state management. - /// Because pulling state is such a mess to get right, all writes to pulling state must go through this class. - /// - [UsedImplicitly] - public sealed class SharedPullingStateManagementSystem : EntitySystem - { - [Dependency] private readonly SharedJointSystem _jointSystem = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly IGameTiming _timing = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnShutdown); - } - - private void OnShutdown(EntityUid uid, SharedPullableComponent component, ComponentShutdown args) - { - if (component.Puller != null) - ForceRelationship(null, component); - } - - // A WARNING: - // The following 2 functions are the most internal part of the pulling system's relationship management. - // They do not expect to be cancellable. - private void ForceDisconnect(SharedPullerComponent puller, SharedPullableComponent pullable) - { - var pullerPhysics = EntityManager.GetComponent(puller.Owner); - var pullablePhysics = EntityManager.GetComponent(pullable.Owner); - - // MovingTo shutdown - ForceSetMovingTo(pullable, null); - - // Joint shutdown - if (!_timing.ApplyingState && // During state-handling, joint component will handle its own state. - pullable.PullJointId != null && - TryComp(puller.Owner, out JointComponent? jointComp)) - { - if (jointComp.GetJoints.TryGetValue(pullable.PullJointId, out var j)) - _jointSystem.RemoveJoint(j); - } - pullable.PullJointId = null; - - // State shutdown - puller.Pulling = null; - pullable.Puller = null; - - // Messaging - var message = new PullStoppedMessage(pullerPhysics, pullablePhysics); - - RaiseLocalEvent(puller.Owner, message, broadcast: false); - - if (Initialized(pullable.Owner)) - RaiseLocalEvent(pullable.Owner, message, true); - - // Networking - Dirty(puller); - Dirty(pullable); - } - - public void ForceRelationship(SharedPullerComponent? puller, SharedPullableComponent? pullable) - { - if (_timing.ApplyingState) - return; - ; - if (pullable != null && puller != null && (puller.Pulling == pullable.Owner)) - { - // Already done - return; - } - - // Start by disconnecting the pullable from whatever it is currently connected to. - var pullableOldPullerE = pullable?.Puller; - if (pullableOldPullerE != null) - { - ForceDisconnect(EntityManager.GetComponent(pullableOldPullerE.Value), pullable!); - } - - // Continue with the puller. - var pullerOldPullableE = puller?.Pulling; - if (pullerOldPullableE != null) - { - ForceDisconnect(puller!, EntityManager.GetComponent(pullerOldPullableE.Value)); - } - - // And now for the actual connection (if any). - - if (puller != null && pullable != null) - { - var pullerPhysics = EntityManager.GetComponent(puller.Owner); - var pullablePhysics = EntityManager.GetComponent(pullable.Owner); - pullable.PullJointId = $"pull-joint-{pullable.Owner}"; - - // State startup - puller.Pulling = pullable.Owner; - pullable.Puller = puller.Owner; - - // joint state handling will manage its own state - if (!_timing.ApplyingState) - { - // Joint startup - var union = _physics.GetHardAABB(puller.Owner).Union(_physics.GetHardAABB(pullable.Owner, body: pullablePhysics)); - var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f; - - var joint = _jointSystem.CreateDistanceJoint(pullablePhysics.Owner, pullerPhysics.Owner, id: pullable.PullJointId); - joint.CollideConnected = false; - // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts. - joint.MaxLength = Math.Max(1.0f, length); - joint.Length = length * 0.75f; - joint.MinLength = 0f; - joint.Stiffness = 1f; - } - - // Messaging - var message = new PullStartedMessage(pullerPhysics, pullablePhysics); - - RaiseLocalEvent(puller.Owner, message, broadcast: false); - RaiseLocalEvent(pullable.Owner, message, true); - - // Networking - Dirty(puller); - Dirty(pullable); - } - } - - // For OnRemove use only. - public void ForceDisconnectPuller(SharedPullerComponent puller) - { - // DO NOT ADD ADDITIONAL LOGIC IN THIS FUNCTION. Do it in ForceRelationship. - ForceRelationship(puller, null); - } - - // For OnRemove use only. - public void ForceDisconnectPullable(SharedPullableComponent pullable) - { - // DO NOT ADD ADDITIONAL LOGIC IN THIS FUNCTION. Do it in ForceRelationship. - ForceRelationship(null, pullable); - } - - public void ForceSetMovingTo(SharedPullableComponent pullable, EntityCoordinates? movingTo) - { - if (_timing.ApplyingState) - return; - - if (pullable.MovingTo == movingTo) - { - return; - } - - // Don't allow setting a MovingTo if there's no puller. - // The other half of this guarantee (shutting down a MovingTo if the puller goes away) is enforced in ForceRelationship. - if (pullable.Puller == null && movingTo != null) - { - return; - } - - pullable.MovingTo = movingTo; - Dirty(pullable); - - if (movingTo == null) - { - RaiseLocalEvent(pullable.Owner, new PullableStopMovingMessage(), true); - } - else - { - RaiseLocalEvent(pullable.Owner, new PullableMoveMessage(), true); - } - } - - /// - /// Changes if the entity needs a hand in order to be able to pull objects. - /// - public void ChangeHandRequirement(EntityUid uid, bool needsHands, SharedPullerComponent? comp) - { - if (!Resolve(uid, ref comp, false)) - return; - - comp.NeedsHands = needsHands; - - Dirty(uid, comp); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs deleted file mode 100644 index 1e2bb90c61e..00000000000 --- a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs +++ /dev/null @@ -1,239 +0,0 @@ -using Content.Shared.ActionBlocker; -using Content.Shared.Administration.Logs; -using Content.Shared.Buckle.Components; -using Content.Shared.Database; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Interaction; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using Content.Shared.Pulling.Events; -using Robust.Shared.Containers; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; -using Robust.Shared.Utility; - -namespace Content.Shared.Pulling -{ - public abstract partial class SharedPullingSystem - { - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedInteractionSystem _interaction = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IGameTiming _timing = default!; - - public bool CanPull(EntityUid puller, EntityUid pulled) - { - if (!EntityManager.TryGetComponent(puller, out var comp)) - { - return false; - } - - if (comp.NeedsHands && !_handsSystem.TryGetEmptyHand(puller, out _)) - { - return false; - } - - if (!_blocker.CanInteract(puller, pulled)) - { - return false; - } - - if (!EntityManager.TryGetComponent(pulled, out var physics)) - { - return false; - } - - if (physics.BodyType == BodyType.Static) - { - return false; - } - - if (puller == pulled) - { - return false; - } - - if(_containerSystem.IsEntityInContainer(puller) || _containerSystem.IsEntityInContainer(pulled)) - { - return false; - } - - if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle)) - { - // Prevent people pulling the chair they're on, etc. - if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pulled)) - { - return false; - } - } - - var getPulled = new BeingPulledAttemptEvent(puller, pulled); - RaiseLocalEvent(pulled, getPulled, true); - var startPull = new StartPullAttemptEvent(puller, pulled); - RaiseLocalEvent(puller, startPull, true); - return (!startPull.Cancelled && !getPulled.Cancelled); - } - - public bool TogglePull(EntityUid puller, SharedPullableComponent pullable) - { - if (pullable.Puller == puller) - { - return TryStopPull(pullable); - } - return TryStartPull(puller, pullable.Owner); - } - - // -- Core attempted actions -- - - public bool TryStopPull(SharedPullableComponent pullable, EntityUid? user = null) - { - if (_timing.ApplyingState) - return false; - - if (!pullable.BeingPulled) - { - return false; - } - - var msg = new StopPullingEvent(user); - RaiseLocalEvent(pullable.Owner, msg, true); - - if (msg.Cancelled) return false; - - // Stop pulling confirmed! - - if (TryComp(pullable.Owner, out var pullablePhysics)) - { - _physics.SetFixedRotation(pullable.Owner, pullable.PrevFixedRotation, body: pullablePhysics); - } - - _pullSm.ForceRelationship(null, pullable); - return true; - } - - public bool TryStartPull(EntityUid puller, EntityUid pullable) - { - if (!EntityManager.TryGetComponent(puller, out SharedPullerComponent? pullerComp)) - { - return false; - } - if (!EntityManager.TryGetComponent(pullable, out SharedPullableComponent? pullableComp)) - { - return false; - } - return TryStartPull(pullerComp, pullableComp); - } - - // The main "start pulling" function. - public bool TryStartPull(SharedPullerComponent puller, SharedPullableComponent pullable) - { - if (_timing.ApplyingState) - return false; - - if (puller.Pulling == pullable.Owner) - return true; - - // Pulling a new object : Perform sanity checks. - - if (!CanPull(puller.Owner, pullable.Owner)) - { - return false; - } - - if (!EntityManager.TryGetComponent(puller.Owner, out var pullerPhysics)) - { - return false; - } - - if (!EntityManager.TryGetComponent(pullable.Owner, out var pullablePhysics)) - { - return false; - } - - // Ensure that the puller is not currently pulling anything. - // If this isn't done, then it happens too late, and the start/stop messages go out of order, - // and next thing you know it thinks it's not pulling anything even though it is! - - var oldPullable = puller.Pulling; - if (oldPullable != null) - { - if (EntityManager.TryGetComponent(oldPullable.Value, out SharedPullableComponent? oldPullableComp)) - { - if (!TryStopPull(oldPullableComp)) - { - return false; - } - } - else - { - Log.Warning("Well now you've done it, haven't you? Someone transferred pulling (onto {0}) while presently pulling something that has no Pullable component (on {1})!", pullable.Owner, oldPullable); - return false; - } - } - - // Ensure that the pullable is not currently being pulled. - // Same sort of reasons as before. - - var oldPuller = pullable.Puller; - if (oldPuller != null) - { - if (!TryStopPull(pullable)) - { - return false; - } - } - - // Continue with pulling process. - - var pullAttempt = new PullAttemptEvent(pullerPhysics, pullablePhysics); - - RaiseLocalEvent(puller.Owner, pullAttempt, broadcast: false); - - if (pullAttempt.Cancelled) - { - return false; - } - - RaiseLocalEvent(pullable.Owner, pullAttempt, true); - - if (pullAttempt.Cancelled) - return false; - - _interaction.DoContactInteraction(pullable.Owner, puller.Owner); - - _pullSm.ForceRelationship(puller, pullable); - pullable.PrevFixedRotation = pullablePhysics.FixedRotation; - _physics.SetFixedRotation(pullable.Owner, pullable.FixedRotationOnPull, body: pullablePhysics); - _adminLogger.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(puller.Owner):user} started pulling {ToPrettyString(pullable.Owner):target}"); - return true; - } - - public bool TryMoveTo(SharedPullableComponent pullable, EntityCoordinates to) - { - if (pullable.Puller == null) - { - return false; - } - - if (!EntityManager.HasComponent(pullable.Owner)) - { - return false; - } - - _pullSm.ForceSetMovingTo(pullable, to); - return true; - } - - public void StopMoveTo(SharedPullableComponent pullable) - { - _pullSm.ForceSetMovingTo(pullable, null); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.cs deleted file mode 100644 index 0c139ee9e35..00000000000 --- a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Alert; -using Content.Shared.GameTicking; -using Content.Shared.Input; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using Content.Shared.Verbs; -using JetBrains.Annotations; -using Robust.Shared.Containers; -using Robust.Shared.Input.Binding; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; - -namespace Content.Shared.Pulling -{ - [UsedImplicitly] - public abstract partial class SharedPullingSystem : EntitySystem - { - [Dependency] private readonly SharedPullingStateManagementSystem _pullSm = default!; - [Dependency] private readonly AlertsSystem _alertsSystem = default!; - [Dependency] private readonly SharedJointSystem _joints = default!; - - /// - /// A mapping of pullers to the entity that they are pulling. - /// - private readonly Dictionary _pullers = - new(); - - private readonly HashSet _moving = new(); - private readonly HashSet _stoppedMoving = new(); - - public IReadOnlySet Moving => _moving; - - public override void Initialize() - { - base.Initialize(); - - UpdatesOutsidePrediction = true; - - SubscribeLocalEvent(Reset); - SubscribeLocalEvent(OnPullStarted); - SubscribeLocalEvent(OnPullStopped); - SubscribeLocalEvent(HandleContainerInsert); - SubscribeLocalEvent(OnJointRemoved); - SubscribeLocalEvent(OnPullableCollisionChange); - - SubscribeLocalEvent(PullableHandlePullStarted); - SubscribeLocalEvent(PullableHandlePullStopped); - - SubscribeLocalEvent>(AddPullVerbs); - - CommandBinds.Builder - .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject)) - .Register(); - } - - private void OnPullableCollisionChange(EntityUid uid, SharedPullableComponent component, ref CollisionChangeEvent args) - { - if (component.PullJointId != null && !args.CanCollide) - { - _joints.RemoveJoint(uid, component.PullJointId); - } - } - - private void OnJointRemoved(EntityUid uid, SharedPullableComponent component, JointRemovedEvent args) - { - if (component.Puller != args.OtherEntity) - return; - - // Do we have some other join with our Puller? - // or alternatively: - // TODO track the relevant joint. - - if (TryComp(uid, out JointComponent? joints)) - { - foreach (var jt in joints.GetJoints.Values) - { - if (jt.BodyAUid == component.Puller || jt.BodyBUid == component.Puller) - return; - } - } - - // No more joints with puller -> force stop pull. - _pullSm.ForceDisconnectPullable(component); - } - - private void AddPullVerbs(EntityUid uid, SharedPullableComponent component, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - // Are they trying to pull themselves up by their bootstraps? - if (args.User == args.Target) - return; - - //TODO VERB ICONS add pulling icon - if (component.Puller == args.User) - { - Verb verb = new() - { - Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling"), - Act = () => TryStopPull(component, args.User), - DoContactInteraction = false // pulling handle its own contact interaction. - }; - args.Verbs.Add(verb); - } - else if (CanPull(args.User, args.Target)) - { - Verb verb = new() - { - Text = Loc.GetString("pulling-verb-get-data-text"), - Act = () => TryStartPull(args.User, args.Target), - DoContactInteraction = false // pulling handle its own contact interaction. - }; - args.Verbs.Add(verb); - } - } - - // Raise a "you are being pulled" alert if the pulled entity has alerts. - private void PullableHandlePullStarted(EntityUid uid, SharedPullableComponent component, PullStartedMessage args) - { - if (args.Pulled.Owner != uid) - return; - - _alertsSystem.ShowAlert(uid, AlertType.Pulled); - } - - private void PullableHandlePullStopped(EntityUid uid, SharedPullableComponent component, PullStoppedMessage args) - { - if (args.Pulled.Owner != uid) - return; - - _alertsSystem.ClearAlert(uid, AlertType.Pulled); - } - - public bool IsPulled(EntityUid uid, SharedPullableComponent? component = null) - { - return Resolve(uid, ref component, false) && component.BeingPulled; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _moving.ExceptWith(_stoppedMoving); - _stoppedMoving.Clear(); - } - - public void Reset(RoundRestartCleanupEvent ev) - { - _pullers.Clear(); - _moving.Clear(); - _stoppedMoving.Clear(); - } - - private void OnPullStarted(PullStartedMessage message) - { - SetPuller(message.Puller.Owner, message.Pulled.Owner); - } - - private void OnPullStopped(PullStoppedMessage message) - { - RemovePuller(message.Puller.Owner); - } - - protected void OnPullableMove(EntityUid uid, SharedPullableComponent component, PullableMoveMessage args) - { - _moving.Add(component); - } - - protected void OnPullableStopMove(EntityUid uid, SharedPullableComponent component, PullableStopMovingMessage args) - { - _stoppedMoving.Add(component); - } - - // TODO: When Joint networking is less shitcodey fix this to use a dedicated joints message. - private void HandleContainerInsert(EntInsertedIntoContainerMessage message) - { - if (TryComp(message.Entity, out SharedPullableComponent? pullable)) - { - TryStopPull(pullable); - } - - if (TryComp(message.Entity, out SharedPullerComponent? puller)) - { - if (puller.Pulling == null) return; - - if (!TryComp(puller.Pulling.Value, out SharedPullableComponent? pulling)) - return; - - TryStopPull(pulling); - } - } - - private bool HandleMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) - { - if (session?.AttachedEntity is not { } player || - !player.IsValid()) - return false; - - if (!TryGetPulled(player, out var pulled)) - return false; - - if (!TryComp(pulled.Value, out SharedPullableComponent? pullable)) - return false; - - if (_containerSystem.IsEntityInContainer(player)) - return false; - - TryMoveTo(pullable, coords); - - return false; - } - - private void SetPuller(EntityUid puller, EntityUid pulled) - { - _pullers[puller] = pulled; - } - - private bool RemovePuller(EntityUid puller) - { - return _pullers.Remove(puller); - } - - public EntityUid GetPulled(EntityUid by) - { - return _pullers.GetValueOrDefault(by); - } - - public bool TryGetPulled(EntityUid by, [NotNullWhen(true)] out EntityUid? pulled) - { - return (pulled = GetPulled(by)) != null; - } - - public bool IsPulling(EntityUid puller) - { - return _pullers.ContainsKey(puller); - } - } -} diff --git a/Content.Shared/Security/Systems/DeployableBarrierSystem.cs b/Content.Shared/Security/Systems/DeployableBarrierSystem.cs index 7b9ce841a99..622edc4b62e 100644 --- a/Content.Shared/Security/Systems/DeployableBarrierSystem.cs +++ b/Content.Shared/Security/Systems/DeployableBarrierSystem.cs @@ -1,6 +1,6 @@ using Content.Shared.Lock; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Security.Components; using Robust.Shared.Physics.Systems; @@ -11,7 +11,7 @@ public sealed class DeployableBarrierSystem : EntitySystem [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedPointLightSystem _pointLight = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() @@ -54,8 +54,8 @@ private void ToggleBarrierDeploy(EntityUid uid, bool isDeployed, DeployableBarri _physics.SetHard(uid, fixture, false); } - if (TryComp(uid, out SharedPullableComponent? pullable)) - _pulling.TryStopPull(pullable); + if (TryComp(uid, out PullableComponent? pullable)) + _pulling.TryStopPull(uid, pullable); SharedPointLightComponent? pointLight = null; if (_pointLight.ResolveLight(uid, ref pointLight)) diff --git a/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs b/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs index ebd83624114..8d67aec518a 100644 --- a/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs +++ b/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs @@ -1,9 +1,9 @@ using System.Linq; using Content.Shared.Ghost; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Popups; using Content.Shared.Projectiles; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Teleportation.Components; using Content.Shared.Verbs; using Robust.Shared.Audio; @@ -28,7 +28,7 @@ public abstract class SharedPortalSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; private const string PortalFixture = "portalFixture"; @@ -93,15 +93,15 @@ private void OnCollide(EntityUid uid, PortalComponent component, ref StartCollid return; // break pulls before portal enter so we dont break shit - if (TryComp(subject, out var pullable) && pullable.BeingPulled) + if (TryComp(subject, out var pullable) && pullable.BeingPulled) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(subject, pullable); } - if (TryComp(subject, out var pulling) - && pulling.Pulling != null && TryComp(pulling.Pulling.Value, out var subjectPulling)) + if (TryComp(subject, out var pullerComp) + && TryComp(pullerComp.Pulling, out var subjectPulling)) { - _pulling.TryStopPull(subjectPulling); + _pulling.TryStopPull(subject, subjectPulling); } // if they came from another portal, just return and wait for them to exit the portal diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 54294318315..7c861a85adb 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -20,6 +20,11 @@ public sealed class ThrowingSystem : EntitySystem { public const float ThrowAngularImpulse = 5f; + /// + /// Speed cap on rotation in case of click-spam. + /// + public const float ThrowAngularCap = 3f * MathF.PI; + public const float PushbackDefault = 2f; /// @@ -42,15 +47,17 @@ public void TryThrow( float strength = 1.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, + bool recoil = true, + bool animated = true, bool playSound = true) { - var thrownPos = Transform(uid).MapPosition; - var mapPos = coordinates.ToMap(EntityManager, _transform); + var thrownPos = _transform.GetMapCoordinates(uid); + var mapPos = _transform.ToMapCoordinates(coordinates); if (mapPos.MapId != thrownPos.MapId) return; - TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, playSound); + TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); } /// @@ -65,6 +72,8 @@ public void TryThrow(EntityUid uid, float strength = 1.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, + bool recoil = true, + bool animated = true, bool playSound = true) { var physicsQuery = GetEntityQuery(); @@ -72,7 +81,6 @@ public void TryThrow(EntityUid uid, return; var projectileQuery = GetEntityQuery(); - var tagQuery = GetEntityQuery(); TryThrow( uid, @@ -82,8 +90,7 @@ public void TryThrow(EntityUid uid, projectileQuery, strength, user, - pushbackRatio, - playSound); + pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); } /// @@ -101,6 +108,8 @@ public void TryThrow(EntityUid uid, float strength = 1.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, + bool recoil = true, + bool animated = true, bool playSound = true) { if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero) @@ -116,12 +125,17 @@ public void TryThrow(EntityUid uid, if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot) return; - var comp = new ThrownItemComponent(); - comp.Thrower = user; + var comp = new ThrownItemComponent + { + Thrower = user, + Animate = animated, + }; // Estimate time to arrival so we can apply OnGround status and slow it much faster. var time = direction.Length() / strength; comp.ThrownTime = _gameTiming.CurTime; + // TODO: This is a bandaid, don't do this. + // if you want to force landtime have the caller handle it or add a new method. // did we launch this with something stronger than our hands? if (TryComp(comp.Thrower, out var hands) && strength > hands.ThrowForceMultiplier) comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(time); @@ -166,7 +180,8 @@ public void TryThrow(EntityUid uid, if (user == null) return; - _recoil.KickCamera(user.Value, -direction * 0.04f); + if (recoil) + _recoil.KickCamera(user.Value, -direction * 0.04f); // Give thrower an impulse in the other direction if (pushbackRatio != 0.0f && diff --git a/Content.Shared/Throwing/ThrownItemComponent.cs b/Content.Shared/Throwing/ThrownItemComponent.cs index 16c9b13254c..f7defaa4aab 100644 --- a/Content.Shared/Throwing/ThrownItemComponent.cs +++ b/Content.Shared/Throwing/ThrownItemComponent.cs @@ -8,6 +8,12 @@ namespace Content.Shared.Throwing [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause] public sealed partial class ThrownItemComponent : Component { + /// + /// Should the in-air throwing animation play. + /// + [DataField, AutoNetworkedField] + public bool Animate = true; + /// /// The entity that threw this entity. /// diff --git a/Content.Shared/Throwing/ThrownItemSystem.cs b/Content.Shared/Throwing/ThrownItemSystem.cs index b3b5bcf7870..664b4a36a46 100644 --- a/Content.Shared/Throwing/ThrownItemSystem.cs +++ b/Content.Shared/Throwing/ThrownItemSystem.cs @@ -3,8 +3,7 @@ using Content.Shared.Database; using Content.Shared.Gravity; using Content.Shared.Physics; -using Content.Shared.Physics.Pull; -using Robust.Shared.GameStates; +using Content.Shared.Movement.Pulling.Events; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; @@ -85,8 +84,8 @@ private void OnSleep(EntityUid uid, ThrownItemComponent thrownItem, ref PhysicsS private void HandlePullStarted(PullStartedMessage message) { // TODO: this isn't directed so things have to be done the bad way - if (EntityManager.TryGetComponent(message.Pulled.Owner, out ThrownItemComponent? thrownItemComponent)) - StopThrow(message.Pulled.Owner, thrownItemComponent); + if (EntityManager.TryGetComponent(message.PulledUid, out ThrownItemComponent? thrownItemComponent)) + StopThrow(message.PulledUid, thrownItemComponent); } public void StopThrow(EntityUid uid, ThrownItemComponent thrownItemComponent) From 69f3df45b879d9cca833449d88f46d502fc57b3a Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:48:52 -0400 Subject: [PATCH 42/54] Store Refund Fix (#26251) Removes else that accidentally enables refunds --- Content.Server/Store/Systems/StoreSystem.Ui.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 25b1cd1f6c0..281d8f57267 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -165,8 +165,6 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi if (!IsOnStartingMap(uid, component)) component.RefundAllowed = false; - else - component.RefundAllowed = true; //subtract the cash foreach (var (currency, value) in listing.Cost) From 713575ebf942f104a26b3e21a1f723c2645eecf4 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:34:00 +1100 Subject: [PATCH 43/54] Fix build (#26258) * Fix build - Fixes a couple paint things that were broken. * wat --- Content.Client/Paint/PaintVisualizerSystem.cs | 152 +++++++++--------- Content.Server/Paint/PaintSystem.cs | 22 ++- Content.Shared/Paint/PaintRemoverSystem.cs | 10 +- 3 files changed, 88 insertions(+), 96 deletions(-) diff --git a/Content.Client/Paint/PaintVisualizerSystem.cs b/Content.Client/Paint/PaintVisualizerSystem.cs index 6c99b2d35f0..8d037811fab 100644 --- a/Content.Client/Paint/PaintVisualizerSystem.cs +++ b/Content.Client/Paint/PaintVisualizerSystem.cs @@ -7,112 +7,108 @@ using Robust.Client.Graphics; using Robust.Shared.Prototypes; -namespace Content.Client.Paint -{ - public sealed class PaintedVisualizerSystem : VisualizerSystem - { - /// - /// Visualizer for Paint which applies a shader and colors the entity. - /// +namespace Content.Client.Paint; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly IPrototypeManager _protoMan = default!; +public sealed class PaintedVisualizerSystem : VisualizerSystem +{ + /// + /// Visualizer for Paint which applies a shader and colors the entity. + /// - public ShaderInstance? Shader; // in Robust.Client.Graphics so cannot move to shared component. + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; - public override void Initialize() - { - base.Initialize(); + public override void Initialize() + { + base.Initialize(); - SubscribeLocalEvent(OnHeldVisualsUpdated); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnEquipmentVisualsUpdated); - } + SubscribeLocalEvent(OnHeldVisualsUpdated); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnEquipmentVisualsUpdated); + } - protected override void OnAppearanceChange(EntityUid uid, PaintedComponent component, ref AppearanceChangeEvent args) - { - // ShaderPrototype sadly in Robust.Client, cannot move to shared component. - Shader = _protoMan.Index(component.ShaderName).Instance(); + protected override void OnAppearanceChange(EntityUid uid, PaintedComponent component, ref AppearanceChangeEvent args) + { + var shader = _protoMan.Index(component.ShaderName).Instance(); - if (args.Sprite == null) - return; + if (args.Sprite == null) + return; - if (!_appearance.TryGetData(uid, PaintVisuals.Painted, out bool isPainted)) - return; + // What is this even doing? It's not even checking what the value is. + if (!_appearance.TryGetData(uid, PaintVisuals.Painted, out bool isPainted)) + return; - var sprite = args.Sprite; + var sprite = args.Sprite; + foreach (var spriteLayer in sprite.AllLayers) + { + if (spriteLayer is not Layer layer) + continue; - foreach (var spriteLayer in sprite.AllLayers) + if (layer.Shader == null) // If shader isn't null we dont want to replace the original shader. { - if (spriteLayer is not Layer layer) - continue; - - if (layer.Shader == null) // If shader isn't null we dont want to replace the original shader. - { - layer.Shader = Shader; - layer.Color = component.Color; - } + layer.Shader = shader; + layer.Color = component.Color; } } + } - private void OnHeldVisualsUpdated(EntityUid uid, PaintedComponent component, HeldVisualsUpdatedEvent args) - { - if (args.RevealedLayers.Count == 0) - return; + private void OnHeldVisualsUpdated(EntityUid uid, PaintedComponent component, HeldVisualsUpdatedEvent args) + { + if (args.RevealedLayers.Count == 0) + return; - if (!TryComp(args.User, out SpriteComponent? sprite)) - return; + if (!TryComp(args.User, out SpriteComponent? sprite)) + return; - foreach (var revealed in args.RevealedLayers) - { - if (!sprite.LayerMapTryGet(revealed, out var layer) || sprite[layer] is not Layer notlayer) - continue; + foreach (var revealed in args.RevealedLayers) + { + if (!sprite.LayerMapTryGet(revealed, out var layer)) + continue; - sprite.LayerSetShader(layer, component.ShaderName); - sprite.LayerSetColor(layer, component.Color); - } + sprite.LayerSetShader(layer, component.ShaderName); + sprite.LayerSetColor(layer, component.Color); } + } - private void OnEquipmentVisualsUpdated(EntityUid uid, PaintedComponent component, EquipmentVisualsUpdatedEvent args) - { - if (args.RevealedLayers.Count == 0) - return; + private void OnEquipmentVisualsUpdated(EntityUid uid, PaintedComponent component, EquipmentVisualsUpdatedEvent args) + { + if (args.RevealedLayers.Count == 0) + return; - if (!TryComp(args.Equipee, out SpriteComponent? sprite)) - return; + if (!TryComp(args.Equipee, out SpriteComponent? sprite)) + return; - foreach (var revealed in args.RevealedLayers) - { - if (!sprite.LayerMapTryGet(revealed, out var layer) || sprite[layer] is not Layer notlayer) - continue; + foreach (var revealed in args.RevealedLayers) + { + if (!sprite.LayerMapTryGet(revealed, out var layer)) + continue; - sprite.LayerSetShader(layer, component.ShaderName); - sprite.LayerSetColor(layer, component.Color); - } + sprite.LayerSetShader(layer, component.ShaderName); + sprite.LayerSetColor(layer, component.Color); } + } - private void OnShutdown(EntityUid uid, PaintedComponent component, ref ComponentShutdown args) - { - if (!TryComp(uid, out SpriteComponent? sprite)) - return; + private void OnShutdown(EntityUid uid, PaintedComponent component, ref ComponentShutdown args) + { + if (!TryComp(uid, out SpriteComponent? sprite)) + return; - component.BeforeColor = sprite.Color; - Shader = _protoMan.Index(component.ShaderName).Instance(); + component.BeforeColor = sprite.Color; + var shader = _protoMan.Index(component.ShaderName).Instance(); - if (!Terminating(uid)) + if (!Terminating(uid)) + { + foreach (var spriteLayer in sprite.AllLayers) { - foreach (var spriteLayer in sprite.AllLayers) + if (spriteLayer is not Layer layer) + continue; + + if (layer.Shader == shader) // If shader isn't same as one in component we need to ignore it. { - if (spriteLayer is not Layer layer) - continue; - - if (layer.Shader == Shader) // If shader isn't same as one in component we need to ignore it. - { - layer.Shader = null; - if (layer.Color == component.Color) // If color isn't the same as one in component we don't want to change it. - layer.Color = component.BeforeColor; - } + layer.Shader = null; + if (layer.Color == component.Color) // If color isn't the same as one in component we don't want to change it. + layer.Color = component.BeforeColor; } } } diff --git a/Content.Server/Paint/PaintSystem.cs b/Content.Server/Paint/PaintSystem.cs index c6718aced68..892f961d634 100644 --- a/Content.Server/Paint/PaintSystem.cs +++ b/Content.Server/Paint/PaintSystem.cs @@ -72,15 +72,12 @@ private void PrepPaint(EntityUid uid, PaintComponent component, EntityUid target var doAfterEventArgs = new DoAfterArgs(EntityManager, user, component.Delay, new PaintDoAfterEvent(), uid, target: target, used: uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true, - BreakOnDamage = true, + BreakOnMove = true, NeedHand = true, BreakOnHandChange = true }; - if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs)) - return; + _doAfterSystem.TryStartDoAfter(doAfterEventArgs); } private void OnPaint(Entity entity, ref PaintDoAfterEvent args) @@ -112,7 +109,6 @@ private void OnPaint(Entity entity, ref PaintDoAfterEvent args) return; } - if (TryPaint(entity, target)) { EnsureComp(target, out PaintedComponent? paint); @@ -131,12 +127,14 @@ private void OnPaint(Entity entity, ref PaintDoAfterEvent args) if (!_inventory.TryGetSlotEntity(target, slot.Name, out var slotEnt)) continue; - if (slotEnt == null) - return; - - if (HasComp(slotEnt.Value) || !entity.Comp.Blacklist?.IsValid(slotEnt.Value, EntityManager) != true - || HasComp(slotEnt.Value) || HasComp(slotEnt.Value)) - return; + if (HasComp(slotEnt.Value) || !entity.Comp.Blacklist?.IsValid(slotEnt.Value, + EntityManager) != true + || HasComp(slotEnt.Value) || + HasComp( + slotEnt.Value)) + { + continue; + } EnsureComp(slotEnt.Value, out PaintedComponent? slotpaint); EnsureComp(slotEnt.Value); diff --git a/Content.Shared/Paint/PaintRemoverSystem.cs b/Content.Shared/Paint/PaintRemoverSystem.cs index ac1cc624cfe..efc1ded0677 100644 --- a/Content.Shared/Paint/PaintRemoverSystem.cs +++ b/Content.Shared/Paint/PaintRemoverSystem.cs @@ -16,7 +16,6 @@ public sealed class PaintRemoverSystem : SharedPaintSystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; public override void Initialize() @@ -39,9 +38,8 @@ private void OnInteract(EntityUid uid, PaintRemoverComponent component, AfterInt _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, - BreakOnTargetMove = true, MovementThreshold = 1.0f, }); args.Handled = true; @@ -77,13 +75,13 @@ private void OnPaintRemoveVerb(EntityUid uid, PaintRemoverComponent component, G var verb = new UtilityVerb() { - Act = () => { + Act = () => + { _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid) { - BreakOnUserMove = true, + BreakOnMove = true, BreakOnDamage = true, - BreakOnTargetMove = true, MovementThreshold = 1.0f, }); }, From 528b7fe866de9a47f7510745c91471d2b405a6da Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Tue, 19 Mar 2024 22:46:03 -0400 Subject: [PATCH 44/54] Adds logging for refunds (#26274) LGTM --- Content.Server/Store/Systems/StoreSystem.Ui.cs | 3 +++ Content.Shared.Database/LogType.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 281d8f57267..7cdbc4e41c3 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -5,6 +5,7 @@ using Content.Server.Stack; using Content.Server.Store.Components; using Content.Shared.Actions; +using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; @@ -321,6 +322,8 @@ private void OnRequestRefund(EntityUid uid, StoreComponent component, StoreReque if (!component.RefundAllowed || component.BoughtEntities.Count == 0) return; + _admin.Add(LogType.StoreRefund, LogImpact.Low, $"{ToPrettyString(buyer):player} has refunded their purchases from {ToPrettyString(uid):store}"); + for (var i = component.BoughtEntities.Count - 1; i >= 0; i--) { var purchase = component.BoughtEntities[i]; diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index cc87dddf302..ba94b0f3738 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -97,4 +97,6 @@ public enum LogType /// ChatRateLimited = 87, AtmosTemperatureChanged = 88, + DeviceNetwork = 89, + StoreRefund = 90 } From 803e4527a3676cdfebdb27188a0c49c1fa082714 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Wed, 20 Mar 2024 04:41:50 -0400 Subject: [PATCH 45/54] make traitor syndicate reinforcements get the traitor role (#25400) inital --- .../Prototypes/Entities/Mobs/Player/human.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index b864984a7f3..6197c82c021 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -9,9 +9,9 @@ #Syndie - type: entity parent: MobHuman - id: MobHumanSyndicateAgent + id: MobHumanSyndicateAgentBase name: syndicate agent - suffix: Human + suffix: Human, Base components: - type: Loadout prototypes: [SyndicateOperativeGearExtremelyBasic] @@ -22,9 +22,20 @@ - Syndicate - type: entity - parent: MobHumanSyndicateAgent + parent: MobHumanSyndicateAgentBase + id: MobHumanSyndicateAgent + name: syndicate agent + suffix: Human, Traitor + components: + # make the player a traitor once its taken + - type: AutoTraitor + giveUplink: false + giveObjectives: false + +- type: entity + parent: MobHumanSyndicateAgentBase id: MobHumanSyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink - suffix: NukeOps + suffix: Human, NukeOps components: - type: NukeOperative From 9d8e78d9910ba716f8d39e7477881d5f151e087c Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:57:39 +0100 Subject: [PATCH 46/54] Thrown soap/banana/(etc?) will fail to slip until it lands (#24494) * throw miss * event * whoops Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * fix popup duplication * Separate cancellable event * no popup, no problem * remove leftover stuff --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Content.Shared/Slippery/SlipperySystem.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs index 1f602b9b52b..d20495cfa6c 100644 --- a/Content.Shared/Slippery/SlipperySystem.cs +++ b/Content.Shared/Slippery/SlipperySystem.cs @@ -1,9 +1,12 @@ using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Inventory; +using Robust.Shared.Network; +using Content.Shared.Popups; using Content.Shared.StatusEffect; using Content.Shared.StepTrigger.Systems; using Content.Shared.Stunnable; +using Content.Shared.Throwing; using JetBrains.Annotations; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -30,6 +33,7 @@ public override void Initialize() SubscribeLocalEvent(HandleAttemptCollide); SubscribeLocalEvent(HandleStepTrigger); SubscribeLocalEvent(OnNoSlipAttempt); + SubscribeLocalEvent(OnThrownSlipAttempt); // as long as slip-resistant mice are never added, this should be fine (otherwise a mouse-hat will transfer it's power to the wearer). SubscribeLocalEvent>((e, c, ev) => OnNoSlipAttempt(e, c, ev.Args)); } @@ -52,6 +56,11 @@ private static void OnNoSlipAttempt(EntityUid uid, NoSlipComponent component, Sl args.Cancel(); } + private void OnThrownSlipAttempt(EntityUid uid, ThrownItemComponent comp, ref SlipCausingAttemptEvent args) + { + args.Cancelled = true; + } + private bool CanSlip(EntityUid uid, EntityUid toSlip) { return !_container.IsEntityInContainer(uid) @@ -68,6 +77,11 @@ private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other if (attemptEv.Cancelled) return; + var attemptCausingEv = new SlipCausingAttemptEvent(); + RaiseLocalEvent(uid, ref attemptCausingEv); + if (attemptCausingEv.Cancelled) + return; + var ev = new SlipEvent(other); RaiseLocalEvent(uid, ref ev); @@ -107,7 +121,13 @@ public sealed class SlipAttemptEvent : CancellableEntityEventArgs, IInventoryRel } /// -/// This event is raised directed at an entity that CAUSED some other entity to slip (e.g., the banana peel). +/// Raised on an entity that is causing the slip event (e.g, the banana peel), to determine if the slip attempt should be cancelled. /// +/// If the slip should be cancelled +[ByRefEvent] +public record struct SlipCausingAttemptEvent (bool Cancelled); + +/// Raised on an entity that CAUSED some other entity to slip (e.g., the banana peel). +/// The entity being slipped [ByRefEvent] public readonly record struct SlipEvent(EntityUid Slipped); From 959f5e63c046bf11a00cd0d98c33fba5cd0dd483 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:22:30 -0400 Subject: [PATCH 47/54] Monkey and gorilla melee weapon fix (#26288) Update animals.yml --- Resources/Prototypes/Entities/Mobs/NPCs/animals.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index e617e9eaf28..5f0ea470b4c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1031,6 +1031,8 @@ bloodMaxVolume: 300 # if you fuck with the gorilla he will harambe you - type: MeleeWeapon + soundHit: + collection: Punch damage: types: Blunt: 20 @@ -1221,6 +1223,15 @@ path: /Audio/Animals/ferret_happy.ogg interactFailureSound: path: /Audio/Items/wirecutter.ogg + - type: MeleeWeapon + soundHit: + path: /Audio/Effects/bite.ogg + angle: 30 + animation: WeaponArcBite + damage: + types: + Blunt: 3 + Piercing: 3 - type: Butcherable butcheringType: Spike spawned: From f1dd8888d1f3a11dd8f0fbec8677a8d8b4d6b461 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:25:38 -0400 Subject: [PATCH 48/54] Fix blunt melee weapon hitsound (#26286) * ok * Update welders.yml --- Resources/Prototypes/Entities/Mobs/base.yml | 2 ++ .../Entities/Objects/Specific/Hydroponics/tools.yml | 2 ++ .../Prototypes/Entities/Objects/Specific/Mech/mechs.yml | 2 ++ .../Prototypes/Entities/Objects/Tools/flashlights.yml | 2 ++ .../Prototypes/Entities/Objects/Tools/jaws_of_life.yml | 2 ++ Resources/Prototypes/Entities/Objects/Tools/tools.yml | 8 ++++++++ Resources/Prototypes/Entities/Objects/Tools/welders.yml | 4 ++++ .../Entities/Objects/Weapons/Melee/baseball_bat.yml | 2 ++ .../Prototypes/Entities/Objects/Weapons/Melee/cult.yml | 2 ++ .../Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml | 2 ++ .../Prototypes/Entities/Objects/Weapons/Melee/mining.yml | 2 ++ .../Entities/Objects/Weapons/Melee/sledgehammer.yml | 2 ++ .../Prototypes/Entities/Objects/Weapons/security.yml | 2 ++ .../Prototypes/Entities/Structures/Furniture/chairs.yml | 2 ++ 14 files changed, 36 insertions(+) diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index d4e9a561c5e..ac9aabbeadb 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -102,6 +102,8 @@ - type: CombatMode - type: MeleeWeapon hidden: true + soundHit: + collection: MetalThud damage: groups: Brute: 5 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml index 00224fd3182..727c75c8794 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml @@ -106,6 +106,8 @@ types: Blunt: 8 Piercing: 2 # I guess you can stab it into them? + soundHit: + collection: MetalThud - type: Item sprite: Objects/Tools/Hydroponics/spade.rsi - type: Shovel diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml index 0e132541e1e..5a2587ff710 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml @@ -48,6 +48,8 @@ types: Blunt: 25 #thwack Structural: 20 + soundHit: + collection: MetalThud - type: Puller needsHands: false - type: InputMover diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml index cbc42b7587f..8a8503e0a9d 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml @@ -112,6 +112,8 @@ types: Blunt: 10 bluntStaminaDamageFactor: 2.0 # DeltaV - seclite is now a viable nonlethal takedown tool against unarmoured targets. + soundHit: + collection: MetalThud - type: Item sprite: Objects/Tools/seclite.rsi - type: PointLight diff --git a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml index bb8545d91ff..8e2b7597970 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml @@ -47,6 +47,8 @@ damage: types: Blunt: 10 + soundHit: + collection: MetalThud - type: entity name: syndicate jaws of life diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index c0a6a6a980d..f1bf2ff98e7 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -114,6 +114,8 @@ damage: types: Blunt: 4.5 + soundHit: + collection: MetalThud - type: Tool qualities: - Anchoring @@ -151,6 +153,8 @@ damage: types: Blunt: 8 + soundHit: + collection: MetalThud - type: Tool qualities: - Prying @@ -504,6 +508,8 @@ damage: types: Blunt: 14 + soundHit: + collection: MetalThud - type: Item size: Normal sprite: Objects/Tools/shovel.rsi @@ -544,6 +550,8 @@ damage: types: Blunt: 7 + soundHit: + collection: MetalThud - type: Tool qualities: - Rolling diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 42b16ba0538..9bf3f2e2cb9 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -47,6 +47,8 @@ params: variation: 0.250 volume: -12 + deactivatedSoundOnHitNoDamage: + collection: MetalThud activatedDamage: types: Heat: 8 @@ -70,6 +72,8 @@ damage: types: Blunt: 5 #i mean... i GUESS you could use it like that + soundHit: + collection: MetalThud - type: RefillableSolution solution: Welder - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml index b7fae7acede..8ba643a1155 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml @@ -13,6 +13,8 @@ types: Blunt: 10 Structural: 5 + soundHit: + collection: MetalThud - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml index db24d3e2cd0..ecb6479de70 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml @@ -67,6 +67,8 @@ Blunt: 10 Slash: 10 Structural: 5 + soundHit: + collection: MetalThud - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml index b46ee096336..93765ec40c3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml @@ -21,6 +21,8 @@ Blunt: 5 Slash: 10 Structural: 10 + soundHit: + collection: MetalThud - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml index eee65a1ca78..ccf45bf59aa 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml @@ -49,6 +49,8 @@ types: Blunt: 10 Slash: 5 + soundHit: + collection: MetalThud - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml index e566f90968e..0c75015d9aa 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml @@ -13,6 +13,8 @@ types: Blunt: 10 Structural: 10 + soundHit: + collection: MetalThud - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index eec5ee833a1..f9ae8a4782f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -96,6 +96,8 @@ damage: types: Blunt: 20 + soundHit: + collection: MetalThud bluntStaminaDamageFactor: 1.5 - type: Item size: Normal diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index cc8cc325fbb..b0f66a77d4d 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -339,6 +339,8 @@ damage: types: Blunt: 8 + soundHit: + collection: MetalThud - type: Construction graph: Seat node: chairFolding From 25e1dd471555b0e619fa1779d3fe64759cf59441 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 20 Mar 2024 15:47:36 +0100 Subject: [PATCH 49/54] Fix EFCore obsoletion warnings in Content.Server.Database (#26285) --- Content.Server.Database/SnakeCaseNaming.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server.Database/SnakeCaseNaming.cs b/Content.Server.Database/SnakeCaseNaming.cs index ca1a9665848..27ce392cd50 100644 --- a/Content.Server.Database/SnakeCaseNaming.cs +++ b/Content.Server.Database/SnakeCaseNaming.cs @@ -301,14 +301,14 @@ public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConven private static void RewriteColumnName(IConventionPropertyBuilder propertyBuilder) { var property = propertyBuilder.Metadata; - var entityType = property.DeclaringEntityType; + var entityType = (IConventionEntityType)property.DeclaringType; if (entityType.ClrType == typeof(Microsoft.EntityFrameworkCore.Migrations.HistoryRow)) return; property.Builder.HasNoAnnotation(RelationalAnnotationNames.ColumnName); - var baseColumnName = StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is { } tableIdentifier + var baseColumnName = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table) is { } tableIdentifier ? property.GetDefaultColumnName(tableIdentifier) : property.GetDefaultColumnName(); From b1763d9593f7907464918a79ef35c5373483c214 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 20 Mar 2024 16:22:47 +0100 Subject: [PATCH 50/54] Fix wire layout inheritance. (#26289) Wire layouts manually navigate the inheritance hierarchy, but the data fields on the prototypes were also automatically inherited already. This meant that inheriting a wire layout prototype and changing nothing would cause the wires to be duplicated unless they were manually modified on top. Fix is easy: just disable inheritance on the data fields. Also, integration test for it. --- .../Tests/Wires/WireLayoutTest.cs | 103 ++++++++++++++++++ Content.Server/Wires/WireLayout.cs | 2 + 2 files changed, 105 insertions(+) create mode 100644 Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs diff --git a/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs new file mode 100644 index 00000000000..920dc088186 --- /dev/null +++ b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs @@ -0,0 +1,103 @@ +using Content.Server.Doors; +using Content.Server.Power; +using Content.Server.Wires; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Tests.Wires; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +[TestOf(typeof(WiresSystem))] +public sealed class WireLayoutTest +{ + [TestPrototypes] + public const string Prototypes = """ + - type: wireLayout + id: WireLayoutTest + dummyWires: 2 + wires: + - !type:PowerWireAction + - !type:DoorBoltWireAction + + - type: wireLayout + id: WireLayoutTest2 + parent: WireLayoutTest + wires: + - !type:PowerWireAction + + - type: wireLayout + id: WireLayoutTest3 + parent: WireLayoutTest + + - type: entity + id: WireLayoutTest + components: + - type: Wires + layoutId: WireLayoutTest + + - type: entity + id: WireLayoutTest2 + components: + - type: Wires + layoutId: WireLayoutTest2 + + - type: entity + id: WireLayoutTest3 + components: + - type: Wires + layoutId: WireLayoutTest3 + """; + + [Test] + public async Task TestLayoutInheritance() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + var testMap = await pair.CreateTestMap(); + + await server.WaitAssertion(() => + { + var wires = IoCManager.Resolve().GetEntitySystem(); + + // Need to spawn these entities to make sure the wire layouts are initialized. + var ent1 = SpawnWithComp(server.EntMan, "WireLayoutTest", testMap.MapCoords); + var ent2 = SpawnWithComp(server.EntMan, "WireLayoutTest2", testMap.MapCoords); + var ent3 = SpawnWithComp(server.EntMan, "WireLayoutTest3", testMap.MapCoords); + + // Assert.That(wires.TryGetLayout("WireLayoutTest", out var layout1)); + // Assert.That(wires.TryGetLayout("WireLayoutTest2", out var layout2)); + // Assert.That(wires.TryGetLayout("WireLayoutTest3", out var layout3)); + + Assert.Multiple(() => + { + // Entity 1. + Assert.That(ent1.Comp.WiresList, Has.Count.EqualTo(4)); + Assert.That(ent1.Comp.WiresList, Has.Exactly(2).With.Property("Action").Null, "2 dummy wires"); + Assert.That(ent1.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 power wire"); + Assert.That(ent1.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire"); + + Assert.That(ent2.Comp.WiresList, Has.Count.EqualTo(5)); + Assert.That(ent2.Comp.WiresList, Has.Exactly(2).With.Property("Action").Null, "2 dummy wires"); + Assert.That(ent2.Comp.WiresList, Has.Exactly(2).With.Property("Action").InstanceOf(), "2 power wire"); + Assert.That(ent2.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire"); + + Assert.That(ent3.Comp.WiresList, Has.Count.EqualTo(4)); + Assert.That(ent3.Comp.WiresList, Has.Exactly(2).With.Property("Action").Null, "2 dummy wires"); + Assert.That(ent3.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 power wire"); + Assert.That(ent3.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire"); + }); + }); + + await pair.CleanReturnAsync(); + } + + private static Entity SpawnWithComp(IEntityManager entityManager, string prototype, MapCoordinates coords) + where T : IComponent, new() + { + var ent = entityManager.Spawn(prototype, coords); + var comp = entityManager.EnsureComponent(ent); + return new Entity(ent, comp); + } +} diff --git a/Content.Server/Wires/WireLayout.cs b/Content.Server/Wires/WireLayout.cs index ecafba013e0..621992c915a 100644 --- a/Content.Server/Wires/WireLayout.cs +++ b/Content.Server/Wires/WireLayout.cs @@ -28,11 +28,13 @@ public sealed partial class WireLayoutPrototype : IPrototype, IInheritingPrototy /// initialization) /// [DataField("dummyWires")] + [NeverPushInheritance] public int DummyWires { get; private set; } = default!; /// /// All the valid IWireActions currently in this layout. /// [DataField("wires")] + [NeverPushInheritance] public List? Wires { get; private set; } } From a449eb74cd445050df81b1adea8279539188b3cd Mon Sep 17 00:00:00 2001 From: Killerqu00 <47712032+Killerqu00@users.noreply.github.com> Date: Wed, 20 Mar 2024 17:07:38 +0100 Subject: [PATCH 51/54] Per-department wire layouts (#26247) * add per-department wire layouts * fix science/research inconsistency * fix wireLayout inheritance * science is no longer security * Security doors back to normal wire count * implement proper wire inheritance * revert WiresSystem.cs fix --- .../Structures/Doors/Airlocks/access.yml | 196 ++++++++++++++++++ Resources/Prototypes/Wires/layouts.yml | 20 ++ 2 files changed, 216 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index 82cdcde7c05..cb4985291c8 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -6,6 +6,8 @@ components: - type: AccessReader access: [["Service"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -14,6 +16,8 @@ components: - type: AccessReader access: [["Lawyer"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -22,6 +26,8 @@ components: - type: AccessReader access: [["Theatre"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockScience # DeltaV - Chapel is in Epistemics @@ -30,6 +36,8 @@ components: - type: AccessReader access: [["Chapel"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -38,6 +46,8 @@ components: - type: AccessReader access: [["Janitor"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -46,6 +56,8 @@ components: - type: AccessReader access: [["Kitchen"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -54,6 +66,8 @@ components: - type: AccessReader access: [["Bar"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -62,6 +76,8 @@ components: - type: AccessReader access: [["Hydroponics"]] + - type: Wires + layoutId: AirlockService - type: entity parent: Airlock @@ -70,6 +86,8 @@ components: - type: AccessReader access: [["Captain"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockExternal @@ -86,6 +104,8 @@ components: - type: AccessReader access: [["Cargo"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockExternal @@ -94,6 +114,8 @@ components: - type: AccessReader access: [["Engineering"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockExternal @@ -102,6 +124,8 @@ components: - type: AccessReader access: [["Atmospherics"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockExternal @@ -126,6 +150,8 @@ components: - type: AccessReader access: [["Kitchen"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockFreezer @@ -134,6 +160,8 @@ components: - type: AccessReader access: [["Kitchen"], ["Hydroponics"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockFreezer @@ -142,6 +170,8 @@ components: - type: AccessReader access: [["Hydroponics"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockEngineering @@ -150,6 +180,8 @@ components: - type: AccessReader access: [["Engineering"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockAtmospherics @@ -158,6 +190,8 @@ components: - type: AccessReader access: [["Atmospherics"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockCargo @@ -166,6 +200,8 @@ components: - type: AccessReader access: [["Cargo"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockCargo @@ -174,6 +210,8 @@ components: - type: AccessReader access: [["Salvage"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockMining @@ -182,6 +220,8 @@ components: - type: AccessReader access: [["Salvage"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMedical @@ -190,6 +230,8 @@ components: - type: AccessReader access: [["Medical"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockMedical @@ -206,6 +248,8 @@ components: - type: AccessReader access: [["Medical"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockChemistry @@ -214,6 +258,8 @@ components: - type: AccessReader access: [["Chemistry"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockScience @@ -222,6 +268,8 @@ components: - type: AccessReader access: [["Research"]] + - type: Wires + layoutId: AirlockScience - type: entity parent: AirlockScience @@ -230,6 +278,8 @@ components: - type: AccessReader access: [["Research"], ["Medical"]] + - type: Wires + layoutId: AirlockScience - type: entity parent: AirlockCentralCommand @@ -258,6 +308,8 @@ components: - type: AccessReader access: [["Captain"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommand @@ -266,6 +318,8 @@ components: - type: AccessReader access: [["ChiefMedicalOfficer"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommand @@ -274,6 +328,8 @@ components: - type: AccessReader access: [["ChiefEngineer"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommand @@ -282,6 +338,8 @@ components: - type: AccessReader access: [["HeadOfSecurity"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommand @@ -290,6 +348,8 @@ components: - type: AccessReader access: [["ResearchDirector"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommand @@ -298,6 +358,8 @@ components: - type: AccessReader access: [["HeadOfPersonnel"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommand @@ -306,6 +368,8 @@ components: - type: AccessReader access: [["Quartermaster"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockSecurity @@ -365,6 +429,8 @@ components: - type: AccessReader access: [["Security", "Command"]] + - type: Wires + layoutId: AirlockSecurity - type: entity parent: AirlockCommand @@ -373,6 +439,8 @@ components: - type: AccessReader access: [["External"]] + - type: Wires + layoutId: AirlockCommand #Delta V: Add Airlocks - type: entity @@ -456,6 +524,8 @@ components: - type: AccessReader access: [["Service"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockGlass @@ -464,6 +534,8 @@ components: - type: AccessReader access: [["Lawyer"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockGlass @@ -472,6 +544,8 @@ components: - type: AccessReader access: [["Theatre"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockGlass @@ -480,6 +554,8 @@ components: - type: AccessReader access: [["Bar"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockExternalGlass @@ -496,6 +572,8 @@ components: - type: AccessReader access: [["Cargo"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockExternalGlass @@ -520,6 +598,8 @@ components: - type: AccessReader access: [["Engineering"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockExternalGlass @@ -528,6 +608,8 @@ components: - type: AccessReader access: [["Atmospherics"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockGlass @@ -536,6 +618,8 @@ components: - type: AccessReader access: [["Kitchen"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockGlass @@ -544,6 +628,8 @@ components: - type: AccessReader access: [["Janitor"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockGlass @@ -552,6 +638,8 @@ components: - type: AccessReader access: [["Hydroponics"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockScienceGlass # DeltaV - Chapel is in Epistemics @@ -560,6 +648,8 @@ components: - type: AccessReader access: [["Chapel"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockEngineeringGlass @@ -568,6 +658,8 @@ components: - type: AccessReader access: [["Engineering"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockAtmosphericsGlass @@ -576,6 +668,8 @@ components: - type: AccessReader access: [["Atmospherics"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockCargoGlass @@ -584,6 +678,8 @@ components: - type: AccessReader access: [["Cargo"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockCargoGlass @@ -592,6 +688,8 @@ components: - type: AccessReader access: [["Salvage"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockMiningGlass @@ -600,6 +698,8 @@ components: - type: AccessReader access: [["Salvage"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockChemistryGlass @@ -608,6 +708,8 @@ components: - type: AccessReader access: [["Chemistry"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockMedicalGlass @@ -616,6 +718,8 @@ components: - type: AccessReader access: [["Medical"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockMedicalGlass @@ -632,6 +736,8 @@ components: - type: AccessReader access: [["Medical"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockScienceGlass @@ -640,6 +746,8 @@ components: - type: AccessReader access: [["Research"]] + - type: Wires + layoutId: AirlockScience - type: entity parent: AirlockScienceGlass @@ -648,6 +756,8 @@ components: - type: AccessReader access: [["Research"], ["Medical"]] + - type: Wires + layoutId: AirlockScience - type: entity parent: AirlockCentralCommandGlass @@ -666,6 +776,8 @@ components: - type: AccessReader access: [["Command"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -674,6 +786,8 @@ components: - type: AccessReader access: [["Captain"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -682,6 +796,8 @@ components: - type: AccessReader access: [["ChiefMedicalOfficer"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -690,6 +806,8 @@ components: - type: AccessReader access: [["ChiefEngineer"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -698,6 +816,8 @@ components: - type: AccessReader access: [["HeadOfSecurity"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -706,6 +826,8 @@ components: - type: AccessReader access: [["ResearchDirector"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -714,6 +836,8 @@ components: - type: AccessReader access: [["HeadOfPersonnel"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockCommandGlass @@ -722,6 +846,8 @@ components: - type: AccessReader access: [["Quartermaster"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockSecurityGlass @@ -730,6 +856,8 @@ components: - type: AccessReader access: [["Security"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockSecurityGlass @@ -738,6 +866,8 @@ components: - type: AccessReader access: [["Detective"]] + - type: Wires + layoutId: AirlockCommand #Delta V: Removed Brig Access #- type: entity @@ -748,6 +878,16 @@ # - type: AccessReader # access: [["Brig"]] +- type: entity + parent: AirlockSecurityGlass + id: AirlockBrigGlassLocked + suffix: Brig, Locked + components: + - type: AccessReader + access: [["Brig"]] + - type: Wires + layoutId: AirlockCommand + - type: entity parent: AirlockSecurityGlass id: AirlockSecurityLawyerGlassLocked @@ -755,6 +895,8 @@ components: - type: AccessReader access: [["Security"], ["Lawyer"]] + - type: Wires + layoutId: AirlockSecurity - type: entity parent: AirlockSecurityGlass @@ -763,6 +905,8 @@ components: - type: AccessReader access: [["Armory"]] + - type: Wires + layoutId: AirlockSecurity - type: entity parent: AirlockCommandGlassLocked @@ -886,6 +1030,8 @@ components: - type: AccessReader access: [["Salvage"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockMaint @@ -894,6 +1040,8 @@ components: - type: AccessReader access: [["Cargo"]] + - type: Wires + layoutId: AirlockCargo - type: entity parent: AirlockMaint @@ -902,6 +1050,8 @@ components: - type: AccessReader access: [["Command"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -918,6 +1068,8 @@ components: - type: AccessReader access: [["Engineering"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockMaint @@ -926,6 +1078,8 @@ components: - type: AccessReader access: [["Atmospherics"]] + - type: Wires + layoutId: AirlockEngineering - type: entity parent: AirlockMaint @@ -934,6 +1088,8 @@ components: - type: AccessReader access: [["Bar"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -942,6 +1098,8 @@ components: - type: AccessReader access: [["Chapel"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -950,6 +1108,8 @@ components: - type: AccessReader access: [["Hydroponics"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -958,6 +1118,8 @@ components: - type: AccessReader access: [["Janitor"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -966,6 +1128,8 @@ components: - type: AccessReader access: [["Lawyer"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -974,6 +1138,8 @@ components: - type: AccessReader access: [["Service"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -982,6 +1148,8 @@ components: - type: AccessReader access: [["Theatre"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -990,6 +1158,8 @@ components: - type: AccessReader access: [["Kitchen"]] + - type: Wires + layoutId: AirlockService - type: entity parent: AirlockMaint @@ -1006,6 +1176,8 @@ components: - type: AccessReader access: [["Medical"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockMaint @@ -1014,6 +1186,8 @@ components: - type: AccessReader access: [["Chemistry"]] + - type: Wires + layoutId: AirlockMedical - type: entity parent: AirlockMaint @@ -1022,6 +1196,8 @@ components: - type: AccessReader access: [["Research"]] + - type: Wires + layoutId: AirlockScience - type: entity parent: AirlockMaint @@ -1030,6 +1206,8 @@ components: - type: AccessReader access: [["Research"], ["Medical"]] + - type: Wires + layoutId: AirlockScience - type: entity parent: AirlockMaint @@ -1038,6 +1216,8 @@ components: - type: AccessReader access: [["Security"]] + - type: Wires + layoutId: AirlockSecurity - type: entity parent: AirlockMaint @@ -1046,6 +1226,8 @@ components: - type: AccessReader access: [["Detective"]] + - type: Wires + layoutId: AirlockSecurity - type: entity parent: AirlockMaint @@ -1054,6 +1236,8 @@ components: - type: AccessReader access: [["HeadOfPersonnel"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -1062,6 +1246,8 @@ components: - type: AccessReader access: [["Captain"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -1070,6 +1256,8 @@ components: - type: AccessReader access: [["ChiefEngineer"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -1078,6 +1266,8 @@ components: - type: AccessReader access: [["ChiefMedicalOfficer"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -1086,6 +1276,8 @@ components: - type: AccessReader access: [["HeadOfSecurity"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -1094,6 +1286,8 @@ components: - type: AccessReader access: [["ResearchDirector"]] + - type: Wires + layoutId: AirlockCommand - type: entity parent: AirlockMaint @@ -1102,6 +1296,8 @@ components: - type: AccessReader access: [["Armory"]] + - type: Wires + layoutId: AirlockSecurity #Delta V: Add Airlocks - type: entity diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index 338bf188ba0..b30e68545df 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -15,6 +15,26 @@ - type: wireLayout parent: Airlock + id: AirlockService + +- type: wireLayout + parent: Airlock + id: AirlockCargo + +- type: wireLayout + parent: Airlock + id: AirlockEngineering + +- type: wireLayout + parent: Airlock + id: AirlockMedical + +- type: wireLayout + parent: Airlock + id: AirlockScience + +- type: wireLayout + parent: HighSec id: AirlockCommand - type: wireLayout From fdbe88400f4d36998f76fa1b151d9d9f36988a27 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 20 Mar 2024 15:03:53 -0400 Subject: [PATCH 52/54] Pizza is not a fruit (#26293) * Pizza is not a fruit * Also the slice * Let the lizards still eat pizza --- .../Entities/Objects/Consumable/Food/Baked/pizza.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml index 95bfe6b6b4c..bdce1d44086 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml @@ -29,7 +29,7 @@ - type: Tag tags: - Pizza - - Fruit # Tomato? idk + - ReptilianFood - type: entity parent: FoodInjectableBase # Not sliceable @@ -58,7 +58,7 @@ - type: Tag tags: - Pizza - - Fruit + - ReptilianFood # Pizza From d6f3955cb5a4a8cb88f5e6f5d19a709c138f1730 Mon Sep 17 00:00:00 2001 From: DangerRevolution <142105406+DangerRevolution@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:08:20 +0000 Subject: [PATCH 53/54] try fix the component error --- .../Strip/Components/StrippableComponent.cs | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/Content.Shared/Strip/Components/StrippableComponent.cs b/Content.Shared/Strip/Components/StrippableComponent.cs index bf6b9e2a7b1..4faca4d8f21 100644 --- a/Content.Shared/Strip/Components/StrippableComponent.cs +++ b/Content.Shared/Strip/Components/StrippableComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.DoAfter; using Content.Shared.Inventory; using Robust.Shared.GameStates; using Robust.Shared.Serialization; @@ -8,10 +9,10 @@ namespace Content.Shared.Strip.Components public sealed partial class StrippableComponent : Component { /// - /// The strip delay for hands. + /// The strip delay for hands. /// [ViewVariables(VVAccess.ReadWrite), DataField("handDelay")] - public float HandStripDelay = 4f; + public TimeSpan HandStripDelay = TimeSpan.FromSeconds(4f); } [NetSerializable, Serializable] @@ -21,57 +22,63 @@ public enum StrippingUiKey : byte } [NetSerializable, Serializable] - public sealed class StrippingSlotButtonPressed : BoundUserInterfaceMessage + public sealed class StrippingSlotButtonPressed(string slot, bool isHand) : BoundUserInterfaceMessage { - public readonly string Slot; - - public readonly bool IsHand; - - public StrippingSlotButtonPressed(string slot, bool isHand) - { - Slot = slot; - IsHand = isHand; - } + public readonly string Slot = slot; + public readonly bool IsHand = isHand; } [NetSerializable, Serializable] - public sealed class StrippingEnsnareButtonPressed : BoundUserInterfaceMessage + public sealed class StrippingEnsnareButtonPressed : BoundUserInterfaceMessage; + + [ByRefEvent] + public abstract class BaseBeforeStripEvent(TimeSpan initialTime, bool stealth = false) : EntityEventArgs, IInventoryRelayEvent { public readonly TimeSpan InitialTime = initialTime; public float Multiplier = 1f; public TimeSpan Additive = TimeSpan.Zero; - public bool Stealth; + public bool Stealth = stealth; public TimeSpan Time => TimeSpan.FromSeconds(MathF.Max(InitialTime.Seconds * Multiplier + Additive.Seconds, 0f)); public SlotFlags TargetSlots { get; } = SlotFlags.GLOVES; - - public BaseBeforeStripEvent(float initialTime, bool stealth = false) - { - InitialTime = initialTime; - Stealth = stealth; - } } /// - /// Used to modify strip times. Raised directed at the user. + /// Used to modify strip times. Raised directed at the user. /// /// - /// This is also used by some stripping related interactions, i.e., interactions with items that are currently equipped by another player. + /// This is also used by some stripping related interactions, i.e., interactions with items that are currently equipped by another player. /// - public sealed class BeforeStripEvent : BaseBeforeStripEvent - { - public BeforeStripEvent(float initialTime, bool stealth = false) : base(initialTime, stealth) { } - } + [ByRefEvent] + public sealed class BeforeStripEvent(TimeSpan initialTime, bool stealth = false) : BaseBeforeStripEvent(initialTime, stealth); /// - /// Used to modify strip times. Raised directed at the target. + /// Used to modify strip times. Raised directed at the target. /// /// - /// This is also used by some stripping related interactions, i.e., interactions with items that are currently equipped by another player. + /// This is also used by some stripping related interactions, i.e., interactions with items that are currently equipped by another player. /// - public sealed class BeforeGettingStrippedEvent : BaseBeforeStripEvent + [ByRefEvent] + public sealed class BeforeGettingStrippedEvent(TimeSpan initialTime, bool stealth = false) : BaseBeforeStripEvent(initialTime, stealth); + + /// + /// Organizes the behavior of DoAfters for . + /// + [Serializable, NetSerializable] + public sealed partial class StrippableDoAfterEvent : DoAfterEvent { - public BeforeGettingStrippedEvent(float initialTime, bool stealth = false) : base(initialTime, stealth) { } + public readonly bool InsertOrRemove; + public readonly bool InventoryOrHand; + public readonly string SlotOrHandName; + + public StrippableDoAfterEvent(bool insertOrRemove, bool inventoryOrHand, string slotOrHandName) + { + InsertOrRemove = insertOrRemove; + InventoryOrHand = inventoryOrHand; + SlotOrHandName = slotOrHandName; + } + + public override DoAfterEvent Clone() => this; } } From 818e8d1b8d5daabde5dd478536382a21c23bfc20 Mon Sep 17 00:00:00 2001 From: DangerRevolution <142105406+DangerRevolution@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:14:16 +0000 Subject: [PATCH 54/54] fix the system, not sure why this wasn't merged correctly?? --- Content.Shared/Strip/SharedStrippableSystem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs index a698ae5035a..7afd4f245a1 100644 --- a/Content.Shared/Strip/SharedStrippableSystem.cs +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -14,12 +14,12 @@ public override void Initialize() SubscribeLocalEvent(OnDragDrop); } - public (float Time, bool Stealth) GetStripTimeModifiers(EntityUid user, EntityUid target, float initialTime) + public (TimeSpan Time, bool Stealth) GetStripTimeModifiers(EntityUid user, EntityUid target, TimeSpan initialTime) { var userEv = new BeforeStripEvent(initialTime); - RaiseLocalEvent(user, userEv); + RaiseLocalEvent(user, ref userEv); var ev = new BeforeGettingStrippedEvent(userEv.Time, userEv.Stealth); - RaiseLocalEvent(target, ev); + RaiseLocalEvent(target, ref ev); return (ev.Time, ev.Stealth); }