Files
tbd-station-14/Content.Server/Weapon/Ranged/GunSystem.Revolvers.cs
2022-03-12 23:33:23 +11:00

230 lines
8.1 KiB
C#

using System;
using Content.Server.Weapon.Ranged.Ammunition.Components;
using Content.Server.Weapon.Ranged.Barrels.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Barrels.Components;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Player;
namespace Content.Server.Weapon.Ranged;
public sealed partial class GunSystem
{
private void OnRevolverUse(EntityUid uid, RevolverBarrelComponent component, UseInHandEvent args)
{
if (args.Handled) return;
EjectAllSlots(component);
component.Dirty(EntityManager);
UpdateRevolverAppearance(component);
args.Handled = true;
}
private void OnRevolverInteractUsing(EntityUid uid, RevolverBarrelComponent component, InteractUsingEvent args)
{
if (args.Handled) return;
if (TryInsertBullet(args.User, args.Used, component))
args.Handled = true;
}
public bool TryInsertBullet(EntityUid user, EntityUid entity, RevolverBarrelComponent component)
{
if (!TryComp(entity, out AmmoComponent? ammoComponent))
{
return false;
}
if (ammoComponent.Caliber != component.Caliber)
{
_popup.PopupEntity(Loc.GetString("revolver-barrel-component-try-insert-bullet-wrong-caliber"), component.Owner, Filter.Entities(user));
return false;
}
// Functions like a stack
// These are inserted in reverse order but then when fired Cycle will go through in order
// The reason we don't just use an actual stack is because spin can select a random slot to point at
for (var i = component.AmmoSlots.Length - 1; i >= 0; i--)
{
var slot = component.AmmoSlots[i];
if (slot == default)
{
component.CurrentSlot = i;
component.AmmoSlots[i] = entity;
component.AmmoContainer.Insert(entity);
SoundSystem.Play(Filter.Pvs(component.Owner), component.SoundInsert.GetSound(), component.Owner, AudioParams.Default.WithVolume(-2));
component.Dirty(EntityManager);
UpdateRevolverAppearance(component);
return true;
}
}
_popup.PopupEntity(Loc.GetString("revolver-barrel-component-try-insert-bullet-ammo-full"), ammoComponent.Owner, Filter.Entities(user));
return false;
}
/// <summary>
/// Russian Roulette
/// </summary>
public void SpinRevolver(RevolverBarrelComponent component)
{
var random = _random.Next(component.AmmoSlots.Length - 1);
component.CurrentSlot = random;
SoundSystem.Play(Filter.Pvs(component.Owner), component.SoundSpin.GetSound(), component.Owner, AudioParams.Default.WithVolume(-2));
component.Dirty(EntityManager);
}
public void CycleRevolver(RevolverBarrelComponent component)
{
// Move up a slot
component.CurrentSlot = (component.CurrentSlot + 1) % component.AmmoSlots.Length;
component.Dirty(EntityManager);
UpdateRevolverAppearance(component);
}
private void EjectAllSlots(RevolverBarrelComponent component)
{
for (var i = 0; i < component.AmmoSlots.Length; i++)
{
var entity = component.AmmoSlots[i];
if (entity == null) continue;
component.AmmoContainer.Remove(entity.Value);
EjectCasing(entity.Value);
component.AmmoSlots[i] = null;
}
if (component.AmmoContainer.ContainedEntities.Count > 0)
{
SoundSystem.Play(Filter.Pvs(component.Owner), component.SoundEject.GetSound(), component.Owner, AudioParams.Default.WithVolume(-1));
}
// May as well point back at the end?
component.CurrentSlot = component.AmmoSlots.Length - 1;
}
private void OnRevolverGetState(EntityUid uid, RevolverBarrelComponent component, ref ComponentGetState args)
{
var slotsSpent = new bool?[component.Capacity];
for (var i = 0; i < component.Capacity; i++)
{
slotsSpent[i] = null;
var ammoEntity = component.AmmoSlots[i];
if (ammoEntity != default && TryComp(ammoEntity, out AmmoComponent? ammo))
{
slotsSpent[i] = ammo.Spent;
}
}
//TODO: make yaml var to not sent currentSlot/UI? (for russian roulette)
args.State = new RevolverBarrelComponentState(
component.CurrentSlot,
component.FireRateSelector,
slotsSpent,
component.SoundGunshot.GetSound());
}
private void OnRevolverMapInit(EntityUid uid, RevolverBarrelComponent component, MapInitEvent args)
{
component.UnspawnedCount = component.Capacity;
var idx = 0;
component.AmmoContainer = component.Owner.EnsureContainer<Container>($"{component.GetType()}-ammoContainer", out var existing);
if (existing)
{
foreach (var entity in component.AmmoContainer.ContainedEntities)
{
component.UnspawnedCount--;
component.AmmoSlots[idx] = entity;
idx++;
}
}
// TODO: Revolvers should also defer spawning T B H
var xform = EntityManager.GetComponent<TransformComponent>(uid);
for (var i = 0; i < component.UnspawnedCount; i++)
{
var entity = EntityManager.SpawnEntity(component.FillPrototype, xform.Coordinates);
component.AmmoSlots[idx] = entity;
component.AmmoContainer.Insert(entity);
idx++;
}
UpdateRevolverAppearance(component);
component.Dirty(EntityManager);
}
private void UpdateRevolverAppearance(RevolverBarrelComponent component)
{
if (!TryComp(component.Owner, out AppearanceComponent? appearance))
{
return;
}
// Placeholder, at this stage it's just here for the RPG
appearance.SetData(MagazineBarrelVisuals.MagLoaded, component.ShotsLeft > 0);
appearance.SetData(AmmoVisuals.AmmoCount, component.ShotsLeft);
appearance.SetData(AmmoVisuals.AmmoMax, component.Capacity);
}
private void AddSpinVerb(EntityUid uid, RevolverBarrelComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
return;
if (component.Capacity <= 1 || component.ShotsLeft == 0)
return;
AlternativeVerb verb = new()
{
Text = Loc.GetString("spin-revolver-verb-get-data-text"),
IconTexture = "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png",
Act = () =>
{
SpinRevolver(component);
component.Owner.PopupMessage(args.User, Loc.GetString("spin-revolver-verb-on-activate"));
}
};
args.Verbs.Add(verb);
}
public EntityUid? PeekAmmo(RevolverBarrelComponent component)
{
return component.AmmoSlots[component.CurrentSlot];
}
/// <summary>
/// Takes a projectile out if possible
/// IEnumerable just to make supporting shotguns saner
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public EntityUid? TakeProjectile(RevolverBarrelComponent component, EntityCoordinates spawnAt)
{
var ammo = component.AmmoSlots[component.CurrentSlot];
EntityUid? bullet = null;
if (ammo != null)
{
var ammoComponent = EntityManager.GetComponent<AmmoComponent>(ammo.Value);
bullet = TakeBullet(ammoComponent, spawnAt);
if (ammoComponent.Caseless)
{
component.AmmoSlots[component.CurrentSlot] = null;
component.AmmoContainer.Remove(ammo.Value);
}
}
CycleRevolver(component);
UpdateRevolverAppearance(component);
return bullet;
}
}