From 6417bb4fa065519827bf652be2d1b7aa5ae8fd7d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 14 May 2023 13:15:18 +1000 Subject: [PATCH] Proto-kinetic crusher (#16277) Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com> --- Content.Client/Markers/MarkerSystem.cs | 61 +++++------ .../Projectiles/ProjectileSystem.cs | 8 -- .../ToggleableLightVisualsComponent.cs | 2 +- .../Weapons/Marker/DamageMarkerSystem.cs | 39 +++++++ .../Weapons/Melee/MeleeWeaponSystem.cs | 11 ++ .../Weapons/Ranged/Systems/GunSystem.cs | 8 +- .../Effects/ProjectileAnomalySystem.cs | 2 +- .../GatheringProjectileComponent.cs | 6 +- .../Gatherable/GatherableSystem.Projectile.cs | 6 +- .../EntitySystems/HandheldLightSystem.cs | 76 -------------- .../Pinpointer/ProximityBeeperSystem.cs | 1 + .../PowerCell/PowerCellSystem.Draw.cs | 91 ++++++++++++++++ Content.Server/PowerCell/PowerCellSystem.cs | 65 ++++-------- .../Projectiles/ProjectileSystem.cs | 6 -- ...ActivatableUIRequiresPowerCellComponent.cs | 1 + Content.Server/Weapons/DamageMarkerSystem.cs | 8 ++ .../Weapons/Ranged/Systems/GunSystem.cs | 15 +-- .../Events/MeleeAttackAttemptEvent.cs | 16 --- .../PowerCell/PowerCellDrawComponent.cs | 28 ++++- .../PowerCell/SharedPowerCellSystem.cs | 2 +- .../Projectiles/ProjectileComponent.cs | 50 +++++---- .../Projectiles/SharedProjectileSystem.cs | 31 ++---- .../Weapons/Marker/DamageMarkerComponent.cs | 38 +++++++ .../Marker/DamageMarkerOnCollideComponent.cs | 30 ++++++ .../Marker/SharedDamageMarkerSystem.cs | 81 +++++++++++++++ .../Components/MeleeRequiresWieldComponent.cs | 12 +++ .../Weapons/Melee/Events/AttackEvent.cs | 3 + .../Weapons/Melee/Events/AttemptMeleeEvent.cs | 7 ++ .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 97 +++++++++++++----- .../Weapons/Ranged/Components/GunComponent.cs | 6 ++ .../Components/GunRequiresWieldComponent.cs | 12 +++ .../Components/UseDelayOnShootComponent.cs | 14 +++ .../Systems/RechargeBasicEntityAmmoSystem.cs | 6 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 58 +++++++---- .../Ranged/Systems/UseDelayOnShootSystem.cs | 20 ++++ Content.Shared/Wieldable/WieldableSystem.cs | 27 +++++ Resources/Audio/Weapons/attributions.yml | 4 + Resources/Audio/Weapons/plasma_cutter.ogg | Bin 0 -> 15881 bytes .../en-US/wieldable/wieldable-component.ftl | 4 +- .../Entities/Objects/Tools/flashlights.yml | 6 -- .../Weapons/Guns/Projectiles/magic.yml | 1 + .../Weapons/Guns/Projectiles/projectiles.yml | 31 ++++++ .../Entities/Objects/Weapons/Melee/mining.yml | 96 +++++++++++++++++ .../Power/Generation/PA/particles.yml | 1 + Resources/Prototypes/tags.yml | 3 - .../Objects/Weapons/Effects/meta.json | 25 +++++ .../Objects/Weapons/Effects/shield2.png | Bin 0 -> 1788 bytes .../Weapons/Melee/crusher.rsi/icon-lit.png | Bin 0 -> 230 bytes .../Melee/crusher.rsi/icon-uncharged.png | Bin 0 -> 198 bytes .../Weapons/Melee/crusher.rsi/icon.png | Bin 0 -> 716 bytes .../Weapons/Melee/crusher.rsi/inhand-left.png | Bin 0 -> 655 bytes .../Melee/crusher.rsi/inhand-right.png | Bin 0 -> 637 bytes .../Weapons/Melee/crusher.rsi/meta.json | 42 ++++++++ .../Melee/crusher.rsi/wielded-inhand-left.png | Bin 0 -> 823 bytes .../crusher.rsi/wielded-inhand-right.png | Bin 0 -> 851 bytes .../Melee/crusher_dagger.rsi/icon-lit.png | Bin 0 -> 185 bytes .../Weapons/Melee/crusher_dagger.rsi/icon.png | Bin 0 -> 353 bytes .../Melee/crusher_dagger.rsi/inhand-left.png | Bin 0 -> 503 bytes .../Melee/crusher_dagger.rsi/inhand-right.png | Bin 0 -> 495 bytes .../Melee/crusher_dagger.rsi/meta.json | 25 +++++ .../Melee/crusher_glaive.rsi/icon-lit.png | Bin 0 -> 190 bytes .../crusher_glaive.rsi/icon-uncharged.png | Bin 0 -> 198 bytes .../Weapons/Melee/crusher_glaive.rsi/icon.png | Bin 0 -> 618 bytes .../Melee/crusher_glaive.rsi/inhand-left.png | Bin 0 -> 576 bytes .../Melee/crusher_glaive.rsi/inhand-right.png | Bin 0 -> 568 bytes .../Melee/crusher_glaive.rsi/meta.json | 42 ++++++++ .../wielded-inhand-left.png | Bin 0 -> 567 bytes .../wielded-inhand-right.png | Bin 0 -> 520 bytes 68 files changed, 919 insertions(+), 305 deletions(-) create mode 100644 Content.Client/Weapons/Marker/DamageMarkerSystem.cs create mode 100644 Content.Server/PowerCell/PowerCellSystem.Draw.cs create mode 100644 Content.Server/Weapons/DamageMarkerSystem.cs delete mode 100644 Content.Shared/Interaction/Events/MeleeAttackAttemptEvent.cs rename {Content.Server => Content.Shared}/PowerCell/PowerCellDrawComponent.cs (56%) create mode 100644 Content.Shared/Weapons/Marker/DamageMarkerComponent.cs create mode 100644 Content.Shared/Weapons/Marker/DamageMarkerOnCollideComponent.cs create mode 100644 Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs create mode 100644 Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs create mode 100644 Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs create mode 100644 Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs create mode 100644 Content.Shared/Weapons/Ranged/Components/UseDelayOnShootComponent.cs create mode 100644 Content.Shared/Weapons/Ranged/Systems/UseDelayOnShootSystem.cs create mode 100644 Resources/Audio/Weapons/attributions.yml create mode 100644 Resources/Audio/Weapons/plasma_cutter.ogg create mode 100644 Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml create mode 100644 Resources/Textures/Objects/Weapons/Effects/meta.json create mode 100644 Resources/Textures/Objects/Weapons/Effects/shield2.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-lit.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon-uncharged.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/meta.json create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher.rsi/wielded-inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon-lit.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_dagger.rsi/meta.json create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-lit.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon-uncharged.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/meta.json create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/wielded-inhand-right.png diff --git a/Content.Client/Markers/MarkerSystem.cs b/Content.Client/Markers/MarkerSystem.cs index bfaf333baa..f12329bd78 100644 --- a/Content.Client/Markers/MarkerSystem.cs +++ b/Content.Client/Markers/MarkerSystem.cs @@ -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(OnStartup); - } + SubscribeLocalEvent(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(); - private void UpdateMarkers() + while (query.MoveNext(out var uid, out var comp)) { - foreach (var markerComponent in EntityManager.EntityQuery(true)) - { - UpdateVisibility(markerComponent); - } + UpdateVisibility(uid); } } } diff --git a/Content.Client/Projectiles/ProjectileSystem.cs b/Content.Client/Projectiles/ProjectileSystem.cs index f5121bbff0..20be44792f 100644 --- a/Content.Client/Projectiles/ProjectileSystem.cs +++ b/Content.Client/Projectiles/ProjectileSystem.cs @@ -14,7 +14,6 @@ public sealed class ProjectileSystem : SharedProjectileSystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnHandleState); SubscribeNetworkEvent(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; - } } diff --git a/Content.Client/Toggleable/ToggleableLightVisualsComponent.cs b/Content.Client/Toggleable/ToggleableLightVisualsComponent.cs index 6c7c3c256d..628726c3c1 100644 --- a/Content.Client/Toggleable/ToggleableLightVisualsComponent.cs +++ b/Content.Client/Toggleable/ToggleableLightVisualsComponent.cs @@ -13,7 +13,7 @@ namespace Content.Client.Toggleable; public sealed class ToggleableLightVisualsComponent : Component { /// - /// 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. /// [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 index 0000000000..43ef216af7 --- /dev/null +++ b/Content.Client/Weapons/Marker/DamageMarkerSystem.cs @@ -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(OnMarkerStartup); + SubscribeLocalEvent(OnMarkerShutdown); + } + + private void OnMarkerStartup(EntityUid uid, DamageMarkerComponent component, ComponentStartup args) + { + if (!_timing.ApplyingState || component.Effect == null || !TryComp(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(uid, out var sprite) || !sprite.LayerMapTryGet(DamageMarkerKey.Key, out var weh)) + return; + + sprite.RemoveLayer(weh); + } + + private enum DamageMarkerKey : byte + { + Key + } +} diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 23271c5478..5fd39c1090 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -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(weaponUid)) + { + return; + } + // We did the click to end the attack but haven't pulled the key up. if (weapon.Attacking) { diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 7d82c98469..191c2b1089 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -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(uid); + if (light.Enabled) + return; + light.NetSyncEnabled = false; light.Enabled = true; light.Color = Color.FromHex("#cc8e2b"); diff --git a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs index 06c2082045..13c58772fe 100644 --- a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs @@ -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); } } diff --git a/Content.Server/Gatherable/Components/GatheringProjectileComponent.cs b/Content.Server/Gatherable/Components/GatheringProjectileComponent.cs index 88388c7d06..0803ee1dde 100644 --- a/Content.Server/Gatherable/Components/GatheringProjectileComponent.cs +++ b/Content.Server/Gatherable/Components/GatheringProjectileComponent.cs @@ -6,5 +6,9 @@ namespace Content.Server.Gatherable.Components; [RegisterComponent] public sealed class GatheringProjectileComponent : Component { - + /// + /// How many more times we can gather. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("amount")] + public int Amount = 1; } diff --git a/Content.Server/Gatherable/GatherableSystem.Projectile.cs b/Content.Server/Gatherable/GatherableSystem.Projectile.cs index 62417bc7b8..b8f30a466c 100644 --- a/Content.Server/Gatherable/GatherableSystem.Projectile.cs +++ b/Content.Server/Gatherable/GatherableSystem.Projectile.cs @@ -17,12 +17,16 @@ public sealed partial class GatherableSystem { if (!args.OtherFixture.Hard || args.OurFixture.ID != SharedProjectileSystem.ProjectileFixture || + component.Amount <= 0 || !TryComp(args.OtherEntity, out var gatherable)) { return; } Gather(args.OtherEntity, uid, gatherable); - QueueDel(uid); + component.Amount--; + + if (component.Amount <= 0) + QueueDel(uid); } } diff --git a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs index 0902602953..303e2a2fd7 100644 --- a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs +++ b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs @@ -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(OnRemove); SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnExamine); @@ -45,41 +38,7 @@ namespace Content.Server.Light.EntitySystems SubscribeLocalEvent(OnActivate); - SubscribeLocalEvent(OnGetActions); SubscribeLocalEvent(OnToggleAction); - - SubscribeLocalEvent(OnEntInserted); - SubscribeLocalEvent(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(); - - 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 args) { if (!args.CanAccess || !args.CanInteract) diff --git a/Content.Server/Pinpointer/ProximityBeeperSystem.cs b/Content.Server/Pinpointer/ProximityBeeperSystem.cs index c0cf6cd605..0751ab50bf 100644 --- a/Content.Server/Pinpointer/ProximityBeeperSystem.cs +++ b/Content.Server/Pinpointer/ProximityBeeperSystem.cs @@ -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 index 0000000000..9d73138346 --- /dev/null +++ b/Content.Server/PowerCell/PowerCellSystem.Draw.cs @@ -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(); + + 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); + } + } +} diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index 6c6cd92d4c..7719773d9d 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -19,7 +19,10 @@ using Robust.Shared.Timing; namespace Content.Server.PowerCell; -public sealed class PowerCellSystem : SharedPowerCellSystem +/// +/// Handles Power cells +/// +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(OnChargeChanged); SubscribeLocalEvent(OnSolutionChange); SubscribeLocalEvent(OnRejuvenate); - SubscribeLocalEvent(OnCellExamined); - SubscribeLocalEvent(OnCellSlotExamined); SubscribeLocalEvent(OnUnpaused); + SubscribeLocalEvent(OnDrawChargeChanged); + SubscribeLocalEvent(OnDrawCellChanged); // funny + SubscribeLocalEvent(OnCellSlotExamined); SubscribeLocalEvent(OnSlotMicrowaved); - SubscribeLocalEvent(OnMicrowaved); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - var query = EntityQueryEnumerator(); - 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(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; } diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index a9b02cdb8e..af517a0231 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -26,12 +26,6 @@ public sealed class ProjectileSystem : SharedProjectileSystem { base.Initialize(); SubscribeLocalEvent(OnStartCollide); - SubscribeLocalEvent(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) diff --git a/Content.Server/UserInterface/ActivatableUIRequiresPowerCellComponent.cs b/Content.Server/UserInterface/ActivatableUIRequiresPowerCellComponent.cs index 14429dd455..df7f91a1c1 100644 --- a/Content.Server/UserInterface/ActivatableUIRequiresPowerCellComponent.cs +++ b/Content.Server/UserInterface/ActivatableUIRequiresPowerCellComponent.cs @@ -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 index 0000000000..be65bca168 --- /dev/null +++ b/Content.Server/Weapons/DamageMarkerSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Weapons.Marker; + +namespace Content.Server.Weapons; + +public sealed class DamageMarkerSystem : SharedDamageMarkerSystem +{ + +} diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 995c1ffbc6..c872a8d975 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -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(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(uid); Physics.SetBodyStatus(physics, BodyStatus.InAir); @@ -293,6 +293,7 @@ public sealed partial class GunSystem : SharedGunSystem { var projectile = EnsureComp(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 index 01083e8987..0000000000 --- a/Content.Shared/Interaction/Events/MeleeAttackAttemptEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Content.Shared.Interaction.Events; - -/// -/// Raised on directed a weapon when being used in a melee attack. -/// -[ByRefEvent] -public struct MeleeAttackAttemptEvent -{ - public bool Cancelled = false; - public readonly EntityUid User; - - public MeleeAttackAttemptEvent(EntityUid user) - { - User = user; - } -} diff --git a/Content.Server/PowerCell/PowerCellDrawComponent.cs b/Content.Shared/PowerCell/PowerCellDrawComponent.cs similarity index 56% rename from Content.Server/PowerCell/PowerCellDrawComponent.cs rename to Content.Shared/PowerCell/PowerCellDrawComponent.cs index 213b414098..6963326b70 100644 --- a/Content.Server/PowerCell/PowerCellDrawComponent.cs +++ b/Content.Shared/PowerCell/PowerCellDrawComponent.cs @@ -1,15 +1,35 @@ +using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.PowerCell; +namespace Content.Shared.PowerCell; /// /// Indicates that the entity's ActivatableUI requires power or else it closes. /// -[RegisterComponent, Access(typeof(PowerCellSystem))] -public sealed class PowerCellDrawComponent : Component +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PowerCellDrawComponent : Component { + #region Prediction + + /// + /// Whether there is any charge available to draw. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("canDraw"), AutoNetworkedField] + public bool CanDraw; + + /// + /// Whether there is sufficient charge to use. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("canUse"), AutoNetworkedField] + public bool CanUse; + + #endregion + + /// + /// Is this power cell currently drawing power every tick. + /// [ViewVariables(VVAccess.ReadWrite), DataField("enabled")] - public bool Enabled; + public bool Drawing; /// /// How much the entity draws while the UI is open. diff --git a/Content.Shared/PowerCell/SharedPowerCellSystem.cs b/Content.Shared/PowerCell/SharedPowerCellSystem.cs index 5c18152a23..57af440360 100644 --- a/Content.Shared/PowerCell/SharedPowerCellSystem.cs +++ b/Content.Shared/PowerCell/SharedPowerCellSystem.cs @@ -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. diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs index bab2298a31..1e77e3954c 100644 --- a/Content.Shared/Projectiles/ProjectileComponent.cs +++ b/Content.Shared/Projectiles/ProjectileComponent.cs @@ -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))] - public string? ImpactEffect; + [ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string? ImpactEffect; + + /// + /// User that shot this projectile. + /// + [DataField("shooter"), AutoNetworkedField] public EntityUid Shooter; - public EntityUid Shooter { get; set; } + /// + /// Weapon used to shoot. + /// + [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; } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 53a271f34c..08b974cfda 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -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 index 0000000000..ef3b712f60 --- /dev/null +++ b/Content.Shared/Weapons/Marker/DamageMarkerComponent.cs @@ -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; + +/// +/// Marks an entity to take additional damage +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedDamageMarkerSystem))] +public sealed partial class DamageMarkerComponent : Component +{ + /// + /// Sprite to apply to the entity while damagemarker is applied. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("effect")] + public SpriteSpecifier.Rsi? Effect = new(new ResPath("/Textures/Objects/Weapons/Effects"), "shield2"); + + /// + /// Sound to play when the damage marker is procced. + /// + [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(); + + /// + /// Entity that marked this entity for a damage surplus. + /// + [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 index 0000000000..e708dd7a2b --- /dev/null +++ b/Content.Shared/Weapons/Marker/DamageMarkerOnCollideComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Damage; +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Marker; + +/// +/// Applies when colliding with an entity. +/// +[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); + + /// + /// Additional damage to be applied. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("damage")] + public DamageSpecifier Damage = new(); + + /// + /// How many more times we can apply it. + /// + [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 index 0000000000..0f6fc34aca --- /dev/null +++ b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs @@ -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(OnMarkerCollide); + SubscribeLocalEvent(OnMarkerUnpaused); + SubscribeLocalEvent(OnMarkerAttacked); + } + + private void OnMarkerAttacked(EntityUid uid, DamageMarkerComponent component, AttackedEvent args) + { + if (component.Marker != args.Used) + return; + + args.BonusDamage += component.Damage; + RemCompDeferred(uid); + _audio.PlayPredicted(component.Sound, uid, args.User); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.EndTime > _timing.CurTime) + continue; + + RemCompDeferred(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(uid, out var projectile) || + !projectile.Weapon.IsValid()) + { + return; + } + + // Markers are exclusive, deal with it. + var marker = EnsureComp(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 index 0000000000..6cf12d58f2 --- /dev/null +++ b/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Melee.Components; + +/// +/// Indicates that this meleeweapon requires wielding to be useable. +/// +[RegisterComponent, NetworkedComponent] +public sealed class MeleeRequiresWieldComponent : Component +{ + +} diff --git a/Content.Shared/Weapons/Melee/Events/AttackEvent.cs b/Content.Shared/Weapons/Melee/Events/AttackEvent.cs index fdee5d507b..cb731258d9 100644 --- a/Content.Shared/Weapons/Melee/Events/AttackEvent.cs +++ b/Content.Shared/Weapons/Melee/Events/AttackEvent.cs @@ -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 /// 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 index 0000000000..2800e3b34d --- /dev/null +++ b/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Weapons.Melee.Events; + +/// +/// Raised directed on a weapon when attempt a melee attack. +/// +[ByRefEvent] +public record struct AttemptMeleeEvent(bool Cancelled, string? Message); diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index faa6119840..124023dc13 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -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(OnHandleState); SubscribeLocalEvent(OnMeleeDropped); SubscribeLocalEvent(OnMeleeSelected); + SubscribeLocalEvent(OnMeleeShot); SubscribeAllEvent(OnHeavyAttack); SubscribeAllEvent(OnLightAttack); @@ -89,6 +92,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem #endif } + private void OnMeleeShot(EntityUid uid, MeleeWeaponComponent component, ref GunShotEvent args) + { + if (!TryComp(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); } /// @@ -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 { diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 0ccc839902..df897bcc0f 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -79,6 +79,12 @@ public partial class GunComponent : Component #endregion + /// + /// Whether this gun is shot via the use key or the alt-use key. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("useKey"), AutoNetworkedField] + public bool UseKey = true; + /// /// Where the gun is being requested to shoot. /// diff --git a/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs new file mode 100644 index 0000000000..b0aca3af14 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Ranged.Components; + +/// +/// Indicates that this gun requires wielding to be useable. +/// +[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 index 0000000000..c2b0950502 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/UseDelayOnShootComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Timing; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Ranged.Components; + +/// +/// Applies UseDelay whenever the entity shoots. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(UseDelayOnShootSystem))] +public sealed class UseDelayOnShootComponent : Component +{ + +} diff --git a/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs b/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs index 9b72136372..ded4ce34a2 100644 --- a/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs @@ -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) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 4768848972..89068db8ae 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -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(OnShootRequest); SubscribeAllEvent(OnStopShootRequest); - SubscribeLocalEvent(OnGunMeleeAttempt); + SubscribeLocalEvent(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(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(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 /// Set this to true if the shot should be cancelled. /// Set this to true if the ammo shouldn't actually be fired, just thrown. [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); /// /// 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 index 0000000000..10593f56df --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Systems/UseDelayOnShootSystem.cs @@ -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(OnUseShoot); + } + + private void OnUseShoot(EntityUid uid, UseDelayOnShootComponent component, ref GunShotEvent args) + { + _delay.BeginDelay(uid); + } +} diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index 929856367b..ea1ac7e42a 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -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>(AddToggleWieldVerb); SubscribeLocalEvent(OnDisarmAttemptEvent); + SubscribeLocalEvent(OnMeleeAttempt); + SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnMeleeHit); } + private void OnMeleeAttempt(EntityUid uid, MeleeRequiresWieldComponent component, ref AttemptMeleeEvent args) + { + if (TryComp(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(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 index 0000000000..c7be93af11 --- /dev/null +++ b/Resources/Audio/Weapons/attributions.yml @@ -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 index 0000000000000000000000000000000000000000..3698a70c4f331a6789af58dbe7e15d524fa874fc GIT binary patch literal 15881 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 +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! + diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml index 132131329d..502bd78b7a 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml @@ -33,12 +33,6 @@ 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: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml index 829af331b6..472233d3b3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml @@ -130,6 +130,7 @@ parent: BaseBulletHighVelocity name: Icicle description: Brrrrr. + noSpawn: true components: - type: Sprite sprite: Structures/Specific/Anomalies/ice_anom.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 796383f0fc..b03a2c9692 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -309,6 +309,37 @@ - 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 index 0000000000..0f034a8df3 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml index 1ec9992ee3..d4729fa13f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml @@ -3,6 +3,7 @@ description: Accelerated particles. id: ParticlesProjectile parent: BaseBullet + noSpawn: true components: - type: Sprite layers: diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 3796775d66..d9720e1912 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -314,9 +314,6 @@ - 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 index 0000000000..408eccd3f8 --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Effects/meta.json @@ -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 index 0000000000000000000000000000000000000000..488dd67f1f72d0c4187861ea0b2be0761fafc40b GIT binary patch literal 1788 zcmVJ2jo6sA*0SRKg*dI7qf+>*WN3 z_s=kpc*~M4zog{9Y2Z*83~;~Kf4^L&t~eLJ&;Wq+XZlJX|NKAQu798ZFZhN6Mk#~l zC$R%RS#Kl1zyOZ|aiwqB1^9n>?IMg<2?c*0JMhoSXN{obuNYvpt}=-e2&5YOx8 zd$$&$RYbv0apb_)F0J)`zh1)mWf0V#F#xhUrT7}~eW@H(Yb0AeR5~XMx@K#oKOy`S zPu>vt9>MSv1}KzoZ2`E9tu?fiz$#xN2kM>3MfmuW43MYR`=^QMjTC=v3=mbiIv~}B zmBM=Omq4~?8CveSKxTl=`l6fDU?ddm)^3I5fbUh{vkW~yx*yh6VAxWR1V1opa+?gGnEiBz za%K{Sd^@7LJyC&q3`j(|A@=soIam5Vs)0!BW36|{dB9PvFqZ)uBvKB_-6Rh9*;4jf z&VrQw*9ETJ+|Y;hfW2H!4SVuO*Vko$P6Kguw&GBLW_Cr&P~&b4!mWkaV?O*XUkf;V z{%_ge^Iy0XfcUEjX?+I%(3TJQ)ja?TviDT&?X-n8WgrWjBDD|SaYM~+f9CqFYLid|8Qzv6<{ z8*p!!K~1o-0MHs{v_eL{p)n#t1DpI9Z^kcNh=GDo?Bk=G z{r(BAw4A?s?HlkL_JVpxAAc7A$PYLeBR}9^eph|~i5>F;&XAvA$q%UG1M&kR`8D|g zdi2W=SkT;?>CE3mP=0_9arprxnmd%8`I~U&2XK&Y1wcW3@%cWyD8F_VB;5^+uKmpTb{d!w`ejJ4S-lI?I{zaz`=Icd}ACRB<8sp+;lzZ~J_1t$PK~yy`WxzdZGX! zJ)tE}`e&`92_eC`+OD@;Z{&M{SX9oiCZl%`&IJa_2Te<)gV{e@d5G^3yt3?e9uTRt z+z7o4LK%BC$_-xQ;c!Ps3&ZthgenLsXRltd^3JeLJ_xuBaHKEJE54%b}}?F zb7Y*NvJtb9LYBs?Ynu8l$j}(Jj&G)gCzT%{f~%$3@&iP(jHHaIwq9`LCuH*EyzJHu z%Bi@loKxEc6_r+Y^XR$wCJ!TrcD;bW%zD&~Qm{zfc+_LS3vBD3qY&w+`;o5cOJq00bP|YHp=|ZeWYgXQ7VQ z2fkOs?=b@ULYW^x0dD)&et$W5R^L6*$`4pTSYNMbV|;!Ms#e*7UjBOwL%x>4{8lpf3fdyu}C`)c_C)oa6W zUjC6Ea4`SK4>%Zam>+O1yh(n*d7;V=Xr2BqzlX=?pBrKM0puFN6`%hZc+vcT&w(mG zfLjfVR>sr^0UQj~({(!KMk6{Fa!Ub^;;*;hD z3>TyWIR-=>P7kz!@&hCfCQYC7-+lQ3p9|CjBoPMl-#wq*e0I3rA8_rH1NKqyygwj? z08YaYc0@%-zg)P-yTJ79==6Ec{c=21bzl-0K#+2OqpoY*Gq8d+fOKz160S04Nj%8> zfJlW&pP~Gz=zb`_Xa3eZg4_%0p{%i8O{2&UxEfBvTCE&?Kim(3G(R8)L-_?pVAjD9 zd4c46EaktG3V`#jl2y8JS+zkHgYUVZ2$7+gu_im-y<51J`+sBX8k_at9pO?y~~e)8d@0V zP5D(A_cE_En(0UX{2r$_ORh5P!0*SFczKrJAdgE)BlY(TNvHW9%kIk2$zoF Z`@yicK)_eWbe9)M(9_k=Wt~$(697v_Ks*2d literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..d9b6ca8157b74b5484b09fb1baecec7238373cd2 GIT binary patch literal 716 zcmV;-0yF)IP)^4h1;~gwhnmp`ejMpdh;2 z%}s|wHf=W-TZcfvLdhU*mHq*iOvO_O2rVW^TuwI|BsXXSQk>5FzVSI<-@J*J41*VlnZ*X_^tkd@C3`TTt-vWN&97y0#2J7Iy1a#MX8IFjlr?MvjDAEq>O91d> zGU;9C+l3_<%=_;8TuT9U+99D1olb%}j8=eeCix!E54_uG1*YQUUe||dz1Y|e3$7K& zOdAg1gT^zmvITK24-4KTAj7Y3r9y?@5>Vl|nsqZ&T?yca#NPo7+-YCoo&aaPI84_5 yLEqp_0$6V1Y<6Pyi+>~F=WG|60RNi3k6Z!1s+~CXx^rLv0000F zuUPAy97`kTtg-ka8sz`M%R{yP%-)Fw=lETGOzym&E}ehK(mkP%gQ3`kVTLl(0U1Gt zXB`X%?ko+KN(^TN8Ljw&bj0S)HZu6blym!>qO*#SfdtPSx5W?H>|*Pfp9)o(8M!`^ zypXkZ&dr>*XUApMr)P7XxWaOwm@{G;>*~9%OFwhpQ3_TO3s`^s?)Q%sJw{C`i#WxM zsMW^%wfA^#xyQ*iccHIF_=Oiw??~>|zgWGw*uUKM`L9aVRiB zbl$eydv~opx9a)^)r;K_{UmTu^pgP#N@!pP)bS36%YT`y`tw~Fc>8{_wrT8~Um9BK z^F>)g(4jfI?p$GErPEg@cBzYZ`x9IomKOK7m(7!6_mPRqwr>(-yrP~E`7`%qjC$f< zjzUAFhI11-Kg2ct5)4}{y6Bszgx~}I^{Y2zE|OQA#t^!nKdS1FYSG=$*tvf>Tv8bC z{i?mvcu2tNm!QZ(rbXA@|84eeuKgwW=RUV+!HZwDLS)z4*}Q$iB}St=AX literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..dcf606531ba187e61220fa6fa327c4490e1d29fc GIT binary patch literal 637 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|S%3 zr;B4q#hkaZ_GcXm5IJUlP`zNnq}YjG2UkXCYjU)1b3SI*7@}G7<{Ep!ipdw27icso zOxaadB0YY8H!<`6Cg{cKsEk9*)uRpzT62FwmOrJ2bZ|(QLn;-aD^DZuHYt(`kRmLq7%}?w< zbJ2QVRek*7)!LIM9lTpUx5b|E-o)#z;f80w=vGf@w>$j8ul>*8(v$lv><`4|%SqYk z9&G;h*?jtMFA2N3Hx6HC-K8I<=O6xsrS`lZym}Cp|ZET;-V~XTo_vpMh2K z3^)TKM_1wHyv8N3^K}(g&STulch!Aq&$`wdH=`UT*)p#UJ3Y^DdB(o8A8OusIQ-dv z52)(pJjP${yKcR+_*}yr{_%Rh%7gvYY!eH1GyQjZ<5a+ zP%CEr%Fxn#k;|j_e8W0+g`NY!3uZr>*QoJZ59*&k3=L8i5*n{>UI(Tp22WQ%mvv4F FO#mFS8wLOX literal 0 HcmV?d00001 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 index 0000000000..2d91fb460b --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Melee/crusher.rsi/meta.json @@ -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 index 0000000000000000000000000000000000000000..6b55e43f72e5bc32ecda58186c6fc39ada252bcc GIT binary patch literal 823 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|V1b zPZ!6KiaBp*9qbkglsUfNDWK$Ln1_p%M7Ln)8qwX`gT#7TGY&RS`OUs%EvuyW1>J9A z5>Grc^jYUxRjydesyK?8%WK+zHjlq zpNG>;_3K_Rc+1(J=V zY|b#}-s<#Ms0Bx$B8T{aUjAMer8jQ^6aGGapEpUoL+Wtc znU#3%&)enie!E<+6v^S1_;#j2&YJgP-~;PlX{9gr6n?VW$N8hRU`dl)?~&%696Nuq zyJRlea3Rj7BH)B#KwuN2XI_)XVpXNc7CEn|Ac2ohT#waCr&-U=n|-$|ywh0z#+yGk z=9<5^HlCe&zeUl~i@~E$h+)!VmWC%9j0)&nGe)6wzfC)D2B+=%PZ@hjGW}&*8@n ziZ-sfnRCl!wQg+UQFZCxQ|x)9HqPGJy<^XZPdAu9 zq}E9B=LM`{&Jxy^*G%qA;BuI`aph+n(?ElKso7fJ-`~r+CH1;&_qNT!cNMmlTPqau zO0To_4c=O~p3OeuzvSEb{%06fyAf5e#%^RAvrpIa%dzNK})dqMc&bZtBPw+&g{*GCU&2t6kUvMlE*&R21 z?rT*8ofbI}$J<~3sQp;)^uqCG==wtwWK9LAW2T4n092wDcTKVPg;A+8Xp0 zEGkSO*3R1o(CIe(|}`G8-+G5k1IzzSG_{}Tv;IX@h-k(i63 z;I8YA-ra9h0O8YV29#3nc^(i#dY>^SAmDLK0pW}>@O}R~04vDsa4qRKH3y$wU z3`6KDptb~JYP*_}occS9ngyhtn0!)Xd zHKq1#NNZyDzR~j6_;cK(nffQ1CZs=`JVQV&++ZH_feJo`$CeB|&l$j+iwu`c)}G!p z=Y#3Bza2~t&f>0(DxMD4bxto*WMNyUXUhHUD`-roFyd%h@)> zp$y-@+XVS7jShld+@I-ReLVqmSDr) z@KJ{0STTddc}8>&$Is(Q>kr-L+9myD>W>MxvktP}<>mAJY|Wy+Rr*cQG?POo-?H7l z6f_Xd~&*EUQOUqufNv5`L zuk_b#*Hzti)bEik?K_p-a7=FhTHP-%&VG!n7rW8^?7i9-%g1XNze#_}*`d*(hzLYr ci2h}KyYJmSfj>-pficP8>FVdQ&MBb@0KZ7qIsgCw literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..cce061bab2933eef43d0399aa85e5c6a4a3fe57d GIT binary patch literal 495 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|Vni zPZ!6KiaBp**?Jvy5NSQ%oO8%-Do14F(zP6nr@u_wmUrlsF9&1w!DFHy`=KLT!h*-T9r|oTjDj*k_w7_kQ(NF9~jao_Q=!7jN#Kyi9AO z)|2+or`D&QZ`b=0QIYsXBgab6#cluI5S2eNYWG>M3B=8?_S?&Y=}sg^H?#`w5_{-- zpuV|9?|QZ1h4uSye-U(QDrRi|`h91M-dRi058;YMT8gR`!Vk3eC{AHfXNqYzZ<&yw zdmy~RIligiYwJ`7$qWCE##b~?EGR90w z;uumf=k2A9ybT6CE*E2tI4BtLTO05>r!LjV)NnX7X(Iou;>jY@{%PB&KH9SF-Q$}@ z>rP+h<1IMWXcAp5a=^5%C;um3K|eF=wK|Oh(){cdj~_DR&tX`xfv=CR!kCFep@9KL c9C+Wrx7k-*#E+A8HPCJbPgg&ebxsLQ06PUcJ^%m! literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..3929ec047e8eb232acba93523813e3583a26912a GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^3P9|@!3HF&`%2dVDb50q$YKTtz9S&aI8~cZ8Yo!d z>EaktG3V`#jl2y8JS+zkHgYUVZ2$7+gu_im-y<51Jrhm@&H8<8>eJuXs-t9p8X61^ zOb)U?r+s&Ao;Sn)kK3<4+-qt{an^LB{Ts5yO}~z literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..980af14aa86c025fc1be958d55d6902884cca2ab GIT binary patch literal 618 zcmV-w0+s!VP)+s?~ z1Q=>{r2>57nQgbOoZxT~X#s*qA`!s+WKT33oX$O`{n7$;xS4Xfj4qGhQUX@E2yHm= z%-z6S3W!->sZ?xrw@@ho>&nC7FqfeAdkJqUAb#HoOi=}IDv%rjW)cI~$k&cl!5sxY zzF`KzgKQA`>H(kas^E_Vtnl4jZP2KM+XNcn$LCcPE^n2>>jLq3+_>tZ4R_-!ye=TJ zkspA5Ui1zANPu29`g4Ix%`g6gK;yLYRY21;-&)?7|DcBzKqjLn$N&HU07*qoM6N<$ Ef=p@_qW}N^ literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..6d9aab74206ef2af097b96ba255ca8bd225b14f3 GIT binary patch literal 576 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|VnH zPZ!6KiaBp*o%cHIAkgmq;ejyAN!ugI@eWK*jso#Z6h8WzOyAQneO}8x$91ad&G{S4 zyu!W-yj^C!`EA*==1Kp2fB)Wf^UutkY!6p32=s9=pimq?Ka}k@O^u9mJjBx9a5XFS z);8ZTrju$DHnk`)TKp+A&X}bZ$$3!uz|;Vd)|bESG&xmfH%v9K^YLQ3nP(8nx#T#b z<~yOA{4H_wxj+9itTzb>O*Na%yE`C^QR2~SwcEO%BNIOqM^1B{ct9&bqU~?)#)yjc zjSqLPYnIr+RiJk0?~@7-rhR!!kIr&gJclPy!p7}Ke#Gs1yR-_GTQ?s_WZ4T{%m1GC z_b0D~kszSo5;iszrfRoV4GY_9^-p+H6j z8BqpIYK{ieieDTSzXWfvCS09SRXZg#BaHFN!@FODJQIW)rXMVfylxtKcEVzj^GXc$ zcZznZ@a8XE=d_vQ0at#Z>Khi8wcV}^=@s2mH_Ya4x^`GeA#6Uw(eE!B1vs8RVtF8^ zalZIN?wT2@2d=#j&S?0Qd*XxGgKO{4OGdYSf9qwb@!-nW-^o8#izmL?%g#DyS6#hM x@LrDKid-g5*7A=(?}~31pV1ABoDT61%&+gsBy#kg{Rd1E44$rjF6*2UngEgz_AdYc literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..e2d24c9ee8af39cd2379d386af1a2122da0879fa GIT binary patch literal 568 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|VnL zPZ!6KiaBp*`DZmdh#cGRAa2_z@_mwCaifw*i_=S2Ce^*3kJI!n`G&FPKQP;|?u(e2 zztO>EStjQ^((nCle8NAo^M1aJddfZC8OlrtWCR(WbubvXvou&LF`N-(47R@U{`=LU zoiPUj)i@*8Ul)~mp2cX_qP|XO!Qp%}=^Zscg%`8B1U~VtNSeaHHD9IE{9)%orWzi9 z9wtN0ib?D#2|8RDISk%#5%bax^ z^2+@C%PT^cmOnH-?(=o(hU)qiW%KVZkgm~vP_cady!Za+_qM29NO=9>%e_plA2NGR zR{H&PW&wICp^t;1*o6VjzsFc-tP-EG`rUyQ440#yU6~(rY86W|Q_fZH8`Bb88hUk$ zU$rlDoVBd<|F(ZC=jj}Hwr=CJf|^X%Ln(~gwx5|Tlu>@mWHRfSK>Y=(H*UOBl%(# zl-kb&mX&SsV(jUMX3?q`=_m>gTe~DWM4f Dl|1t* literal 0 HcmV?d00001 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 index 0000000000..2d91fb460b --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Melee/crusher_glaive.rsi/meta.json @@ -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 index 0000000000000000000000000000000000000000..ce6c530f30cbc37d39d8a17ab069a3a9c60f73a4 GIT binary patch literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|Vnr zPZ!6KiaBp*?e{wrAacxJnB6=!MImolqJr92MxH*sJ{xu^aruKsHMp7AA9!&n#l)_` zmxnpgHR;tO=@?5D@!M6=JEz$nF0bQ$yZ1uz`SsJwEYhW)tYDb1n8~4!i@{Qh0gclT z%AR2&b<8ww{q>7kTLV^K-L}DT4%4A#+v4KVmd&T9UwM>cG}SA$Oz!x{nsa?$tM*>W zSGlOvc6edt_S+IXY%4#WOli895fbJ1|J{+8`}-87>Mhql6)C9N)Bo@B@%4p~Q~s_A zTmAX-lL!HpV|p=tPxYQFmdCT4C=8ntqhG!DKgUEHmH8_fp6>}h@VD7IposBu@^zax zNpHU|_Y~7J&{XtXetD+)yxzCYaeC9H+A!CMM}LutICg&lM=f`Qj3`4<2*Zg$Mg@0P zJe)aOrybvY_g&QKhuufwW;LX3&Ri0cxBAGm{ihzEcHvsNZ=*N2!0AiU0C?=X7Mh$H&4g%w5-3G@sqDCQ*Qq9UqAnbZBMYZcUkl{ zcgGLCZ`BW6G?}OyuYXDS!Ra--lmE=Iet)@t!cp%7@n_j)FY@7U+{fmK2!Ch&2hyK+ W9#3a}tRDhQ2@IaDelF{r5}E*3Z1E!i literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..84abf8014872d68e3dda5c27c3762dd628caffae GIT binary patch literal 520 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|VnO zPZ!6KiaBp*?e#hwAklW;N$zN=eHG@SM@tXy0keB`Kif#BOq(wXgkP9NErE-hp+ z*9;PO-=4hRDQnjJ$xB`+y#MXsd2agW&re?}p8m-+L77p(oynn(r9p<1!BU8!NQL2v z2g8Yp3?K>h2)*ffCQ^wTBc8~ed;U3Wm#qX(%su&i@W5{ zuP&5-_;TXz-Ij;1el31$m>O};^<&-s{)YuWHZ$u?NZlH>)1q&?XUXo^?V0Ky7wv21 zJTvcJVP#zoQAt7!d*SuhsBPR`a(%}iPxDRsesam|>~-;hIu8ql z%>Rk=OHMwOs{j0D$*WVpZtZ42*_-e5$M}-v?|Wx2xCTX5U-3~h|LUzWmisSDv|bCo$=Lhu)>7*QZ(?ktjT1N{mCAfKl>PbD z==^cb-rrL%Zds?Gejr>wAtjrwW-Fub%FSh0WqiwHAM&lR3jTk3R^zIz_0qL0j)+iw c!CJ#?%5Yd}k#M&xFlre*UHx3vIVCg!0Kwwl6#xJL literal 0 HcmV?d00001 -- 2.51.2