Files
tbd-station-14/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineComponent.cs
2020-05-23 11:27:31 +02:00

287 lines
8.8 KiB
C#

using System;
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
{
[RegisterComponent]
public class BallisticMagazineComponent : Component, IMapInit, IAttackBy
{
public override string Name => "BallisticMagazine";
// Stack of loaded bullets.
[ViewVariables] private readonly Stack<IEntity> _loadedBullets = new Stack<IEntity>();
private string _fillType;
[ViewVariables] private Container _bulletContainer;
[ViewVariables] private AppearanceComponent _appearance;
private BallisticMagazineType _magazineType;
private BallisticCaliber _caliber;
private int _capacity;
[ViewVariables] public string FillType => _fillType;
[ViewVariables] public BallisticMagazineType MagazineType => _magazineType;
[ViewVariables] public BallisticCaliber Caliber => _caliber;
[ViewVariables] public int Capacity => _capacity;
[ViewVariables] public int CountLoaded => _loadedBullets.Count + _availableSpawnCount;
[ViewVariables] private int _availableSpawnCount;
public event Action OnAmmoCountChanged;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _magazineType, "magazine", BallisticMagazineType.Unspecified);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _fillType, "fill", null);
serializer.DataField(ref _capacity, "capacity", 20);
serializer.DataField(ref _availableSpawnCount, "availableSpawnCount", Capacity);
}
public override void Initialize()
{
base.Initialize();
_appearance = Owner.GetComponent<AppearanceComponent>();
}
/// <inheritdoc />
protected override void Startup()
{
base.Startup();
_bulletContainer =
ContainerManagerComponent.Ensure<Container>("magazine_bullet_container", Owner, out var existed);
if (existed)
{
foreach (var entity in _bulletContainer.ContainedEntities)
{
_loadedBullets.Push(entity);
}
}
UpdateAppearance();
OnAmmoCountChanged?.Invoke();
_appearance.SetData(BallisticMagazineVisuals.AmmoCapacity, Capacity);
}
public void AddBullet(IEntity bullet)
{
if (!bullet.TryGetComponent(out BallisticBulletComponent component))
{
throw new ArgumentException("entity isn't a bullet.", nameof(bullet));
}
if (component.Caliber != Caliber)
{
throw new ArgumentException("entity is of the wrong caliber.", nameof(bullet));
}
if (CountLoaded >= Capacity)
{
throw new InvalidOperationException("Magazine is full.");
}
_bulletContainer.Insert(bullet);
_loadedBullets.Push(bullet);
UpdateAppearance();
OnAmmoCountChanged?.Invoke();
}
public IEntity TakeBullet()
{
IEntity bullet;
if (_loadedBullets.Count == 0)
{
if (_availableSpawnCount == 0)
{
return null;
}
_availableSpawnCount -= 1;
bullet = Owner.EntityManager.SpawnEntity(FillType, Owner.Transform.GridPosition);
}
else
{
bullet = _loadedBullets.Pop();
_bulletContainer.Remove(bullet);
}
UpdateAppearance();
OnAmmoCountChanged?.Invoke();
return bullet;
}
// TODO: Allow putting individual casings into mag (also box)
AmmoMagTransferPopupMessage CanTransferFrom(IEntity source)
{
// Currently the below duplicates box but at some stage these will likely differ
if (source.TryGetComponent(out BallisticMagazineComponent magazineComponent))
{
if (magazineComponent.Caliber != Caliber)
{
return new AmmoMagTransferPopupMessage(result: false, message: "Wrong caliber");
}
if (CountLoaded == Capacity)
{
return new AmmoMagTransferPopupMessage(result: false, message: "Already full");
}
if (magazineComponent.CountLoaded == 0)
{
return new AmmoMagTransferPopupMessage(result: false, message: "No ammo to transfer");
}
return new AmmoMagTransferPopupMessage(result: true, message: "");
}
// If box
if (source.TryGetComponent(out AmmoBoxComponent boxComponent))
{
if (boxComponent.Caliber != Caliber)
{
return new AmmoMagTransferPopupMessage(result: false, message: "Wrong caliber");
}
if (CountLoaded == Capacity)
{
return new AmmoMagTransferPopupMessage(result: false, message: "Already full");
}
if (boxComponent.CountLeft == 0)
{
return new AmmoMagTransferPopupMessage(result: false, message: "No ammo to transfer");
}
return new AmmoMagTransferPopupMessage(result: true, message: "");
}
return new AmmoMagTransferPopupMessage(result: false, message: "");
}
// TODO: Potentially abstract out to reduce duplicate structs
private struct AmmoMagTransferPopupMessage
{
public readonly bool Result;
public readonly string Message;
public AmmoMagTransferPopupMessage(bool result, string message)
{
Result = result;
Message = message;
}
}
bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
{
var ammoMagTransfer = CanTransferFrom(eventArgs.AttackWith);
if (ammoMagTransfer.Result) {
IEntity bullet;
if (eventArgs.AttackWith.TryGetComponent(out BallisticMagazineComponent magazineComponent))
{
int fillCount = Math.Min(magazineComponent.CountLoaded, Capacity - CountLoaded);
for (int i = 0; i < fillCount; i++)
{
bullet = magazineComponent.TakeBullet();
AddBullet(bullet);
}
eventArgs.User.PopupMessage(eventArgs.User, $"Transferred {fillCount} rounds");
return true;
}
if (eventArgs.AttackWith.TryGetComponent(out AmmoBoxComponent boxComponent))
{
int fillCount = Math.Min(boxComponent.CountLeft, Capacity - CountLoaded);
for (int i = 0; i < fillCount; i++)
{
bullet = boxComponent.TakeBullet();
AddBullet(bullet);
}
eventArgs.User.PopupMessage(eventArgs.User, $"Transferred {fillCount} rounds");
return true;
}
}
else
{
eventArgs.User.PopupMessage(eventArgs.User, ammoMagTransfer.Message);
}
return false;
}
private void UpdateAppearance()
{
_appearance.SetData(BallisticMagazineVisuals.AmmoLeft, CountLoaded);
}
public void MapInit()
{
_availableSpawnCount = Capacity;
}
}
public enum BallisticMagazineType
{
Unspecified = 0,
// .32
A32,
// .357
A357,
// .44
A44,
// .45mm
A45mm,
// .50 cal
A50,
// 5.56mm
A556mm,
// 6.5mm
A65mm,
// 7.62mm
A762mm,
Maxim,
// 9mm
A9mm,
A9mmSMG,
A9mmTopMounted,
// 10mm
A10mm,
A10mmSMG,
// 20mm
A20mm,
// 24mm
A24mm,
// 12g
A12g,
}
}