ECS Ammo (#5862)
This commit is contained in:
@@ -3,14 +3,18 @@ using Content.Shared.Examine;
|
|||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Analyzers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -21,15 +25,10 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
|||||||
/// Generally used for bullets but can be used for other things like bananas
|
/// Generally used for bullets but can be used for other things like bananas
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
#pragma warning disable 618
|
[ComponentProtoName("Ammo")]
|
||||||
public class AmmoComponent : Component, IExamine, ISerializationHooks
|
[Friend(typeof(GunSystem))]
|
||||||
#pragma warning restore 618
|
public sealed class AmmoComponent : Component, ISerializationHooks
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
public override string Name => "Ammo";
|
|
||||||
|
|
||||||
[DataField("caliber")]
|
[DataField("caliber")]
|
||||||
public BallisticCaliber Caliber { get; } = BallisticCaliber.Unspecified;
|
public BallisticCaliber Caliber { get; } = BallisticCaliber.Unspecified;
|
||||||
|
|
||||||
@@ -37,22 +36,23 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_ammoIsProjectile)
|
if (AmmoIsProjectile)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _spent;
|
return _spent;
|
||||||
}
|
}
|
||||||
|
set => _spent = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _spent;
|
private bool _spent;
|
||||||
|
|
||||||
|
// TODO: Make it so null projectile = dis
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for anything without a case that fires itself
|
/// Used for anything without a case that fires itself
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("isProjectile")]
|
[DataField("isProjectile")] public bool AmmoIsProjectile;
|
||||||
private bool _ammoIsProjectile;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for something that is deleted when the projectile is retrieved
|
/// Used for something that is deleted when the projectile is retrieved
|
||||||
@@ -69,8 +69,8 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
|||||||
[DataField("projectilesFired")]
|
[DataField("projectilesFired")]
|
||||||
public int ProjectilesFired { get; } = 1;
|
public int ProjectilesFired { get; } = 1;
|
||||||
|
|
||||||
[DataField("projectile")]
|
[DataField("projectile", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
private string? _projectileId;
|
public string? ProjectileId;
|
||||||
|
|
||||||
// How far apart each entity is if multiple are shot
|
// How far apart each entity is if multiple are shot
|
||||||
[DataField("ammoSpread")]
|
[DataField("ammoSpread")]
|
||||||
@@ -82,8 +82,8 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
|||||||
[DataField("ammoVelocity")]
|
[DataField("ammoVelocity")]
|
||||||
public float Velocity { get; } = 20f;
|
public float Velocity { get; } = 20f;
|
||||||
|
|
||||||
[DataField("muzzleFlash")]
|
[DataField("muzzleFlash", customTypeSerializer:typeof(ResourcePathSerializer))]
|
||||||
private string _muzzleFlashSprite = "Objects/Weapons/Guns/Projectiles/bullet_muzzle.png";
|
public ResourcePath? MuzzleFlashSprite = new("Objects/Weapons/Guns/Projectiles/bullet_muzzle.png");
|
||||||
|
|
||||||
[DataField("soundCollectionEject")]
|
[DataField("soundCollectionEject")]
|
||||||
public SoundSpecifier SoundCollectionEject { get; } = new SoundCollectionSpecifier("CasingEject");
|
public SoundSpecifier SoundCollectionEject { get; } = new SoundCollectionSpecifier("CasingEject");
|
||||||
@@ -91,7 +91,7 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
|||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
// Being both caseless and shooting yourself doesn't make sense
|
// Being both caseless and shooting yourself doesn't make sense
|
||||||
DebugTools.Assert(!(_ammoIsProjectile == true && Caseless == true));
|
DebugTools.Assert(!(AmmoIsProjectile && Caseless));
|
||||||
|
|
||||||
if (ProjectilesFired < 1)
|
if (ProjectilesFired < 1)
|
||||||
{
|
{
|
||||||
@@ -104,63 +104,6 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityUid? TakeBullet(EntityCoordinates spawnAt)
|
|
||||||
{
|
|
||||||
if (_ammoIsProjectile)
|
|
||||||
{
|
|
||||||
return Owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_spent)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_spent = true;
|
|
||||||
if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearanceComponent))
|
|
||||||
{
|
|
||||||
appearanceComponent.SetData(AmmoVisuals.Spent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var entity = _entMan.SpawnEntity(_projectileId, spawnAt);
|
|
||||||
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MuzzleFlash(EntityUid entity, Angle angle)
|
|
||||||
{
|
|
||||||
if (_muzzleFlashSprite == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var time = _gameTiming.CurTime;
|
|
||||||
var deathTime = time + TimeSpan.FromMilliseconds(200);
|
|
||||||
// Offset the sprite so it actually looks like it's coming from the gun
|
|
||||||
var offset = angle.ToVec().Normalized / 2;
|
|
||||||
|
|
||||||
var message = new EffectSystemMessage
|
|
||||||
{
|
|
||||||
EffectSprite = _muzzleFlashSprite,
|
|
||||||
Born = time,
|
|
||||||
DeathTime = deathTime,
|
|
||||||
AttachedEntityUid = entity,
|
|
||||||
AttachedOffset = offset,
|
|
||||||
//Rotated from east facing
|
|
||||||
Rotation = (float) angle.Theta,
|
|
||||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
|
|
||||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
|
||||||
Shaded = false
|
|
||||||
};
|
|
||||||
EntitySystem.Get<EffectSystem>().CreateParticle(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Examine(FormattedMessage message, bool inDetailsRange)
|
|
||||||
{
|
|
||||||
var text = Loc.GetString("ammo-component-on-examine",("caliber", Caliber));
|
|
||||||
message.AddMarkup(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum BallisticCaliber
|
public enum BallisticCaliber
|
||||||
|
|||||||
@@ -26,12 +26,11 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[NetworkedComponent()]
|
[NetworkedComponent()]
|
||||||
#pragma warning disable 618
|
#pragma warning disable 618
|
||||||
public sealed class BoltActionBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, IExamine
|
public sealed class BoltActionBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit
|
||||||
#pragma warning restore 618
|
#pragma warning restore 618
|
||||||
{
|
{
|
||||||
// Originally I had this logic shared with PumpBarrel and used a couple of variables to control things
|
// Originally I had this logic shared with PumpBarrel and used a couple of variables to control things
|
||||||
// but it felt a lot messier to play around with, especially when adding verbs
|
// but it felt a lot messier to play around with, especially when adding verbs
|
||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
|
||||||
|
|
||||||
public override string Name => "BoltActionBarrel";
|
public override string Name => "BoltActionBarrel";
|
||||||
|
|
||||||
@@ -111,7 +110,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
if (_unspawnedCount > 0)
|
if (_unspawnedCount > 0)
|
||||||
{
|
{
|
||||||
_unspawnedCount--;
|
_unspawnedCount--;
|
||||||
var chamberEntity = _entities.SpawnEntity(_fillPrototype, _entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
var chamberEntity = Entities.SpawnEntity(_fillPrototype, Entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
||||||
_chamberContainer.Insert(chamberEntity);
|
_chamberContainer.Insert(chamberEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +124,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
// (Is one chambered?, is the bullet spend)
|
// (Is one chambered?, is the bullet spend)
|
||||||
var chamber = (chamberedExists, false);
|
var chamber = (chamberedExists, false);
|
||||||
|
|
||||||
if (chamberedExists && _entities.TryGetComponent<AmmoComponent?>(_chamberContainer.ContainedEntity!.Value, out var ammo))
|
if (chamberedExists && Entities.TryGetComponent<AmmoComponent?>(_chamberContainer.ContainedEntity!.Value, out var ammo))
|
||||||
{
|
{
|
||||||
chamber.Item2 = ammo.Spent;
|
chamber.Item2 = ammo.Spent;
|
||||||
}
|
}
|
||||||
@@ -155,7 +154,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
|
|
||||||
_chamberContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-chamber-container");
|
_chamberContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-chamber-container");
|
||||||
|
|
||||||
if (_entities.TryGetComponent(Owner, out AppearanceComponent? appearanceComponent))
|
if (Entities.TryGetComponent(Owner, out AppearanceComponent? appearanceComponent))
|
||||||
{
|
{
|
||||||
_appearanceComponent = appearanceComponent;
|
_appearanceComponent = appearanceComponent;
|
||||||
}
|
}
|
||||||
@@ -188,10 +187,11 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_chamberContainer.ContainedEntity is not {Valid: true} chamberEntity)
|
if (_chamberContainer.ContainedEntity is not {Valid: true} chamberEntity) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
return _entities.GetComponentOrNull<AmmoComponent>(chamberEntity)?.TakeBullet(spawnAt);
|
var ammoComponent = Entities.GetComponentOrNull<AmmoComponent>(chamberEntity);
|
||||||
|
|
||||||
|
return ammoComponent == null ? null : EntitySystem.Get<GunSystem>().TakeBullet(ammoComponent, spawnAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool WeaponCanFire()
|
protected override bool WeaponCanFire()
|
||||||
@@ -229,7 +229,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
|
|
||||||
public bool TryInsertBullet(EntityUid user, EntityUid ammo)
|
public bool TryInsertBullet(EntityUid user, EntityUid ammo)
|
||||||
{
|
{
|
||||||
if (!_entities.TryGetComponent(ammo, out AmmoComponent? ammoComponent))
|
if (!Entities.TryGetComponent(ammo, out AmmoComponent? ammoComponent))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -297,7 +297,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!_entities.GetComponent<AmmoComponent>(chambered).Caseless)
|
if (!Entities.GetComponent<AmmoComponent>(chambered).Caseless)
|
||||||
{
|
{
|
||||||
EjectCasing(chambered);
|
EjectCasing(chambered);
|
||||||
}
|
}
|
||||||
@@ -321,7 +321,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
else if (_unspawnedCount > 0)
|
else if (_unspawnedCount > 0)
|
||||||
{
|
{
|
||||||
_unspawnedCount--;
|
_unspawnedCount--;
|
||||||
var ammoEntity = _entities.SpawnEntity(_fillPrototype, _entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
var ammoEntity = Entities.SpawnEntity(_fillPrototype, Entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
||||||
_chamberContainer.Insert(ammoEntity);
|
_chamberContainer.Insert(ammoEntity);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
[NetworkedComponent()]
|
[NetworkedComponent()]
|
||||||
public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, ISerializationHooks
|
public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, ISerializationHooks
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
|
|
||||||
public override string Name => "PumpBarrel";
|
public override string Name => "PumpBarrel";
|
||||||
|
|
||||||
public override int ShotsLeft
|
public override int ShotsLeft
|
||||||
@@ -87,7 +85,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
// (Is one chambered?, is the bullet spend)
|
// (Is one chambered?, is the bullet spend)
|
||||||
var chamber = (chamberedExists, false);
|
var chamber = (chamberedExists, false);
|
||||||
|
|
||||||
if (chamberedExists && _entMan.TryGetComponent<AmmoComponent?>(_chamberContainer.ContainedEntity!.Value, out var ammo))
|
if (chamberedExists && Entities.TryGetComponent<AmmoComponent?>(_chamberContainer.ContainedEntity!.Value, out var ammo))
|
||||||
{
|
{
|
||||||
chamber.Item2 = ammo.Spent;
|
chamber.Item2 = ammo.Spent;
|
||||||
}
|
}
|
||||||
@@ -126,7 +124,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
_unspawnedCount--;
|
_unspawnedCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearanceComponent))
|
if (Entities.TryGetComponent(Owner, out AppearanceComponent? appearanceComponent))
|
||||||
{
|
{
|
||||||
_appearanceComponent = appearanceComponent;
|
_appearanceComponent = appearanceComponent;
|
||||||
}
|
}
|
||||||
@@ -158,10 +156,11 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_chamberContainer.ContainedEntity is not {Valid: true} chamberEntity)
|
if (_chamberContainer.ContainedEntity is not {Valid: true} chamberEntity) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
return _entMan.GetComponentOrNull<AmmoComponent>(chamberEntity)?.TakeBullet(spawnAt);
|
var ammoComponent = Entities.GetComponentOrNull<AmmoComponent>(chamberEntity);
|
||||||
|
|
||||||
|
return ammoComponent == null ? null : EntitySystem.Get<GunSystem>().TakeBullet(ammoComponent, spawnAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Cycle(bool manual = false)
|
private void Cycle(bool manual = false)
|
||||||
@@ -169,7 +168,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
if (_chamberContainer.ContainedEntity is {Valid: true} chamberedEntity)
|
if (_chamberContainer.ContainedEntity is {Valid: true} chamberedEntity)
|
||||||
{
|
{
|
||||||
_chamberContainer.Remove(chamberedEntity);
|
_chamberContainer.Remove(chamberedEntity);
|
||||||
var ammoComponent = _entMan.GetComponent<AmmoComponent>(chamberedEntity);
|
var ammoComponent = Entities.GetComponent<AmmoComponent>(chamberedEntity);
|
||||||
if (!ammoComponent.Caseless)
|
if (!ammoComponent.Caseless)
|
||||||
{
|
{
|
||||||
EjectCasing(chamberedEntity);
|
EjectCasing(chamberedEntity);
|
||||||
@@ -185,7 +184,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
if (_unspawnedCount > 0)
|
if (_unspawnedCount > 0)
|
||||||
{
|
{
|
||||||
_unspawnedCount--;
|
_unspawnedCount--;
|
||||||
var ammoEntity = _entMan.SpawnEntity(_fillPrototype, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
|
var ammoEntity = Entities.SpawnEntity(_fillPrototype, Entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
||||||
_chamberContainer.Insert(ammoEntity);
|
_chamberContainer.Insert(ammoEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +199,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
|
|
||||||
public bool TryInsertBullet(InteractUsingEventArgs eventArgs)
|
public bool TryInsertBullet(InteractUsingEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!_entMan.TryGetComponent(eventArgs.Using, out AmmoComponent? ammoComponent))
|
if (!Entities.TryGetComponent(eventArgs.Using, out AmmoComponent? ammoComponent))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
[NetworkedComponent()]
|
[NetworkedComponent()]
|
||||||
public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, ISerializationHooks
|
public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, ISerializationHooks
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
public override string Name => "RevolverBarrel";
|
public override string Name => "RevolverBarrel";
|
||||||
@@ -82,7 +81,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
{
|
{
|
||||||
slotsSpent[i] = null;
|
slotsSpent[i] = null;
|
||||||
var ammoEntity = _ammoSlots[i];
|
var ammoEntity = _ammoSlots[i];
|
||||||
if (ammoEntity != default && _entMan.TryGetComponent(ammoEntity, out AmmoComponent? ammo))
|
if (ammoEntity != default && Entities.TryGetComponent(ammoEntity, out AmmoComponent? ammo))
|
||||||
{
|
{
|
||||||
slotsSpent[i] = ammo.Spent;
|
slotsSpent[i] = ammo.Spent;
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
|
|
||||||
for (var i = 0; i < _unspawnedCount; i++)
|
for (var i = 0; i < _unspawnedCount; i++)
|
||||||
{
|
{
|
||||||
var entity = _entMan.SpawnEntity(_fillPrototype, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
|
var entity = Entities.SpawnEntity(_fillPrototype, Entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
||||||
_ammoSlots[idx] = entity;
|
_ammoSlots[idx] = entity;
|
||||||
_ammoContainer.Insert(entity);
|
_ammoContainer.Insert(entity);
|
||||||
idx++;
|
idx++;
|
||||||
@@ -126,7 +125,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
|
|
||||||
private void UpdateAppearance()
|
private void UpdateAppearance()
|
||||||
{
|
{
|
||||||
if (!_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance))
|
if (!Entities.TryGetComponent(Owner, out AppearanceComponent? appearance))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -139,7 +138,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
|
|
||||||
public bool TryInsertBullet(EntityUid user, EntityUid entity)
|
public bool TryInsertBullet(EntityUid user, EntityUid entity)
|
||||||
{
|
{
|
||||||
if (!_entMan.TryGetComponent(entity, out AmmoComponent? ammoComponent))
|
if (!Entities.TryGetComponent(entity, out AmmoComponent? ammoComponent))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -209,8 +208,8 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
EntityUid? bullet = null;
|
EntityUid? bullet = null;
|
||||||
if (ammo != default)
|
if (ammo != default)
|
||||||
{
|
{
|
||||||
var ammoComponent = _entMan.GetComponent<AmmoComponent>(ammo);
|
var ammoComponent = Entities.GetComponent<AmmoComponent>(ammo);
|
||||||
bullet = ammoComponent.TakeBullet(spawnAt);
|
bullet = EntitySystem.Get<GunSystem>().TakeBullet(ammoComponent, spawnAt);
|
||||||
if (ammoComponent.Caseless)
|
if (ammoComponent.Caseless)
|
||||||
{
|
{
|
||||||
_ammoSlots[_currentSlot] = default;
|
_ammoSlots[_currentSlot] = default;
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
var entity = _chamberContainer.ContainedEntity ?? default;
|
var entity = _chamberContainer.ContainedEntity ?? default;
|
||||||
|
|
||||||
Cycle();
|
Cycle();
|
||||||
return entity != default ? _entities.GetComponent<AmmoComponent>(entity).TakeBullet(spawnAt) : null;
|
return entity != default ? EntitySystem.Get<GunSystem>().TakeBullet(_entities.GetComponent<AmmoComponent>(entity), spawnAt) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Cycle(bool manual = false)
|
private void Cycle(bool manual = false)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
// it's just when I re-organised it changed me as the contributor
|
// it's just when I re-organised it changed me as the contributor
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
[Dependency] protected readonly IEntityManager Entities = default!;
|
||||||
|
|
||||||
public override FireRateSelector FireRateSelector => _fireRateSelector;
|
public override FireRateSelector FireRateSelector => _fireRateSelector;
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
protected override void OnRemove()
|
protected override void OnRemove()
|
||||||
{
|
{
|
||||||
base.OnRemove();
|
base.OnRemove();
|
||||||
if (_entities.TryGetComponent(Owner, out ServerRangedWeaponComponent? rangedWeaponComponent))
|
if (Entities.TryGetComponent(Owner, out ServerRangedWeaponComponent? rangedWeaponComponent))
|
||||||
{
|
{
|
||||||
rangedWeaponComponent.Barrel = null;
|
rangedWeaponComponent.Barrel = null;
|
||||||
rangedWeaponComponent.FireHandler -= Fire;
|
rangedWeaponComponent.FireHandler -= Fire;
|
||||||
@@ -198,39 +198,39 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ammo = PeekAmmo();
|
var ammo = PeekAmmo();
|
||||||
if (TakeProjectile(_entities.GetComponent<TransformComponent>(shooter).Coordinates) is not {Valid: true} projectile)
|
if (TakeProjectile(Entities.GetComponent<TransformComponent>(shooter).Coordinates) is not {Valid: true} projectile)
|
||||||
{
|
{
|
||||||
SoundSystem.Play(Filter.Broadcast(), SoundEmpty.GetSound(), Owner);
|
SoundSystem.Play(Filter.Broadcast(), SoundEmpty.GetSound(), Owner);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point firing is confirmed
|
// At this point firing is confirmed
|
||||||
var direction = (targetPos - _entities.GetComponent<TransformComponent>(shooter).WorldPosition).ToAngle();
|
var direction = (targetPos - Entities.GetComponent<TransformComponent>(shooter).WorldPosition).ToAngle();
|
||||||
var angle = GetRecoilAngle(direction);
|
var angle = GetRecoilAngle(direction);
|
||||||
// This should really be client-side but for now we'll just leave it here
|
// This should really be client-side but for now we'll just leave it here
|
||||||
if (_entities.TryGetComponent(shooter, out CameraRecoilComponent? recoilComponent))
|
if (Entities.TryGetComponent(shooter, out CameraRecoilComponent? recoilComponent))
|
||||||
{
|
{
|
||||||
recoilComponent.Kick(-angle.ToVec() * 0.15f);
|
recoilComponent.Kick(-angle.ToVec() * 0.15f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This section probably needs tweaking so there can be caseless hitscan etc.
|
// This section probably needs tweaking so there can be caseless hitscan etc.
|
||||||
if (_entities.TryGetComponent(projectile, out HitscanComponent? hitscan))
|
if (Entities.TryGetComponent(projectile, out HitscanComponent? hitscan))
|
||||||
{
|
{
|
||||||
FireHitscan(shooter, hitscan, angle);
|
FireHitscan(shooter, hitscan, angle);
|
||||||
}
|
}
|
||||||
else if (_entities.HasComponent<ProjectileComponent>(projectile) &&
|
else if (Entities.HasComponent<ProjectileComponent>(projectile) &&
|
||||||
_entities.TryGetComponent(ammo, out AmmoComponent? ammoComponent))
|
Entities.TryGetComponent(ammo, out AmmoComponent? ammoComponent))
|
||||||
{
|
{
|
||||||
FireProjectiles(shooter, projectile, ammoComponent.ProjectilesFired, ammoComponent.EvenSpreadAngle, angle, ammoComponent.Velocity, ammo.Value);
|
FireProjectiles(shooter, projectile, ammoComponent.ProjectilesFired, ammoComponent.EvenSpreadAngle, angle, ammoComponent.Velocity, ammo.Value);
|
||||||
|
|
||||||
if (CanMuzzleFlash)
|
if (CanMuzzleFlash)
|
||||||
{
|
{
|
||||||
ammoComponent.MuzzleFlash(Owner, angle);
|
EntitySystem.Get<GunSystem>().MuzzleFlash(Owner, ammoComponent, angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ammoComponent.Caseless)
|
if (ammoComponent.Caseless)
|
||||||
{
|
{
|
||||||
_entities.DeleteEntity(ammo.Value);
|
Entities.DeleteEntity(ammo.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -324,9 +324,9 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
projectile = _entities.SpawnEntity(
|
projectile = Entities.SpawnEntity(
|
||||||
_entities.GetComponent<MetaDataComponent>(baseProjectile).EntityPrototype?.ID,
|
Entities.GetComponent<MetaDataComponent>(baseProjectile).EntityPrototype?.ID,
|
||||||
_entities.GetComponent<TransformComponent>(baseProjectile).Coordinates);
|
Entities.GetComponent<TransformComponent>(baseProjectile).Coordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
firedProjectiles[i] = projectile;
|
firedProjectiles[i] = projectile;
|
||||||
@@ -342,10 +342,10 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
projectileAngle = angle;
|
projectileAngle = angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
var physics = _entities.GetComponent<IPhysBody>(projectile);
|
var physics = Entities.GetComponent<IPhysBody>(projectile);
|
||||||
physics.BodyStatus = BodyStatus.InAir;
|
physics.BodyStatus = BodyStatus.InAir;
|
||||||
|
|
||||||
var projectileComponent = _entities.GetComponent<ProjectileComponent>(projectile);
|
var projectileComponent = Entities.GetComponent<ProjectileComponent>(projectile);
|
||||||
projectileComponent.IgnoreEntity(shooter);
|
projectileComponent.IgnoreEntity(shooter);
|
||||||
|
|
||||||
// FIXME: Work around issue where inserting and removing an entity from a container,
|
// FIXME: Work around issue where inserting and removing an entity from a container,
|
||||||
@@ -353,16 +353,16 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
// See SharedBroadphaseSystem.HandleContainerInsert()... It sets Awake to false, which causes this.
|
// See SharedBroadphaseSystem.HandleContainerInsert()... It sets Awake to false, which causes this.
|
||||||
projectile.SpawnTimer(TimeSpan.FromMilliseconds(25), () =>
|
projectile.SpawnTimer(TimeSpan.FromMilliseconds(25), () =>
|
||||||
{
|
{
|
||||||
_entities.GetComponent<IPhysBody>(projectile)
|
Entities.GetComponent<IPhysBody>(projectile)
|
||||||
.LinearVelocity = projectileAngle.ToVec() * velocity;
|
.LinearVelocity = projectileAngle.ToVec() * velocity;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_entities.GetComponent<TransformComponent>(projectile).WorldRotation = projectileAngle + MathHelper.PiOver2;
|
Entities.GetComponent<TransformComponent>(projectile).WorldRotation = projectileAngle + MathHelper.PiOver2;
|
||||||
}
|
}
|
||||||
|
|
||||||
_entities.EventBus.RaiseLocalEvent(Owner, new GunShotEvent(firedProjectiles));
|
Entities.EventBus.RaiseLocalEvent(Owner, new GunShotEvent(firedProjectiles));
|
||||||
_entities.EventBus.RaiseLocalEvent(ammo, new AmmoShotEvent(firedProjectiles));
|
Entities.EventBus.RaiseLocalEvent(ammo, new AmmoShotEvent(firedProjectiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -386,9 +386,9 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void FireHitscan(EntityUid shooter, HitscanComponent hitscan, Angle angle)
|
private void FireHitscan(EntityUid shooter, HitscanComponent hitscan, Angle angle)
|
||||||
{
|
{
|
||||||
var ray = new CollisionRay(_entities.GetComponent<TransformComponent>(Owner).Coordinates.ToMapPos(_entities), angle.ToVec(), (int) hitscan.CollisionMask);
|
var ray = new CollisionRay(Entities.GetComponent<TransformComponent>(Owner).Coordinates.ToMapPos(Entities), angle.ToVec(), (int) hitscan.CollisionMask);
|
||||||
var physicsManager = EntitySystem.Get<SharedPhysicsSystem>();
|
var physicsManager = EntitySystem.Get<SharedPhysicsSystem>();
|
||||||
var rayCastResults = physicsManager.IntersectRay(_entities.GetComponent<TransformComponent>(Owner).MapID, ray, hitscan.MaxLength, shooter, false).ToList();
|
var rayCastResults = physicsManager.IntersectRay(Entities.GetComponent<TransformComponent>(Owner).MapID, ray, hitscan.MaxLength, shooter, false).ToList();
|
||||||
|
|
||||||
if (rayCastResults.Count >= 1)
|
if (rayCastResults.Count >= 1)
|
||||||
{
|
{
|
||||||
@@ -398,7 +398,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
var dmg = EntitySystem.Get<DamageableSystem>().TryChangeDamage(result.HitEntity, hitscan.Damage);
|
var dmg = EntitySystem.Get<DamageableSystem>().TryChangeDamage(result.HitEntity, hitscan.Damage);
|
||||||
if (dmg != null)
|
if (dmg != null)
|
||||||
EntitySystem.Get<AdminLogSystem>().Add(LogType.HitScanHit,
|
EntitySystem.Get<AdminLogSystem>().Add(LogType.HitScanHit,
|
||||||
$"{_entities.ToPrettyString(shooter):user} hit {_entities.ToPrettyString(result.HitEntity):target} using {_entities.ToPrettyString(hitscan.Owner):used} and dealt {dmg.Total:damage} damage");
|
$"{Entities.ToPrettyString(shooter):user} hit {Entities.ToPrettyString(result.HitEntity):target} using {Entities.ToPrettyString(hitscan.Owner):used} and dealt {dmg.Total:damage} damage");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
95
Content.Server/Weapon/Ranged/GunSystem.Ammo.cs
Normal file
95
Content.Server/Weapon/Ranged/GunSystem.Ammo.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Server.Weapon.Ranged.Ammunition.Components;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Server.Weapon.Ranged;
|
||||||
|
|
||||||
|
public sealed partial class GunSystem
|
||||||
|
{
|
||||||
|
private void OnAmmoExamine(EntityUid uid, AmmoComponent component, ExaminedEvent args)
|
||||||
|
{
|
||||||
|
var text = Loc.GetString("ammo-component-on-examine",("caliber", component.Caliber));
|
||||||
|
args.PushMarkup(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityUid? TakeBullet(AmmoComponent component, EntityCoordinates spawnAt)
|
||||||
|
{
|
||||||
|
if (component.AmmoIsProjectile)
|
||||||
|
{
|
||||||
|
return component.Owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Spent)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.Spent = true;
|
||||||
|
|
||||||
|
if (TryComp(component.Owner, out AppearanceComponent? appearanceComponent))
|
||||||
|
{
|
||||||
|
appearanceComponent.SetData(AmmoVisuals.Spent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = EntityManager.SpawnEntity(component.ProjectileId, spawnAt);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MuzzleFlash(EntityUid entity, AmmoComponent component, Angle angle)
|
||||||
|
{
|
||||||
|
if (component.MuzzleFlashSprite == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var time = _gameTiming.CurTime;
|
||||||
|
var deathTime = time + TimeSpan.FromMilliseconds(200);
|
||||||
|
// Offset the sprite so it actually looks like it's coming from the gun
|
||||||
|
var offset = new Vector2(0.0f, -0.5f);
|
||||||
|
|
||||||
|
var message = new EffectSystemMessage
|
||||||
|
{
|
||||||
|
EffectSprite = component.MuzzleFlashSprite.ToString(),
|
||||||
|
Born = time,
|
||||||
|
DeathTime = deathTime,
|
||||||
|
AttachedEntityUid = entity,
|
||||||
|
AttachedOffset = offset,
|
||||||
|
//Rotated from east facing
|
||||||
|
Rotation = -MathF.PI / 2f,
|
||||||
|
Color = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
|
||||||
|
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
||||||
|
Shaded = false
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO: Fix rotation when shooting sideways. This was the closest I got but still had issues.
|
||||||
|
* var time = _gameTiming.CurTime;
|
||||||
|
var deathTime = time + TimeSpan.FromMilliseconds(200);
|
||||||
|
var entityRotation = EntityManager.GetComponent<TransformComponent>(entity).WorldRotation;
|
||||||
|
var localAngle = entityRotation - (angle + MathF.PI / 2f);
|
||||||
|
// Offset the sprite so it actually looks like it's coming from the gun
|
||||||
|
var offset = localAngle.RotateVec(new Vector2(0.0f, -0.5f));
|
||||||
|
|
||||||
|
var message = new EffectSystemMessage
|
||||||
|
{
|
||||||
|
EffectSprite = component.MuzzleFlashSprite.ToString(),
|
||||||
|
Born = time,
|
||||||
|
DeathTime = deathTime,
|
||||||
|
AttachedEntityUid = entity,
|
||||||
|
AttachedOffset = offset,
|
||||||
|
//Rotated from east facing
|
||||||
|
Rotation = (float) (localAngle - MathF.PI / 2),
|
||||||
|
Color = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
|
||||||
|
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
||||||
|
Shaded = false
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
_effects.CreateParticle(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,20 +19,6 @@ public sealed partial class GunSystem
|
|||||||
{
|
{
|
||||||
// Probably needs combining with magazines in future given the common functionality.
|
// Probably needs combining with magazines in future given the common functionality.
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, ComponentInit>(OnAmmoBoxInit);
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, MapInitEvent>(OnAmmoBoxMapInit);
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, ExaminedEvent>(OnAmmoBoxExamine);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, InteractUsingEvent>(OnAmmoBoxInteractUsing);
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, UseInHandEvent>(OnAmmoBoxUse);
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, InteractHandEvent>(OnAmmoBoxInteractHand);
|
|
||||||
SubscribeLocalEvent<AmmoBoxComponent, GetAlternativeVerbsEvent>(OnAmmoBoxAltVerbs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAmmoBoxAltVerbs(EntityUid uid, AmmoBoxComponent component, GetAlternativeVerbsEvent args)
|
private void OnAmmoBoxAltVerbs(EntityUid uid, AmmoBoxComponent component, GetAlternativeVerbsEvent args)
|
||||||
{
|
{
|
||||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||||
|
|||||||
@@ -1,10 +1,34 @@
|
|||||||
|
using Content.Server.Weapon.Ranged.Ammunition.Components;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Weapon.Ranged;
|
namespace Content.Server.Weapon.Ranged;
|
||||||
|
|
||||||
public sealed partial class GunSystem : EntitySystem
|
public sealed partial class GunSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly EffectSystem _effects = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<AmmoComponent, ExaminedEvent>(OnAmmoExamine);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, ComponentInit>(OnAmmoBoxInit);
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, MapInitEvent>(OnAmmoBoxMapInit);
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, ExaminedEvent>(OnAmmoBoxExamine);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, InteractUsingEvent>(OnAmmoBoxInteractUsing);
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, UseInHandEvent>(OnAmmoBoxUse);
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, InteractHandEvent>(OnAmmoBoxInteractHand);
|
||||||
|
SubscribeLocalEvent<AmmoBoxComponent, GetAlternativeVerbsEvent>(OnAmmoBoxAltVerbs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user