Bows & arrows (#19771)
@@ -0,0 +1,29 @@
|
|||||||
|
using Content.Client.Chemistry.Visualizers;
|
||||||
|
|
||||||
|
namespace Content.Client.Storage.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Essentially a version of <see cref="SolutionContainerVisualsComponent"/> fill level handling but for item storage.
|
||||||
|
/// Depending on the fraction of storage that's filled, will change the sprite at <see cref="FillLayer"/> to the nearest
|
||||||
|
/// fill level, up to <see cref="MaxFillLevels"/>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class StorageContainerVisualsComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("maxFillLevels")]
|
||||||
|
public int MaxFillLevels = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A prefix to use for the fill states., i.e. {FillBaseName}{fill level} for the state
|
||||||
|
/// </summary>
|
||||||
|
[DataField("fillBaseName")]
|
||||||
|
public string? FillBaseName;
|
||||||
|
|
||||||
|
[DataField("layer")]
|
||||||
|
public StorageContainerVisualLayers FillLayer = StorageContainerVisualLayers.Fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum StorageContainerVisualLayers : byte
|
||||||
|
{
|
||||||
|
Fill
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="StorageContainerVisualsComponent"/>
|
||||||
|
public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageContainerVisualsComponent>
|
||||||
|
{
|
||||||
|
protected override void OnAppearanceChange(EntityUid uid, StorageContainerVisualsComponent component, ref AppearanceChangeEvent args)
|
||||||
|
{
|
||||||
|
if (args.Sprite == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!AppearanceSystem.TryGetData<int>(uid, StorageVisuals.StorageUsed, out var used, args.Component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!AppearanceSystem.TryGetData<int>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Item;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
@@ -15,11 +16,9 @@ public sealed class ItemSystem : SharedItemSystem
|
|||||||
|
|
||||||
if (!Container.TryGetContainingContainer(uid, out var container) ||
|
if (!Container.TryGetContainingContainer(uid, out var container) ||
|
||||||
!TryComp<StorageComponent>(container.Owner, out var storage))
|
!TryComp<StorageComponent>(container.Owner, out var storage))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
_storage.RecalculateStorageUsed(storage);
|
_storage.RecalculateStorageUsed(container.Owner, storage);
|
||||||
_storage.UpdateUI(container.Owner, storage);
|
_storage.UpdateUI(container.Owner, storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Content.Shared.Projectiles;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
using Content.Shared.Effects;
|
|
||||||
|
|
||||||
namespace Content.Server.Projectiles;
|
namespace Content.Server.Projectiles;
|
||||||
|
|
||||||
@@ -30,7 +29,8 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args)
|
private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args)
|
||||||
{
|
{
|
||||||
// This is so entities that shouldn't get a collision are ignored.
|
// 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;
|
return;
|
||||||
|
|
||||||
var target = args.OtherEntity;
|
var target = args.OtherEntity;
|
||||||
@@ -60,7 +60,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
|
|
||||||
_adminLogger.Add(LogType.BulletHit,
|
_adminLogger.Add(LogType.BulletHit,
|
||||||
HasComp<ActorComponent>(target) ? LogImpact.Extreme : LogImpact.High,
|
HasComp<ActorComponent>(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)
|
if (!deleted)
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ public sealed partial class EmbeddableProjectileComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite), DataField("removalTime"), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), DataField("removalTime"), AutoNetworkedField]
|
||||||
public float? RemovalTime = 3f;
|
public float? RemovalTime = 3f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this entity will embed when thrown, or only when shot as a projectile.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite), DataField("embedOnThrow"), AutoNetworkedField]
|
||||||
|
public bool EmbedOnThrow = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How far into the entity should we offset (0 is wherever we collided).
|
/// How far into the entity should we offset (0 is wherever we collided).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ public sealed partial class ProjectileComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// User that shot this projectile.
|
/// User that shot this projectile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("shooter"), AutoNetworkedField] public EntityUid Shooter;
|
[DataField("shooter"), AutoNetworkedField]
|
||||||
|
public EntityUid? Shooter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Weapon used to shoot.
|
/// Weapon used to shoot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("weapon"), AutoNetworkedField]
|
[DataField("weapon"), AutoNetworkedField]
|
||||||
public EntityUid Weapon;
|
public EntityUid? Weapon;
|
||||||
|
|
||||||
[DataField("ignoreShooter"), AutoNetworkedField]
|
[DataField("ignoreShooter"), AutoNetworkedField]
|
||||||
public bool IgnoreShooter = true;
|
public bool IgnoreShooter = true;
|
||||||
@@ -41,5 +42,14 @@ public sealed partial class ProjectileComponent : Component
|
|||||||
[DataField("soundForce")]
|
[DataField("soundForce")]
|
||||||
public bool ForceSound = false;
|
public bool ForceSound = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this projectile will only collide with entities if it was shot from a gun (if <see cref="Weapon"/> is not null)
|
||||||
|
/// </summary>
|
||||||
|
[DataField("onlyCollideWhenShot")]
|
||||||
|
public bool OnlyCollideWhenShot = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this projectile has already damaged an entity.
|
||||||
|
/// </summary>
|
||||||
public bool DamagedEntity;
|
public bool DamagedEntity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,14 @@ public abstract partial class SharedProjectileSystem : EntitySystem
|
|||||||
_physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform);
|
_physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform);
|
||||||
_transform.AttachToGridOrMap(uid, xform);
|
_transform.AttachToGridOrMap(uid, xform);
|
||||||
|
|
||||||
|
// Reset whether the projectile has damaged anything if it successfully was removed
|
||||||
|
if (TryComp<ProjectileComponent>(uid, out var projectile))
|
||||||
|
{
|
||||||
|
projectile.Shooter = null;
|
||||||
|
projectile.Weapon = null;
|
||||||
|
projectile.DamagedEntity = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Land it just coz uhhh yeah
|
// Land it just coz uhhh yeah
|
||||||
var landEv = new LandEvent(args.User, true);
|
var landEv = new LandEvent(args.User, true);
|
||||||
RaiseLocalEvent(uid, ref landEv);
|
RaiseLocalEvent(uid, ref landEv);
|
||||||
@@ -81,6 +89,9 @@ public abstract partial class SharedProjectileSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args)
|
private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args)
|
||||||
{
|
{
|
||||||
|
if (!component.EmbedOnThrow)
|
||||||
|
return;
|
||||||
|
|
||||||
Embed(uid, args.Target, component);
|
Embed(uid, args.Target, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +102,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
|
|||||||
// Raise a specific event for projectiles.
|
// Raise a specific event for projectiles.
|
||||||
if (TryComp<ProjectileComponent>(uid, out var projectile))
|
if (TryComp<ProjectileComponent>(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);
|
RaiseLocalEvent(uid, ref ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ namespace Content.Shared.Storage.Components
|
|||||||
[DataField("containerWhitelist")]
|
[DataField("containerWhitelist")]
|
||||||
public HashSet<string>? ContainerWhitelist;
|
public HashSet<string>? ContainerWhitelist;
|
||||||
|
|
||||||
public readonly List<string> SpriteLayers = new();
|
/// <summary>
|
||||||
|
/// The list of map layer keys that are valid targets for changing in <see cref="MapLayers"/>
|
||||||
|
/// Can be initialized if already existing on the sprite, or inferred automatically
|
||||||
|
/// </summary>
|
||||||
|
[DataField("spriteLayers")]
|
||||||
|
public List<string> SpriteLayers = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
if (component.Container == default)
|
if (component.Container == default)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RecalculateStorageUsed(component);
|
RecalculateStorageUsed(uid, component);
|
||||||
UpdateStorageVisualization(uid, component);
|
UpdateStorageVisualization(uid, component);
|
||||||
UpdateUI(uid, component);
|
UpdateUI(uid, component);
|
||||||
Dirty(uid, component);
|
Dirty(uid, component);
|
||||||
@@ -394,7 +394,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
_appearance.SetData(uid, StackVisuals.Hide, !storageComp.IsUiOpen);
|
_appearance.SetData(uid, StackVisuals.Hide, !storageComp.IsUiOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RecalculateStorageUsed(StorageComponent storageComp)
|
public void RecalculateStorageUsed(EntityUid uid, StorageComponent storageComp)
|
||||||
{
|
{
|
||||||
storageComp.StorageUsed = 0;
|
storageComp.StorageUsed = 0;
|
||||||
|
|
||||||
@@ -406,6 +406,9 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
var size = itemComp.Size;
|
var size = itemComp.Size;
|
||||||
storageComp.StorageUsed += 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)
|
public int GetAvailableSpace(EntityUid uid, StorageComponent? component = null)
|
||||||
|
|||||||
0
Content.Shared/Storage/SharedStorageComponent.cs
Normal file
@@ -130,6 +130,8 @@ namespace Content.Shared.Storage
|
|||||||
Open,
|
Open,
|
||||||
HasContents,
|
HasContents,
|
||||||
CanLock,
|
CanLock,
|
||||||
Locked
|
Locked,
|
||||||
|
StorageUsed,
|
||||||
|
Capacity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ public sealed class ThrowingSystem : EntitySystem
|
|||||||
return;
|
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;
|
return;
|
||||||
|
|
||||||
var comp = EnsureComp<ThrownItemComponent>(uid);
|
var comp = EnsureComp<ThrownItemComponent>(uid);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public abstract class SharedDamageMarkerSystem : EntitySystem
|
|||||||
component.Amount <= 0 ||
|
component.Amount <= 0 ||
|
||||||
component.Whitelist?.IsValid(args.OtherEntity, EntityManager) == false ||
|
component.Whitelist?.IsValid(args.OtherEntity, EntityManager) == false ||
|
||||||
!TryComp<ProjectileComponent>(uid, out var projectile) ||
|
!TryComp<ProjectileComponent>(uid, out var projectile) ||
|
||||||
!projectile.Weapon.IsValid())
|
projectile.Weapon == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public abstract class SharedDamageMarkerSystem : EntitySystem
|
|||||||
// Markers are exclusive, deal with it.
|
// Markers are exclusive, deal with it.
|
||||||
var marker = EnsureComp<DamageMarkerComponent>(args.OtherEntity);
|
var marker = EnsureComp<DamageMarkerComponent>(args.OtherEntity);
|
||||||
marker.Damage = new DamageSpecifier(component.Damage);
|
marker.Damage = new DamageSpecifier(component.Damage);
|
||||||
marker.Marker = projectile.Weapon;
|
marker.Marker = projectile.Weapon.Value;
|
||||||
marker.EndTime = _timing.CurTime + component.Duration;
|
marker.EndTime = _timing.CurTime + component.Duration;
|
||||||
component.Amount--;
|
component.Amount--;
|
||||||
Dirty(marker);
|
Dirty(marker);
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public abstract class SharedReflectSystem : EntitySystem
|
|||||||
|
|
||||||
if (Resolve(projectile, ref projectileComp, false))
|
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.Shooter = user;
|
||||||
projectileComp.Weapon = user;
|
projectileComp.Weapon = user;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Wieldable.Components;
|
namespace Content.Shared.Wieldable.Components;
|
||||||
|
|
||||||
@@ -33,3 +34,9 @@ public sealed partial class WieldableComponent : Component
|
|||||||
[DataField("wieldTime")]
|
[DataField("wieldTime")]
|
||||||
public float WieldTime = 1.5f;
|
public float WieldTime = 1.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum WieldableVisuals : byte
|
||||||
|
{
|
||||||
|
Wielded
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public sealed class WieldableSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -224,6 +225,7 @@ public sealed class WieldableSystem : EntitySystem
|
|||||||
|
|
||||||
var ev = new ItemWieldedEvent();
|
var ev = new ItemWieldedEvent();
|
||||||
RaiseLocalEvent(uid, ref ev);
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
_appearance.SetData(uid, WieldableVisuals.Wielded, true);
|
||||||
|
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
args.Handled = true;
|
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);
|
("user", args.User.Value), ("item", uid)), args.User.Value, Filter.PvsExcept(args.User.Value), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_appearance.SetData(uid, WieldableVisuals.Wielded, false);
|
||||||
|
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
_virtualItemSystem.DeleteInHandsMatching(args.User.Value, uid);
|
_virtualItemSystem.DeleteInHandsMatching(args.User.Value, uid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,3 +62,8 @@
|
|||||||
license: "CC-BY-4.0"
|
license: "CC-BY-4.0"
|
||||||
copyright: "User volivieri on freesound.org. Modified by Velcroboy on github."
|
copyright: "User volivieri on freesound.org. Modified by Velcroboy on github."
|
||||||
source: "https://freesound.org/people/volivieri/sounds/37190/"
|
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/"
|
||||||
BIN
Resources/Audio/Items/bow_pull.ogg
Normal file
BIN
Resources/Audio/Weapons/Guns/Misc/arrow_nock.ogg
Normal file
4
Resources/Audio/Weapons/Guns/Misc/attributions.yml
Normal file
@@ -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/"
|
||||||
@@ -204,6 +204,8 @@
|
|||||||
prob: 0.2
|
prob: 0.2
|
||||||
- id: ClothingHandsGlovesColorYellow
|
- id: ClothingHandsGlovesColorYellow
|
||||||
prob: 0.05
|
prob: 0.05
|
||||||
|
- id: ClothingBeltQuiver
|
||||||
|
prob: 0.02
|
||||||
- id: ClothingBeltUtility
|
- id: ClothingBeltUtility
|
||||||
prob: 0.10
|
prob: 0.10
|
||||||
- id: ClothingHeadHatCone
|
- id: ClothingHeadHatCone
|
||||||
|
|||||||
22
Resources/Prototypes/Entities/Clothing/Belt/quiver.yml
Normal file
@@ -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-
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
category: construction-category-weapons
|
category: construction-category-weapons
|
||||||
description: A uranium shard with a piece of cloth wrapped around it.
|
description: A uranium shard with a piece of cloth wrapped around it.
|
||||||
icon: { sprite: Objects/Weapons/Melee/uranium_shiv.rsi, state: icon }
|
icon: { sprite: Objects/Weapons/Melee/uranium_shiv.rsi, state: icon }
|
||||||
objectType: Item
|
objectType: Item
|
||||||
|
|
||||||
- type: construction
|
- type: construction
|
||||||
name: crude spear
|
name: crude spear
|
||||||
@@ -118,3 +118,25 @@
|
|||||||
description: Crude and falling apart. Why would you make this?
|
description: Crude and falling apart. Why would you make this?
|
||||||
icon: { sprite: Objects/Weapons/Melee/shields.rsi, state: makeshift-icon }
|
icon: { sprite: Objects/Weapons/Melee/shields.rsi, state: makeshift-icon }
|
||||||
objectType: Item
|
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
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
id: ArtifactFragment
|
id: ArtifactFragment
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: Arrow
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: ATVKeys
|
id: ATVKeys
|
||||||
|
|
||||||
@@ -26,7 +29,7 @@
|
|||||||
id: Balloon
|
id: Balloon
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: BaseballBat
|
id: BaseballBat
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: BBQsauce
|
id: BBQsauce
|
||||||
@@ -306,7 +309,7 @@
|
|||||||
id: CluwneHorn
|
id: CluwneHorn
|
||||||
|
|
||||||
- type: Tag #Ohioans die happy
|
- type: Tag #Ohioans die happy
|
||||||
id: Corn
|
id: Corn
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: Coldsauce
|
id: Coldsauce
|
||||||
@@ -1014,7 +1017,7 @@
|
|||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: WallmountSubstationElectronics
|
id: WallmountSubstationElectronics
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: WeaponPistolCHIMPUpgradeKit
|
id: WeaponPistolCHIMPUpgradeKit
|
||||||
|
|
||||||
|
|||||||
BIN
Resources/Textures/Clothing/Belt/quiver.rsi/equipped-BELT.png
Normal file
|
After Width: | Height: | Size: 510 B |
BIN
Resources/Textures/Clothing/Belt/quiver.rsi/fill-1.png
Normal file
|
After Width: | Height: | Size: 116 B |
BIN
Resources/Textures/Clothing/Belt/quiver.rsi/fill-2.png
Normal file
|
After Width: | Height: | Size: 129 B |
BIN
Resources/Textures/Clothing/Belt/quiver.rsi/fill-3.png
Normal file
|
After Width: | Height: | Size: 133 B |
BIN
Resources/Textures/Clothing/Belt/quiver.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 473 B |
27
Resources/Textures/Clothing/Belt/quiver.rsi/meta.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 472 B |
|
After Width: | Height: | Size: 588 B |
|
After Width: | Height: | Size: 584 B |
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 354 B |
|
After Width: | Height: | Size: 511 B |
|
After Width: | Height: | Size: 349 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 410 B |
BIN
Resources/Textures/Objects/Weapons/Guns/Bow/bow.rsi/wielded.png
Normal file
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 435 B |
|
After Width: | Height: | Size: 440 B |
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 257 B |
|
After Width: | Height: | Size: 153 B |
|
After Width: | Height: | Size: 161 B |