From d0431ee61e4707977613e8347dd5de4877cbcc3d Mon Sep 17 00:00:00 2001 From: Aviu00 <93730715+Aviu00@users.noreply.github.com> Date: Thu, 24 Aug 2023 21:05:56 +0300 Subject: [PATCH] Crossbow (#327) * Add powered crossbow * Add spear embedding (#18578) * Add spear embedding * fuck this copy-paste * Juicier * the river * Add crossbow embedding * Fix crossbow construction * Finish crossbow * Remove unused --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Crossbow/DrawableVisualizerSystem.cs | 21 ++++ .../Damage/Systems/DamageOtherOnHitSystem.cs | 13 ++- .../Ranged/Systems/GunSystem.Ballistic.cs | 9 ++ .../Weapons/Ranged/Systems/GunSystem.cs | 15 ++- .../Crossbow/ModifyDamageOnShootComponent.cs | 19 ++++ .../Crossbow/ModifyDamageOnShootSystem.cs | 33 +++++++ .../White/Crossbow/PoweredComponent.cs | 15 +++ .../White/Crossbow/PoweredSystem.cs | 91 ++++++++++++++++++ .../Crossbow/ThrowDamageModifierComponent.cs | 13 +++ .../Crossbow/ThrowDamageModifierSystem.cs | 43 +++++++++ .../EmbeddableProjectileComponent.cs | 11 +++ .../Projectiles/SharedProjectileSystem.cs | 88 +++++++++++++++-- .../Weapons/Ranged/Components/GunComponent.cs | 10 ++ .../Systems/SharedGunSystem.Ballistic.cs | 16 ++- .../White/Crossbow/DrawableComponent.cs | 16 +++ .../White/Crossbow/DrawableSystem.cs | 71 ++++++++++++++ Content.Shared/White/Crossbow/EmbedEvents.cs | 10 ++ .../White/Crossbow/PenetratedComponent.cs | 9 ++ .../White/Crossbow/PenetratedSystem.cs | 41 ++++++++ .../Prototypes/Entities/Mobs/Species/base.yml | 1 + .../Entities/Objects/Materials/parts.yml | 26 +++++ .../Objects/Misc/improvised_gun_parts.yml | 5 +- .../Objects/Weapons/Guns/crossbow.yml | 79 +++++++++++++++ .../White/Recipes/hidden_crafts.yml | 52 ++++++++++ Resources/Prototypes/White/tags.yml | 3 + .../Objects/Weapons/crossbow.rsi/base.png | Bin 0 -> 845 bytes .../Objects/Weapons/crossbow.rsi/drawn.png | Bin 0 -> 919 bytes .../Weapons/crossbow.rsi/inhand-left.png | Bin 0 -> 291 bytes .../Weapons/crossbow.rsi/inhand-right.png | Bin 0 -> 547 bytes .../Objects/Weapons/crossbow.rsi/loaded.png | Bin 0 -> 973 bytes .../Objects/Weapons/crossbow.rsi/meta.json | 28 ++++++ 31 files changed, 723 insertions(+), 15 deletions(-) create mode 100644 Content.Client/White/Crossbow/DrawableVisualizerSystem.cs create mode 100644 Content.Server/White/Crossbow/ModifyDamageOnShootComponent.cs create mode 100644 Content.Server/White/Crossbow/ModifyDamageOnShootSystem.cs create mode 100644 Content.Server/White/Crossbow/PoweredComponent.cs create mode 100644 Content.Server/White/Crossbow/PoweredSystem.cs create mode 100644 Content.Server/White/Crossbow/ThrowDamageModifierComponent.cs create mode 100644 Content.Server/White/Crossbow/ThrowDamageModifierSystem.cs create mode 100644 Content.Shared/White/Crossbow/DrawableComponent.cs create mode 100644 Content.Shared/White/Crossbow/DrawableSystem.cs create mode 100644 Content.Shared/White/Crossbow/EmbedEvents.cs create mode 100644 Content.Shared/White/Crossbow/PenetratedComponent.cs create mode 100644 Content.Shared/White/Crossbow/PenetratedSystem.cs create mode 100644 Resources/Prototypes/White/Entities/Objects/Weapons/Guns/crossbow.yml create mode 100644 Resources/Textures/White/Objects/Weapons/crossbow.rsi/base.png create mode 100644 Resources/Textures/White/Objects/Weapons/crossbow.rsi/drawn.png create mode 100644 Resources/Textures/White/Objects/Weapons/crossbow.rsi/inhand-left.png create mode 100644 Resources/Textures/White/Objects/Weapons/crossbow.rsi/inhand-right.png create mode 100644 Resources/Textures/White/Objects/Weapons/crossbow.rsi/loaded.png create mode 100644 Resources/Textures/White/Objects/Weapons/crossbow.rsi/meta.json diff --git a/Content.Client/White/Crossbow/DrawableVisualizerSystem.cs b/Content.Client/White/Crossbow/DrawableVisualizerSystem.cs new file mode 100644 index 0000000000..ea30ff4016 --- /dev/null +++ b/Content.Client/White/Crossbow/DrawableVisualizerSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Weapons.Ranged.Systems; +using Content.Shared.White.Crossbow; +using Robust.Client.GameObjects; + +namespace Content.Client.White.Crossbow; + +public sealed class DrawableSystem : VisualizerSystem +{ + protected override void OnAppearanceChange(EntityUid uid, DrawableComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + var drawn = args.AppearanceData.TryGetValue(DrawableVisuals.Drawn, out var drawnObj) && drawnObj is true; + + var hasAmmo = args.AppearanceData.TryGetValue(AmmoVisuals.AmmoCount, out var ammoCount) && (int) ammoCount > 0; + + var state = drawn ? "drawn" : hasAmmo ? "loaded" : "base"; + args.Sprite.LayerSetState(0, state); + } +} diff --git a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs index c7600ec221..92008babca 100644 --- a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs +++ b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Administration.Logs; using Content.Server.Damage.Components; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Camera; +using Content.Server.White.Crossbow; using Content.Shared.Damage; using Content.Shared.Damage.Events; using Content.Shared.Damage.Systems; @@ -32,7 +33,17 @@ namespace Content.Server.Damage.Systems private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args) { - var dmg = _damageable.TryChangeDamage(args.Target, component.Damage, component.IgnoreResistances, origin: args.Component.Thrower); + // WD EDIT START + if (args.Handled) + return; + + var damage = component.Damage; + + if (TryComp(uid, out ThrowDamageModifierComponent? modifier)) + damage += modifier.Damage; + + var dmg = _damageable.TryChangeDamage(args.Target, damage, component.IgnoreResistances, origin: args.Component.Thrower); + // WD EDIT END // Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying. if (dmg != null && HasComp(args.Target)) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs index 798be3fc8e..c159fb36d3 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!; + protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates) { EntityUid? ent = null; @@ -32,4 +36,9 @@ public sealed partial class GunSystem var cycledEvent = new GunCycledEvent(); RaiseLocalEvent(uid, ref cycledEvent); } + + protected override EntityUid GetStackEntity(EntityUid uid, StackComponent stack) // WD + { + return _stack.Split(uid, 1, Transform(uid).Coordinates, stack) ?? uid; + } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 5f8ab0ecb6..5bc82ca67e 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Interaction; using Content.Server.Power.EntitySystems; using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Components; +using Content.Server.White.Crossbow; using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Database; @@ -13,6 +14,7 @@ using Content.Shared.Effects; using Content.Shared.FixedPoint; 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; @@ -42,6 +44,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; + [Dependency] private readonly PoweredSystem _powered = default!; // WD public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; public const float GunClumsyChance = 0.5f; @@ -306,7 +309,17 @@ public sealed partial class GunSystem : SharedGunSystem { RemoveShootable(uid); // TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack. - ThrowingSystem.TryThrow(uid, mapDirection, gun.ProjectileSpeed, user); + // WD EDIT START + var coefficient = _powered.GetPowerCoefficient(gunUid); + if (gun.ForceThrowingAngle) + { + var angle = EnsureComp(uid); + angle.Angle = gun.Angle; + } + ThrowingSystem.TryThrow(uid, mapDirection.Normalized() * 7f * coefficient, gun.ProjectileSpeed, user); + if (gun.ForceThrowingAngle) + RemComp(uid); + // WD EDIT END return; } diff --git a/Content.Server/White/Crossbow/ModifyDamageOnShootComponent.cs b/Content.Server/White/Crossbow/ModifyDamageOnShootComponent.cs new file mode 100644 index 0000000000..de6d76b64f --- /dev/null +++ b/Content.Server/White/Crossbow/ModifyDamageOnShootComponent.cs @@ -0,0 +1,19 @@ +using System.Numerics; +using Content.Shared.Damage; + +namespace Content.Server.White.Crossbow; + +[RegisterComponent] +public sealed partial class ModifyDamageOnShootComponent : Component +{ + [DataField("damage", required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = new(); + + [DataField("addEmbedding")] + [ViewVariables(VVAccess.ReadWrite)] + public bool AddEmbedding; + + [DataField("offset")] + public Vector2 Offset = Vector2.Zero; +} diff --git a/Content.Server/White/Crossbow/ModifyDamageOnShootSystem.cs b/Content.Server/White/Crossbow/ModifyDamageOnShootSystem.cs new file mode 100644 index 0000000000..ac39bcdd20 --- /dev/null +++ b/Content.Server/White/Crossbow/ModifyDamageOnShootSystem.cs @@ -0,0 +1,33 @@ +using Content.Shared.Projectiles; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Events; + +namespace Content.Server.White.Crossbow; + +public sealed class ModifyDamageOnShootSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShoot); + } + + private void OnShoot(EntityUid uid, ModifyDamageOnShootComponent component, AmmoShotEvent args) + { + foreach (var proj in args.FiredProjectiles) + { + var comp = EnsureComp(proj); + comp.Damage += component.Damage; + + if (!component.AddEmbedding) + continue; + + comp.AddEmbedding = true; + var embed = EnsureComp(proj); + embed.Offset = component.Offset; + embed.PreventEmbedding = false; + Dirty(proj, embed); + } + } +} diff --git a/Content.Server/White/Crossbow/PoweredComponent.cs b/Content.Server/White/Crossbow/PoweredComponent.cs new file mode 100644 index 0000000000..4ef3fef4ad --- /dev/null +++ b/Content.Server/White/Crossbow/PoweredComponent.cs @@ -0,0 +1,15 @@ +using Content.Shared.Damage; + +namespace Content.Server.White.Crossbow; + +[RegisterComponent] +public sealed partial class PoweredComponent : Component +{ + [DataField("charge", required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public float Charge; + + [DataField("damage", required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = new(); +} diff --git a/Content.Server/White/Crossbow/PoweredSystem.cs b/Content.Server/White/Crossbow/PoweredSystem.cs new file mode 100644 index 0000000000..e349354e7c --- /dev/null +++ b/Content.Server/White/Crossbow/PoweredSystem.cs @@ -0,0 +1,91 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Shared.Projectiles; +using Content.Shared.Weapons.Ranged.Events; +using Robust.Shared.Containers; +using Robust.Shared.Utility; + +namespace Content.Server.White.Crossbow; + +public sealed class PoweredSystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly BatterySystem _battery = default!; + + private const string CellSlot = "cell_slot"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShoot); + } + + private void OnShoot(EntityUid uid, PoweredComponent component, AmmoShotEvent args) + { + if (!TryGetBatteryComponent(uid, out var battery, out var batteryUid)) + return; + + var (factor, charge) = GetFactor(component, battery, batteryUid.Value); + + // Чтобы затриггерить взрыв на полную мощь, если есть плазма в батарейке + _battery.SetCharge(batteryUid.Value, battery.CurrentCharge, battery); + + _battery.SetCharge(batteryUid.Value, battery.CurrentCharge - charge, battery); + var damage = component.Damage * factor; + + foreach (var proj in args.FiredProjectiles) + { + EnsureComp(proj).Damage += damage; + + if (factor == 0) + continue; + + var embed = EnsureComp(proj); + embed.Penetrate = true; + Dirty(proj, embed); + } + } + + public float GetPowerCoefficient(EntityUid uid) + { + if (!TryComp(uid, out PoweredComponent? component) || + !TryGetBatteryComponent(uid, out var battery, out var batteryUid)) + return 1f; + + return 1f + GetFactor(component, battery, batteryUid.Value).Item1; + } + + private (float, float) GetFactor(PoweredComponent component, BatteryComponent battery, EntityUid batteryUid) + { + DebugTools.Assert(component.Charge != 0f); + var charge = MathF.Min(battery.CurrentCharge, component.Charge); + var factor = charge / component.Charge; + + if (TryComp(batteryUid, out RiggableComponent? rig) && rig.IsRigged) + factor *= 2f; + + return (factor, charge); + } + + private bool TryGetBatteryComponent(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, + [NotNullWhen(true)] out EntityUid? batteryUid) + { + if (!_containers.TryGetContainer(uid, CellSlot, 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; + } +} diff --git a/Content.Server/White/Crossbow/ThrowDamageModifierComponent.cs b/Content.Server/White/Crossbow/ThrowDamageModifierComponent.cs new file mode 100644 index 0000000000..48641d4e44 --- /dev/null +++ b/Content.Server/White/Crossbow/ThrowDamageModifierComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Damage; + +namespace Content.Server.White.Crossbow; + +[RegisterComponent] +public sealed partial class ThrowDamageModifierComponent : Component +{ + public DamageSpecifier Damage = new(); + + public bool AddEmbedding; + + public bool ClearDamageOnRemove; +} diff --git a/Content.Server/White/Crossbow/ThrowDamageModifierSystem.cs b/Content.Server/White/Crossbow/ThrowDamageModifierSystem.cs new file mode 100644 index 0000000000..24abf95286 --- /dev/null +++ b/Content.Server/White/Crossbow/ThrowDamageModifierSystem.cs @@ -0,0 +1,43 @@ +using Content.Shared.Projectiles; +using Content.Shared.Throwing; +using Content.Shared.White.Crossbow; + +namespace Content.Server.White.Crossbow; + +public sealed class ThrowDamageModifierSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStopped); + SubscribeLocalEvent(OnEmbedStart); + SubscribeLocalEvent(OnEmbedRemoved); + } + + private void OnEmbedStart(EntityUid uid, ThrowDamageModifierComponent component, ref EmbedStartEvent args) + { + component.ClearDamageOnRemove = true; + + if (component.AddEmbedding) + args.Embed.PreventEmbedding = true; + } + + private void OnEmbedRemoved(EntityUid uid, ThrowDamageModifierComponent component, EmbedRemovedEvent args) + { + if (!component.ClearDamageOnRemove) + return; + + component.ClearDamageOnRemove = false; + component.Damage.DamageDict.Clear(); + + if (component.AddEmbedding) + RemComp(uid); + } + + private void OnStopped(EntityUid uid, ThrowDamageModifierComponent component, StopThrowEvent args) + { + if (!component.ClearDamageOnRemove) + component.Damage.DamageDict.Clear(); + } +} diff --git a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs index 008b7c2ced..0ea90bcbd7 100644 --- a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs +++ b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs @@ -46,4 +46,15 @@ public sealed partial class EmbeddableProjectileComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? Sound; + + // WD START + [AutoNetworkedField] + public bool PreventEmbedding; + + [AutoNetworkedField] + public bool Penetrate; + + [AutoNetworkedField] + public EntityUid? PenetratedUid; + // WD END } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index e003764f92..56ffc3c597 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Throwing; +using Content.Shared.White.Crossbow; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Network; @@ -26,6 +27,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly PenetratedSystem _penetratedSystem = default!; // WD public override void Initialize() { @@ -37,24 +39,19 @@ public abstract partial class SharedProjectileSystem : EntitySystem SubscribeLocalEvent(OnEmbedActivate); SubscribeLocalEvent(OnEmbedRemove); SubscribeLocalEvent(OnAttemptPacifiedThrow); + SubscribeLocalEvent(OnLand); // WD } private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) { - // Nuh uh - if (component.RemovalTime == null) - return; - + // WD EDIT START 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, - }); + AttemptEmbedRemove(uid, args.User, component); + // WD EDIT END } private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent component, RemoveEmbeddedProjectileEvent args) @@ -82,6 +79,16 @@ public abstract partial class SharedProjectileSystem : EntitySystem projectile.DamagedEntity = false; } + // WD START + if (component.PenetratedUid != null) + { + _penetratedSystem.FreePenetrated(component.PenetratedUid.Value); + component.PenetratedUid = null; + } + + RaiseLocalEvent(uid, new EmbedRemovedEvent()); + // WD END + // Land it just coz uhhh yeah var landEv = new LandEvent(args.User, true); RaiseLocalEvent(uid, ref landEv); @@ -96,6 +103,28 @@ public abstract partial class SharedProjectileSystem : EntitySystem if (!component.EmbedOnThrow) return; + // WD START + if (component is {Penetrate: true, PenetratedUid: null} && + TryComp(args.Target, out PenetratedComponent? penetrated) && + penetrated is {ProjectileUid: null, IsPinned: false} && + TryComp(args.Target, out PhysicsComponent? physics)) + { + component.PenetratedUid = args.Target; + penetrated.ProjectileUid = uid; + _physics.SetLinearVelocity(args.Target, Vector2.Zero, body: physics); + _physics.SetBodyType(args.Target, BodyType.Static, body: physics); + var xform = Transform(args.Target); + _transform.SetParent(args.Target, xform, uid); + _transform.SetLocalPosition(args.Target, + xform.LocalPosition + Transform(uid).LocalRotation.RotateVec(new Vector2(0.5f, 0.5f)), xform); + Dirty(uid, component); + return; + } + + if (component.PenetratedUid == args.Target) + args.Handled = true; + // WD END + Embed(uid, args.Target, component); } @@ -113,6 +142,16 @@ public abstract partial class SharedProjectileSystem : EntitySystem private void Embed(EntityUid uid, EntityUid target, EmbeddableProjectileComponent component) { + if (component.PreventEmbedding || component.PenetratedUid == target) // WD START + return; + + var ev = new EmbedStartEvent(component); + RaiseLocalEvent(uid, ref ev); + + if (TryComp(component.PenetratedUid, out PenetratedComponent? penetrated)) + penetrated.IsPinned = true; + // WD END + TryComp(uid, out var physics); _physics.SetLinearVelocity(uid, Vector2.Zero, body: physics); _physics.SetBodyType(uid, BodyType.Static, body: physics); @@ -160,6 +199,37 @@ public abstract partial class SharedProjectileSystem : EntitySystem { args.Cancel("pacified-cannot-throw-embed"); } + + // WD EDIT START + private void OnLand(EntityUid uid, EmbeddableProjectileComponent component, ref LandEvent args) + { + if (component.PenetratedUid == null) + return; + + var penetratedUid = component.PenetratedUid.Value; + component.PenetratedUid = null; + + _penetratedSystem.FreePenetrated(penetratedUid); + + Embed(uid, penetratedUid, component); + } + + public void AttemptEmbedRemove(EntityUid uid, EntityUid user, EmbeddableProjectileComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return; + + // Nuh uh + if (component.RemovalTime == null) + return; + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.RemovalTime.Value, + new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) + { + DistanceThreshold = SharedInteractionSystem.InteractionRange, + }); + } + // WD EDIT END } [Serializable, NetSerializable] diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index dc5be423a9..fa170a4637 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -167,6 +167,16 @@ public partial class GunComponent : Component /// [DataField("clumsyProof"), ViewVariables(VVAccess.ReadWrite)] public bool ClumsyProof = false; + + // WD START + [DataField("forceThrowingAngle")] + [ViewVariables(VVAccess.ReadWrite)] + public bool ForceThrowingAngle; + + [DataField("angle")] + [ViewVariables(VVAccess.ReadWrite)] + public Angle Angle; + // WD END } [Flags] diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 501d0069a7..cd0d768098 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.DoAfter; 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,17 @@ public abstract partial class SharedGunSystem if (GetBallisticShots(component) >= component.Capacity) return; - component.Entities.Add(args.Used); - Containers.Insert(args.Used, component.Container); + var entity = args.Used; // WD EDIT START + 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; @@ -56,6 +66,8 @@ public abstract partial class SharedGunSystem Dirty(uid, component); } + protected virtual EntityUid GetStackEntity(EntityUid uid, StackComponent stack) { return uid; } + private void OnBallisticAfterInteract(EntityUid uid, BallisticAmmoProviderComponent component, AfterInteractEvent args) { if (args.Handled || diff --git a/Content.Shared/White/Crossbow/DrawableComponent.cs b/Content.Shared/White/Crossbow/DrawableComponent.cs new file mode 100644 index 0000000000..fb41861125 --- /dev/null +++ b/Content.Shared/White/Crossbow/DrawableComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Weapons.Ranged.Components; +using Robust.Shared.Audio; + +namespace Content.Shared.White.Crossbow; + +[RegisterComponent] +public sealed partial class DrawableComponent : Component +{ + [ViewVariables] + public bool Drawn; + + public BallisticAmmoProviderComponent Provider = default!; + + [ViewVariables(VVAccess.ReadWrite), DataField("soundInsert")] + public SoundSpecifier? SoundDraw = new SoundPathSpecifier("/Audio/Weapons/drawbow2.ogg"); +} diff --git a/Content.Shared/White/Crossbow/DrawableSystem.cs b/Content.Shared/White/Crossbow/DrawableSystem.cs new file mode 100644 index 0000000000..72340f43ec --- /dev/null +++ b/Content.Shared/White/Crossbow/DrawableSystem.cs @@ -0,0 +1,71 @@ +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.Crossbow; + +public sealed class DrawableSystem : 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 OnUse(EntityUid uid, DrawableComponent component, UseInHandEvent args) + { + if (component.Drawn || component.Provider.Count == 0) + return; + + args.Handled = true; + + _audio.PlayPredicted(component.SoundDraw, uid, args.User); + component.Drawn = true; + + UpdateDrawableAppearance(uid, component); + } + + private void OnItemRemove(EntityUid uid, DrawableComponent component, EntRemovedFromContainerMessage args) + { + if (!component.Drawn || args.Container.ID != component.Provider.Container.ID) + return; + + component.Drawn = false; + UpdateDrawableAppearance(uid, component); + } + + private void OnStartup(EntityUid uid, DrawableComponent component, ComponentStartup args) + { + component.Provider = EnsureComp(uid); + } + + private void OnAttemptShoot(EntityUid uid, DrawableComponent component, ref AttemptShootEvent args) + { + if (!component.Drawn) + args.Cancelled = true; + } + + private void UpdateDrawableAppearance(EntityUid uid, DrawableComponent component) + { + if (!TryComp(uid, out var appearance)) + return; + + _appearance.SetData(uid, DrawableVisuals.Drawn, component.Drawn, appearance); + } +} + +[Serializable, NetSerializable] +public enum DrawableVisuals : byte +{ + Drawn +} diff --git a/Content.Shared/White/Crossbow/EmbedEvents.cs b/Content.Shared/White/Crossbow/EmbedEvents.cs new file mode 100644 index 0000000000..25c05d34df --- /dev/null +++ b/Content.Shared/White/Crossbow/EmbedEvents.cs @@ -0,0 +1,10 @@ +using Content.Shared.Projectiles; + +namespace Content.Shared.White.Crossbow; + +[ByRefEvent] +public readonly record struct EmbedStartEvent(EmbeddableProjectileComponent Embed); + +public sealed class EmbedRemovedEvent : EntityEventArgs +{ +} diff --git a/Content.Shared/White/Crossbow/PenetratedComponent.cs b/Content.Shared/White/Crossbow/PenetratedComponent.cs new file mode 100644 index 0000000000..334bbc0992 --- /dev/null +++ b/Content.Shared/White/Crossbow/PenetratedComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.White.Crossbow; + +[RegisterComponent] +public sealed partial class PenetratedComponent : Component +{ + public EntityUid? ProjectileUid; + + public bool IsPinned; +} diff --git a/Content.Shared/White/Crossbow/PenetratedSystem.cs b/Content.Shared/White/Crossbow/PenetratedSystem.cs new file mode 100644 index 0000000000..ad679fe122 --- /dev/null +++ b/Content.Shared/White/Crossbow/PenetratedSystem.cs @@ -0,0 +1,41 @@ +using Content.Shared.Movement.Events; +using Content.Shared.Projectiles; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared.White.Crossbow; + +public sealed class PenetratedSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedProjectileSystem _projectile = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMoveInput); + } + + private void OnMoveInput(EntityUid uid, PenetratedComponent component, ref MoveInputEvent args) + { + if (component is {ProjectileUid: not null, IsPinned: true}) + _projectile.AttemptEmbedRemove(component.ProjectileUid.Value, uid); + } + + public void FreePenetrated(EntityUid uid, PenetratedComponent? penetrated = null) + { + if (!Resolve(uid, ref penetrated, false)) + return; + + var xform = Transform(uid); + TryComp(uid, out var physics); + _physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform); + _transform.AttachToGridOrMap(uid, xform); + penetrated.ProjectileUid = null; + penetrated.IsPinned = false; + _physics.WakeBody(uid, body: physics); + } +} diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 2dffc838ef..9652da27d4 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -233,6 +233,7 @@ - type: DeathGasps - type: ExaminableClothes - type: CharacterInformation + - type: Penetrated - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Materials/parts.yml b/Resources/Prototypes/Entities/Objects/Materials/parts.yml index a15675995c..4db618761e 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/parts.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/parts.yml @@ -32,6 +32,10 @@ - type: PhysicalComposition materialComposition: Steel: 50 #Half of a regular steel sheet to reflect the crafting recipe + - type: Tag + tags: + - DroneUsable + - CrossbowBolt - type: Stack stackType: MetalRod baseLayer: base @@ -82,6 +86,7 @@ tags: - RodMetal1 - DroneUsable + - CrossbowBolt - type: Sprite state: rods - type: Stack @@ -97,10 +102,31 @@ tags: - RodMetal1 - DroneUsable + - CrossbowBolt + - type: Ammo + muzzleFlash: null + - type: DamageOtherOnHit + damage: + types: + Blunt: 0 - type: Sprite state: rods - type: Stack count: 1 + - type: Fixtures + fixtures: + fix1: + shape: !type:PolygonShape + vertices: + - -0.20,-0.10 + - -0.10,-0.20 + - 0.40,0.30 + - 0.30,0.40 + density: 20 + mask: + - ItemMask + restitution: 0.3 + friction: 0.2 - type: entity parent: PartRodMetal diff --git a/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml index abf4e0974a..bc7be35905 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/improvised_gun_parts.yml @@ -28,8 +28,9 @@ sprite: Objects/Misc/rifle_stock.rsi state: icon - type: Construction - graph: RifleStockGraph - node: riflestock + deconstructionTarget: null + graph: WeaponPoweredCrossbowGraph + node: stock - type: Tag tags: - RifleStock 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..25d361c28e --- /dev/null +++ b/Resources/Prototypes/White/Entities/Objects/Weapons/Guns/crossbow.yml @@ -0,0 +1,79 @@ +- type: entity + name: арбалет + parent: BaseItem + id: WeaponPoweredCrossbow + description: Опасная штука, страшная вещь. + components: + - type: Sprite + sprite: White/Objects/Weapons/crossbow.rsi + state: base + - type: Clothing + quickEquip: false + slots: + - Back + - type: Item + size: 80 + sprite: White/Objects/Weapons/crossbow.rsi + - type: Gun + forceThrowingAngle: true + angle: 225 + projectileSpeed: 35 + fireRate: 0.5 + soundGunshot: + path: /Audio/Weapons/click.ogg + - 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 + - type: PowerCellSlot + cellSlotId: cell_slot + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + - type: Drawable + - type: Appearance + - type: ModifyDamageOnShoot + addEmbedding: true + offset: 0.2,0.2 + damage: + types: + Blunt: 15 + - type: Powered + charge: 360 + damage: + types: + Blunt: 30 + - type: Construction + deconstructionTarget: null + graph: WeaponPoweredCrossbowGraph + node: crossbow + +- type: entity + name: часть арбалета + parent: BaseItem + id: WeaponPoweredCrossbowUnfinished + description: Недоделанный арбалет. + components: + - type: Sprite + sprite: White/Objects/Weapons/crossbow.rsi + state: base + - type: Item + size: 80 + sprite: White/Objects/Weapons/crossbow.rsi + - type: Clothing + quickEquip: false + slots: + - Back + - 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 f5df7fae0f..c3afbfe4fb 100644 --- a/Resources/Prototypes/White/Recipes/hidden_crafts.yml +++ b/Resources/Prototypes/White/Recipes/hidden_crafts.yml @@ -43,3 +43,55 @@ doAfter: 1 - node: flamethrower entity: WeaponFlamethrower + +- type: constructionGraph + id: WeaponPoweredCrossbowGraph + start: stock + graph: + - node: stock + entity: RifleStock + edges: + - to: unfinished + steps: + - material: MetalRod + amount: 3 + doAfter: 3 + - node: unfinished + edges: + - to: rods + - node: unfinished + entity: WeaponPoweredCrossbowUnfinished + edges: + - to: welded + steps: + - tool: Welding + doAfter: 5 + - node: welded + edges: + - to: cables + steps: + - material: Cable + amount: 5 + doAfter: 0.5 + - node: cables + edges: + - to: plastic + steps: + - material: Plastic + amount: 3 + doAfter: 0.5 + - node: plastic + edges: + - to: unscrewed + steps: + - material: Cable + amount: 5 + doAfter: 0.5 + - node: unscrewed + edges: + - to: crossbow + steps: + - tool: Screwing + doAfter: 1 + - node: crossbow + entity: WeaponPoweredCrossbow diff --git a/Resources/Prototypes/White/tags.yml b/Resources/Prototypes/White/tags.yml index 77fb65e2eb..776329923e 100644 --- a/Resources/Prototypes/White/tags.yml +++ b/Resources/Prototypes/White/tags.yml @@ -3,3 +3,6 @@ - type: Tag id: EnergySword + +- type: Tag + id: CrossbowBolt diff --git a/Resources/Textures/White/Objects/Weapons/crossbow.rsi/base.png b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/base.png new file mode 100644 index 0000000000000000000000000000000000000000..62741449a4e23097c24edda78e9f9f5b30d0c208 GIT binary patch literal 845 zcmV-T1G4;yP)Px&2T4RhR9J=0mr-leP!z}i>GtBFY8T2Jj*S+V$_N{yb7K48`f!U7=%d{B(TDcc zZ_w>y#1An0JSfQWBEnw8F@)i0I$8u*l`fl7(Kn9|?Tty3w4J)n1A&~}B>&$%=bn=r z34|-P(ACQSmjKduli~)DK%DcjLb#=z9zhxhfF}i<^Ks|~16Y>TJsDFP2B$y*U3Mxw z5nzn{b_kM|R!JO}0Wj(E2$CTg)Ia-;Ri!(g237YC|7k?LeL#d?-G2ewfc&$qyEu~_8X#ua_y~pBQ z6aa8L8o|p06G7?FpXt8*0q@`4P`Zz-XD8~zw z;{|d)qw6}A%Vi3DCjpGH?pe?GkATlrEu~^662|BA4kqT8u~XSbju*OPnTPP3v7hlg zC(E*67zRL)yi$Hhy^v{Xm1ZJgoQ7jA;e%QoJI}XAt`27^CWqZzEEdtsHhWMXu`oN0 z`}exSM<<^Egz!kyfP5Ex6cD@HVps}#GcT5k1OUlo60KGXUyoj)5t;=6#FYdZW({#A z0cG(HUR0|CAy}4?EXx=ifxh-DgiFPeor?9PDeQl{freSbN^%XIP6uZF*p7xDgb;T| zU|IILNAdhf*%TOyO`CaV6o3gb-gzu3`4pjO{yT z?}`A+G6Dc&*SPS#8Zzp7oL6l2>**FEA;{xF3 z^z=x=!n|cDHYITHj)nmV8&x4KBQEjxn@36c6V&gjIA2F|8`OoPu XhIg*+*v@0x00000NkvXXu0mjf)~I}< literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Weapons/crossbow.rsi/drawn.png b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/drawn.png new file mode 100644 index 0000000000000000000000000000000000000000..480133d9c6d068a00482084a3e54f5cdb82aab1f GIT binary patch literal 919 zcmV;I18Dq-P)Px&QAtEWR9J=0m(Ob>Wf;dl?^Z;`fOb}Pn?vgq>|zNGno?wo(1UM?3>NlKnDn6H zp*{IGbn`dtY33m8B@K93$P$Dd5B_Qop+UNYE}LDpW)Bmkov4ktUer%XM@`02R$8iD(V?Gr?Eq?MjuIo~`UijCbdX4t87AVWIDBrw6 zWwXwrxeP#gy(lc(Qm*TgOeUWj==4eplyNo1$oLFTAKzu+ohiQeUZT8S>=Nt;B(qXE zjuUyZP6e=ROObrs`kin-|tsyofTPV@^i zZ>mzM)CCcH7r3qqKxMN|b*IUt*AqN_e3y~&8Seem;(hHe7W2zzI;y4hl8{n%r5y=i z*_J943c}Ge51X@gtjk5m-BoYb8W7W*p@AHT}Rh-DjRiC-DxuUcZ==X7Kucn%ZZ#}^mRjKr8?CC*L8c2if!4! zy0TFh#(YZ6O}|2|wgo_XVTtOkFPNi9&RB*#D#{68Jk|lrw$vb9E$jrqc5RDyuYE?d z)n@P8Vx$u?E0wP6c%DbB13jCs4}>wFQX}JIOio>4yS7DoVTm8^e1%rIP0siT@}VxH z!NI`+0O4K`IAG>Y)m8riA>eRB`VjABKg9DqzN_2@Af1^dXBfvEq3j{l5@c2?0OyW# z=8I)9zpT=kSvs8#j~e#@c;fAG?c+~rJa`DeqsD!nczc{|$HzTMprn-i69^yvW$g{p znOVjzU!w8gAwM_n@#5%30Dfz?DVK_U?t-JCG`vgWfP7IuG)?1AE-*QD1%UScULXwd z{ULH5kW%6wmy^dPEExXY2Y79CbRc0rdinqg;Rr{;;dB%y7#D*I#{(Qe07QXAMKHpJ t{yS(4Hv9Jb)pHE4Lw*tKjdOoFeFjq)ugia+V7LGP002ovPDHLkV1iMPrVs!C literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Weapons/crossbow.rsi/inhand-left.png b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..385c96238dadee703e098eaa0a24a02f4d2c3b2f GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|?s~d7hE&XX zd&`io*?_0@VY$e9gO=`)y?*Zm3e(GrL={Cja#tz*z0cKlqvEkAPy-Mg@VAlYpRQtD z>>1_#ajQst(5Jn-R8{+{U;kRXYoW_$%XIV2<;Ir^mA`)7Dq?PY-tvAv$0QT~$?n4G z`|3J$J9bshdUv&A1^@oLEsJ+W{FO*LUtA{4et+H9nX`7i+r}IbWVQF3?R-XvT|ly= z#Ek#Kqu?@yy5szdRrxO@>029I5s3QCpxbtiFXk-smVL=L-W-@}b71Q8>+dD982bP0l+XkK?Wc0f literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Weapons/crossbow.rsi/inhand-right.png b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..93a062d9ee3eb808eec55f34af0667eaeba3e4cf GIT binary patch literal 547 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z?|Qm8hE&XX zJIk@F*+9bWKTq+JN4wZJ2J8;@YI$V1Bw*r>)r~WSOT_YaPrIkN+3TWK^AVr@V*j`6 zT-#@}SVXvRZ(Qcdngy&E3K+I9Nbxm@IUHuZz?5OZ5XHdD?!dj^AwveEi422oLmM+t z(HEvAL9?Q71Cf-{qPJz)9UXm@c>+c2mIQtK^LECY=doG8_{Xn* zeU~qi=t(wUe)C}KwMQ}6w{$OhE>WE(?pm@g`%+YQkmjwHhc62EGL-GgSnH~_wDP$2 zeb>;b9!pK4Uf;{Tn_9qKvXQ@cw`k2{kp~k}n>t=RFIreTDPX0@ z&DHOV6~Bgv96Hw&q#3{bt>J4)gHoW{yMMVnWvchfrrEPBUvqHxr=GuDo2MP0zqfwD r@9KSV-TUAEV~7*~z=9Iy&%_U3d6zK%n7AA;))_oq{an^LB{Ts5C8g;5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Weapons/crossbow.rsi/loaded.png b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/loaded.png new file mode 100644 index 0000000000000000000000000000000000000000..36cb5cf2e7b9c812d55da36393a1ec9cac230be3 GIT binary patch literal 973 zcmV;;12X)HP)Px&heWf;dl)9pc5qqZ^B(PjGsHl<5wsi{>PWSheq3IhRIl}V3# zNUmNyxBVZyqz8}PKv7{Kuyj@^NI_(mMJ-AjNvLkKNH*y%VOKopvxj7|GntvR6*u*P zm&}v*$Mb!@&yROTLpW0zI9v1oO8{DbgW?9DA*N~e6@=TAQxmj)0PuvsG|hh44F*uJ z*IN&L3PbM@Xvp&pg@*y9)H6lUj6_WA@jL(wx->zvG)ua@U!0A$<`YP9XZJs4#M=jK z%54z9VeN>++7XMhQT`1RDy1YSr+Rn7Hki`A8bhy~KQ)N_dR|smSH#+~G^LbpV)xE< z4KWsC%wHbm`=1ZEbRh!3Tah8Q@9q;6j{JIFs?{oMt81=#Gh<4jQ0VG05-=Q^u@IAM z#`|I{#N^XYk^)HDT$Z%CEY3aod|p;oRwVFD0i{&ysjd4n;PbDs5MyL0#2-gZ&Rv}5 z^=5^sF`aMgFJL6E;ODWQm>E;*^*WYifuMO!`9W$^W+Y-VG8E!LXdHmQfBTU+b(On& zC2m~bIB|72Los;V;%qicB~@vIy3eRS%)9TjJbo0N7T=hY5I&r64NwY zSsDb$Hfl>a9OelFOhhLEXgsI^FnDl>sWF|;KL5(sDc?@0l)^5T?y_+jbk5rVUK<_; z;9;Wyv>KCLeue7=4~U+3fgg}wzfT-(rMnpgf$;AG?KoMQ)dA8=EX(rj^{m;lwC=Fk v8;&2xtvJaU?O~dxYki+T#JqCmAE!?NpWoarv42^B00000NkvXXu0mjfIW@wr literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Weapons/crossbow.rsi/meta.json b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/meta.json new file mode 100644 index 0000000000..fa3ce2ddec --- /dev/null +++ b/Resources/Textures/White/Objects/Weapons/crossbow.rsi/meta.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from paradise at https://github.com/ParadiseSS13/Paradise at 76d0428022d17f3249585d96ac9b69076206efd4", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "base" + }, + { + "name": "loaded" + }, + { + "name": "drawn" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +}