Files
OldThink/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs

399 lines
16 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Hands.Components;
using Content.Server.Nutrition.Components;
using Content.Server.Storage.Components;
using Content.Server.Stunnable;
2021-12-06 00:52:58 +01:00
using Content.Server.Throwing;
using Content.Server.Tools.Components;
using Content.Shared.Camera;
using Content.Shared.CombatMode;
2021-12-05 18:09:01 +01:00
using Content.Shared.Interaction;
get that crap outta here (completely rewrites inventorysystem) (#5807) * some work * equip: done unequip: todo * unequipping done & refactored events * workin * movin * reee namespaces * stun * mobstate * fixes * some work on events * removes serverside itemcomp & misc fixes * work * smol merge fix * ports template to prototype & finishes ui * moves relay & adds containerenumerator * actions & cuffs * my god what is actioncode * more fixes * im loosing my grasp on reality * more fixes * more work * explosions * yes * more work * more fixes * merge master & misc fixed because i forgot to commit before merging master * more fixes * fixes * moar * more work * moar fixes * suffixmap * more work on client * motivation low * no. no containers * mirroring client to server * fixes * move serverinvcomp * serverinventorycomponent is dead * gaming * only strippable & ai left... * only ai and richtext left * fixes ai * fixes * fixes sprite layers * more fixes * resolves optional * yes * stable:tm: * fixes * moar fixes * moar * fix some tests * lmao * no comment * good to merge:tm: * fixes build but for real * adresses some reviews * adresses some more reviews * nullables, yo * fixes lobbyscreen * timid refactor to differentiate actor & target * adresses more reviews * more * my god what a mess * removed the rest of duplicates * removed duplicate slotflags and renamed shoes to feet * removes another unused one * yes * fixes lobby & makes tryunequip return unequipped item * fixes * some funny renames * fixes * misc improvements to attemptevents * fixes * merge fixes Co-authored-by: Paul Ritter <ritter.paul1@gmail.com>
2021-12-30 22:56:10 +01:00
using Content.Shared.Item;
2021-12-05 18:09:01 +01:00
using Content.Shared.PneumaticCannon;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
2021-12-05 18:09:01 +01:00
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
2021-12-05 18:09:01 +01:00
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Server.PneumaticCannon
{
public sealed class PneumaticCannonSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly CameraRecoilSystem _cameraRecoil = default!;
private HashSet<PneumaticCannonComponent> _currentlyFiring = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PneumaticCannonComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<PneumaticCannonComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<PneumaticCannonComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<PneumaticCannonComponent, GetVerbsEvent<AlternativeVerb>>(OnAlternativeVerbs);
SubscribeLocalEvent<PneumaticCannonComponent, GetVerbsEvent<Verb>>(OnOtherVerbs);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (_currentlyFiring.Count == 0)
return;
foreach (var comp in _currentlyFiring.ToArray())
{
if (comp.FireQueue.Count == 0)
{
_currentlyFiring.Remove(comp);
// reset acc frametime to the fire interval if we're instant firing
if (comp.InstantFire)
{
comp.AccumulatedFrametime = comp.FireInterval;
}
else
{
comp.AccumulatedFrametime = 0f;
}
return;
}
comp.AccumulatedFrametime += frameTime;
if (comp.AccumulatedFrametime > comp.FireInterval)
{
var dat = comp.FireQueue.Dequeue();
Fire(comp, dat);
comp.AccumulatedFrametime -= comp.FireInterval;
}
}
}
private void OnComponentInit(EntityUid uid, PneumaticCannonComponent component, ComponentInit args)
{
component.GasTankSlot = component.Owner.EnsureContainer<ContainerSlot>($"{component.Name}-gasTank");
if (component.InstantFire)
component.AccumulatedFrametime = component.FireInterval;
}
private void OnInteractUsing(EntityUid uid, PneumaticCannonComponent component, InteractUsingEvent args)
{
args.Handled = true;
2021-12-06 00:52:58 +01:00
if (EntityManager.HasComponent<GasTankComponent>(args.Used)
&& component.GasTankSlot.CanInsert(args.Used)
&& component.GasTankRequired)
{
component.GasTankSlot.Insert(args.Used);
args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-insert",
("tank", args.Used), ("cannon", component.Owner)));
UpdateAppearance(component);
return;
}
2021-12-06 00:52:58 +01:00
if (EntityManager.TryGetComponent<ToolComponent?>(args.Used, out var tool))
{
if (tool.Qualities.Contains(component.ToolModifyMode))
{
// this is kind of ugly but it just cycles the enum
var val = (int) component.Mode;
val = (val + 1) % (int) PneumaticCannonFireMode.Len;
component.Mode = (PneumaticCannonFireMode) val;
args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-change-fire-mode",
("mode", component.Mode.ToString())));
// sound
return;
}
if (tool.Qualities.Contains(component.ToolModifyPower))
{
var val = (int) component.Power;
val = (val + 1) % (int) PneumaticCannonPower.Len;
component.Power = (PneumaticCannonPower) val;
args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-change-power",
("power", component.Power.ToString())));
// sound
return;
}
}
// this overrides the ServerStorageComponent's insertion stuff because
// it's not event-based yet and I can't cancel it, so tools and stuff
// will modify mode/power then get put in anyway
get that crap outta here (completely rewrites inventorysystem) (#5807) * some work * equip: done unequip: todo * unequipping done & refactored events * workin * movin * reee namespaces * stun * mobstate * fixes * some work on events * removes serverside itemcomp & misc fixes * work * smol merge fix * ports template to prototype & finishes ui * moves relay & adds containerenumerator * actions & cuffs * my god what is actioncode * more fixes * im loosing my grasp on reality * more fixes * more work * explosions * yes * more work * more fixes * merge master & misc fixed because i forgot to commit before merging master * more fixes * fixes * moar * more work * moar fixes * suffixmap * more work on client * motivation low * no. no containers * mirroring client to server * fixes * move serverinvcomp * serverinventorycomponent is dead * gaming * only strippable & ai left... * only ai and richtext left * fixes ai * fixes * fixes sprite layers * more fixes * resolves optional * yes * stable:tm: * fixes * moar fixes * moar * fix some tests * lmao * no comment * good to merge:tm: * fixes build but for real * adresses some reviews * adresses some more reviews * nullables, yo * fixes lobbyscreen * timid refactor to differentiate actor & target * adresses more reviews * more * my god what a mess * removed the rest of duplicates * removed duplicate slotflags and renamed shoes to feet * removes another unused one * yes * fixes lobby & makes tryunequip return unequipped item * fixes * some funny renames * fixes * misc improvements to attemptevents * fixes * merge fixes Co-authored-by: Paul Ritter <ritter.paul1@gmail.com>
2021-12-30 22:56:10 +01:00
if (EntityManager.TryGetComponent<SharedItemComponent?>(args.Used, out var item)
2021-12-06 00:52:58 +01:00
&& EntityManager.TryGetComponent<ServerStorageComponent?>(component.Owner, out var storage))
{
if (storage.CanInsert(args.Used))
{
storage.Insert(args.Used);
args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-insert-item-success",
("item", args.Used), ("cannon", component.Owner)));
}
else
{
args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-insert-item-failure",
("item", args.Used), ("cannon", component.Owner)));
}
}
}
private void OnAfterInteract(EntityUid uid, PneumaticCannonComponent component, AfterInteractEvent args)
{
if (EntityManager.TryGetComponent<SharedCombatModeComponent>(uid, out var combat)
&& !combat.IsInCombatMode)
return;
args.Handled = true;
if (!HasGas(component) && component.GasTankRequired)
{
args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-fire-no-gas",
("cannon", component.Owner)));
2021-12-05 18:09:01 +01:00
SoundSystem.Play(Filter.Pvs(args.Used), "/Audio/Items/hiss.ogg", args.Used, AudioParams.Default);
return;
}
AddToQueue(component, args.User, args.ClickLocation);
}
2021-12-05 18:09:01 +01:00
public void AddToQueue(PneumaticCannonComponent comp, EntityUid user, EntityCoordinates click)
{
2021-12-06 00:52:58 +01:00
if (!EntityManager.TryGetComponent<ServerStorageComponent?>(comp.Owner, out var storage))
return;
if (storage.StoredEntities == null) return;
if (storage.StoredEntities.Count == 0)
{
SoundSystem.Play(Filter.Pvs((comp).Owner), "/Audio/Weapons/click.ogg", ((IComponent) comp).Owner, AudioParams.Default);
return;
}
_currentlyFiring.Add(comp);
int entCounts = comp.Mode switch
{
PneumaticCannonFireMode.All => storage.StoredEntities.Count,
PneumaticCannonFireMode.Single => 1,
_ => 0
};
for (int i = 0; i < entCounts; i++)
{
2021-12-06 00:52:58 +01:00
var dir = (click.ToMapPos(EntityManager) - EntityManager.GetComponent<TransformComponent>(user).WorldPosition).Normalized;
var randomAngle = GetRandomFireAngleFromPower(comp.Power).RotateVec(dir);
var randomStrengthMult = _random.NextFloat(0.75f, 1.25f);
var throwMult = GetRangeMultFromPower(comp.Power);
var data = new PneumaticCannonComponent.FireData
{
User = user,
Strength = comp.ThrowStrength * randomStrengthMult,
Direction = (dir + randomAngle).Normalized * comp.BaseThrowRange * throwMult,
};
comp.FireQueue.Enqueue(data);
}
}
public void Fire(PneumaticCannonComponent comp, PneumaticCannonComponent.FireData data)
{
if (!HasGas(comp) && comp.GasTankRequired)
{
data.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-fire-no-gas",
("cannon", comp.Owner)));
2021-12-03 16:30:34 +01:00
SoundSystem.Play(Filter.Pvs(((IComponent) comp).Owner), "/Audio/Items/hiss.ogg", ((IComponent) comp).Owner, AudioParams.Default);
return;
}
2021-12-06 00:52:58 +01:00
if (!EntityManager.TryGetComponent<ServerStorageComponent?>(comp.Owner, out var storage))
return;
2021-12-09 12:29:27 +01:00
if (Deleted(data.User))
return;
if (storage.StoredEntities == null) return;
if (storage.StoredEntities.Count == 0) return; // click sound?
2021-12-06 00:52:58 +01:00
var ent = _random.Pick(storage.StoredEntities);
storage.Remove(ent);
2021-12-03 16:30:34 +01:00
SoundSystem.Play(Filter.Pvs(data.User), comp.FireSound.GetSound(), ((IComponent) comp).Owner, AudioParams.Default);
if (EntityManager.HasComponent<CameraRecoilComponent>(data.User))
{
var kick = Vector2.One * data.Strength;
_cameraRecoil.KickCamera(data.User, kick);
}
ent.TryThrow(data.Direction, data.Strength, data.User, GetPushbackRatioFromPower(comp.Power));
// lasagna, anybody?
ent.EnsureComponent<ForcefeedOnCollideComponent>();
2021-12-06 00:52:58 +01:00
if(EntityManager.TryGetComponent<StatusEffectsComponent?>(data.User, out var status)
&& comp.Power == PneumaticCannonPower.High)
{
Merge branch 'master' into 2021-12-03-remove-IEntity-komm-süsser-todd # Conflicts: # Content.Client/Crayon/CrayonDecalVisualizer.cs # Content.Client/Tabletop/TabletopSystem.cs # Content.IntegrationTests/Tests/InventoryHelpersTest.cs # Content.Server/AI/EntitySystems/AiSystem.cs # Content.Server/AI/Utility/AiLogic/UtilityAI.cs # Content.Server/AME/AMENodeGroup.cs # Content.Server/Administration/AdminVerbSystem.cs # Content.Server/Body/Systems/RespiratorSystem.cs # Content.Server/Chemistry/Components/InjectorComponent.cs # Content.Server/Chemistry/TileReactions/CleanTileReaction.cs # Content.Server/Chemistry/TileReactions/SpillTileReaction.cs # Content.Server/Crayon/CrayonComponent.cs # Content.Server/Doors/Components/ServerDoorComponent.cs # Content.Server/Explosion/EntitySystems/TriggerSystem.cs # Content.Server/Fluids/Components/MopComponent.cs # Content.Server/Fluids/Components/SpillExtensions.cs # Content.Server/Fluids/EntitySystems/PuddleSystem.cs # Content.Server/Instruments/InstrumentSystem.cs # Content.Server/Nutrition/EntitySystems/DrinkSystem.cs # Content.Server/Nutrition/EntitySystems/FoodSystem.cs # Content.Server/PneumaticCannon/PneumaticCannonSystem.cs # Content.Server/Storage/Components/EntityStorageComponent.cs # Content.Server/Storage/Components/StorageFillComponent.cs # Content.Server/Stunnable/StunbatonSystem.cs # Content.Server/Throwing/ThrowHelper.cs # Content.Server/Weapon/Ranged/Barrels/BarrelSystem.cs # Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs # Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs # Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs # Content.Shared/Damage/Components/DamageableComponent.cs # Content.Shared/Damage/Systems/DamageableSystem.cs # Content.Shared/MobState/Components/MobStateComponent.cs # Content.Shared/Slippery/SharedSlipperySystem.cs
2021-12-07 17:48:49 +01:00
_stun.TryParalyze(data.User, TimeSpan.FromSeconds(comp.HighPowerStunTime), true, status);
2021-12-09 12:29:27 +01:00
data.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-power-stun",
("cannon", comp.Owner)));
}
2021-12-06 00:52:58 +01:00
if (comp.GasTankSlot.ContainedEntity is {Valid: true} contained && comp.GasTankRequired)
{
// we checked for this earlier in HasGas so a GetComp is okay
2021-12-06 00:52:58 +01:00
var gas = EntityManager.GetComponent<GasTankComponent>(contained);
var environment = _atmos.GetTileMixture(EntityManager.GetComponent<TransformComponent>(comp.Owner).Coordinates, true);
var removed = gas.RemoveAir(GetMoleUsageFromPower(comp.Power));
if (environment != null && removed != null)
{
_atmos.Merge(environment, removed);
}
}
}
/// <summary>
/// Returns whether the pneumatic cannon has enough gas to shoot an item.
/// </summary>
public bool HasGas(PneumaticCannonComponent component)
{
var usage = GetMoleUsageFromPower(component.Power);
2021-12-06 00:52:58 +01:00
if (component.GasTankSlot.ContainedEntity is not {Valid: true } contained)
return false;
// not sure how it wouldnt, but it might not! who knows
2021-12-06 00:52:58 +01:00
if (EntityManager.TryGetComponent<GasTankComponent?>(contained, out var tank))
{
if (tank.Air.TotalMoles < usage)
return false;
return true;
}
return false;
}
private void OnAlternativeVerbs(EntityUid uid, PneumaticCannonComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (component.GasTankSlot.ContainedEntities.Count == 0 || !component.GasTankRequired)
return;
if (!args.CanInteract)
return;
AlternativeVerb ejectTank = new();
ejectTank.Act = () => TryRemoveGasTank(component, args.User);
ejectTank.Text = Loc.GetString("pneumatic-cannon-component-verb-gas-tank-name");
args.Verbs.Add(ejectTank);
}
private void OnOtherVerbs(EntityUid uid, PneumaticCannonComponent component, GetVerbsEvent<Verb> args)
{
if (!args.CanInteract)
return;
Verb ejectItems = new();
ejectItems.Act = () => TryEjectAllItems(component, args.User);
ejectItems.Text = Loc.GetString("pneumatic-cannon-component-verb-eject-items-name");
args.Verbs.Add(ejectItems);
}
2021-12-05 21:02:04 +01:00
public void TryRemoveGasTank(PneumaticCannonComponent component, EntityUid user)
{
2021-12-06 00:52:58 +01:00
if (component.GasTankSlot.ContainedEntity is not {Valid: true} contained)
{
user.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-none",
("cannon", component.Owner)));
return;
}
2021-12-06 00:52:58 +01:00
if (component.GasTankSlot.Remove(contained))
{
2021-12-06 00:52:58 +01:00
if (EntityManager.TryGetComponent<HandsComponent?>(user, out var hands))
{
hands.PutInHand(contained);
}
user.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-remove",
2021-12-06 00:52:58 +01:00
("tank", contained), ("cannon", component.Owner)));
UpdateAppearance(component);
}
}
2021-12-05 21:02:04 +01:00
public void TryEjectAllItems(PneumaticCannonComponent component, EntityUid user)
{
2021-12-06 00:52:58 +01:00
if (EntityManager.TryGetComponent<ServerStorageComponent?>(component.Owner, out var storage))
{
if (storage.StoredEntities == null) return;
foreach (var entity in storage.StoredEntities.ToArray())
{
storage.Remove(entity);
}
user.PopupMessage(Loc.GetString("pneumatic-cannon-component-ejected-all",
("cannon", (component.Owner))));
}
}
private void UpdateAppearance(PneumaticCannonComponent component)
{
2021-12-06 00:52:58 +01:00
if (EntityManager.TryGetComponent<AppearanceComponent?>(component.Owner, out var appearance))
{
appearance.SetData(PneumaticCannonVisuals.Tank,
component.GasTankSlot.ContainedEntities.Count != 0);
}
}
private Angle GetRandomFireAngleFromPower(PneumaticCannonPower power)
{
return power switch
{
PneumaticCannonPower.High => _random.NextAngle(-0.3, 0.3),
PneumaticCannonPower.Medium => _random.NextAngle(-0.2, 0.2),
PneumaticCannonPower.Low or _ => _random.NextAngle(-0.1, 0.1),
};
}
private float GetRangeMultFromPower(PneumaticCannonPower power)
{
return power switch
{
PneumaticCannonPower.High => 1.6f,
PneumaticCannonPower.Medium => 1.3f,
PneumaticCannonPower.Low or _ => 1.0f,
};
}
private float GetMoleUsageFromPower(PneumaticCannonPower power)
{
return power switch
{
2021-11-22 02:08:29 -07:00
PneumaticCannonPower.High => 9f,
PneumaticCannonPower.Medium => 6f,
PneumaticCannonPower.Low or _ => 3f,
};
}
private float GetPushbackRatioFromPower(PneumaticCannonPower power)
{
return power switch
{
PneumaticCannonPower.Medium => 8.0f,
PneumaticCannonPower.High => 16.0f,
PneumaticCannonPower.Low or _ => 0f
};
}
}
}