]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Proto-kinetic crusher (#16277)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sun, 14 May 2023 03:15:18 +0000 (13:15 +1000)
committerGitHub <noreply@github.com>
Sun, 14 May 2023 03:15:18 +0000 (13:15 +1000)
Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
68 files changed:
Content.Client/Markers/MarkerSystem.cs
Content.Client/Projectiles/ProjectileSystem.cs
Content.Client/Toggleable/ToggleableLightVisualsComponent.cs
Content.Client/Weapons/Marker/DamageMarkerSystem.cs [new file with mode: 0644]
Content.Client/Weapons/Melee/MeleeWeaponSystem.cs
Content.Client/Weapons/Ranged/Systems/GunSystem.cs
Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs
Content.Server/Gatherable/Components/GatheringProjectileComponent.cs
Content.Server/Gatherable/GatherableSystem.Projectile.cs
Content.Server/Light/EntitySystems/HandheldLightSystem.cs
Content.Server/Pinpointer/ProximityBeeperSystem.cs
Content.Server/PowerCell/PowerCellSystem.Draw.cs [new file with mode: 0644]
Content.Server/PowerCell/PowerCellSystem.cs
Content.Server/Projectiles/ProjectileSystem.cs
Content.Server/UserInterface/ActivatableUIRequiresPowerCellComponent.cs
Content.Server/Weapons/DamageMarkerSystem.cs [new file with mode: 0644]
Content.Server/Weapons/Ranged/Systems/GunSystem.cs
Content.Shared/Interaction/Events/MeleeAttackAttemptEvent.cs [deleted file]
Content.Shared/PowerCell/PowerCellDrawComponent.cs [moved from Content.Server/PowerCell/PowerCellDrawComponent.cs with 56% similarity]
Content.Shared/PowerCell/SharedPowerCellSystem.cs
Content.Shared/Projectiles/ProjectileComponent.cs
Content.Shared/Projectiles/SharedProjectileSystem.cs
Content.Shared/Weapons/Marker/DamageMarkerComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Marker/DamageMarkerOnCollideComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs [new file with mode: 0644]
Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Melee/Events/AttackEvent.cs
Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs [new file with mode: 0644]
Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
Content.Shared/Weapons/Ranged/Components/GunComponent.cs
Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Ranged/Components/UseDelayOnShootComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs
Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
Content.Shared/Weapons/Ranged/Systems/UseDelayOnShootSystem.cs [new file with mode: 0644]
Content.Shared/Wieldable/WieldableSystem.cs
Resources/Audio/Weapons/attributions.yml [new file with mode: 0644]
Resources/Audio/Weapons/plasma_cutter.ogg [new file with mode: 0644]
Resources/Locale/en-US/wieldable/wieldable-component.ftl
Resources/Prototypes/Entities/Objects/Tools/flashlights.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml
Resources/Prototypes/tags.yml
Resources/Textures/Objects/Weapons/Effects/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Effects/shield2.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-lit.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-uncharged.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon-lit.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-lit.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-uncharged.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-right.png [new file with mode: 0644]

index bfaf333baa976e913eb2301c9017a5d59954689e..f12329bd786e095ec87dda8172442a15defb506f 100644 (file)
@@ -1,48 +1,49 @@
 using Robust.Client.GameObjects;
 using Robust.Shared.GameObjects;
 
-namespace Content.Client.Markers
+namespace Content.Client.Markers;
+
+public sealed class MarkerSystem : EntitySystem
 {
-    public sealed class MarkerSystem : EntitySystem
-    {
-        private bool _markersVisible;
+    private bool _markersVisible;
 
-        public bool MarkersVisible
+    public bool MarkersVisible
+    {
+        get => _markersVisible;
+        set
         {
-            get => _markersVisible;
-            set
-            {
-                _markersVisible = value;
-                UpdateMarkers();
-            }
+            _markersVisible = value;
+            UpdateMarkers();
         }
+    }
 
-        public override void Initialize()
-        {
-            base.Initialize();
+    public override void Initialize()
+    {
+        base.Initialize();
 
-            SubscribeLocalEvent<MarkerComponent, ComponentStartup>(OnStartup);
-        }
+        SubscribeLocalEvent<MarkerComponent, ComponentStartup>(OnStartup);
+    }
 
-        private void OnStartup(EntityUid uid, MarkerComponent marker, ComponentStartup args)
-        {
-            UpdateVisibility(marker);
-        }
+    private void OnStartup(EntityUid uid, MarkerComponent marker, ComponentStartup args)
+    {
+        UpdateVisibility(uid);
+    }
 
-        private void UpdateVisibility(MarkerComponent marker)
+    private void UpdateVisibility(EntityUid uid)
+    {
+        if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
         {
-            if (EntityManager.TryGetComponent(marker.Owner, out SpriteComponent? sprite))
-            {
-                sprite.Visible = MarkersVisible;
-            }
+            sprite.Visible = MarkersVisible;
         }
+    }
+
+    private void UpdateMarkers()
+    {
+        var query = AllEntityQuery<MarkerComponent>();
 
-        private void UpdateMarkers()
+        while (query.MoveNext(out var uid, out var comp))
         {
-            foreach (var markerComponent in EntityManager.EntityQuery<MarkerComponent>(true))
-            {
-                UpdateVisibility(markerComponent);
-            }
+            UpdateVisibility(uid);
         }
     }
 }
index f5121bbff0d25662097136d057a72721eac887fe..20be44792f69cbdf4e7472a43e1a634589079a70 100644 (file)
@@ -14,7 +14,6 @@ public sealed class ProjectileSystem : SharedProjectileSystem
     public override void Initialize()
     {
         base.Initialize();
-        SubscribeLocalEvent<ProjectileComponent, ComponentHandleState>(OnHandleState);
         SubscribeNetworkEvent<ImpactEffectEvent>(OnProjectileImpact);
     }
 
@@ -54,11 +53,4 @@ public sealed class ProjectileSystem : SharedProjectileSystem
             _player.Play(ent, anim, "impact-effect");
         }
     }
-
-    private void OnHandleState(EntityUid uid, ProjectileComponent component, ref ComponentHandleState args)
-    {
-        if (args.Current is not ProjectileComponentState state) return;
-        component.Shooter = state.Shooter;
-        component.IgnoreShooter = state.IgnoreShooter;
-    }
 }
index 6c7c3c256ded88f2f34fa2b48c407b7a7bfe9189..628726c3c13752f7e9a74160283a9043f5aa24f0 100644 (file)
@@ -13,7 +13,7 @@ namespace Content.Client.Toggleable;
 public sealed class ToggleableLightVisualsComponent : Component
 {
     /// <summary>
-    ///     Sprite layer that will have it's visibility toggled when this item is toggled.
+    ///     Sprite layer that will have its visibility toggled when this item is toggled.
     /// </summary>
     [DataField("spriteLayer")]
     public string SpriteLayer = "light";
diff --git a/Content.Client/Weapons/Marker/DamageMarkerSystem.cs b/Content.Client/Weapons/Marker/DamageMarkerSystem.cs
new file mode 100644 (file)
index 0000000..43ef216
--- /dev/null
@@ -0,0 +1,39 @@
+using Content.Shared.Weapons.Marker;
+using Robust.Client.GameObjects;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Weapons.Marker;
+
+public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
+{
+    [Dependency] private readonly IGameTiming _timing = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<DamageMarkerComponent, ComponentStartup>(OnMarkerStartup);
+        SubscribeLocalEvent<DamageMarkerComponent, ComponentShutdown>(OnMarkerShutdown);
+    }
+
+    private void OnMarkerStartup(EntityUid uid, DamageMarkerComponent component, ComponentStartup args)
+    {
+        if (!_timing.ApplyingState || component.Effect == null || !TryComp<SpriteComponent>(uid, out var sprite))
+            return;
+
+        var layer = sprite.LayerMapReserveBlank(DamageMarkerKey.Key);
+        sprite.LayerSetState(layer, component.Effect.RsiState, component.Effect.RsiPath);
+    }
+
+    private void OnMarkerShutdown(EntityUid uid, DamageMarkerComponent component, ComponentShutdown args)
+    {
+        if (!_timing.ApplyingState || !TryComp<SpriteComponent>(uid, out var sprite) || !sprite.LayerMapTryGet(DamageMarkerKey.Key, out var weh))
+            return;
+
+        sprite.RemoveLayer(weh);
+    }
+
+    private enum DamageMarkerKey : byte
+    {
+        Key
+    }
+}
index 23271c54786c968f7e2f6cb1e10ff75bff01dfb4..5fd39c1090d5a9871d413484976a5e21254ebda7 100644 (file)
@@ -6,6 +6,7 @@ using Content.Shared.Mobs.Components;
 using Content.Shared.StatusEffect;
 using Content.Shared.Weapons.Melee;
 using Content.Shared.Weapons.Melee.Events;
+using Content.Shared.Weapons.Ranged.Components;
 using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
 using Robust.Client.Input;
@@ -85,6 +86,16 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
         // Heavy attack.
         if (altDown == BoundKeyState.Down)
         {
+            // TODO: Need to make alt-fire melee its own component I guess?
+            // Melee and guns share a lot in the middle but share virtually nothing at the start and end so
+            // it's kinda tricky.
+            // I think as long as we make secondaries their own component it's probably fine
+            // as long as guncomp has an alt-use key then it shouldn't be too much of a PITA to deal with.
+            if (HasComp<GunComponent>(weaponUid))
+            {
+                return;
+            }
+
             // We did the click to end the attack but haven't pulled the key up.
             if (weapon.Attacking)
             {
index 7d82c98469d058eba78f2a107714ebf52733db80..191c2b1089a98e888cf70f01612302378e5bf268 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Client.Items;
 using Content.Client.Weapons.Ranged.Components;
 using Content.Shared.Camera;
+using Content.Shared.Input;
 using Content.Shared.Spawners.Components;
 using Content.Shared.Weapons.Ranged;
 using Content.Shared.Weapons.Ranged.Components;
@@ -137,7 +138,9 @@ public sealed partial class GunSystem : SharedGunSystem
             return;
         }
 
-        if (_inputSystem.CmdStates.GetState(EngineKeyFunctions.Use) != BoundKeyState.Down)
+        var useKey = gun.UseKey ? EngineKeyFunctions.Use : EngineKeyFunctions.UseSecondary;
+
+        if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down)
         {
             if (gun.ShotCounter != 0)
                 EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gunUid });
@@ -296,6 +299,9 @@ public sealed partial class GunSystem : SharedGunSystem
         _animPlayer.Play(ent, anim, "muzzle-flash");
         var light = EnsureComp<PointLightComponent>(uid);
 
+        if (light.Enabled)
+            return;
+
         light.NetSyncEnabled = false;
         light.Enabled = true;
         light.Color = Color.FromHex("#cc8e2b");
index 06c208204563aaa690d2ad03ddbaae60c5c6e9bf..13c58772fe9ac6792da38a173844b6467016ca83 100644 (file)
@@ -86,6 +86,6 @@ public sealed class ProjectileAnomalySystem : EntitySystem
 
         comp.Damage *= severity;
 
-        _gunSystem.ShootProjectile(ent, direction, Vector2.Zero, uid, component.MaxProjectileSpeed * severity);
+        _gunSystem.ShootProjectile(ent, direction, Vector2.Zero, uid, uid, component.MaxProjectileSpeed * severity);
     }
 }
index 88388c7d064cc4fc32832067332a848c3d46b201..0803ee1dde7ea2329462724117829c9b1a76c25e 100644 (file)
@@ -6,5 +6,9 @@ namespace Content.Server.Gatherable.Components;
 [RegisterComponent]
 public sealed class GatheringProjectileComponent : Component
 {
-
+    /// <summary>
+    /// How many more times we can gather.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("amount")]
+    public int Amount = 1;
 }
index 62417bc7b8449fbf5d731373393ab46ea85952ec..b8f30a466c9d55311da360c254b7ea3d8350c15a 100644 (file)
@@ -17,12 +17,16 @@ public sealed partial class GatherableSystem
     {
         if (!args.OtherFixture.Hard ||
             args.OurFixture.ID != SharedProjectileSystem.ProjectileFixture ||
+            component.Amount <= 0 ||
             !TryComp<GatherableComponent>(args.OtherEntity, out var gatherable))
         {
             return;
         }
 
         Gather(args.OtherEntity, uid, gatherable);
-        QueueDel(uid);
+        component.Amount--;
+
+        if (component.Amount <= 0)
+            QueueDel(uid);
     }
 }
index 0902602953f078806c5e9280da7c175bc2851521..303e2a2fd76a9628a99788ba0adff12eb2542bf2 100644 (file)
@@ -1,8 +1,5 @@
-using Content.Server.Actions;
 using Content.Server.Popups;
 using Content.Server.PowerCell;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Light;
@@ -11,10 +8,7 @@ using Content.Shared.Toggleable;
 using Content.Shared.Verbs;
 using JetBrains.Annotations;
 using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
-using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
 
@@ -37,7 +31,6 @@ namespace Content.Server.Light.EntitySystems
         {
             base.Initialize();
 
-            SubscribeLocalEvent<HandheldLightComponent, ComponentRemove>(OnRemove);
             SubscribeLocalEvent<HandheldLightComponent, ComponentGetState>(OnGetState);
 
             SubscribeLocalEvent<HandheldLightComponent, ExaminedEvent>(OnExamine);
@@ -45,41 +38,7 @@ namespace Content.Server.Light.EntitySystems
 
             SubscribeLocalEvent<HandheldLightComponent, ActivateInWorldEvent>(OnActivate);
 
-            SubscribeLocalEvent<HandheldLightComponent, GetItemActionsEvent>(OnGetActions);
             SubscribeLocalEvent<HandheldLightComponent, ToggleActionEvent>(OnToggleAction);
-
-            SubscribeLocalEvent<HandheldLightComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
-            SubscribeLocalEvent<HandheldLightComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
-        }
-
-        private void OnEntInserted(
-            EntityUid uid,
-            HandheldLightComponent component,
-            EntInsertedIntoContainerMessage args)
-        {
-            // Not guaranteed to be the correct container for our slot, I don't care.
-            UpdateLevel(uid, component);
-        }
-
-        private void OnEntRemoved(
-            EntityUid uid,
-            HandheldLightComponent component,
-            EntRemovedFromContainerMessage args)
-        {
-            // Ditto above
-            UpdateLevel(uid, component);
-        }
-
-        private void OnGetActions(EntityUid uid, HandheldLightComponent component, GetItemActionsEvent args)
-        {
-            if (component.ToggleAction == null
-                && _proto.TryIndex(component.ToggleActionId, out InstantActionPrototype? act))
-            {
-                component.ToggleAction = new(act);
-            }
-
-            if (component.ToggleAction != null)
-                args.Actions.Add(component.ToggleAction);
         }
 
         private void OnToggleAction(EntityUid uid, HandheldLightComponent component, ToggleActionEvent args)
@@ -114,11 +73,6 @@ namespace Content.Server.Light.EntitySystems
             return (byte?) ContentHelpers.RoundToNearestLevels(battery.CurrentCharge / battery.MaxCharge * 255, 255, HandheldLightComponent.StatusLevels);
         }
 
-        private void OnRemove(EntityUid uid, HandheldLightComponent component, ComponentRemove args)
-        {
-            _activeLights.Remove(component);
-        }
-
         private void OnActivate(EntityUid uid, HandheldLightComponent component, ActivateInWorldEvent args)
         {
             if (args.Handled)
@@ -144,36 +98,6 @@ namespace Content.Server.Light.EntitySystems
                 : Loc.GetString("handheld-light-component-on-examine-is-off-message"));
         }
 
-        public override void Shutdown()
-        {
-            base.Shutdown();
-            _activeLights.Clear();
-        }
-
-        public override void Update(float frameTime)
-        {
-            var toRemove = new RemQueue<HandheldLightComponent>();
-
-            foreach (var handheld in _activeLights)
-            {
-                var uid = handheld.Owner;
-
-                if (handheld.Deleted)
-                {
-                    toRemove.Add(handheld);
-                    continue;
-                }
-
-                if (Paused(uid)) continue;
-                TryUpdate(uid, handheld, frameTime);
-            }
-
-            foreach (var light in toRemove)
-            {
-                _activeLights.Remove(light);
-            }
-        }
-
         private void AddToggleLightVerb(EntityUid uid, HandheldLightComponent component, GetVerbsEvent<ActivationVerb> args)
         {
             if (!args.CanAccess || !args.CanInteract)
index c0cf6cd6055c91975384f7b5b230083dfcef0987..0751ab50bf059764724ff5cee3bcf1f932a15b4f 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Server.PowerCell;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Pinpointer;
+using Content.Shared.PowerCell;
 using Robust.Server.GameObjects;
 using Robust.Shared.Timing;
 
diff --git a/Content.Server/PowerCell/PowerCellSystem.Draw.cs b/Content.Server/PowerCell/PowerCellSystem.Draw.cs
new file mode 100644 (file)
index 0000000..9d73138
--- /dev/null
@@ -0,0 +1,91 @@
+using Content.Server.Power.Components;
+using Content.Shared.PowerCell;
+using Content.Shared.PowerCell.Components;
+
+namespace Content.Server.PowerCell;
+
+public sealed partial class PowerCellSystem
+{
+    /*
+     * Handles PowerCellDraw
+     */
+
+    private static readonly TimeSpan Delay = TimeSpan.FromSeconds(1);
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+        var query = EntityQueryEnumerator<PowerCellDrawComponent, PowerCellSlotComponent>();
+
+        while (query.MoveNext(out var uid, out var comp, out var slot))
+        {
+            if (!comp.Drawing)
+                continue;
+
+            if (_timing.CurTime < comp.NextUpdateTime)
+                continue;
+
+            comp.NextUpdateTime += Delay;
+
+            if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
+                continue;
+
+            if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery))
+                continue;
+
+            comp.Drawing = false;
+            var ev = new PowerCellSlotEmptyEvent();
+            RaiseLocalEvent(uid, ref ev);
+        }
+    }
+
+    private void OnUnpaused(EntityUid uid, PowerCellDrawComponent component, ref EntityUnpausedEvent args)
+    {
+        component.NextUpdateTime += args.PausedTime;
+    }
+
+    private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args)
+    {
+        // Update the bools for client prediction.
+        bool canDraw;
+        bool canUse;
+
+        if (component.UseRate > 0f)
+        {
+            canUse = args.Charge > component.UseRate;
+        }
+        else
+        {
+            canUse = true;
+        }
+
+        if (component.DrawRate > 0f)
+        {
+            canDraw = args.Charge > 0f;
+        }
+        else
+        {
+            canDraw = true;
+        }
+
+        if (canUse != component.CanUse || canDraw != component.CanDraw)
+        {
+            component.CanDraw = canDraw;
+            component.CanUse = canUse;
+            Dirty(component);
+        }
+    }
+
+    private void OnDrawCellChanged(EntityUid uid, PowerCellDrawComponent component, PowerCellChangedEvent args)
+    {
+        var canDraw = !args.Ejected && HasCharge(uid, float.MinValue);
+        var canUse = !args.Ejected && HasActivatableCharge(uid, component);
+
+        if (canUse != component.CanUse || canDraw != component.CanDraw)
+        {
+            component.CanDraw = canDraw;
+            component.CanUse = canUse;
+            Dirty(component);
+        }
+    }
+}
index 6c6cd92d4c347573ef7726cec981647c3187dc7c..7719773d9d4518ae7ce3fac2d444305004f7a4d2 100644 (file)
@@ -19,7 +19,10 @@ using Robust.Shared.Timing;
 
 namespace Content.Server.PowerCell;
 
-public sealed class PowerCellSystem : SharedPowerCellSystem
+/// <summary>
+/// Handles Power cells
+/// </summary>
+public sealed partial class PowerCellSystem : SharedPowerCellSystem
 {
     [Dependency] private readonly IAdminLogManager _adminLogger = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
@@ -39,41 +42,17 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
         SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
         SubscribeLocalEvent<PowerCellComponent, SolutionChangedEvent>(OnSolutionChange);
         SubscribeLocalEvent<PowerCellComponent, RejuvenateEvent>(OnRejuvenate);
-
         SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
-        SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
 
         SubscribeLocalEvent<PowerCellDrawComponent, EntityUnpausedEvent>(OnUnpaused);
+        SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
+        SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
 
         // funny
+        SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
         SubscribeLocalEvent<PowerCellSlotComponent, BeingMicrowavedEvent>(OnSlotMicrowaved);
-        SubscribeLocalEvent<BatteryComponent, BeingMicrowavedEvent>(OnMicrowaved);
-    }
-
-    public override void Update(float frameTime)
-    {
-        base.Update(frameTime);
-        var query = EntityQueryEnumerator<PowerCellDrawComponent, PowerCellSlotComponent>();
 
-        while (query.MoveNext(out var uid, out var comp, out var slot))
-        {
-            if (!comp.Enabled)
-                continue;
-
-            if (_timing.CurTime < comp.NextUpdateTime)
-                continue;
-            comp.NextUpdateTime += TimeSpan.FromSeconds(1);
-
-            if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
-                continue;
-
-            if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery))
-                continue;
-
-            comp.Enabled = false;
-            var ev = new PowerCellSlotEmptyEvent();
-            RaiseLocalEvent(uid, ref ev);
-        }
+        SubscribeLocalEvent<BatteryComponent, BeingMicrowavedEvent>(OnMicrowaved);
     }
 
     private void OnRejuvenate(EntityUid uid, PowerCellComponent component, RejuvenateEvent args)
@@ -83,13 +62,13 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
 
     private void OnSlotMicrowaved(EntityUid uid, PowerCellSlotComponent component, BeingMicrowavedEvent args)
     {
-        if (_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out ItemSlot? slot))
-        {
-            if (slot.Item == null)
-                return;
+        if (!_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out var slot))
+            return;
 
-            RaiseLocalEvent(slot.Item.Value, args);
-        }
+        if (slot.Item == null)
+            return;
+
+        RaiseLocalEvent(slot.Item.Value, args);
     }
 
     private void OnMicrowaved(EntityUid uid, BatteryComponent component, BeingMicrowavedEvent args)
@@ -111,17 +90,14 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
             return;
         }
 
-        if (!TryComp(uid, out AppearanceComponent? appearance))
-            return;
-
         var frac = args.Charge / args.MaxCharge;
         var level = (byte) ContentHelpers.RoundToNearestLevels(frac, 1, PowerCellComponent.PowerCellVisualsLevels);
-        _sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level, appearance);
+        _sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level);
 
         // If this power cell is inside a cell-slot, inform that entity that the power has changed (for updating visuals n such).
         if (_containerSystem.TryGetContainingContainer(uid, out var container)
             && TryComp(container.Owner, out PowerCellSlotComponent? slot)
-            && _itemSlotsSystem.TryGetSlot(container.Owner, slot.CellSlotId, out ItemSlot? itemSlot))
+            && _itemSlotsSystem.TryGetSlot(container.Owner, slot.CellSlotId, out var itemSlot))
         {
             if (itemSlot.Item == uid)
                 RaiseLocalEvent(container.Owner, new PowerCellChangedEvent(false));
@@ -136,11 +112,6 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
         RaiseLocalEvent(uid, ref ev);
     }
 
-    private void OnUnpaused(EntityUid uid, PowerCellDrawComponent component, ref EntityUnpausedEvent args)
-    {
-        component.NextUpdateTime += args.PausedTime;
-    }
-
     private void Explode(EntityUid uid, BatteryComponent? battery = null, EntityUid? cause = null)
     {
         if (!Resolve(uid, ref battery))
@@ -190,10 +161,10 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
 
     public void SetPowerCellDrawEnabled(EntityUid uid, bool enabled, PowerCellDrawComponent? component = null)
     {
-        if (!Resolve(uid, ref component, false))
+        if (!Resolve(uid, ref component, false) || enabled == component.Drawing)
             return;
 
-        component.Enabled = enabled;
+        component.Drawing = enabled;
         component.NextUpdateTime = _timing.CurTime;
     }
 
index a9b02cdb8e861f7630f0ad07b03741d4f5c12124..af517a0231a9d73a3e89592e4b3032564756c68b 100644 (file)
@@ -26,12 +26,6 @@ public sealed class ProjectileSystem : SharedProjectileSystem
     {
         base.Initialize();
         SubscribeLocalEvent<ProjectileComponent, StartCollideEvent>(OnStartCollide);
-        SubscribeLocalEvent<ProjectileComponent, ComponentGetState>(OnGetState);
-    }
-
-    private void OnGetState(EntityUid uid, ProjectileComponent component, ref ComponentGetState args)
-    {
-        args.State = new ProjectileComponentState(component.Shooter, component.IgnoreShooter);
     }
 
     private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args)
index 14429dd455e3dc4122ba918cb9d3f59a9da29f81..df7f91a1c1caf47e74d57245c3e52c204d67e1dd 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.PowerCell;
+using Content.Shared.PowerCell;
 using Content.Shared.UserInterface;
 
 namespace Content.Server.UserInterface;
diff --git a/Content.Server/Weapons/DamageMarkerSystem.cs b/Content.Server/Weapons/DamageMarkerSystem.cs
new file mode 100644 (file)
index 0000000..be65bca
--- /dev/null
@@ -0,0 +1,8 @@
+using Content.Shared.Weapons.Marker;
+
+namespace Content.Server.Weapons;
+
+public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
+{
+
+}
index 995c1ffbc6b4938e4168356aa0156aef183f09ab..c872a8d975d2ca71007a124a6af5ea6064db1b5d 100644 (file)
@@ -118,7 +118,7 @@ public sealed partial class GunSystem : SharedGunSystem
             // pneumatic cannon doesn't shoot bullets it just throws them, ignore ammo handling
             if (throwItems && ent != null)
             {
-                ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, user);
+                ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
                 continue;
             }
 
@@ -136,14 +136,14 @@ public sealed partial class GunSystem : SharedGunSystem
                             for (var i = 0; i < cartridge.Count; i++)
                             {
                                 var uid = Spawn(cartridge.Prototype, fromEnt);
-                                ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, user);
+                                ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user);
                                 shotProjectiles.Add(uid);
                             }
                         }
                         else
                         {
                             var uid = Spawn(cartridge.Prototype, fromEnt);
-                            ShootOrThrow(uid, mapDirection, gunVelocity, gun, user);
+                            ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user);
                             shotProjectiles.Add(uid);
                         }
 
@@ -175,7 +175,7 @@ public sealed partial class GunSystem : SharedGunSystem
                     shotProjectiles.Add(ent!.Value);
                     MuzzleFlash(gunUid, newAmmo, user);
                     Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
-                    ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, user);
+                    ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
                     break;
                 case HitscanPrototype hitscan:
 
@@ -265,7 +265,7 @@ public sealed partial class GunSystem : SharedGunSystem
         });
     }
 
-    private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid? user)
+    private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user)
     {
         // Do a throw
         if (!HasComp<ProjectileComponent>(uid))
@@ -276,10 +276,10 @@ public sealed partial class GunSystem : SharedGunSystem
             return;
         }
 
-        ShootProjectile(uid, mapDirection, gunVelocity, user, gun.ProjectileSpeed);
+        ShootProjectile(uid, mapDirection, gunVelocity, gunUid, user, gun.ProjectileSpeed);
     }
 
-    public void ShootProjectile(EntityUid uid, Vector2 direction, Vector2 gunVelocity, EntityUid? user = null, float speed = 20f)
+    public void ShootProjectile(EntityUid uid, Vector2 direction, Vector2 gunVelocity, EntityUid gunUid, EntityUid? user = null, float speed = 20f)
     {
         var physics = EnsureComp<PhysicsComponent>(uid);
         Physics.SetBodyStatus(physics, BodyStatus.InAir);
@@ -293,6 +293,7 @@ public sealed partial class GunSystem : SharedGunSystem
         {
             var projectile = EnsureComp<ProjectileComponent>(uid);
             Projectiles.SetShooter(projectile, user.Value);
+            projectile.Weapon = gunUid;
         }
 
         TransformSystem.SetWorldRotation(uid, direction.ToWorldAngle());
diff --git a/Content.Shared/Interaction/Events/MeleeAttackAttemptEvent.cs b/Content.Shared/Interaction/Events/MeleeAttackAttemptEvent.cs
deleted file mode 100644 (file)
index 01083e8..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Content.Shared.Interaction.Events;
-
-/// <summary>
-/// Raised on directed a weapon when being used in a melee attack.
-/// </summary>
-[ByRefEvent]
-public struct MeleeAttackAttemptEvent
-{
-    public bool Cancelled = false;
-    public readonly EntityUid User;
-
-    public MeleeAttackAttemptEvent(EntityUid user)
-    {
-        User = user;
-    }
-}
similarity index 56%
rename from Content.Server/PowerCell/PowerCellDrawComponent.cs
rename to Content.Shared/PowerCell/PowerCellDrawComponent.cs
index 213b414098af7afae2cf601e62f25f1ef4f08de9..6963326b709c3245b4df1f0f6287f39b761ed82c 100644 (file)
@@ -1,15 +1,35 @@
+using Robust.Shared.GameStates;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
-namespace Content.Server.PowerCell;
+namespace Content.Shared.PowerCell;
 
 /// <summary>
 /// Indicates that the entity's ActivatableUI requires power or else it closes.
 /// </summary>
-[RegisterComponent, Access(typeof(PowerCellSystem))]
-public sealed class PowerCellDrawComponent : Component
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class PowerCellDrawComponent : Component
 {
+    #region Prediction
+
+    /// <summary>
+    /// Whether there is any charge available to draw.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("canDraw"), AutoNetworkedField]
+    public bool CanDraw;
+
+    /// <summary>
+    /// Whether there is sufficient charge to use.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("canUse"), AutoNetworkedField]
+    public bool CanUse;
+
+    #endregion
+
+    /// <summary>
+    /// Is this power cell currently drawing power every tick.
+    /// </summary>
     [ViewVariables(VVAccess.ReadWrite), DataField("enabled")]
-    public bool Enabled;
+    public bool Drawing;
 
     /// <summary>
     /// How much the entity draws while the UI is open.
index 5c18152a23873d4b7ade1a8d2bbe0900b8f2c3af..57af4403606e6ae933d7db78fbf5d8f9c4ac8387 100644 (file)
@@ -21,7 +21,7 @@ public abstract class SharedPowerCellSystem : EntitySystem
 
     private void OnRejuventate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args)
     {
-        if (!_itemSlots.TryGetSlot(uid, component.CellSlotId, out ItemSlot? itemSlot) || !itemSlot.Item.HasValue)
+        if (!_itemSlots.TryGetSlot(uid, component.CellSlotId, out var itemSlot) || !itemSlot.Item.HasValue)
             return;
 
         // charge entity batteries and remove booby traps.
index bab2298a311f6a1c0fb590130f44462229ccebff..1e77e3954cec6ab67ed9278a884fc11f438b5fb3 100644 (file)
@@ -4,34 +4,42 @@ using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
-namespace Content.Shared.Projectiles
+namespace Content.Shared.Projectiles;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class ProjectileComponent : Component
 {
-    [RegisterComponent, NetworkedComponent]
-    public sealed class ProjectileComponent : Component
-    {
-        [ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
-        public string? ImpactEffect;
+    [ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
+    public string? ImpactEffect;
+
+    /// <summary>
+    /// User that shot this projectile.
+    /// </summary>
+    [DataField("shooter"), AutoNetworkedField] public EntityUid Shooter;
 
-        public EntityUid Shooter { get; set; }
+    /// <summary>
+    /// Weapon used to shoot.
+    /// </summary>
+    [DataField("weapon"), AutoNetworkedField]
+    public EntityUid Weapon;
 
-        public bool IgnoreShooter = true;
+    [DataField("ignoreShooter"), AutoNetworkedField]
+    public bool IgnoreShooter = true;
 
-        [DataField("damage", required: true)]
-        [ViewVariables(VVAccess.ReadWrite)]
-        public DamageSpecifier Damage = default!;
+    [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)]
+    public DamageSpecifier Damage = new();
 
-        [DataField("deleteOnCollide")]
-        public bool DeleteOnCollide { get; } = true;
+    [DataField("deleteOnCollide")]
+    public bool DeleteOnCollide = true;
 
-        [DataField("ignoreResistances")]
-        public bool IgnoreResistances { get; } = false;
+    [DataField("ignoreResistances")]
+    public bool IgnoreResistances = false;
 
-        // Get that juicy FPS hit sound
-        [DataField("soundHit")] public SoundSpecifier? SoundHit;
+    // Get that juicy FPS hit sound
+    [DataField("soundHit")] public SoundSpecifier? SoundHit;
 
-        [DataField("soundForce")]
-        public bool ForceSound = false;
+    [DataField("soundForce")]
+    public bool ForceSound = false;
 
-        public bool DamagedEntity;
-    }
+    public bool DamagedEntity;
 }
index 53a271f34cfc756bda3d81e978c7d2bedc694cb6..08b974cfdac731a1e40581874d1b0f6409b4ea72 100644 (file)
@@ -30,31 +30,18 @@ namespace Content.Shared.Projectiles
             component.Shooter = uid;
             Dirty(component);
         }
+    }
 
-        [NetSerializable, Serializable]
-        public sealed class ProjectileComponentState : ComponentState
-        {
-            public ProjectileComponentState(EntityUid shooter, bool ignoreShooter)
-            {
-                Shooter = shooter;
-                IgnoreShooter = ignoreShooter;
-            }
-
-            public EntityUid Shooter { get; }
-            public bool IgnoreShooter { get; }
-        }
+    [Serializable, NetSerializable]
+    public sealed class ImpactEffectEvent : EntityEventArgs
+    {
+        public string Prototype;
+        public EntityCoordinates Coordinates;
 
-        [Serializable, NetSerializable]
-        public sealed class ImpactEffectEvent : EntityEventArgs
+        public ImpactEffectEvent(string prototype, EntityCoordinates coordinates)
         {
-            public string Prototype;
-            public EntityCoordinates Coordinates;
-
-            public ImpactEffectEvent(string prototype, EntityCoordinates coordinates)
-            {
-                Prototype = prototype;
-                Coordinates = coordinates;
-            }
+            Prototype = prototype;
+            Coordinates = coordinates;
         }
     }
 }
diff --git a/Content.Shared/Weapons/Marker/DamageMarkerComponent.cs b/Content.Shared/Weapons/Marker/DamageMarkerComponent.cs
new file mode 100644 (file)
index 0000000..ef3b712
--- /dev/null
@@ -0,0 +1,38 @@
+using Content.Shared.Damage;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Weapons.Marker;
+
+/// <summary>
+/// Marks an entity to take additional damage
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedDamageMarkerSystem))]
+public sealed partial class DamageMarkerComponent : Component
+{
+    /// <summary>
+    /// Sprite to apply to the entity while damagemarker is applied.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("effect")]
+    public SpriteSpecifier.Rsi? Effect = new(new ResPath("/Textures/Objects/Weapons/Effects"), "shield2");
+
+    /// <summary>
+    /// Sound to play when the damage marker is procced.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("sound")]
+    public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/kinetic_accel.ogg");
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("damage")]
+    public DamageSpecifier Damage = new();
+
+    /// <summary>
+    /// Entity that marked this entity for a damage surplus.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("marker"), AutoNetworkedField]
+    public EntityUid Marker;
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("endTime", customTypeSerializer:typeof(TimeOffsetSerializer)), AutoNetworkedField]
+    public TimeSpan EndTime;
+}
diff --git a/Content.Shared/Weapons/Marker/DamageMarkerOnCollideComponent.cs b/Content.Shared/Weapons/Marker/DamageMarkerOnCollideComponent.cs
new file mode 100644 (file)
index 0000000..e708dd7
--- /dev/null
@@ -0,0 +1,30 @@
+using Content.Shared.Damage;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Weapons.Marker;
+
+/// <summary>
+/// Applies <see cref="DamageMarkerComponent"/> when colliding with an entity.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedDamageMarkerSystem))]
+public sealed partial class DamageMarkerOnCollideComponent : Component
+{
+    [DataField("whitelist"), AutoNetworkedField]
+    public EntityWhitelist? Whitelist = new();
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("duration"), AutoNetworkedField]
+    public TimeSpan Duration = TimeSpan.FromSeconds(5);
+
+    /// <summary>
+    /// Additional damage to be applied.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("damage")]
+    public DamageSpecifier Damage = new();
+
+    /// <summary>
+    /// How many more times we can apply it.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("amount"), AutoNetworkedField]
+    public int Amount = 1;
+}
diff --git a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs
new file mode 100644 (file)
index 0000000..0f6fc34
--- /dev/null
@@ -0,0 +1,81 @@
+using Content.Shared.Damage;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Melee.Events;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Weapons.Marker;
+
+public abstract class SharedDamageMarkerSystem : EntitySystem
+{
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<DamageMarkerOnCollideComponent, StartCollideEvent>(OnMarkerCollide);
+        SubscribeLocalEvent<DamageMarkerComponent, EntityUnpausedEvent>(OnMarkerUnpaused);
+        SubscribeLocalEvent<DamageMarkerComponent, AttackedEvent>(OnMarkerAttacked);
+    }
+
+    private void OnMarkerAttacked(EntityUid uid, DamageMarkerComponent component, AttackedEvent args)
+    {
+        if (component.Marker != args.Used)
+            return;
+
+        args.BonusDamage += component.Damage;
+        RemCompDeferred<DamageMarkerComponent>(uid);
+        _audio.PlayPredicted(component.Sound, uid, args.User);
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<DamageMarkerComponent>();
+
+        while (query.MoveNext(out var uid, out var comp))
+        {
+            if (comp.EndTime > _timing.CurTime)
+                continue;
+
+            RemCompDeferred<DamageMarkerComponent>(uid);
+        }
+    }
+
+    private void OnMarkerUnpaused(EntityUid uid, DamageMarkerComponent component, ref EntityUnpausedEvent args)
+    {
+        component.EndTime += args.PausedTime;
+    }
+
+    private void OnMarkerCollide(EntityUid uid, DamageMarkerOnCollideComponent component, ref StartCollideEvent args)
+    {
+        if (!args.OtherFixture.Hard ||
+            args.OurFixture.ID != SharedProjectileSystem.ProjectileFixture ||
+            component.Amount <= 0 ||
+            component.Whitelist?.IsValid(args.OtherEntity, EntityManager) == false ||
+            !TryComp<ProjectileComponent>(uid, out var projectile) ||
+            !projectile.Weapon.IsValid())
+        {
+            return;
+        }
+
+        // Markers are exclusive, deal with it.
+        var marker = EnsureComp<DamageMarkerComponent>(args.OtherEntity);
+        marker.Damage = new DamageSpecifier(component.Damage);
+        marker.Marker = projectile.Weapon;
+        marker.EndTime = _timing.CurTime + component.Duration;
+        component.Amount--;
+        Dirty(marker);
+
+        if (component.Amount <= 0)
+        {
+            QueueDel(uid);
+        }
+        else
+        {
+            Dirty(component);
+        }
+    }
+}
diff --git a/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs b/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs
new file mode 100644 (file)
index 0000000..6cf12d5
--- /dev/null
@@ -0,0 +1,12 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Weapons.Melee.Components;
+
+/// <summary>
+/// Indicates that this meleeweapon requires wielding to be useable.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed class MeleeRequiresWieldComponent : Component
+{
+
+}
index fdee5d507b2b03a48b7beab7b951ec2599bceffa..cb731258d96ce71480f6d092ea3a6fbcd0ba8afa 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Damage;
 using Robust.Shared.Map;
 using Robust.Shared.Serialization;
 
@@ -37,6 +38,8 @@ namespace Content.Shared.Weapons.Melee.Events
         /// </summary>
         public EntityCoordinates ClickLocation { get; }
 
+        public DamageSpecifier BonusDamage = new();
+
         public AttackedEvent(EntityUid used, EntityUid user, EntityCoordinates clickLocation)
         {
             Used = used;
diff --git a/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs b/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs
new file mode 100644 (file)
index 0000000..2800e3b
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Content.Shared.Weapons.Melee.Events;
+
+/// <summary>
+/// Raised directed on a weapon when attempt a melee attack.
+/// </summary>
+[ByRefEvent]
+public record struct AttemptMeleeEvent(bool Cancelled, string? Message);
index faa61198405ed333dfa84fc078001ac80b879e88..124023dc13c8a2779f27fdeccbcc76c995b5299a 100644 (file)
@@ -15,6 +15,8 @@ using Content.Shared.Physics;
 using Content.Shared.Popups;
 using Content.Shared.Weapons.Melee.Components;
 using Content.Shared.Weapons.Melee.Events;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Systems;
 using Robust.Shared.Audio;
 using Robust.Shared.Collections;
 using Robust.Shared.GameStates;
@@ -70,6 +72,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         SubscribeLocalEvent<MeleeWeaponComponent, ComponentHandleState>(OnHandleState);
         SubscribeLocalEvent<MeleeWeaponComponent, HandDeselectedEvent>(OnMeleeDropped);
         SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
+        SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot);
 
         SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack);
         SubscribeAllEvent<LightAttackEvent>(OnLightAttack);
@@ -89,6 +92,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
 #endif
     }
 
+    private void OnMeleeShot(EntityUid uid, MeleeWeaponComponent component, ref GunShotEvent args)
+    {
+        if (!TryComp<GunComponent>(uid, out var gun))
+            return;
+
+        if (gun.NextFire > component.NextAttack)
+        {
+            component.NextAttack = gun.NextFire;
+            Dirty(component);
+        }
+    }
+
     private void OnMeleeUnpaused(EntityUid uid, MeleeWeaponComponent component, ref EntityUnpausedEvent args)
     {
         component.NextAttack += args.PausedTime;
@@ -356,38 +371,64 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         }
 
         // Windup time checked elsewhere.
+        var fireRate = TimeSpan.FromSeconds(1f / weapon.AttackRate);
+        var swings = 0;
 
+        // TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly.
         if (weapon.NextAttack < curTime)
             weapon.NextAttack = curTime;
 
-        weapon.NextAttack += TimeSpan.FromSeconds(1f / weapon.AttackRate);
+        while (weapon.NextAttack <= curTime)
+        {
+            weapon.NextAttack += fireRate;
+            swings++;
+        }
 
-        // Attack confirmed
-        string animation;
+        Dirty(weapon);
 
-        switch (attack)
+        // Do this AFTER attack so it doesn't spam every tick
+        var ev = new AttemptMeleeEvent();
+        RaiseLocalEvent(weaponUid, ref ev);
+
+        if (ev.Cancelled)
         {
-            case LightAttackEvent light:
-                DoLightAttack(user, light, weaponUid, weapon, session);
-                animation = weapon.ClickAnimation;
-                break;
-            case DisarmAttackEvent disarm:
-                if (!DoDisarm(user, disarm, weaponUid, weapon, session))
-                    return;
+            if (ev.Message != null)
+            {
+                PopupSystem.PopupClient(ev.Message, weaponUid, user);
+            }
 
-                animation = weapon.ClickAnimation;
-                break;
-            case HeavyAttackEvent heavy:
-                DoHeavyAttack(user, heavy, weaponUid, weapon, session);
-                animation = weapon.WideAnimation;
-                break;
-            default:
-                throw new NotImplementedException();
+            return;
+        }
+
+        // Attack confirmed
+        for (var i = 0; i < swings; i++)
+        {
+            string animation;
+
+            switch (attack)
+            {
+                case LightAttackEvent light:
+                    DoLightAttack(user, light, weaponUid, weapon, session);
+                    animation = weapon.ClickAnimation;
+                    break;
+                case DisarmAttackEvent disarm:
+                    if (!DoDisarm(user, disarm, weaponUid, weapon, session))
+                        return;
+
+                    animation = weapon.ClickAnimation;
+                    break;
+                case HeavyAttackEvent heavy:
+                    DoHeavyAttack(user, heavy, weaponUid, weapon, session);
+                    animation = weapon.WideAnimation;
+                    break;
+                default:
+                    throw new NotImplementedException();
+            }
+
+            DoLungeAnimation(user, weapon.Angle, attack.Coordinates.ToMap(EntityManager, TransformSystem), weapon.Range, animation);
         }
 
-        DoLungeAnimation(user, weapon.Angle, attack.Coordinates.ToMap(EntityManager, TransformSystem), weapon.Range, animation);
         weapon.Attacking = true;
-        Dirty(weapon);
     }
 
     /// <summary>
@@ -469,9 +510,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         Interaction.DoContactInteraction(user, ev.Target);
 
         // For stuff that cares about it being attacked.
-        RaiseLocalEvent(ev.Target.Value, new AttackedEvent(meleeUid, user, targetXform.Coordinates));
+        var attackedEvent = new AttackedEvent(meleeUid, user, targetXform.Coordinates);
+        RaiseLocalEvent(ev.Target.Value, attackedEvent);
 
-        var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
+        var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
         var damageResult = Damageable.TryChangeDamage(ev.Target, modifiedDamage, origin:user);
 
         if (damageResult != null && damageResult.Total > FixedPoint2.Zero)
@@ -596,16 +638,15 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             // If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a
             // somewhat messy scuffle. See also, light attacks.
             Interaction.DoContactInteraction(user, target);
-
-            RaiseLocalEvent(target, new AttackedEvent(meleeUid, user, Transform(target).Coordinates));
         }
 
-        var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
         var appliedDamage = new DamageSpecifier();
 
         foreach (var entity in targets)
         {
-            RaiseLocalEvent(entity, new AttackedEvent(meleeUid, user, ev.Coordinates));
+            var attackedEvent = new AttackedEvent(meleeUid, user, ev.Coordinates);
+            RaiseLocalEvent(entity, attackedEvent);
+            var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
 
             var damageResult = Damageable.TryChangeDamage(entity, modifiedDamage, origin:user);
 
@@ -631,7 +672,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             if (appliedDamage.Total > FixedPoint2.Zero)
             {
                 var target = entities.First();
-                PlayHitSound(target, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound);
+                PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound);
             }
             else
             {
index 0ccc839902ce33032edab6cc78ee374eb212a67f..df897bcc0fc01011d7d3c0b4727327c4b749cb53 100644 (file)
@@ -79,6 +79,12 @@ public partial class GunComponent : Component
 
     #endregion
 
+    /// <summary>
+    /// Whether this gun is shot via the use key or the alt-use key.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("useKey"), AutoNetworkedField]
+    public bool UseKey = true;
+
     /// <summary>
     /// Where the gun is being requested to shoot.
     /// </summary>
diff --git a/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs
new file mode 100644 (file)
index 0000000..b0aca3a
--- /dev/null
@@ -0,0 +1,12 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Weapons.Ranged.Components;
+
+/// <summary>
+/// Indicates that this gun requires wielding to be useable.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed class GunRequiresWieldComponent : Component
+{
+
+}
diff --git a/Content.Shared/Weapons/Ranged/Components/UseDelayOnShootComponent.cs b/Content.Shared/Weapons/Ranged/Components/UseDelayOnShootComponent.cs
new file mode 100644 (file)
index 0000000..c2b0950
--- /dev/null
@@ -0,0 +1,14 @@
+using Content.Shared.Timing;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Weapons.Ranged.Components;
+
+/// <summary>
+/// Applies UseDelay whenever the entity shoots.
+/// </summary>
+[RegisterComponent, NetworkedComponent, Access(typeof(UseDelayOnShootSystem))]
+public sealed class UseDelayOnShootComponent : Component
+{
+
+}
index 9b72136372dc54482e9815270b6b00e12161b163..ded4ce34a231ab681bc86d087ae0dedb5dbf93f6 100644 (file)
@@ -47,8 +47,10 @@ public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
 
             if (_gun.UpdateBasicEntityAmmoCount(uid, ammo.Count.Value + 1, ammo))
             {
-                if (_netManager.IsClient && _timing.IsFirstTimePredicted)
-                    _audio.Play(recharge.RechargeSound, Filter.Local(), uid, true);
+                // We don't predict this because occasionally on client it may not play.
+                // PlayPredicted will still be predicted on the client.
+                if (_netManager.IsServer)
+                    _audio.PlayPvs(recharge.RechargeSound, uid);
             }
 
             if (ammo.Count == ammo.Capacity)
index 47688489728bf255413a3a46d329b4179b4e6888..89068db8ae9553d6eff8ba55eba661b625d319ba 100644 (file)
@@ -13,11 +13,12 @@ using Content.Shared.Projectiles;
 using Content.Shared.Tag;
 using Content.Shared.Throwing;
 using Content.Shared.Verbs;
+using Content.Shared.Weapons.Melee;
+using Content.Shared.Weapons.Melee.Events;
 using Content.Shared.Weapons.Ranged.Components;
 using Content.Shared.Weapons.Ranged.Events;
 using Robust.Shared.Audio;
 using Robust.Shared.Containers;
-using Robust.Shared.GameStates;
 using Robust.Shared.Map;
 using Robust.Shared.Network;
 using Robust.Shared.Physics.Components;
@@ -70,7 +71,7 @@ public abstract partial class SharedGunSystem : EntitySystem
         Sawmill.Level = LogLevel.Info;
         SubscribeAllEvent<RequestShootEvent>(OnShootRequest);
         SubscribeAllEvent<RequestStopShootEvent>(OnStopShootRequest);
-        SubscribeLocalEvent<GunComponent, MeleeAttackAttemptEvent>(OnGunMeleeAttempt);
+        SubscribeLocalEvent<GunComponent, MeleeHitEvent>(OnGunMelee);
 
         // Ammo providers
         InitializeBallistic();
@@ -103,17 +104,21 @@ public abstract partial class SharedGunSystem : EntitySystem
 #endif
     }
 
-    private void OnGunUnpaused(EntityUid uid, GunComponent component, ref EntityUnpausedEvent args)
+    private void OnGunMelee(EntityUid uid, GunComponent component, MeleeHitEvent args)
     {
-        component.NextFire += args.PausedTime;
+        if (!TryComp<MeleeWeaponComponent>(uid, out var melee))
+            return;
+
+        if (melee.NextAttack > component.NextFire)
+        {
+            component.NextFire = melee.NextAttack;
+            Dirty(component);
+        }
     }
 
-    private void OnGunMeleeAttempt(EntityUid uid, GunComponent component, ref MeleeAttackAttemptEvent args)
+    private void OnGunUnpaused(EntityUid uid, GunComponent component, ref EntityUnpausedEvent args)
     {
-        if (TagSystem.HasTag(args.User, "GunsDisabled"))
-            return;
-
-        args.Cancelled = true;
+        component.NextFire += args.PausedTime;
     }
 
     private void OnShootRequest(RequestShootEvent msg, EntitySessionEventArgs args)
@@ -214,15 +219,12 @@ public abstract partial class SharedGunSystem : EntitySystem
         if (toCoordinates == null)
             return;
 
-        if (TagSystem.HasTag(user, "GunsDisabled"))
-        {
-            if (Timing.IsFirstTimePredicted)
-                Popup(Loc.GetString("gun-disabled"), user, user);
-            return;
-        }
-
         var curTime = Timing.CurTime;
 
+        // Maybe Raise an event for this? CanAttack doesn't seem appropriate.
+        if (TryComp<MeleeWeaponComponent>(gunUid, out var melee) && melee.NextAttack > curTime)
+            return;
+
         // Need to do this to play the clicking sound for empty automatic weapons
         // but not play anything for burst fire.
         if (gun.NextFire > curTime)
@@ -232,7 +234,8 @@ public abstract partial class SharedGunSystem : EntitySystem
 
         // First shot
         // Previously we checked shotcounter but in some cases all the bullets got dumped at once
-        if (gun.NextFire < curTime - fireRate)
+        // curTime - fireRate is insufficient because if you time it just right you can get a 3rd shot out slightly quicker.
+        if (gun.NextFire < curTime - fireRate || gun.ShotCounter == 0 && gun.NextFire < curTime)
             gun.NextFire = curTime;
 
         var shots = 0;
@@ -263,6 +266,20 @@ public abstract partial class SharedGunSystem : EntitySystem
                 throw new ArgumentOutOfRangeException($"No implemented shooting behavior for {gun.SelectedMode}!");
         }
 
+        var attemptEv = new AttemptShootEvent(user, null);
+        RaiseLocalEvent(gunUid, ref attemptEv);
+
+        if (attemptEv.Cancelled)
+        {
+            if (attemptEv.Message != null)
+            {
+                PopupSystem.PopupClient(attemptEv.Message, gunUid, user);
+            }
+
+            gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
+            return;
+        }
+
         var fromCoordinates = Transform(user).Coordinates;
         // Remove ammo
         var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user);
@@ -279,10 +296,7 @@ public abstract partial class SharedGunSystem : EntitySystem
         // where the gun may be SemiAuto or Burst.
         gun.ShotCounter += shots;
 
-        var attemptEv = new AttemptShootEvent(user);
-        RaiseLocalEvent(gunUid, ref attemptEv);
-
-        if (ev.Ammo.Count <= 0 || attemptEv.Cancelled)
+        if (ev.Ammo.Count <= 0)
         {
             // Play empty gun sounds if relevant
             // If they're firing an existing clip then don't play anything.
@@ -415,7 +429,7 @@ public abstract partial class SharedGunSystem : EntitySystem
 /// <param name="Cancelled">Set this to true if the shot should be cancelled.</param>
 /// <param name="ThrowItems">Set this to true if the ammo shouldn't actually be fired, just thrown.</param>
 [ByRefEvent]
-public record struct AttemptShootEvent(EntityUid User, bool Cancelled = false, bool ThrowItems = false);
+public record struct AttemptShootEvent(EntityUid User, string? Message, bool Cancelled = false, bool ThrowItems = false);
 
 /// <summary>
 ///     Raised directed on the gun after firing.
diff --git a/Content.Shared/Weapons/Ranged/Systems/UseDelayOnShootSystem.cs b/Content.Shared/Weapons/Ranged/Systems/UseDelayOnShootSystem.cs
new file mode 100644 (file)
index 0000000..10593f5
--- /dev/null
@@ -0,0 +1,20 @@
+using Content.Shared.Timing;
+using Content.Shared.Weapons.Ranged.Components;
+
+namespace Content.Shared.Weapons.Ranged.Systems;
+
+public sealed class UseDelayOnShootSystem : EntitySystem
+{
+    [Dependency] private readonly UseDelaySystem _delay = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<UseDelayOnShootComponent, GunShotEvent>(OnUseShoot);
+    }
+
+    private void OnUseShoot(EntityUid uid, UseDelayOnShootComponent component, ref GunShotEvent args)
+    {
+        _delay.BeginDelay(uid);
+    }
+}
index 929856367b1805920a93412bc53fa80230836c5c..ea1ac7e42a752879a8e9705f4b123fa26fafca0a 100644 (file)
@@ -8,6 +8,10 @@ using Content.Shared.Item;
 using Content.Shared.Popups;
 using Content.Shared.Verbs;
 using Content.Shared.Weapons.Melee.Events;
+using Content.Shared.Weapons.Melee.Components;
+using Content.Shared.Weapons.Melee.Events;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Systems;
 using Content.Shared.Wieldable.Components;
 using Robust.Shared.Player;
 
@@ -34,9 +38,32 @@ public sealed class WieldableSystem : EntitySystem
         SubscribeLocalEvent<WieldableComponent, GetVerbsEvent<InteractionVerb>>(AddToggleWieldVerb);
         SubscribeLocalEvent<WieldableComponent, DisarmAttemptEvent>(OnDisarmAttemptEvent);
 
+        SubscribeLocalEvent<MeleeRequiresWieldComponent, AttemptMeleeEvent>(OnMeleeAttempt);
+        SubscribeLocalEvent<GunRequiresWieldComponent, AttemptShootEvent>(OnShootAttempt);
+
         SubscribeLocalEvent<IncreaseDamageOnWieldComponent, MeleeHitEvent>(OnMeleeHit);
     }
 
+    private void OnMeleeAttempt(EntityUid uid, MeleeRequiresWieldComponent component, ref AttemptMeleeEvent args)
+    {
+        if (TryComp<WieldableComponent>(uid, out var wieldable) &&
+            !wieldable.Wielded)
+        {
+            args.Cancelled = true;
+            args.Message = Loc.GetString("wieldable-component-requires", ("item", uid));
+        }
+    }
+
+    private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref AttemptShootEvent args)
+    {
+        if (TryComp<WieldableComponent>(uid, out var wieldable) &&
+            !wieldable.Wielded)
+        {
+            args.Cancelled = true;
+            args.Message = Loc.GetString("wieldable-component-requires", ("item", uid));
+        }
+    }
+
     private void OnDisarmAttemptEvent(EntityUid uid, WieldableComponent component, DisarmAttemptEvent args)
     {
         if (component.Wielded)
diff --git a/Resources/Audio/Weapons/attributions.yml b/Resources/Audio/Weapons/attributions.yml
new file mode 100644 (file)
index 0000000..c7be93a
--- /dev/null
@@ -0,0 +1,4 @@
+- files: ["plasm_cutter.ogg"]
+  license: "CC-BY-SA-3.0"
+  copyright: "Taken from Citadel station."
+  source: "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/5b43cb2545a19957ec6ce3352dceac5e347e77df/sound/weapons/plasma_cutter.ogg"
diff --git a/Resources/Audio/Weapons/plasma_cutter.ogg b/Resources/Audio/Weapons/plasma_cutter.ogg
new file mode 100644 (file)
index 0000000..3698a70
Binary files /dev/null and b/Resources/Audio/Weapons/plasma_cutter.ogg differ
index 30f81f5ca9c7001476beb639706bf60cc262623f..91eee8c2eaf50e3c328b3fcd502a58e9ee2ab7ee 100644 (file)
@@ -9,9 +9,11 @@ wieldable-component-successful-wield-other = { THE($user) } wields { THE($item)
 wieldable-component-failed-wield-other = { THE($user) } unwields { THE($item) }.
 
 wieldable-component-no-hands = You don't have enough hands!
-wieldable-component-not-enough-free-hands = {$number -> 
+wieldable-component-not-enough-free-hands = {$number ->
     [one] You need a free hand to wield { THE($item) }.
     *[other] You need { $number } free hands to wield { THE($item) }.
 }
 wieldable-component-not-in-hands = { CAPITALIZE(THE($item)) } isn't in your hands!
 
+wieldable-component-requires = { CAPITALIZE(THE($item))} must be wielded!
+
index 132131329d32083e1398a0650c7530bfacfe31e0..502bd78b7ad5bb1f6adf1c09bf814a97fb0bbc12 100644 (file)
         isLooped: true
         property: Radius
         enabled: false
-    toggleAction:
-      name: action-name-toggle-light
-      description: action-description-toggle-light
-      icon: { sprite: Objects/Tools/flashlight.rsi, state: flashlight }
-      iconOn: Objects/Tools/flashlight.rsi/flashlight-on.png
-      event: !type:ToggleActionEvent
   - type: ToggleableLightVisuals
     spriteLayer: light
     inhandVisuals:
index 829af331b6b7c15684f5c63f07dd4fb4a819a9bf..472233d3b3eb90bb325a53e94af53e107b08537b 100644 (file)
   parent: BaseBulletHighVelocity
   name: Icicle
   description: Brrrrr.
+  noSpawn: true
   components:
   - type: Sprite
     sprite: Structures/Specific/Anomalies/ice_anom.rsi
index 796383f0fcd938a2c830247838285f2c236c6ce5..b03a2c969228393c345a21c070d5d01ae5ed2523 100644 (file)
   - type: TimedDespawn
     lifetime: 0.4
 
+- type: entity
+  id: BulletCharge
+  name: charge bolt
+  parent: BaseBulletHighVelocity
+  noSpawn: true
+  description: Marks a target for additional damage.
+  components:
+  - type: Sprite
+    noRot: false
+    sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
+    layers:
+      - state: chronobolt
+        shader: unshaded
+  - type: GatheringProjectile
+  - type: DamageMarkerOnCollide
+    whitelist:
+      components:
+        - MobState
+    damage:
+      types:
+        Blunt: 20
+        Slash: 5
+  - type: Projectile
+    impactEffect: BulletImpactEffectKinetic
+    damage:
+      types:
+        Blunt: 0
+  # Short lifespan
+  - type: TimedDespawn
+    lifetime: 0.4
+
 - type: entity
   parent: BaseBullet
   id: AnomalousParticleDelta
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml
new file mode 100644 (file)
index 0000000..0f034a8
--- /dev/null
@@ -0,0 +1,96 @@
+- type: entity
+  name: crusher
+  parent: BaseItem
+  id: BaseWeaponCrusher # Crusher? But I...
+  abstract: true
+  description: An early design of the proto-kinetic accelerator.
+  components:
+  - type: Sharp
+  - type: UnpoweredFlashlight
+    toggleAction:
+      name: action-name-toggle-light
+      description: action-description-toggle-light
+      icon: { sprite: Objects/Tools/flashlight.rsi, state: flashlight }
+      iconOn: Objects/Tools/flashlight.rsi/flashlight-on.png
+      event: !type:ToggleActionEvent
+  - type: PointLight
+    enabled: false
+    radius: 4
+
+- type: entity
+  name: crusher
+  parent: BaseWeaponCrusher
+  id: WeaponCrusher
+  components:
+  - type: Sprite
+    sprite: Objects/Weapons/Melee/crusher.rsi
+    state: icon
+  - type: AmmoCounter
+  - type: UseDelayOnShoot
+  - type: UseDelay
+    delay: 1.9
+  - type: Gun
+    soundGunshot: /Audio/Weapons/plasma_cutter.ogg
+    fireRate: 0.5
+    useKey: false
+  - type: RechargeBasicEntityAmmo
+    rechargeCooldown: 1.5
+    rechargeSound:
+      path: /Audio/Weapons/Guns/MagIn/kinetic_reload.ogg
+  - type: BasicEntityAmmoProvider
+    proto: BulletCharge
+    capacity: 1
+    count: 1
+  - type: MeleeWeapon
+    attackRate: 0.75
+    damage:
+      types:
+        Blunt: 10
+        Slash: 5
+  - type: Wieldable
+  - type: MeleeRequiresWield
+  - type: GunRequiresWield
+  - type: Item
+    size: 150
+  - type: DisarmMalus
+  - type: Tool
+    qualities:
+      - Prying
+
+- type: entity
+  name: crusher dagger
+  parent: BaseWeaponCrusher
+  id: WeaponCrusherDagger
+  description: A scaled down version of a proto-kinetic crusher, usually used in a last ditch scenario.
+  components:
+  - type: Sprite
+    sprite: Objects/Weapons/Melee/crusher_dagger.rsi
+    state: icon
+  - type: MeleeWeapon
+    attackRate: 1.5
+    damage:
+      types:
+        Slash: 6.5
+  - type: Item
+    size: 30
+
+# Like a crusher... but better
+- type: entity
+  name: crusher glaive
+  parent: WeaponCrusher
+  id: WeaponCrusherGlaive
+  description: An early design of the proto-kinetic accelerator, in glaive form.
+  components:
+  - type: UseDelayOnShoot
+  - type: UseDelay
+    delay: 1.9
+  - type: Gun
+    fireRate: 1
+  - type: RechargeBasicEntityAmmo
+    rechargeCooldown: 0.5
+  - type: Sprite
+    sprite: Objects/Weapons/Melee/crusher_glaive.rsi
+  - type: MeleeWeapon
+    attackRate: 1.25
+  - type: Item
+    size: 150
index 1ec9992ee3375cce916e8b2b174104d5a965160a..d4729fa13f75941b9c29492f6b58613e240d1ce8 100644 (file)
@@ -3,6 +3,7 @@
   description: Accelerated particles.
   id: ParticlesProjectile
   parent: BaseBullet
+  noSpawn: true
   components:
     - type: Sprite
       layers:
index 3796775d66ab59895771474a8edc4d629a612f41..d9720e1912cbb4ec825456596c7e0df1ba4d6139 100644 (file)
 - type: Tag
   id: GuideEmbeded
 
-- type: Tag
-  id: GunsDisabled # Allow certain entities to not use guns without complicating the system with an event
-
 - type: Tag
   id: Handcuffs
 
diff --git a/Resources/Textures/Objects/Weapons/Effects/meta.json b/Resources/Textures/Objects/Weapons/Effects/meta.json
new file mode 100644 (file)
index 0000000..408eccd
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "https://github.com/tgstation/tgstation/blob/192e2ce0821c8ed347f3b4164e7d76fe344f4bbf/icons/effects/effects.dmi",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "shield2",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Weapons/Effects/shield2.png b/Resources/Textures/Objects/Weapons/Effects/shield2.png
new file mode 100644 (file)
index 0000000..488dd67
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Effects/shield2.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-lit.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-lit.png
new file mode 100644 (file)
index 0000000..dbd25ec
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-lit.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-uncharged.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-uncharged.png
new file mode 100644 (file)
index 0000000..f78929c
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-uncharged.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon.png
new file mode 100644 (file)
index 0000000..d9b6ca8
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..28eab44
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..dcf6065
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/meta.json
new file mode 100644 (file)
index 0000000..2d91fb4
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/817e7c1f225876b45891e3f06908e6d032f0a8bc/icons/obj/mining.dmi",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "icon"
+    },
+    {
+      "name": "icon-lit"
+    },
+    {
+      "name": "icon-uncharged",
+      "delays": [
+        [
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "inhand-right",
+      "directions": 4
+    },
+    {
+      "name": "wielded-inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "wielded-inhand-right",
+      "directions": 4
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-left.png
new file mode 100644 (file)
index 0000000..6b55e43
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-right.png
new file mode 100644 (file)
index 0000000..b3d449f
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon-lit.png b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon-lit.png
new file mode 100644 (file)
index 0000000..292963f
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon-lit.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon.png
new file mode 100644 (file)
index 0000000..2c1bdcb
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..5d64ccb
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..cce061b
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/meta.json
new file mode 100644 (file)
index 0000000..f076030
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/817e7c1f225876b45891e3f06908e6d032f0a8bc/icons/obj/mining.dmi",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "icon"
+    },
+    {
+      "name": "icon-lit"
+    },
+    {
+      "name": "inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "inhand-right",
+      "directions": 4
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-lit.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-lit.png
new file mode 100644 (file)
index 0000000..e33398a
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-lit.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-uncharged.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-uncharged.png
new file mode 100644 (file)
index 0000000..3929ec0
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-uncharged.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon.png
new file mode 100644 (file)
index 0000000..980af14
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..6d9aab7
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..e2d24c9
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/meta.json
new file mode 100644 (file)
index 0000000..2d91fb4
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/817e7c1f225876b45891e3f06908e6d032f0a8bc/icons/obj/mining.dmi",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "icon"
+    },
+    {
+      "name": "icon-lit"
+    },
+    {
+      "name": "icon-uncharged",
+      "delays": [
+        [
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "inhand-right",
+      "directions": 4
+    },
+    {
+      "name": "wielded-inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "wielded-inhand-right",
+      "directions": 4
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-left.png
new file mode 100644 (file)
index 0000000..ce6c530
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-right.png
new file mode 100644 (file)
index 0000000..84abf80
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-right.png differ