diff --git a/Content.Client/_White/Guns/Stretched/StretchedVisualizerSystem.cs b/Content.Client/_White/Guns/Stretched/StretchedVisualizerSystem.cs new file mode 100644 index 0000000000..d6e094e5ac --- /dev/null +++ b/Content.Client/_White/Guns/Stretched/StretchedVisualizerSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared._White.Guns.Stretched; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Client.GameObjects; + +namespace Content.Client._White.Guns.Stretched; + +public sealed class StretchedVisualizerSystem : VisualizerSystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + + protected override void OnAppearanceChange(EntityUid uid, StretchedVisualsComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + _appearance.TryGetData(uid, StretchedVisuals.Stretched, out var stretched, args.Component); + _appearance.TryGetData(uid, AmmoVisuals.HasAmmo, out var hasAmmo, args.Component); + + // StretchedState: Weapon is stretched and ready to fire + // LoadedState: Weapon is loaded but not stretched + // UnstrungState: Weapon is neither stretched nor loaded + args.Sprite.LayerSetState(StretchedVisuals.Layer, stretched ? component.StretchedState : hasAmmo ? component.LoadedState : component.UnstrungState); + } +} diff --git a/Content.Client/_White/Guns/Stretched/StretchedVisualsComponent.cs b/Content.Client/_White/Guns/Stretched/StretchedVisualsComponent.cs new file mode 100644 index 0000000000..97b1e6865e --- /dev/null +++ b/Content.Client/_White/Guns/Stretched/StretchedVisualsComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Client._White.Guns.Stretched; + +[RegisterComponent, Access(typeof(StretchedVisualizerSystem))] +public sealed partial class StretchedVisualsComponent : Component +{ + [DataField(required: true)] + public string LoadedState = string.Empty; + + [DataField(required: true)] + public string StretchedState = string.Empty; + + [DataField(required: true)] + public string UnstrungState = string.Empty; +} diff --git a/Content.Server/Power/EntitySystems/BatterySystem.cs b/Content.Server/Power/EntitySystems/BatterySystem.cs index cbe61f6671..0567a2222b 100644 --- a/Content.Server/Power/EntitySystems/BatterySystem.cs +++ b/Content.Server/Power/EntitySystems/BatterySystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Cargo.Systems; using Content.Server.Emp; using Content.Shared.Emp; @@ -5,6 +6,7 @@ using Content.Shared.Examine; using Content.Shared.Rejuvenate; using JetBrains.Annotations; +using Robust.Shared.Containers; using Robust.Shared.Utility; namespace Content.Server.Power.EntitySystems @@ -12,6 +14,8 @@ namespace Content.Server.Power.EntitySystems [UsedImplicitly] public sealed class BatterySystem : EntitySystem { + [Dependency] private readonly SharedContainerSystem _containers = default!; // WD EDIT + public override void Initialize() { base.Initialize(); @@ -181,5 +185,33 @@ public bool IsFull(EntityUid uid, BatteryComponent? battery = null) return battery.CurrentCharge / battery.MaxCharge >= 0.99f; } + + // WD EDIT START + public bool TryGetBatteryComponent(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, + [NotNullWhen(true)] out EntityUid? batteryUid) + { + if (TryComp(uid, out battery)) + { + batteryUid = uid; + return true; + } + + if (!_containers.TryGetContainer(uid, "cell_slot", out var container) + || container is not ContainerSlot slot) + { + battery = null; + batteryUid = null; + return false; + } + + batteryUid = slot.ContainedEntity; + + if (batteryUid != null) + return TryComp(batteryUid, out battery); + + battery = null; + return false; + } + // WD EDIT END } } diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 0061b16e47..5c49ce68b5 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -1,10 +1,18 @@ using Content.Server.Administration.Logs; using Content.Server.Effects; +using Content.Server.Hands.Systems; using Content.Server.Weapons.Ranged.Systems; +using Content.Shared._White.Penetrated; +using Content.Shared._White.Projectile; using Content.Shared.Camera; using Content.Shared.Damage; using Content.Shared.Database; +using Content.Shared.DoAfter; using Content.Shared.Projectiles; +using Content.Shared.Throwing; +using Robust.Server.GameObjects; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Player; @@ -17,11 +25,22 @@ public sealed class ProjectileSystem : SharedProjectileSystem [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly GunSystem _guns = default!; [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; + // WD EDIT START + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly PenetratedSystem _penetrated = default!; + // WD EDIT END public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStartCollide); + // WD EDIT START + SubscribeLocalEvent(OnEmbed); + SubscribeLocalEvent(OnEmbedRemove); + // WD EDIT END } private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args) @@ -77,4 +96,64 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager)); } } + + // WD EDIT START + private void OnEmbed(EntityUid uid, EmbeddableProjectileComponent component, ref EmbedEvent args) + { + var dmg = _damageable.TryChangeDamage(args.Embedded, component.Damage, origin: args.Shooter); + if (dmg is { Empty: false }) + _color.RaiseEffect(Color.Red, new List() { args.Embedded }, Filter.Pvs(args.Embedded, entityManager: EntityManager)); + } + + private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent component, RemoveEmbeddedProjectileEvent args) + { + // Whacky prediction issues. + if (args.Cancelled) + return; + + if (component.DeleteOnRemove) + { + QueueDel(uid); + FreePenetrated(uid); + return; + } + + if (!TryComp(uid, out var physics)) + { + FreePenetrated(uid); + return; + } + + var xform = Transform(uid); + _physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform); + _transform.AttachToGridOrMap(uid, xform); + + // Reset whether the projectile has damaged anything if it successfully was removed + if (TryComp(uid, out var projectile)) + { + projectile.Shooter = null; + projectile.Weapon = null; + projectile.DamagedEntity = false; + } + + FreePenetrated(uid); + + // Land it just coz uhhh yeah + var landEv = new LandEvent(args.User, true); + RaiseLocalEvent(uid, ref landEv); + _physics.WakeBody(uid, body: physics); + + // try place it in the user's hand + _hands.TryPickupAnyHand(args.User, uid); + } + + private void FreePenetrated(EntityUid uid, PenetratedProjectileComponent? penetratedProjectile = null) + { + if (!Resolve(uid, ref penetratedProjectile) + || !penetratedProjectile.PenetratedUid.HasValue) + return; + + _penetrated.FreePenetrated(penetratedProjectile.PenetratedUid.Value); + } + // WD EDIT END } diff --git a/Content.Server/Stunnable/Systems/StunbatonSystem.cs b/Content.Server/Stunnable/Systems/StunbatonSystem.cs index 614331fe0a..4b52bf1fb6 100644 --- a/Content.Server/Stunnable/Systems/StunbatonSystem.cs +++ b/Content.Server/Stunnable/Systems/StunbatonSystem.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Power.Events; @@ -12,7 +11,6 @@ using Content.Shared.Popups; using Content.Shared.PowerCell.Components; using Content.Shared.Stunnable; -using Robust.Shared.Containers; namespace Content.Server.Stunnable.Systems { @@ -23,8 +21,6 @@ public sealed class StunbatonSystem : SharedStunbatonSystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!; - [Dependency] private readonly SharedContainerSystem _containers = default!; // WD EDIT - public override void Initialize() { base.Initialize(); @@ -42,7 +38,7 @@ private void OnStaminaHitAttempt(Entity entity, ref StaminaD { // WD EDIT START if (!_itemToggle.IsActivated(entity.Owner) - || !TryGetBatteryComponent(entity, out var battery, out var batteryUid) + || !_battery.TryGetBatteryComponent(entity, out var battery, out var batteryUid) || !_battery.TryUseCharge(batteryUid.Value, entity.Comp.EnergyPerUse, battery)) args.Cancelled = true; // WD EDIT END @@ -55,7 +51,7 @@ private void OnExamined(Entity entity, ref ExaminedEvent arg : Loc.GetString("comp-stunbaton-examined-off"); args.PushMarkup(onMsg); - if (TryGetBatteryComponent(entity, out var battery, out _)) // WD EDIT + if (_battery.TryGetBatteryComponent(entity, out var battery, out _)) // WD EDIT { var count = (int) (battery.CurrentCharge / entity.Comp.EnergyPerUse); args.PushMarkup(Loc.GetString("melee-battery-examine", ("color", "yellow"), ("count", count))); @@ -70,7 +66,7 @@ private void ToggleDone(Entity entity, ref ItemToggledEvent private void TryTurnOn(Entity entity, ref ItemToggleActivateAttemptEvent args) { // WD EDIT START - if (!TryGetBatteryComponent(entity, out var battery, out _) + if (!_battery.TryGetBatteryComponent(entity, out var battery, out _) || battery.CurrentCharge < entity.Comp.EnergyPerUse) { @@ -81,7 +77,7 @@ private void TryTurnOn(Entity entity, ref ItemToggleActivate _popup.PopupEntity(Loc.GetString("stunbaton-component-low-charge"), (EntityUid) args.User, (EntityUid) args.User); } - + return; } // WD EDIT END @@ -126,36 +122,10 @@ private void OnPowerCellChanged(Entity entity, ref PowerCell private void CheckCharge(Entity entity) { - if (!TryGetBatteryComponent(entity, out var battery, out _) + if (!_battery.TryGetBatteryComponent(entity, out var battery, out _) || battery.CurrentCharge < entity.Comp.EnergyPerUse) _itemToggle.TryDeactivate(entity.Owner, predicted: false); } - - private bool TryGetBatteryComponent(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, - [NotNullWhen(true)] out EntityUid? batteryUid) - { - if (TryComp(uid, out battery)) - { - batteryUid = uid; - return true; - } - - if (!_containers.TryGetContainer(uid, "cell_slot", out var container) - || container is not ContainerSlot slot) - { - battery = null; - batteryUid = null; - return false; - } - - batteryUid = slot.ContainedEntity; - - if (batteryUid != null) - return TryComp(batteryUid, out battery); - - battery = null; - return false; - } // WD EDIT END } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs index 798be3fc8e..c75b4e6f82 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs @@ -1,3 +1,5 @@ +using Content.Server.Stack; +using Content.Shared.Stacks; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Robust.Shared.Map; @@ -6,6 +8,8 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem { + [Dependency] private readonly StackSystem _stack = default!; // WD EDIT + protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates) { EntityUid? ent = null; @@ -32,4 +36,11 @@ protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent comp var cycledEvent = new GunCycledEvent(); RaiseLocalEvent(uid, ref cycledEvent); } + + // WD EDIT START + protected override EntityUid GetStackEntity(EntityUid uid, StackComponent stack) + { + return _stack.Split(uid, 1, Transform(uid).Coordinates, stack) ?? uid; + } + // WD EDIT END } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 8c7234593f..e3cd2ff776 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Effects; using Content.Shared.Interaction.Components; using Content.Shared.Projectiles; +using Content.Shared.Throwing; using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Ranged; using Content.Shared.Weapons.Ranged.Components; @@ -293,7 +294,12 @@ private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVeloci { RemoveShootable(uid); // TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack. + // WD EDIT START + if (gun.ThrowAngle.HasValue) + EnsureComp(uid).Angle = gun.ThrowAngle.Value; + // WD EDIT END ThrowingSystem.TryThrow(uid, mapDirection, gun.ProjectileSpeedModified, user); + RemComp(uid); // WD EDIT return; } diff --git a/Content.Server/_White/Guns/PoweredComponent.cs b/Content.Server/_White/Guns/PoweredComponent.cs new file mode 100644 index 0000000000..e32670a0e6 --- /dev/null +++ b/Content.Server/_White/Guns/PoweredComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server._White.Guns; + +[RegisterComponent] +public sealed partial class PoweredComponent : Component +{ + [DataField] + public float EnergyPerUse = 180f; + + /// + /// Modifies the speed of projectiles fired from this powered weapon. + /// + [DataField] + public float ProjectileSpeedModified = 15f; +} diff --git a/Content.Server/_White/Guns/PoweredSystem.cs b/Content.Server/_White/Guns/PoweredSystem.cs new file mode 100644 index 0000000000..ad5b8c7d71 --- /dev/null +++ b/Content.Server/_White/Guns/PoweredSystem.cs @@ -0,0 +1,32 @@ +using Content.Server.Power.EntitySystems; +using Content.Server.Weapons.Ranged.Systems; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Weapons.Ranged.Systems; + +namespace Content.Server._White.Guns; + +public sealed class PoweredSystem : EntitySystem +{ + [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly GunSystem _gun = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnShoot); + SubscribeLocalEvent(OnGunRefreshModifiers); + } + + private void OnShoot(EntityUid uid, PoweredComponent component, AttemptShootEvent args) + { + _gun.RefreshModifiers(uid); + } + + private void OnGunRefreshModifiers(EntityUid uid, PoweredComponent component, ref GunRefreshModifiersEvent args) + { + if (!_battery.TryGetBatteryComponent(uid, out var battery, out var batteryUid) + || !_battery.TryUseCharge(batteryUid.Value, component.EnergyPerUse, battery)) + return; + + args.ProjectileSpeed += component.ProjectileSpeedModified; + } +} diff --git a/Content.Server/_White/Projectiles/PenetratedProjectileSystem.cs b/Content.Server/_White/Projectiles/PenetratedProjectileSystem.cs new file mode 100644 index 0000000000..71bcc4e8cf --- /dev/null +++ b/Content.Server/_White/Projectiles/PenetratedProjectileSystem.cs @@ -0,0 +1,82 @@ +using System.Numerics; +using Content.Shared._White.Penetrated; +using Content.Shared._White.Projectile; +using Content.Shared.Buckle; +using Content.Shared.Damage; +using Content.Shared.Projectiles; +using Content.Shared.Throwing; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; + +namespace Content.Server._White.Projectiles; + +public sealed class PenetratedProjectileSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedBuckleSystem _buckle = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly PenetratedSystem _penetrated = default!; + [Dependency] private readonly SharedProjectileSystem _projectile = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnEmbed); + SubscribeLocalEvent(OnLand); + SubscribeLocalEvent(OnRemove); + SubscribeLocalEvent(OnEntityTerminating); + } + + private void OnEmbed(EntityUid uid, PenetratedProjectileComponent component, AttemptEmbedEvent args) + { + if (!TryComp(args.Embedded, out PenetratedComponent? penetrated) + || penetrated.IsPinned + || component.PenetratedUid.HasValue + || !TryComp(uid, out PhysicsComponent? physics) + || !TryComp(args.Embedded, out PhysicsComponent? physicEmbedded) + || physics.LinearVelocity.Length() < component.MinimumSpeed) + return; + + component.PenetratedUid = args.Embedded; + penetrated.ProjectileUid = uid; + penetrated.IsPinned = true; + + _buckle.TryUnbuckle(args.Embedded, args.Embedded, true); + _damageable.TryChangeDamage(args.Embedded, component.Damage, origin: args.Shooter); + _physics.SetLinearVelocity(args.Embedded, Vector2.Zero, body: physicEmbedded); + _physics.SetBodyType(args.Embedded, BodyType.Static, body: physicEmbedded); + var xform = Transform(args.Embedded); + _transform.AttachToGridOrMap(args.Embedded, xform); + _transform.SetLocalPosition(args.Embedded, Transform(uid).LocalPosition, xform); + _transform.SetParent(args.Embedded, xform, uid); + _physics.SetLinearVelocity(uid, physics.LinearVelocity / 2, body: physics); + } + + private void OnLand(EntityUid uid, PenetratedProjectileComponent component, ref LandEvent args) + { + FreePenetrated(uid, component); + } + + private void OnEntityTerminating(EntityUid uid, PenetratedProjectileComponent component, ref EntityTerminatingEvent args) + { + FreePenetrated(uid, component); + } + + private void OnRemove(EntityUid uid, PenetratedProjectileComponent component, ComponentRemove args) + { + FreePenetrated(uid, component); + } + + private void FreePenetrated(EntityUid uid, PenetratedProjectileComponent component) + { + if (!component.PenetratedUid.HasValue) + return; + + var penetratedUid = component.PenetratedUid.Value; + _penetrated.FreePenetrated(penetratedUid); + + if (TryComp(uid, out var embeddable)) + _projectile.Embed(uid, penetratedUid, null, embeddable, false); + } +} diff --git a/Content.Shared/Projectiles/EmbedEvent.cs b/Content.Shared/Projectiles/EmbedEvent.cs index 521a691f45..7398d7c83d 100644 --- a/Content.Shared/Projectiles/EmbedEvent.cs +++ b/Content.Shared/Projectiles/EmbedEvent.cs @@ -13,3 +13,16 @@ public readonly record struct EmbedEvent(EntityUid? Shooter, EntityUid Embedded) /// public readonly EntityUid Embedded = Embedded; } + +// WD EDIT START +[ByRefEvent] +public readonly record struct AttemptEmbedEvent(EntityUid? Shooter, EntityUid Embedded) +{ + public readonly EntityUid? Shooter = Shooter; + + /// + /// Entity that is embedded in. + /// + public readonly EntityUid Embedded = Embedded; +} +// WD EDIT END diff --git a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs index 008b7c2ced..4c9bdc5c89 100644 --- a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs +++ b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.GameStates; @@ -46,4 +47,9 @@ public sealed partial class EmbeddableProjectileComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? Sound; + + // WD EDIT START + [DataField] + public DamageSpecifier Damage = new(); + // WD EDIT END } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index f40a7a0363..c5f024b424 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -1,17 +1,18 @@ using System.Numerics; +using Content.Shared._White.Penetrated; +using Content.Shared._White.Projectile; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage; using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; -using Content.Shared.Mobs.Components; using Content.Shared.Throwing; +using Content.Shared.UserInterface; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; using Robust.Shared.Serialization; @@ -24,10 +25,9 @@ public abstract partial class SharedProjectileSystem : EntitySystem [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; // WD EDIT public override void Initialize() { @@ -36,61 +36,8 @@ public override void Initialize() SubscribeLocalEvent(PreventCollision); SubscribeLocalEvent(OnEmbedProjectileHit); SubscribeLocalEvent(OnEmbedThrowDoHit); - SubscribeLocalEvent(OnEmbedActivate); - SubscribeLocalEvent(OnEmbedRemove); SubscribeLocalEvent(OnAttemptPacifiedThrow); - } - - private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) - { - // Nuh uh - if (component.RemovalTime == null) - return; - - if (args.Handled || !TryComp(uid, out var physics) || physics.BodyType != BodyType.Static) - return; - - args.Handled = true; - - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.RemovalTime.Value, - new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) - { - DistanceThreshold = SharedInteractionSystem.InteractionRange, - }); - } - - private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent component, RemoveEmbeddedProjectileEvent args) - { - // Whacky prediction issues. - if (args.Cancelled || _netManager.IsClient) - return; - - if (component.DeleteOnRemove) - { - QueueDel(uid); - return; - } - - var xform = Transform(uid); - TryComp(uid, out var physics); - _physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform); - _transform.AttachToGridOrMap(uid, xform); - - // Reset whether the projectile has damaged anything if it successfully was removed - if (TryComp(uid, out var projectile)) - { - projectile.Shooter = null; - projectile.Weapon = null; - projectile.DamagedEntity = false; - } - - // Land it just coz uhhh yeah - var landEv = new LandEvent(args.User, true); - RaiseLocalEvent(uid, ref landEv); - _physics.WakeBody(uid, body: physics); - - // try place it in the user's hand - _hands.TryPickupAnyHand(args.User, uid); + SubscribeLocalEvent(OnEmbedActivate, before: new[] {typeof(ActivatableUISystem)}); // WD EDI } private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args) @@ -113,13 +60,29 @@ private void OnEmbedProjectileHit(EntityUid uid, EmbeddableProjectileComponent c } } - private void Embed(EntityUid uid, EntityUid target, EntityUid? user, EmbeddableProjectileComponent component) + public void Embed(EntityUid uid, EntityUid target, EntityUid? user, EmbeddableProjectileComponent component, bool raiseEvent = true) // WD EDIT { - TryComp(uid, out var physics); - _physics.SetLinearVelocity(uid, Vector2.Zero, body: physics); - _physics.SetBodyType(uid, BodyType.Static, body: physics); + // WD EDIT START + if (!TryComp(uid, out var physics) + || physics.LinearVelocity.Length() < component.MinimumSpeed + || _netManager.IsClient) + return; + + var attemptEmbedEvent = new AttemptEmbedEvent(user, target); + RaiseLocalEvent(uid, ref attemptEmbedEvent); + var xform = Transform(uid); - _transform.SetParent(uid, xform, target); + + if (!TryComp(uid, out var penetratedProjectile) + || !penetratedProjectile.PenetratedUid.HasValue + || (penetratedProjectile.PenetratedUid != target + && !HasComp(target))) + { + _physics.SetLinearVelocity(uid, Vector2.Zero, body: physics); + _physics.SetBodyType(uid, BodyType.Static, body: physics); + _transform.SetParent(uid, xform, target); + } + // WD EDIT END if (component.Offset != Vector2.Zero) { @@ -127,6 +90,11 @@ private void Embed(EntityUid uid, EntityUid target, EntityUid? user, EmbeddableP xform); } + // WD EDIT START + if (!raiseEvent) + return; + // WD EDIT END + _audio.PlayPredicted(component.Sound, uid, null); var ev = new EmbedEvent(user, target); RaiseLocalEvent(uid, ref ev); @@ -149,12 +117,6 @@ public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid sh Dirty(id, component); } - [Serializable, NetSerializable] - private sealed partial class RemoveEmbeddedProjectileEvent : DoAfterEvent - { - public override DoAfterEvent Clone() => this; - } - /// /// Prevent players with the Pacified status effect from throwing embeddable projectiles. /// @@ -162,6 +124,34 @@ private void OnAttemptPacifiedThrow(Entity ent, r { args.Cancel("pacified-cannot-throw-embed"); } + + // WD EDIT START + private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) + { + if (args.Handled + || !AttemptEmbedRemove(uid, args.User, component)) + return; + + args.Handled = true; + } + + private bool AttemptEmbedRemove(EntityUid uid, EntityUid user, EmbeddableProjectileComponent? component = null) + { + if (!Resolve(uid, ref component, false) + || component.RemovalTime == null + || !TryComp(uid, out var physics) + || physics.BodyType != BodyType.Static) + return false; + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.RemovalTime.Value, + new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) + { + DistanceThreshold = SharedInteractionSystem.InteractionRange, + }); + + return true; + } + // WD EDIT END } [Serializable, NetSerializable] @@ -188,3 +178,14 @@ public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, Projectile /// [ByRefEvent] public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null); + +// WD EDIT START +[Serializable, NetSerializable] +public sealed partial class RemoveEmbeddedProjectileEvent : DoAfterEvent +{ + public override DoAfterEvent Clone() + { + return this; + } +} +// WD EDIT END diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 8d7ecae1a8..398076747d 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -247,6 +247,11 @@ public sealed partial class GunComponent : Component /// [DataField] public bool DoRecoil = true; + + // WD EDIT START + [DataField] + public Angle? ThrowAngle; + // WD EDIT END } [Flags] diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 061a84ee3b..ec1b4c5ce7 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -2,6 +2,7 @@ using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Stacks; using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; @@ -47,8 +48,19 @@ private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderCompon if (GetBallisticShots(component) >= component.Capacity) return; - component.Entities.Add(args.Used); - Containers.Insert(args.Used, component.Container); + // WD EDIT START + var entity = args.Used; + var doInsert = true; + if (TryComp(args.Used, out StackComponent? stack) && stack.Count > 1) + { + entity = GetStackEntity(args.Used, stack); + doInsert = false; + } + + component.Entities.Add(entity); + if (_netManager.IsServer || doInsert) + Containers.Insert(entity, component.Container); + // WD EDIT END // Not predicted so Audio.PlayPredicted(component.SoundInsert, uid, args.User); args.Handled = true; @@ -281,9 +293,19 @@ public void UpdateBallisticAppearance(EntityUid uid, BallisticAmmoProviderCompon if (!Timing.IsFirstTimePredicted || !TryComp(uid, out var appearance)) return; - Appearance.SetData(uid, AmmoVisuals.AmmoCount, GetBallisticShots(component), appearance); + var count = GetBallisticShots(component); // WD EDIT + + Appearance.SetData(uid, AmmoVisuals.AmmoCount, count, appearance); // WD EDIT Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance); + Appearance.SetData(uid, AmmoVisuals.HasAmmo, count != 0, appearance); // WD EDIT + } + + // WD EDIT START + protected virtual EntityUid GetStackEntity(EntityUid uid, StackComponent stack) + { + return uid; } + // WD EDIT END } /// diff --git a/Content.Shared/_White/Guns/Stretched/StretchedComponent.cs b/Content.Shared/_White/Guns/Stretched/StretchedComponent.cs new file mode 100644 index 0000000000..1b0145d874 --- /dev/null +++ b/Content.Shared/_White/Guns/Stretched/StretchedComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Weapons.Ranged.Components; +using Robust.Shared.Audio; + +namespace Content.Shared._White.Guns.Stretched; + +[RegisterComponent] +public sealed partial class StretchedComponent : Component +{ + [ViewVariables] + public bool Stretched; + + [DataField, ViewVariables] + public SoundSpecifier? SoundDraw = new SoundPathSpecifier("/Audio/Weapons/drawbow2.ogg"); + + public BallisticAmmoProviderComponent Provider = default!; +} diff --git a/Content.Shared/_White/Guns/Stretched/StretchedSystem.cs b/Content.Shared/_White/Guns/Stretched/StretchedSystem.cs new file mode 100644 index 0000000000..393846c71e --- /dev/null +++ b/Content.Shared/_White/Guns/Stretched/StretchedSystem.cs @@ -0,0 +1,72 @@ +using Content.Shared.Interaction.Events; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Guns.Stretched; + +public sealed class StretchedSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnItemRemove); + SubscribeLocalEvent(OnAttemptShoot); + SubscribeLocalEvent(OnUse); + } + + private void OnStartup(EntityUid uid, StretchedComponent component, ComponentStartup args) + { + component.Provider = EnsureComp(uid); + } + + private void OnItemRemove(EntityUid uid, StretchedComponent component, EntRemovedFromContainerMessage args) + { + if (!component.Stretched || args.Container.ID != component.Provider.Container.ID) + return; + + component.Stretched = false; + UpdateDrawableAppearance(uid, component); + } + + private void OnAttemptShoot(EntityUid uid, StretchedComponent component, ref AttemptShootEvent args) + { + if (!component.Stretched) + args.Cancelled = true; + } + + private void OnUse(EntityUid uid, StretchedComponent component, UseInHandEvent args) + { + if (component.Stretched || component.Provider.Count == 0) + return; + + args.Handled = true; + + _audio.PlayPredicted(component.SoundDraw, uid, args.User); + component.Stretched = true; + + UpdateDrawableAppearance(uid, component); + } + + private void UpdateDrawableAppearance(EntityUid uid, StretchedComponent component) + { + if (!TryComp(uid, out var appearance)) + return; + + _appearance.SetData(uid, StretchedVisuals.Stretched, component.Stretched, appearance); + } +} + +[Serializable, NetSerializable] +public enum StretchedVisuals : byte +{ + Stretched, + Layer +} diff --git a/Content.Shared/_White/Penetrated/PenetratedComponent.cs b/Content.Shared/_White/Penetrated/PenetratedComponent.cs new file mode 100644 index 0000000000..f6f82a7031 --- /dev/null +++ b/Content.Shared/_White/Penetrated/PenetratedComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared._White.Penetrated; + +[RegisterComponent] +public sealed partial class PenetratedComponent : Component +{ + [DataField] + public EntityUid? ProjectileUid; + + [DataField] + public bool IsPinned; +} diff --git a/Content.Shared/_White/Penetrated/PenetratedSystem.cs b/Content.Shared/_White/Penetrated/PenetratedSystem.cs new file mode 100644 index 0000000000..8506286fe1 --- /dev/null +++ b/Content.Shared/_White/Penetrated/PenetratedSystem.cs @@ -0,0 +1,34 @@ +using Content.Shared._White.Projectile; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared._White.Penetrated; + +public sealed class PenetratedSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public void FreePenetrated(EntityUid uid, PenetratedComponent? penetrated = null, PhysicsComponent? physics = null) + { + var xform = Transform(uid); + _transform.AttachToGridOrMap(uid, xform); + + if (Resolve(uid, ref physics, false)) + { + _physics.SetBodyType(uid, BodyType.KinematicController, body: physics, xform: xform); + _physics.WakeBody(uid, body: physics); + } + + if (!Resolve(uid, ref penetrated, false)) + return; + + penetrated.IsPinned = false; + + if (TryComp(penetrated.ProjectileUid, out var penetratedProjectile)) + penetratedProjectile.PenetratedUid = null; + + penetrated.ProjectileUid = null; + } +} diff --git a/Content.Shared/_White/Projectile/PenetratedProjectileComponent.cs b/Content.Shared/_White/Projectile/PenetratedProjectileComponent.cs new file mode 100644 index 0000000000..846a6045c2 --- /dev/null +++ b/Content.Shared/_White/Projectile/PenetratedProjectileComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Damage; + +namespace Content.Shared._White.Projectile; + +[RegisterComponent] +public sealed partial class PenetratedProjectileComponent : Component +{ + [DataField] + public float MinimumSpeed = 40f; + + [DataField] + public DamageSpecifier Damage = new(); + + [DataField] + public EntityUid? PenetratedUid; +} diff --git a/Resources/Locale/ru-RU/_white/prototypes/entities/objects/weapons/guns/crossbow.ftl b/Resources/Locale/ru-RU/_white/prototypes/entities/objects/weapons/guns/crossbow.ftl new file mode 100644 index 0000000000..352d8731dc --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/entities/objects/weapons/guns/crossbow.ftl @@ -0,0 +1,4 @@ +ent-WeaponPoweredCrossbow = арбалет + .desc = Опасная вещь +ent-WeaponPoweredCrossbowUnfinished = недоделанный арбалет + .desc = Будет опасной вещью \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_white/prototypes/entities/objects/weapons/melee/snatcherprod.ftl b/Resources/Locale/ru-RU/_white/prototypes/entities/objects/weapons/melee/snatcherprod.ftl new file mode 100644 index 0000000000..e4b9736f24 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/entities/objects/weapons/melee/snatcherprod.ftl @@ -0,0 +1,2 @@ +ent-Snatcherprod = хваталка + .desc = Искрится жаждой воровства и коварства. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/prototypes/entities/objects/weapons/melee/stunprod.ftl b/Resources/Locale/ru-RU/prototypes/entities/objects/weapons/melee/stunprod.ftl index 6ea518b4d9..f53c8519f8 100644 --- a/Resources/Locale/ru-RU/prototypes/entities/objects/weapons/melee/stunprod.ftl +++ b/Resources/Locale/ru-RU/prototypes/entities/objects/weapons/melee/stunprod.ftl @@ -1,2 +1,7 @@ -ent-Stunprod = шок-палка - .desc = Электрошокер для незаконного обезвреживания. \ No newline at end of file +ent-StunprodBase = шок-палка + +ent-Stunprod = { ent-StunprodBase } + .desc = Электрошокер для незаконного обезвреживания. + +ent-ProdUnfinished = обмотанный стержень + .desc = Стержень с проводами. \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 5ab790feee..a815bc0d1a 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -48,6 +48,7 @@ active: False - type: OwnInteractionVerbs allowedVerbs: [] # TODO: define something here, or don't. + - type: Penetrated # WD EDIT # Used for mobs that have health and can take damage. - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Materials/parts.yml b/Resources/Prototypes/Entities/Objects/Materials/parts.yml index 95640dbbb5..aa6e44f71f 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/parts.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/parts.yml @@ -99,6 +99,21 @@ - prototype: SheetRUGlass - prototype: SheetRUGlass0 - prototype: SheetRGlass + # WD EDIT START + - type: Tag + tags: + - CrossbowBolt + - type: EmbeddableProjectile + minimumSpeed: 15 + damage: + types: + Piercing: 15 + - type: PenetratedProjectile + minimumSpeed: 30 + damage: + types: + Piercing: 15 + # WD EDIT END - type: entity parent: PartRodMetal @@ -108,6 +123,7 @@ components: - type: Tag tags: + - CrossbowBolt # WD EDIT - RodMetal1 - type: Sprite state: rods @@ -122,6 +138,7 @@ components: - type: Tag tags: + - CrossbowBolt # WD EDIT - RodMetal1 - type: Sprite state: rods diff --git a/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml index abf4e0974a..48516cb7ee 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml @@ -27,9 +27,12 @@ - type: Sprite sprite: Objects/Misc/rifle_stock.rsi state: icon + # WD EDIT STRART - type: Construction - graph: RifleStockGraph - node: riflestock + deconstructionTarget: null + graph: WeaponPoweredCrossbowGraph + node: stock + # WD EDIT END - type: Tag tags: - RifleStock diff --git a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml index 1041256a59..ce7e2bdc8e 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml @@ -21,6 +21,9 @@ - type: Currency price: Telecrystal: 1 + - type: Tag # WD EDIT + tags: + - Telecrystal - type: entity parent: Telecrystal diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/crossbow.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/crossbow.yml new file mode 100644 index 0000000000..137cacf0de --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/crossbow.yml @@ -0,0 +1,77 @@ +- type: entity + parent: BaseItem + id: WeaponPoweredCrossbow + name: Crossbow + description: It's a dangerous thing. + components: + - type: Sprite + sprite: _White/Objects/Weapons/Guns/crossbow.rsi + layers: + - state: crossbow + - state: unstrung + map: [ "enum.StretchedVisuals.Layer" ] + - type: Clothing + quickEquip: false + slots: + - Back + - SuitStorage + - type: Item + size: Huge + sprite: _White/Objects/Weapons/Guns/crossbow.rsi + - type: Gun + fireRate: 0.5 + soundGunshot: + path: /Audio/Weapons/click.ogg + throwAngle: 225 + - type: BallisticAmmoProvider + whitelist: + tags: + - CrossbowBolt + capacity: 1 + soundInsert: + path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg + - type: ContainerContainer + containers: + ballistic-ammo: !type:Container + ents: [] + cell_slot: !type:ContainerSlot + crystal_slot: !type:ContainerSlot + - type: PowerCellSlot + cellSlotId: cell_slot + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + - type: Stretched + - type: Powered + - type: Appearance + - type: StretchedVisuals + loadedState: loaded + stretchedState: stretched + unstrungState: unstrung + - type: Construction + deconstructionTarget: null + graph: WeaponPoweredCrossbowGraph + node: crossbow + +- type: entity + parent: BaseItem + id: WeaponPoweredCrossbowUnfinished + name: Unfinished crossbow + description: It's going to be a dangerous thing. + components: + - type: Sprite + sprite: _White/Objects/Weapons/Guns/crossbow.rsi + state: crossbow + - type: Item + size: Huge + sprite: _White/Objects/Weapons/Guns/crossbow.rsi + - type: Clothing + quickEquip: false + slots: + - Back + - SuitStorage + - type: Construction + deconstructionTarget: null + graph: WeaponPoweredCrossbowGraph + node: unfinished diff --git a/Resources/Prototypes/_White/Recipes/hidden_crafts.yml b/Resources/Prototypes/_White/Recipes/hidden_crafts.yml index 830ecf0e43..a202afb1eb 100644 --- a/Resources/Prototypes/_White/Recipes/hidden_crafts.yml +++ b/Resources/Prototypes/_White/Recipes/hidden_crafts.yml @@ -84,4 +84,36 @@ - !type:SpawnPrototype prototype: Igniter - !type:EmptyAllContainers - - !type:DeleteEntity \ No newline at end of file + - !type:DeleteEntity + +- type: constructionGraph + id: WeaponPoweredCrossbowGraph + start: stock + graph: + - node: stock + edges: + - to: unfinished + steps: + - material: MetalRod + amount: 3 + doAfter: 3 + - node: unfinished + entity: WeaponPoweredCrossbowUnfinished + edges: + - to: crossbow + steps: + - tool: Welding + doAfter: 5 + - material: Cable + amount: 5 + doAfter: 0.5 + - material: Plastic + amount: 3 + doAfter: 0.5 + - material: Cable + amount: 5 + doAfter: 0.5 + - tool: Screwing + doAfter: 1 + - node: crossbow + entity: WeaponPoweredCrossbow \ No newline at end of file diff --git a/Resources/Prototypes/_White/tags.yml b/Resources/Prototypes/_White/tags.yml index ca84c8038b..20f3f26ddd 100644 --- a/Resources/Prototypes/_White/tags.yml +++ b/Resources/Prototypes/_White/tags.yml @@ -1,8 +1,14 @@ - type: Tag id: EnergySword +- type: Tag + id: CrossbowBolt + +- type: Tag + id: Telecrystal + - type: Tag id: NeuroStabilization - type: Tag - id: MindSlave \ No newline at end of file + id: MindSlave diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/crossbow.png b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/crossbow.png new file mode 100644 index 0000000000..3dc28e472c Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/crossbow.png differ diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/inhand-left.png b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/inhand-left.png new file mode 100644 index 0000000000..385c96238d Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/inhand-left.png differ diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/inhand-right.png b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/inhand-right.png new file mode 100644 index 0000000000..93a062d9ee Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/inhand-right.png differ diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/loaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/loaded.png new file mode 100644 index 0000000000..f3e97fa1fa Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/loaded.png differ diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/meta.json new file mode 100644 index 0000000000..f6bcd5b43b --- /dev/null +++ b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/meta.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from paradise at https://github.com/ParadiseSS13/Paradise at 76d0428022d17f3249585d96ac9b69076206efd4. Edit by Spatison", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "crossbow" + }, + { + "name": "unstrung" + }, + { + "name": "loaded" + }, + { + "name": "stretched" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/stretched.png b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/stretched.png new file mode 100644 index 0000000000..86266fa82c Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/stretched.png differ diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/unstrung.png b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/unstrung.png new file mode 100644 index 0000000000..4c57580ab2 Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/crossbow.rsi/unstrung.png differ