Clusterbang (#2712)

* Code is ready but item now spawning

* Prototype of SeveralExplosive component

* Remaked to FlashExplosiveComponent using

* Done. But i feel myself retarted

* Remaked. Looks good

* Full loaded prototype added

* Throwing in progress. Fatal error is here

* I forgot about shared

* Sloth refactor

* Delayed spawning and fix crashes

* Full clusterbang code.

* Removed useless variable and tuned delay

* Delete wrong  in CreamPiedComponent

* Now yaml is code quality followed

* Reworked to GetLevel with bugs

* Never forget resources, guys

* RoundToLevels added. Now it works.

* New textures and sloth refactor is returned

* Now it's TryGetComponent

* Visualizer maximum fix and look fix

* Logging and no max and min check

* Removed max grenades sending

* vizualizer is better now

* GrenadesMax removed

* grammar, checks, NextFloat and no more try catch

* Unused using removed

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
komunre
2021-01-18 17:16:34 +07:00
committed by GitHub
parent c30dc030c5
commit 2172d00409
12 changed files with 303 additions and 11 deletions

View File

@@ -0,0 +1,40 @@
using Content.Shared.GameObjects.Components.Explosion;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Explosion
{
[UsedImplicitly]
// ReSharper disable once InconsistentNaming
public class ClusterFlashVisualizer : AppearanceVisualizer
{
private string _state;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
if (node.TryGetNode("state", out var state))
{
_state = state.AsString();
}
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent<ISpriteComponent>(out var sprite))
{
return;
}
if (component.TryGetData(ClusterFlashVisuals.GrenadesCounter, out int grenadesCounter))
{
sprite.LayerSetState(0, $"{_state}-{grenadesCounter}");
}
}
}
}

View File

@@ -0,0 +1,181 @@
#nullable enable
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Server.GameObjects.Components.Explosion;
using Robust.Shared.GameObjects;
using System.Threading.Tasks;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using System;
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameObjects.Components.Trigger.TimerTrigger;
using Content.Server.Throw;
using Robust.Server.GameObjects;
using Content.Shared.GameObjects.Components.Explosion;
using Robust.Shared.GameObjects.Components.Timers;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Explosives
{
[RegisterComponent]
public sealed class ClusterFlashComponent : Component, IInteractUsing, IUse
{
public override string Name => "ClusterFlash";
private Container _grenadesContainer = default!;
/// <summary>
/// What we fill our prototype with if we want to pre-spawn with grenades.
/// </summary>
[ViewVariables]
private string? _fillPrototype;
/// <summary>
/// If we have a pre-fill how many more can we spawn.
/// </summary>
private int _unspawnedCount;
/// <summary>
/// Maximum grenades in the container.
/// </summary>
[ViewVariables]
private int _maxGrenades;
/// <summary>
/// How long until our grenades are shot out and armed.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
private float _delay;
/// <summary>
/// Max distance grenades can be thrown.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
private float _throwDistance;
/// <summary>
/// This is the end.
/// </summary>
private bool _countDown;
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs args)
{
if (_grenadesContainer.ContainedEntities.Count >= _maxGrenades || !args.Using.HasComponent<FlashExplosiveComponent>())
return false;
_grenadesContainer.Insert(args.Using);
UpdateAppearance();
return true;
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
serializer.DataField(ref _maxGrenades, "maxGrenadesCount", 3);
serializer.DataField(ref _delay, "delay", 1.0f);
serializer.DataField(ref _throwDistance, "distance", 3.0f);
}
public override void Initialize()
{
base.Initialize();
_grenadesContainer = ContainerManagerComponent.Ensure<Container>("cluster-flash", Owner);
}
protected override void Startup()
{
base.Startup();
if (_fillPrototype != null)
{
_unspawnedCount = Math.Max(0, _maxGrenades - _grenadesContainer.ContainedEntities.Count);
UpdateAppearance();
}
}
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{
if (_countDown || (_grenadesContainer.ContainedEntities.Count + _unspawnedCount) <= 0)
return false;
Owner.SpawnTimer((int) (_delay * 1000), () =>
{
if (Owner.Deleted)
return;
_countDown = true;
var random = IoCManager.Resolve<IRobustRandom>();
var delay = 20;
var grenadesInserted = _grenadesContainer.ContainedEntities.Count + _unspawnedCount;
var thrownCount = 0;
var segmentAngle = (int) (360 / grenadesInserted);
while (TryGetGrenade(out var grenade))
{
var angleMin = segmentAngle * thrownCount;
var angleMax = segmentAngle * (thrownCount + 1);
var angle = Angle.FromDegrees(random.Next(angleMin, angleMax));
var distance = (float)random.NextFloat() * _throwDistance;
var target = new EntityCoordinates(Owner.Uid, angle.ToVec().Normalized * distance);
grenade.Throw(0.5f, target, grenade.Transform.Coordinates);
grenade.SpawnTimer(delay, () =>
{
if (grenade.Deleted)
return;
if (grenade.TryGetComponent(out OnUseTimerTriggerComponent? useTimer))
{
useTimer.Trigger(eventArgs.User);
}
});
delay += random.Next(550, 900);
thrownCount++;
}
Owner.Delete();
});
return true;
}
private bool TryGetGrenade([NotNullWhen(true)] out IEntity? grenade)
{
grenade = null;
if (_unspawnedCount > 0)
{
_unspawnedCount--;
grenade = Owner.EntityManager.SpawnEntity(_fillPrototype, Owner.Transform.Coordinates);
return true;
}
if (_grenadesContainer.ContainedEntities.Count > 0)
{
grenade = _grenadesContainer.ContainedEntities[0];
// This shouldn't happen but you never know.
if (!_grenadesContainer.Remove(grenade))
return false;
return true;
}
return false;
}
private void UpdateAppearance()
{
if (!Owner.TryGetComponent(out AppearanceComponent? appearance)) return;
appearance.SetData(ClusterFlashVisuals.GrenadesCounter, _grenadesContainer.ContainedEntities.Count + _unspawnedCount);
}
}
}

View File

@@ -19,7 +19,9 @@ namespace Content.Server.GameObjects.Components.Explosion
public override string Name => "FlashExplosive";
private float _range;
private float _duration;
private string _sound;
private bool _deleteOnFlash;

View File

@@ -50,7 +50,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
// after impacting the first object.
// For realism this should actually be changed when the velocity of the object is less than a threshold.
// This would allow ricochets off walls, and weird gravity effects from slowing the object.
if (Owner.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1)
if (!Owner.Deleted && Owner.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1)
{
_shouldCollide = false;
}

View File

@@ -1,11 +1,12 @@
using System;
#nullable enable
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Trigger;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Trigger.TimerTrigger
@@ -13,11 +14,9 @@ namespace Content.Server.GameObjects.Components.Trigger.TimerTrigger
[RegisterComponent]
public class OnUseTimerTriggerComponent : Component, IUse
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
public override string Name => "OnUseTimerTrigger";
private float _delay = 0f;
private float _delay;
public override void ExposeData(ObjectSerializer serializer)
{
@@ -26,13 +25,17 @@ namespace Content.Server.GameObjects.Components.Trigger.TimerTrigger
serializer.DataField(ref _delay, "delay", 0f);
}
public void Trigger(IEntity user)
{
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
appearance.SetData(TriggerVisuals.VisualState, TriggerVisualState.Primed);
EntitySystem.Get<TriggerSystem>().HandleTimerTrigger(TimeSpan.FromSeconds(_delay), user, Owner);
}
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{
var triggerSystem = _entitySystemManager.GetEntitySystem<TriggerSystem>();
if (Owner.TryGetComponent<AppearanceComponent>(out var appearance)) {
appearance.SetData(TriggerVisuals.VisualState, TriggerVisualState.Primed);
}
triggerSystem.HandleTimerTrigger(TimeSpan.FromSeconds(_delay), eventArgs.User, Owner);
Trigger(eventArgs.User);
return true;
}
}

View File

@@ -0,0 +1,13 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Explosion
{
[Serializable, NetSerializable]
public enum ClusterFlashVisuals : byte
{
GrenadesCounter
}
}

View File

@@ -0,0 +1,26 @@
- type: entity
parent: BaseItem
id: ClusterBang
name: ClusterBang
description: Can be used only with flashbangs. Explodes several times.
components:
- type: Sprite
sprite: Objects/Weapons/Grenades/clusterbang.rsi
netsync: false
state: base-0
- type: Appearance
visuals:
- type: ClusterFlashVisualizer
state: base
- type: ClusterFlash
- type: entity
parent: ClusterBang
id: ClusterBangFull
suffix: Full
components:
- type: Sprite
state: base-3
- type: ClusterFlash
fillPrototype: GrenadeFlashBang

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

View File

@@ -0,0 +1,27 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "base-0",
"directions": 1
},
{
"name": "base-1",
"directions": 1
},
{
"name": "base-2",
"directions": 1
},
{
"name": "base-3",
"directions": 1
}
]
}