]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add hot potato (#14204)
authorSlava0135 <40753025+Slava0135@users.noreply.github.com>
Sat, 22 Apr 2023 11:40:36 +0000 (14:40 +0300)
committerGitHub <noreply@github.com>
Sat, 22 Apr 2023 11:40:36 +0000 (21:40 +1000)
Co-authored-by: AJCM <AJCM@tutanota.com>
14 files changed:
Content.Client/HotPotato/HotPotatoSystem.cs [new file with mode: 0644]
Content.Server/Explosion/EntitySystems/TriggerSystem.cs
Content.Server/HotPotato/HotPotatoSystem.cs [new file with mode: 0644]
Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs
Content.Shared/HotPotato/ActiveHotPotatoComponent.cs [new file with mode: 0644]
Content.Shared/HotPotato/HotPotatoComponent.cs [new file with mode: 0644]
Content.Shared/HotPotato/SharedHotPotatoSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/hot-potato/hot-potato.ftl [new file with mode: 0644]
Resources/Locale/en-US/store/uplink-catalog.ftl
Resources/Prototypes/Catalog/uplink_catalog.yml
Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/activated.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/meta.json [new file with mode: 0644]

diff --git a/Content.Client/HotPotato/HotPotatoSystem.cs b/Content.Client/HotPotato/HotPotatoSystem.cs
new file mode 100644 (file)
index 0000000..8e9c0f9
--- /dev/null
@@ -0,0 +1,26 @@
+using Content.Shared.HotPotato;
+using Robust.Shared.Random;
+using Robust.Shared.Timing;
+
+public sealed class HotPotatoSystem : SharedHotPotatoSystem
+{
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        if (!_timing.IsFirstTimePredicted)
+            return;
+
+        var query = AllEntityQuery<ActiveHotPotatoComponent>();
+        while (query.MoveNext(out var uid, out var comp))
+        {
+            if (_timing.CurTime < comp.TargetTime)
+                continue;
+            comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
+            Spawn("HotPotatoEffect", Transform(uid).MapPosition.Offset(_random.NextVector2(0.25f)));
+        }
+    }
+}
index 10539dd86023ff91aa7faa9c2126ca5dcfcc5394..6e4360b5f8e1c86eeba21a901d8f6f8c0b5d20a9 100644 (file)
@@ -36,6 +36,12 @@ namespace Content.Server.Explosion.EntitySystems
         }
     }
 
+    /// <summary>
+    /// Raised when timer trigger becomes active.
+    /// </summary>
+    [ByRefEvent]
+    public readonly record struct ActiveTimerTriggerEvent(EntityUid Triggered, EntityUid? User);
+
     [UsedImplicitly]
     public sealed partial class TriggerSystem : EntitySystem
     {
@@ -184,6 +190,9 @@ namespace Content.Server.Explosion.EntitySystems
             active.BeepInterval = beepInterval;
             active.TimeUntilBeep = initialBeepDelay == null ? active.BeepInterval : initialBeepDelay.Value;
 
+            var ev = new ActiveTimerTriggerEvent(uid, user);
+            RaiseLocalEvent(uid, ref ev);
+
             if (TryComp<AppearanceComponent>(uid, out var appearance))
                 _appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Primed, appearance);
         }
diff --git a/Content.Server/HotPotato/HotPotatoSystem.cs b/Content.Server/HotPotato/HotPotatoSystem.cs
new file mode 100644 (file)
index 0000000..79be700
--- /dev/null
@@ -0,0 +1,55 @@
+using Content.Server.Explosion.EntitySystems;
+using Content.Shared.Hands.Components;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.HotPotato;
+using Content.Shared.Popups;
+using Content.Shared.Weapons.Melee.Events;
+
+namespace Content.Server.HotPotato;
+
+public sealed class HotPotatoSystem : SharedHotPotatoSystem
+{
+    [Dependency] private readonly SharedHandsSystem _hands = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<HotPotatoComponent, ActiveTimerTriggerEvent>(OnActiveTimer);
+        SubscribeLocalEvent<HotPotatoComponent, MeleeHitEvent>(OnMeleeHit);
+    }
+
+    private void OnActiveTimer(EntityUid uid, HotPotatoComponent comp, ref ActiveTimerTriggerEvent args)
+    {
+        EnsureComp<ActiveHotPotatoComponent>(uid);
+        comp.CanTransfer = false;
+        Dirty(comp);
+    }
+
+    private void OnMeleeHit(EntityUid uid, HotPotatoComponent comp, MeleeHitEvent args)
+    {
+        if (!HasComp<ActiveHotPotatoComponent>(uid))
+            return;
+
+        comp.CanTransfer = true;
+        foreach (var hitEntity in args.HitEntities)
+        {
+            if (!TryComp<HandsComponent>(hitEntity, out var hands))
+                continue;
+
+            if (!_hands.IsHolding(hitEntity, uid, out _, hands) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
+            {
+                _popup.PopupEntity(Loc.GetString("hot-potato-passed",
+                    ("from", args.User), ("to", hitEntity)), uid, PopupType.Medium);
+                break;
+            }
+
+            _popup.PopupEntity(Loc.GetString("hot-potato-failed",
+                ("to", hitEntity)), uid, PopupType.Medium);
+
+            break;
+        }
+        comp.CanTransfer = false;
+        Dirty(comp);
+    }
+}
index 9732d3c215a061425db33ad39856f60ca68daf2f..846d67849cbb88acebdf367e39c9f270fc0368da 100644 (file)
@@ -104,6 +104,29 @@ public abstract partial class SharedHandsSystem : EntitySystem
         return true;
     }
 
+    /// <summary>
+    ///     Tries to pick up an entity into any hand, forcing to drop an item if there are no free hands
+    ///     By default it does check if it's possible to drop items
+    /// </summary>
+    public bool TryForcePickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
+    {
+        if (!Resolve(uid, ref handsComp, false))
+            return false;
+
+        if (TryPickupAnyHand(uid, entity, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
+            return true;
+
+        foreach (var hand in handsComp.Hands.Values)
+        {
+            if (TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp) &&
+                TryPickup(uid, entity, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public bool CanPickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
     {
         if (!Resolve(uid, ref handsComp, false))
diff --git a/Content.Shared/HotPotato/ActiveHotPotatoComponent.cs b/Content.Shared/HotPotato/ActiveHotPotatoComponent.cs
new file mode 100644 (file)
index 0000000..3a2a343
--- /dev/null
@@ -0,0 +1,23 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.HotPotato;
+
+/// <summary>
+/// Added to an activated hot potato. Controls hot potato transfer on server / effect spawning on client.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedHotPotatoSystem))]
+public sealed class ActiveHotPotatoComponent : Component
+{
+    /// <summary>
+    /// Hot potato effect spawn cooldown in seconds
+    /// </summary>
+    [DataField("effectCooldown"), ViewVariables(VVAccess.ReadWrite)]
+    public float EffectCooldown = 0.3f;
+
+    /// <summary>
+    /// Moment in time next effect will be spawned
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite)]
+    public TimeSpan TargetTime = TimeSpan.Zero;
+}
diff --git a/Content.Shared/HotPotato/HotPotatoComponent.cs b/Content.Shared/HotPotato/HotPotatoComponent.cs
new file mode 100644 (file)
index 0000000..2d02e10
--- /dev/null
@@ -0,0 +1,20 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.HotPotato;
+
+/// <summary>
+/// Similar to <see cref="Content.Shared.Interaction.Components.UnremoveableComponent"/>
+/// except entities with this component can be removed in specific case: <see cref="CanTransfer"/>
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState]
+[Access(typeof(SharedHotPotatoSystem))]
+public sealed partial class HotPotatoComponent : Component
+{
+    /// <summary>
+    /// If set to true entity can be removed by hitting entities if they have hands
+    /// </summary>
+    [DataField("canTransfer"), ViewVariables(VVAccess.ReadWrite)]
+    [AutoNetworkedField]
+    public bool CanTransfer = true;
+}
diff --git a/Content.Shared/HotPotato/SharedHotPotatoSystem.cs b/Content.Shared/HotPotato/SharedHotPotatoSystem.cs
new file mode 100644 (file)
index 0000000..cd7a5d6
--- /dev/null
@@ -0,0 +1,18 @@
+using Robust.Shared.Containers;
+
+namespace Content.Shared.HotPotato;
+
+public abstract class SharedHotPotatoSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<HotPotatoComponent, ContainerGettingRemovedAttemptEvent>(OnRemoveAttempt);
+    }
+
+    private void OnRemoveAttempt(EntityUid uid, HotPotatoComponent comp, ContainerGettingRemovedAttemptEvent args)
+    {
+        if (!comp.CanTransfer)
+            args.Cancel();
+    }
+}
diff --git a/Resources/Locale/en-US/hot-potato/hot-potato.ftl b/Resources/Locale/en-US/hot-potato/hot-potato.ftl
new file mode 100644 (file)
index 0000000..76a6912
--- /dev/null
@@ -0,0 +1,2 @@
+hot-potato-passed = {$from} passed hot potato to {$to}!
+hot-potato-failed = Can't pass the potato to {$to}!
index 0c5225b619aa644059b873fc14a3345c28b757c3..0341c9a3871357bcd81336777cb128e963577888 100644 (file)
@@ -157,6 +157,9 @@ uplink-revolver-cap-gun-fake-desc = Fool your enemy! It can use both cap and mag
 uplink-banana-peel-explosive-name = Explosive Banana Peel
 uplink-banana-peel-explosive-desc = They will burst into laughter when they slip on it!
 
+uplink-hot-potato-name = Hot Potato
+uplink-hot-potato-desc = Once activated, this time bomb can't be dropped - only passed to someone else!
+
 # Armor
 uplink-chameleon-name = Chameleon Kit
 uplink-chameleon-desc = A backpack full of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more!
index d42987c09ece5c2d887b37e8584c7a190822a16b..b73174416c137ac0f10f72f491e79491256da329 100644 (file)
     whitelist:
     - Clown
 
+- type: listing
+  id: uplinkHotPotato
+  name: uplink-hot-potato-name
+  description: uplink-hot-potato-desc
+  productEntity: HotPotato
+  cost:
+    Telecrystal: 4
+  categories:
+  - UplinkJob
+  conditions:
+  - !type:BuyerJobCondition
+    whitelist:
+    - Chef
+    - Botanist
+    - Clown
+    - Mime
+
 # Armor
 
 - type: listing
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml
new file mode 100644 (file)
index 0000000..c7c7f27
--- /dev/null
@@ -0,0 +1,55 @@
+- type: entity
+  name: hot potato
+  description: Once activated, this time bomb can't be dropped - only passed to someone else!
+  parent: BaseItem
+  id: HotPotato
+  components:
+    - type: Sprite
+      sprite: Objects/Weapons/Bombs/hot_potato.rsi
+      state: icon
+      netsync: false
+    - type: Item
+      sprite: Objects/Weapons/Bombs/hot_potato.rsi
+      size: 5
+    - type: MeleeWeapon
+      damage:
+        types:
+          Blunt: 5
+    - type: OnUseTimerTrigger
+      delay: 180
+      beepSound: /Audio/Machines/Nuke/general_beep.ogg
+    - type: ExplodeOnTrigger
+    - type: Explosive
+      explosionType: Default
+      maxIntensity: 8
+      intensitySlope: 5
+      totalIntensity: 20
+      canCreateVacuum: false
+    - type: DeleteOnTrigger
+    - type: HotPotato
+    - type: Appearance
+      visuals:
+        - type: GenericEnumVisualizer
+          key: enum.Trigger.TriggerVisuals.VisualState
+          states:
+            enum.Trigger.TriggerVisualState.Primed: activated
+            enum.Trigger.TriggerVisualState.Unprimed: complete
+
+- type: entity
+  id: HotPotatoEffect
+  noSpawn: true
+  components:
+  - type: TimedDespawn
+    lifetime: 0.6
+  - type: Sprite
+    netsync: false
+    noRot: true
+    drawdepth: Effects
+    sprite: Effects/chemsmoke.rsi
+    state: chemsmoke
+    scale: "0.15, 0.15"
+  - type: EffectVisuals
+  - type: Tag
+    tags:
+      - HideContextMenu
+  - type: AnimationPlayer
diff --git a/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/activated.png b/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/activated.png
new file mode 100644 (file)
index 0000000..07785ce
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/activated.png differ
diff --git a/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/icon.png b/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/icon.png
new file mode 100644 (file)
index 0000000..426106d
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/meta.json b/Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/meta.json
new file mode 100644 (file)
index 0000000..664e16d
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/1dbcf389b0ec6b2c51b002df5fef8dd1519f8068",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "icon"
+    },
+    {
+      "name": "activated",
+      "delays": [
+        [
+          0.1,
+          0.1
+        ]
+      ]
+    }
+  ]
+}
\ No newline at end of file