diff --git a/Content.Client/Storage/Components/StorageContainerVisualsComponent.cs b/Content.Client/Storage/Components/StorageContainerVisualsComponent.cs
new file mode 100644
index 0000000000..9f07867da8
--- /dev/null
+++ b/Content.Client/Storage/Components/StorageContainerVisualsComponent.cs
@@ -0,0 +1,29 @@
+using Content.Client.Chemistry.Visualizers;
+
+namespace Content.Client.Storage.Components;
+
+///
+/// Essentially a version of fill level handling but for item storage.
+/// Depending on the fraction of storage that's filled, will change the sprite at to the nearest
+/// fill level, up to .
+///
+[RegisterComponent]
+public sealed partial class StorageContainerVisualsComponent : Component
+{
+ [DataField("maxFillLevels")]
+ public int MaxFillLevels = 0;
+
+ ///
+ /// A prefix to use for the fill states., i.e. {FillBaseName}{fill level} for the state
+ ///
+ [DataField("fillBaseName")]
+ public string? FillBaseName;
+
+ [DataField("layer")]
+ public StorageContainerVisualLayers FillLayer = StorageContainerVisualLayers.Fill;
+}
+
+public enum StorageContainerVisualLayers : byte
+{
+ Fill
+}
diff --git a/Content.Client/Storage/Systems/StorageContainerVisualsSystem.cs b/Content.Client/Storage/Systems/StorageContainerVisualsSystem.cs
new file mode 100644
index 0000000000..a13d6cba22
--- /dev/null
+++ b/Content.Client/Storage/Systems/StorageContainerVisualsSystem.cs
@@ -0,0 +1,44 @@
+using Content.Client.Storage.Components;
+using Content.Shared.Rounding;
+using Content.Shared.Storage;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Storage.Systems;
+
+///
+public sealed class StorageContainerVisualsSystem : VisualizerSystem
+{
+ protected override void OnAppearanceChange(EntityUid uid, StorageContainerVisualsComponent component, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null)
+ return;
+
+ if (!AppearanceSystem.TryGetData(uid, StorageVisuals.StorageUsed, out var used, args.Component))
+ return;
+
+ if (!AppearanceSystem.TryGetData(uid, StorageVisuals.Capacity, out var capacity, args.Component))
+ return;
+
+ var fraction = used / (float) capacity;
+
+ if (!args.Sprite.LayerMapTryGet(component.FillLayer, out var fillLayer))
+ return;
+
+ var closestFillSprite = Math.Min(ContentHelpers.RoundToNearestLevels(fraction, 1, component.MaxFillLevels + 1),
+ component.MaxFillLevels);
+
+ if (closestFillSprite > 0)
+ {
+ if (component.FillBaseName == null)
+ return;
+
+ args.Sprite.LayerSetVisible(fillLayer, true);
+ var stateName = component.FillBaseName + closestFillSprite;
+ args.Sprite.LayerSetState(fillLayer, stateName);
+ }
+ else
+ {
+ args.Sprite.LayerSetVisible(fillLayer, false);
+ }
+ }
+}
diff --git a/Content.Server/Item/ItemSystem.cs b/Content.Server/Item/ItemSystem.cs
index 55fb5aae09..efb99ae653 100644
--- a/Content.Server/Item/ItemSystem.cs
+++ b/Content.Server/Item/ItemSystem.cs
@@ -1,4 +1,5 @@
-using Content.Server.Storage.EntitySystems;
+using Content.Server.Storage.Components;
+using Content.Server.Storage.EntitySystems;
using Content.Shared.Item;
using Content.Shared.Stacks;
using Content.Shared.Storage;
@@ -15,11 +16,9 @@ public sealed class ItemSystem : SharedItemSystem
if (!Container.TryGetContainingContainer(uid, out var container) ||
!TryComp(container.Owner, out var storage))
- {
return;
- }
- _storage.RecalculateStorageUsed(storage);
+ _storage.RecalculateStorageUsed(container.Owner, storage);
_storage.UpdateUI(container.Owner, storage);
}
}
diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs
index 61d67a469b..60fc0c3b5c 100644
--- a/Content.Server/Projectiles/ProjectileSystem.cs
+++ b/Content.Server/Projectiles/ProjectileSystem.cs
@@ -9,7 +9,6 @@ using Content.Shared.Projectiles;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Robust.Shared.Physics.Events;
-using Content.Shared.Effects;
namespace Content.Server.Projectiles;
@@ -30,7 +29,8 @@ public sealed class ProjectileSystem : SharedProjectileSystem
private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args)
{
// This is so entities that shouldn't get a collision are ignored.
- if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard || component.DamagedEntity)
+ if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard
+ || component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true })
return;
var target = args.OtherEntity;
@@ -60,7 +60,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
_adminLogger.Add(LogType.BulletHit,
HasComp(target) ? LogImpact.Extreme : LogImpact.High,
- $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter):user} hit {otherName:target} and dealt {modifiedDamage.Total:damage} damage");
+ $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter!.Value):user} hit {otherName:target} and dealt {modifiedDamage.Total:damage} damage");
}
if (!deleted)
diff --git a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs
index cf9c15a10d..cde7e637d4 100644
--- a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs
+++ b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs
@@ -29,6 +29,12 @@ public sealed partial class EmbeddableProjectileComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("removalTime"), AutoNetworkedField]
public float? RemovalTime = 3f;
+ ///
+ /// Whether this entity will embed when thrown, or only when shot as a projectile.
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField("embedOnThrow"), AutoNetworkedField]
+ public bool EmbedOnThrow = true;
+
///
/// How far into the entity should we offset (0 is wherever we collided).
///
diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs
index 1e77e3954c..276e0943e0 100644
--- a/Content.Shared/Projectiles/ProjectileComponent.cs
+++ b/Content.Shared/Projectiles/ProjectileComponent.cs
@@ -15,13 +15,14 @@ public sealed partial class ProjectileComponent : Component
///
/// User that shot this projectile.
///
- [DataField("shooter"), AutoNetworkedField] public EntityUid Shooter;
+ [DataField("shooter"), AutoNetworkedField]
+ public EntityUid? Shooter;
///
/// Weapon used to shoot.
///
[DataField("weapon"), AutoNetworkedField]
- public EntityUid Weapon;
+ public EntityUid? Weapon;
[DataField("ignoreShooter"), AutoNetworkedField]
public bool IgnoreShooter = true;
@@ -41,5 +42,14 @@ public sealed partial class ProjectileComponent : Component
[DataField("soundForce")]
public bool ForceSound = false;
+ ///
+ /// Whether this projectile will only collide with entities if it was shot from a gun (if is not null)
+ ///
+ [DataField("onlyCollideWhenShot")]
+ public bool OnlyCollideWhenShot = false;
+
+ ///
+ /// Whether this projectile has already damaged an entity.
+ ///
public bool DamagedEntity;
}
diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs
index 5d4046556a..92465be2e5 100644
--- a/Content.Shared/Projectiles/SharedProjectileSystem.cs
+++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs
@@ -73,6 +73,14 @@ public abstract partial class SharedProjectileSystem : EntitySystem
_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);
@@ -81,6 +89,9 @@ public abstract partial class SharedProjectileSystem : EntitySystem
private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args)
{
+ if (!component.EmbedOnThrow)
+ return;
+
Embed(uid, args.Target, component);
}
@@ -91,7 +102,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
// Raise a specific event for projectiles.
if (TryComp(uid, out var projectile))
{
- var ev = new ProjectileEmbedEvent(projectile.Shooter, projectile.Weapon, args.Target);
+ var ev = new ProjectileEmbedEvent(projectile.Shooter!.Value, projectile.Weapon!.Value, args.Target);
RaiseLocalEvent(uid, ref ev);
}
}
diff --git a/Content.Shared/Storage/Components/ItemMapperComponent.cs b/Content.Shared/Storage/Components/ItemMapperComponent.cs
index 4512503fde..fd83120af0 100644
--- a/Content.Shared/Storage/Components/ItemMapperComponent.cs
+++ b/Content.Shared/Storage/Components/ItemMapperComponent.cs
@@ -66,6 +66,11 @@ namespace Content.Shared.Storage.Components
[DataField("containerWhitelist")]
public HashSet? ContainerWhitelist;
- public readonly List SpriteLayers = new();
+ ///
+ /// The list of map layer keys that are valid targets for changing in
+ /// Can be initialized if already existing on the sprite, or inferred automatically
+ ///
+ [DataField("spriteLayers")]
+ public List SpriteLayers = new();
}
}
diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs
index 6f1336a53b..5faec99fd5 100644
--- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs
+++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs
@@ -92,7 +92,7 @@ public abstract class SharedStorageSystem : EntitySystem
if (component.Container == default)
return;
- RecalculateStorageUsed(component);
+ RecalculateStorageUsed(uid, component);
UpdateStorageVisualization(uid, component);
UpdateUI(uid, component);
Dirty(uid, component);
@@ -394,7 +394,7 @@ public abstract class SharedStorageSystem : EntitySystem
_appearance.SetData(uid, StackVisuals.Hide, !storageComp.IsUiOpen);
}
- public void RecalculateStorageUsed(StorageComponent storageComp)
+ public void RecalculateStorageUsed(EntityUid uid, StorageComponent storageComp)
{
storageComp.StorageUsed = 0;
@@ -406,6 +406,9 @@ public abstract class SharedStorageSystem : EntitySystem
var size = itemComp.Size;
storageComp.StorageUsed += size;
}
+
+ _appearance.SetData(uid, StorageVisuals.StorageUsed, storageComp.StorageUsed);
+ _appearance.SetData(uid, StorageVisuals.Capacity, storageComp.StorageCapacityMax);
}
public int GetAvailableSpace(EntityUid uid, StorageComponent? component = null)
diff --git a/Content.Shared/Storage/SharedStorageComponent.cs b/Content.Shared/Storage/SharedStorageComponent.cs
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Content.Shared/Storage/StorageComponent.cs b/Content.Shared/Storage/StorageComponent.cs
index 0a924365a4..fdcf06bf5d 100644
--- a/Content.Shared/Storage/StorageComponent.cs
+++ b/Content.Shared/Storage/StorageComponent.cs
@@ -130,6 +130,8 @@ namespace Content.Shared.Storage
Open,
HasContents,
CanLock,
- Locked
+ Locked,
+ StorageUsed,
+ Capacity
}
}
diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs
index e98beb96c9..35bfc069eb 100644
--- a/Content.Shared/Throwing/ThrowingSystem.cs
+++ b/Content.Shared/Throwing/ThrowingSystem.cs
@@ -106,7 +106,8 @@ public sealed class ThrowingSystem : EntitySystem
return;
}
- if (projectileQuery.HasComponent(uid))
+ // Allow throwing if this projectile only acts as a projectile when shot, otherwise disallow
+ if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot)
return;
var comp = EnsureComp(uid);
diff --git a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs
index 7289b232dc..119b10218d 100644
--- a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs
+++ b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs
@@ -64,7 +64,7 @@ public abstract class SharedDamageMarkerSystem : EntitySystem
component.Amount <= 0 ||
component.Whitelist?.IsValid(args.OtherEntity, EntityManager) == false ||
!TryComp(uid, out var projectile) ||
- !projectile.Weapon.IsValid())
+ projectile.Weapon == null)
{
return;
}
@@ -72,7 +72,7 @@ public abstract class SharedDamageMarkerSystem : EntitySystem
// Markers are exclusive, deal with it.
var marker = EnsureComp(args.OtherEntity);
marker.Damage = new DamageSpecifier(component.Damage);
- marker.Marker = projectile.Weapon;
+ marker.Marker = projectile.Weapon.Value;
marker.EndTime = _timing.CurTime + component.Duration;
component.Amount--;
Dirty(marker);
diff --git a/Content.Shared/Weapons/Reflect/SharedReflectSystem.cs b/Content.Shared/Weapons/Reflect/SharedReflectSystem.cs
index f6193c6898..ffa8180e1a 100644
--- a/Content.Shared/Weapons/Reflect/SharedReflectSystem.cs
+++ b/Content.Shared/Weapons/Reflect/SharedReflectSystem.cs
@@ -119,7 +119,7 @@ public abstract class SharedReflectSystem : EntitySystem
if (Resolve(projectile, ref projectileComp, false))
{
- _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectileComp.Weapon)} shot by {projectileComp.Shooter}");
+ _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectileComp.Weapon!.Value)} shot by {projectileComp.Shooter!.Value}");
projectileComp.Shooter = user;
projectileComp.Weapon = user;
diff --git a/Content.Shared/Wieldable/Components/WieldableComponent.cs b/Content.Shared/Wieldable/Components/WieldableComponent.cs
index 5a8c533c29..050b638215 100644
--- a/Content.Shared/Wieldable/Components/WieldableComponent.cs
+++ b/Content.Shared/Wieldable/Components/WieldableComponent.cs
@@ -1,5 +1,6 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
namespace Content.Shared.Wieldable.Components;
@@ -33,3 +34,9 @@ public sealed partial class WieldableComponent : Component
[DataField("wieldTime")]
public float WieldTime = 1.5f;
}
+
+[Serializable, NetSerializable]
+public enum WieldableVisuals : byte
+{
+ Wielded
+}
diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs
index d4d83dfdf7..bf5aa1b723 100644
--- a/Content.Shared/Wieldable/WieldableSystem.cs
+++ b/Content.Shared/Wieldable/WieldableSystem.cs
@@ -24,6 +24,7 @@ public sealed class WieldableSystem : EntitySystem
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
public override void Initialize()
{
@@ -224,6 +225,7 @@ public sealed class WieldableSystem : EntitySystem
var ev = new ItemWieldedEvent();
RaiseLocalEvent(uid, ref ev);
+ _appearance.SetData(uid, WieldableVisuals.Wielded, true);
Dirty(component);
args.Handled = true;
@@ -254,6 +256,8 @@ public sealed class WieldableSystem : EntitySystem
("user", args.User.Value), ("item", uid)), args.User.Value, Filter.PvsExcept(args.User.Value), true);
}
+ _appearance.SetData(uid, WieldableVisuals.Wielded, false);
+
Dirty(component);
_virtualItemSystem.DeleteInHandsMatching(args.User.Value, uid);
}
diff --git a/Resources/Audio/Items/attributions.yml b/Resources/Audio/Items/attributions.yml
index 267d6f3a0a..b69704ce69 100644
--- a/Resources/Audio/Items/attributions.yml
+++ b/Resources/Audio/Items/attributions.yml
@@ -62,3 +62,8 @@
license: "CC-BY-4.0"
copyright: "User volivieri on freesound.org. Modified by Velcroboy on github."
source: "https://freesound.org/people/volivieri/sounds/37190/"
+
+- files: ["bow_pull.ogg"]
+ license: "CC-BY-3.0"
+ copyright: "User jzdnvdoosj on freesound.org. Converted to ogg by mirrorcult"
+ source: "https://freesound.org/people/jzdnvdoosj/sounds/626262/"
\ No newline at end of file
diff --git a/Resources/Audio/Items/bow_pull.ogg b/Resources/Audio/Items/bow_pull.ogg
new file mode 100644
index 0000000000..728489b976
Binary files /dev/null and b/Resources/Audio/Items/bow_pull.ogg differ
diff --git a/Resources/Audio/Weapons/Guns/Misc/arrow_nock.ogg b/Resources/Audio/Weapons/Guns/Misc/arrow_nock.ogg
new file mode 100644
index 0000000000..2285c47edf
Binary files /dev/null and b/Resources/Audio/Weapons/Guns/Misc/arrow_nock.ogg differ
diff --git a/Resources/Audio/Weapons/Guns/Misc/attributions.yml b/Resources/Audio/Weapons/Guns/Misc/attributions.yml
new file mode 100644
index 0000000000..066c26206e
--- /dev/null
+++ b/Resources/Audio/Weapons/Guns/Misc/attributions.yml
@@ -0,0 +1,4 @@
+- files: ["arrow_nock.ogg"]
+ license: "CC-BY-NC-4.0"
+ copyright: "Created by LiamG_SFX, converted to ogg by mirrorcult"
+ source: "https://freesound.org/people/LiamG_SFX/sounds/322224/"
\ No newline at end of file
diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml
index 32c1cfb506..9ed3acc049 100644
--- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml
+++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml
@@ -204,6 +204,8 @@
prob: 0.2
- id: ClothingHandsGlovesColorYellow
prob: 0.05
+ - id: ClothingBeltQuiver
+ prob: 0.02
- id: ClothingBeltUtility
prob: 0.10
- id: ClothingHeadHatCone
diff --git a/Resources/Prototypes/Entities/Clothing/Belt/quiver.yml b/Resources/Prototypes/Entities/Clothing/Belt/quiver.yml
new file mode 100644
index 0000000000..10332e46aa
--- /dev/null
+++ b/Resources/Prototypes/Entities/Clothing/Belt/quiver.yml
@@ -0,0 +1,22 @@
+- type: entity
+ parent: ClothingBeltStorageBase
+ id: ClothingBeltQuiver
+ name: quiver
+ description: Can hold up to 15 arrows, and fits snug around your waist.
+ components:
+ - type: Sprite
+ sprite: Clothing/Belt/quiver.rsi
+ layers:
+ - state: icon
+ - map: [ "enum.StorageContainerVisualLayers.Fill" ]
+ visible: false
+ - type: Clothing
+ - type: Storage
+ capacity: 150
+ whitelist:
+ tags:
+ - Arrow
+ - type: Appearance
+ - type: StorageContainerVisuals
+ maxFillLevels: 3
+ fillBaseName: fill-
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Bow/bow.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Bow/bow.yml
new file mode 100644
index 0000000000..95282ee63a
--- /dev/null
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Bow/bow.yml
@@ -0,0 +1,81 @@
+- type: entity
+ name: bow
+ parent: BaseItem
+ id: BaseBow
+ description: The original rooty tooty point and shooty.
+ abstract: true
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Bow/bow.rsi
+ - type: Item
+ size: 60
+ - type: Clothing
+ quickEquip: false
+ slots:
+ - Back
+ - type: Wieldable
+ wieldTime: 0.5
+ wieldSound:
+ path: /Audio/Items/bow_pull.ogg
+ - type: GunRequiresWield
+ - type: Gun
+ minAngle: 0
+ maxAngle: 5
+ fireRate: 1
+ selectedMode: SemiAuto
+ availableModes:
+ - SemiAuto
+ soundGunshot:
+ collection: BulletMiss
+ soundEmpty: null
+ - type: ItemSlots
+ slots:
+ arrow:
+ name: Arrow
+ startingItem: null
+ insertSound: /Audio/Weapons/Guns/Misc/arrow_nock.ogg
+ whitelist:
+ tags:
+ - Arrow
+ - type: ContainerContainer
+ containers:
+ arrow: !type:ContainerSlot
+ - type: ContainerAmmoProvider
+ container: arrow
+
+- type: entity
+ id: BowImprovised
+ parent: BaseBow
+ components:
+ - type: Sprite
+ layers:
+ - state: unwielded
+ map: [ base ]
+ - state: unwielded-arrow
+ map: [ arrow ]
+ visible: false
+ # to elucidate whats intended here:
+ # arrow is inserted -> ItemMapper sets layer with map `arrow` to visible
+ # bow is wielded -> generic vis sets states of layers with map `arrow` and `base`
+ # arrow is removed -> itemmapper sets layer with map `arrow` to invisible
+ - type: Appearance
+ - type: ItemMapper
+ spriteLayers:
+ - arrow
+ mapLayers:
+ arrow:
+ whitelist:
+ tags:
+ - Arrow
+ - type: GenericVisualizer
+ visuals:
+ enum.WieldableVisuals.Wielded:
+ arrow:
+ True: { state: wielded-arrow }
+ False: { state: unwielded-arrow }
+ base:
+ True: { state: wielded }
+ False: { state: unwielded }
+ - type: Construction
+ graph: ImprovisedBow
+ node: ImprovisedBow
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml
new file mode 100644
index 0000000000..7112d07f2f
--- /dev/null
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml
@@ -0,0 +1,84 @@
+- type: entity
+ parent: BaseItem
+ id: BaseArrow
+ abstract: true
+ components:
+ - type: Item
+ size: 10
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/arrows.rsi
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape: !type:PhysShapeCircle
+ radius: 0.2
+ density: 5
+ mask:
+ - ItemMask
+ restitution: 0.3
+ friction: 0.2
+ projectile:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.1,-0.1,0.1,0.1"
+ hard: false
+ mask:
+ - Impassable
+ - BulletImpassable
+ - type: EmbeddableProjectile
+ sound: /Audio/Weapons/star_hit.ogg
+ embedOnThrow: false
+ - type: ThrowingAngle
+ angle: 0
+ - type: Ammo
+ muzzleFlash: null
+ - type: Tag
+ tags:
+ - Arrow
+ - type: Projectile
+ deleteOnCollide: false
+ onlyCollideWhenShot: true
+ damage:
+ types:
+ Piercing: 25
+
+- type: entity
+ parent: BaseArrow
+ id: ArrowRegular
+ name: arrow
+ description: You can feel the power of the steppe within you.
+ components:
+ - type: Sprite
+ layers:
+ - state: tail
+ color: red
+ - state: rod
+ color: brown
+ - state: tip
+ - type: Projectile
+ damage:
+ types:
+ Piercing: 35
+
+- type: entity
+ parent: BaseArrow
+ id: ArrowImprovised
+ name: glass shard arrow
+ description: The greyshirt's preferred projectile.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/arrows.rsi
+ layers:
+ - state: tail
+ color: white
+ - state: rod
+ color: darkgray
+ - state: tip
+ color: lightblue
+ - type: Projectile
+ damage:
+ types:
+ Piercing: 25
+ - type: Construction
+ graph: ImprovisedArrow
+ node: ImprovisedArrow
diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/improvised_arrow.yml b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/improvised_arrow.yml
new file mode 100644
index 0000000000..346b49cf2f
--- /dev/null
+++ b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/improvised_arrow.yml
@@ -0,0 +1,23 @@
+- type: constructionGraph
+ id: ImprovisedArrow
+ start: start
+ graph:
+ - node: start
+ edges:
+ - to: ImprovisedArrow
+ steps:
+ - material: MetalRod
+ amount: 1
+ doAfter: 0.5
+ - material: Cloth
+ amount: 1
+ doAfter: 0.5
+ - tag: GlassShard
+ name: Glass Shard
+ icon:
+ sprite: Objects/Materials/Shards/shard.rsi
+ state: shard1
+ doAfter: 0.5
+
+ - node: ImprovisedArrow
+ entity: ArrowImprovised
diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/improvised_bow.yml b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/improvised_bow.yml
new file mode 100644
index 0000000000..33808fc0ac
--- /dev/null
+++ b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/improvised_bow.yml
@@ -0,0 +1,17 @@
+- type: constructionGraph
+ id: ImprovisedBow
+ start: start
+ graph:
+ - node: start
+ edges:
+ - to: ImprovisedBow
+ steps:
+ - material: WoodPlank
+ amount: 10
+ doAfter: 4
+ - material: Cloth
+ amount: 5
+ doAfter: 4
+
+ - node: ImprovisedBow
+ entity: BowImprovised
diff --git a/Resources/Prototypes/Recipes/Construction/weapons.yml b/Resources/Prototypes/Recipes/Construction/weapons.yml
index 9d8be93601..25da0d1073 100644
--- a/Resources/Prototypes/Recipes/Construction/weapons.yml
+++ b/Resources/Prototypes/Recipes/Construction/weapons.yml
@@ -40,7 +40,7 @@
category: construction-category-weapons
description: A uranium shard with a piece of cloth wrapped around it.
icon: { sprite: Objects/Weapons/Melee/uranium_shiv.rsi, state: icon }
- objectType: Item
+ objectType: Item
- type: construction
name: crude spear
@@ -118,3 +118,25 @@
description: Crude and falling apart. Why would you make this?
icon: { sprite: Objects/Weapons/Melee/shields.rsi, state: makeshift-icon }
objectType: Item
+
+- type: construction
+ name: glass shard arrow
+ id: ImprovisedArrow
+ graph: ImprovisedArrow
+ startNode: start
+ targetNode: ImprovisedArrow
+ category: construction-category-weapons
+ description: An arrow tipped with pieces of a glass shard, for use with a bow.
+ icon: { sprite: Objects/Weapons/Guns/Bow/bow.rsi, state: wielded-arrow }
+ objectType: Item
+
+- type: construction
+ name: improvised bow
+ id: ImprovisedBow
+ graph: ImprovisedBow
+ startNode: start
+ targetNode: ImprovisedBow
+ category: construction-category-weapons
+ description: A shoddily constructed bow made out of wood and cloth. It's not much, but it's gotten the job done for millennia.
+ icon: { sprite: Objects/Weapons/Guns/Bow/bow.rsi, state: unwielded }
+ objectType: Item
diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml
index db260b5155..61acbe2ba8 100644
--- a/Resources/Prototypes/tags.yml
+++ b/Resources/Prototypes/tags.yml
@@ -16,6 +16,9 @@
- type: Tag
id: ArtifactFragment
+- type: Tag
+ id: Arrow
+
- type: Tag
id: ATVKeys
@@ -26,7 +29,7 @@
id: Balloon
- type: Tag
- id: BaseballBat
+ id: BaseballBat
- type: Tag
id: BBQsauce
@@ -306,7 +309,7 @@
id: CluwneHorn
- type: Tag #Ohioans die happy
- id: Corn
+ id: Corn
- type: Tag
id: Coldsauce
@@ -1014,7 +1017,7 @@
- type: Tag
id: WallmountSubstationElectronics
-
+
- type: Tag
id: WeaponPistolCHIMPUpgradeKit
diff --git a/Resources/Textures/Clothing/Belt/quiver.rsi/equipped-BELT.png b/Resources/Textures/Clothing/Belt/quiver.rsi/equipped-BELT.png
new file mode 100644
index 0000000000..951e4421f0
Binary files /dev/null and b/Resources/Textures/Clothing/Belt/quiver.rsi/equipped-BELT.png differ
diff --git a/Resources/Textures/Clothing/Belt/quiver.rsi/fill-1.png b/Resources/Textures/Clothing/Belt/quiver.rsi/fill-1.png
new file mode 100644
index 0000000000..3b46a22c61
Binary files /dev/null and b/Resources/Textures/Clothing/Belt/quiver.rsi/fill-1.png differ
diff --git a/Resources/Textures/Clothing/Belt/quiver.rsi/fill-2.png b/Resources/Textures/Clothing/Belt/quiver.rsi/fill-2.png
new file mode 100644
index 0000000000..3fcf6fa919
Binary files /dev/null and b/Resources/Textures/Clothing/Belt/quiver.rsi/fill-2.png differ
diff --git a/Resources/Textures/Clothing/Belt/quiver.rsi/fill-3.png b/Resources/Textures/Clothing/Belt/quiver.rsi/fill-3.png
new file mode 100644
index 0000000000..8870c8892f
Binary files /dev/null and b/Resources/Textures/Clothing/Belt/quiver.rsi/fill-3.png differ
diff --git a/Resources/Textures/Clothing/Belt/quiver.rsi/icon.png b/Resources/Textures/Clothing/Belt/quiver.rsi/icon.png
new file mode 100644
index 0000000000..c719b3030c
Binary files /dev/null and b/Resources/Textures/Clothing/Belt/quiver.rsi/icon.png differ
diff --git a/Resources/Textures/Clothing/Belt/quiver.rsi/meta.json b/Resources/Textures/Clothing/Belt/quiver.rsi/meta.json
new file mode 100644
index 0000000000..9f9ed50989
--- /dev/null
+++ b/Resources/Textures/Clothing/Belt/quiver.rsi/meta.json
@@ -0,0 +1,27 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "tgstation at a373b4cb08298523d40acc14f9c390a0c403fc31, modified by mirrorcult",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "equipped-BELT",
+ "directions": 4
+ },
+ {
+ "name": "fill-1"
+ },
+ {
+ "name": "fill-2"
+ },
+ {
+ "name": "fill-3"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/equipped-BACKPACK.png
new file mode 100644
index 0000000000..b09e803ed6
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/equipped-BACKPACK.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/inhand-left.png
new file mode 100644
index 0000000000..2ed02fb5a5
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/inhand-right.png
new file mode 100644
index 0000000000..c281a19870
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/meta.json
new file mode 100644
index 0000000000..f19b1121c1
--- /dev/null
+++ b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/meta.json
@@ -0,0 +1,43 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "tgstation at a373b4cb08298523d40acc14f9c390a0c403fc31, equipped-BACKPACK and wielded sprites added by mirrorcult",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "unwielded"
+ },
+ {
+ "name": "unwielded-arrow"
+ },
+ {
+ "name": "wielded"
+ },
+ {
+ "name": "wielded-arrow"
+ },
+ {
+ "name": "equipped-BACKPACK",
+ "directions": 4
+ },
+ {
+ "name": "inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "wielded-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "wielded-inhand-right",
+ "directions": 4
+ }
+ ]
+}
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/unwielded-arrow.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/unwielded-arrow.png
new file mode 100644
index 0000000000..d1417b707f
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/unwielded-arrow.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/unwielded.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/unwielded.png
new file mode 100644
index 0000000000..c5dc00243c
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/unwielded.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-arrow.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-arrow.png
new file mode 100644
index 0000000000..69419f9a7b
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-arrow.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-inhand-left.png
new file mode 100644
index 0000000000..75a9329f70
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-inhand-right.png
new file mode 100644
index 0000000000..87451fed6c
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded-inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded.png b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded.png
new file mode 100644
index 0000000000..41d15ef89a
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/inhand-left.png
new file mode 100644
index 0000000000..668bed84d1
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/inhand-right.png
new file mode 100644
index 0000000000..42e1ebc3a6
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/meta.json
new file mode 100644
index 0000000000..b8313f7810
--- /dev/null
+++ b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/meta.json
@@ -0,0 +1,31 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "tgstation at a373b4cb08298523d40acc14f9c390a0c403fc31, sprites modified and cut into layers by mirrorcult",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "tail"
+ },
+ {
+ "name": "rod"
+ },
+ {
+ "name": "tip"
+ },
+ {
+ "name": "solution"
+ },
+ {
+ "name": "inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "inhand-right",
+ "directions": 4
+ }
+ ]
+}
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/rod.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/rod.png
new file mode 100644
index 0000000000..935df1f13d
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/rod.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/solution.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/solution.png
new file mode 100644
index 0000000000..235f318e60
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/solution.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/tail.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/tail.png
new file mode 100644
index 0000000000..31c4ed7e1f
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/tail.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/tip.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/tip.png
new file mode 100644
index 0000000000..e4e795295d
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/arrows.rsi/tip.png differ