Files
tbd-station-14/Content.Server/Explosion/EntitySystems/ClusterGrenadeSystem.cs
Magnus Larsen 9cd6e4dccd Fix clientside storage Whitelists (#24063)
* Fix outdated component name in assaultbelt whitelist

RangedMagazine was replaced with BallisticAmmoProvider in the Gun
refactor (#8301)

* Move FlashOnTrigger, SmokeOnTrigger, Flash components to Shared

* Move LightReplacerComponent to Shared

* Move Utensil, Mousetrap components to Shared

* Move SprayPainterComponent to Shared

The PaintableAirlock tag has also been removed, as it was unused &
unnecessary, likely a vestige of spray painter development when the
PaintableAirlock component wasn't in Content.Shared.

* Add trivial Produce and Seed components to Client

This allows the plant bag and botanical belt whitelists to correctly
match produce and seeds on the client, fixing the extraneous "Can't
insert" message that previously appeared.

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-02-02 00:33:57 +11:00

181 lines
6.8 KiB
C#

using Content.Server.Explosion.Components;
using Content.Shared.Flash.Components;
using Content.Shared.Explosion;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Throwing;
using Robust.Shared.Containers;
using Robust.Shared.Random;
using Content.Server.Weapons.Ranged.Systems;
using System.Numerics;
using Content.Shared.Explosion.Components;
using Content.Shared.Flash.Components;
using Robust.Server.Containers;
using Robust.Server.GameObjects;
namespace Content.Server.Explosion.EntitySystems;
public sealed class ClusterGrenadeSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly GunSystem _gun = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly ContainerSystem _containerSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ClusterGrenadeComponent, ComponentInit>(OnClugInit);
SubscribeLocalEvent<ClusterGrenadeComponent, ComponentStartup>(OnClugStartup);
SubscribeLocalEvent<ClusterGrenadeComponent, InteractUsingEvent>(OnClugUsing);
SubscribeLocalEvent<ClusterGrenadeComponent, TriggerEvent>(OnClugTrigger);
}
private void OnClugInit(EntityUid uid, ClusterGrenadeComponent component, ComponentInit args)
{
component.GrenadesContainer = _container.EnsureContainer<Container>(uid, "cluster-payload");
}
private void OnClugStartup(Entity<ClusterGrenadeComponent> clug, ref ComponentStartup args)
{
var component = clug.Comp;
if (component.FillPrototype != null)
{
component.UnspawnedCount = Math.Max(0, component.MaxGrenades - component.GrenadesContainer.ContainedEntities.Count);
UpdateAppearance(clug);
}
}
private void OnClugUsing(Entity<ClusterGrenadeComponent> clug, ref InteractUsingEvent args)
{
if (args.Handled)
return;
var component = clug.Comp;
// TODO: Should use whitelist.
if (component.GrenadesContainer.ContainedEntities.Count >= component.MaxGrenades ||
!HasComp<FlashOnTriggerComponent>(args.Used))
return;
_containerSystem.Insert(args.Used, component.GrenadesContainer);
UpdateAppearance(clug);
args.Handled = true;
}
private void OnClugTrigger(Entity<ClusterGrenadeComponent> clug, ref TriggerEvent args)
{
var component = clug.Comp;
component.CountDown = true;
args.Handled = true;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<ClusterGrenadeComponent>();
while (query.MoveNext(out var uid, out var clug))
{
if (clug.CountDown && clug.UnspawnedCount > 0)
{
var grenadesInserted = clug.GrenadesContainer.ContainedEntities.Count + clug.UnspawnedCount;
var thrownCount = 0;
var segmentAngle = 360 / grenadesInserted;
var grenadeDelay = 0f;
while (TryGetGrenade(uid, clug, out var grenade))
{
// var distance = random.NextFloat() * _throwDistance;
var angleMin = segmentAngle * thrownCount;
var angleMax = segmentAngle * (thrownCount + 1);
var angle = Angle.FromDegrees(_random.Next(angleMin, angleMax));
if (clug.RandomAngle)
angle = _random.NextAngle();
thrownCount++;
switch (clug.GrenadeType)
{
case GrenadeType.Shoot:
ShootProjectile(grenade, angle, clug, uid);
break;
case GrenadeType.Throw:
ThrowGrenade(grenade, angle, clug);
break;
}
// give an active timer trigger to the contained grenades when they get launched
if (clug.TriggerGrenades)
{
grenadeDelay += _random.NextFloat(clug.GrenadeTriggerIntervalMin, clug.GrenadeTriggerIntervalMax);
var grenadeTimer = EnsureComp<ActiveTimerTriggerComponent>(grenade);
grenadeTimer.TimeRemaining = (clug.BaseTriggerDelay + grenadeDelay);
var ev = new ActiveTimerTriggerEvent(grenade, uid);
RaiseLocalEvent(uid, ref ev);
}
}
// delete the empty shell of the clusterbomb
Del(uid);
}
}
}
private void ShootProjectile(EntityUid grenade, Angle angle, ClusterGrenadeComponent clug, EntityUid clugUid)
{
var direction = angle.ToVec().Normalized();
if (clug.RandomSpread)
direction = _random.NextVector2().Normalized();
_gun.ShootProjectile(grenade, direction, Vector2.One.Normalized(), clugUid);
}
private void ThrowGrenade(EntityUid grenade, Angle angle, ClusterGrenadeComponent clug)
{
var direction = angle.ToVec().Normalized() * clug.Distance;
if (clug.RandomSpread)
direction = angle.ToVec().Normalized() * _random.NextFloat(clug.MinSpreadDistance, clug.MaxSpreadDistance);
_throwingSystem.TryThrow(grenade, direction, clug.Velocity);
}
private bool TryGetGrenade(EntityUid clugUid, ClusterGrenadeComponent component, out EntityUid grenade)
{
grenade = default;
if (component.UnspawnedCount > 0)
{
component.UnspawnedCount--;
grenade = Spawn(component.FillPrototype, _transformSystem.GetMapCoordinates(clugUid));
return true;
}
if (component.GrenadesContainer.ContainedEntities.Count > 0)
{
grenade = component.GrenadesContainer.ContainedEntities[0];
// This shouldn't happen but you never know.
if (!_containerSystem.Remove(grenade, component.GrenadesContainer))
return false;
return true;
}
return false;
}
private void UpdateAppearance(Entity<ClusterGrenadeComponent> clug)
{
var component = clug.Comp;
if (!TryComp<AppearanceComponent>(clug, out var appearance))
return;
_appearance.SetData(clug, ClusterGrenadeVisuals.GrenadesCounter, component.GrenadesContainer.ContainedEntities.Count + component.UnspawnedCount, appearance);
}
}