OverlayManager refactor and Flash (#1218)
* Flash component, overlay and shader Add BeginDraw method to Overlay.cs * Add flash icons, sounds * Progress * Multiple overlays without enums * This is probably the worst way to do this IDK * Remove nullable reference type * Add AttackEventArgs as parameter to OnHitEntities MeleeWeaponComponent.Attack now continues when OnHitEntities returns true (it hit something) Add OverlayType enum so client and server can agree on overlay ids Move IConfigurable to its own file Add AoE flash with shorter duration Flashing someone slows them down * Add arc to flash Set item size to something reasonable Remove chat log message when flash burns out * Remove unused interface
This commit is contained in:
@@ -1,18 +1,21 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
using Content.Client.Graphics.Overlays;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics.Overlays;
|
using Robust.Client.Graphics.Overlays;
|
||||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Interfaces.Network;
|
using Robust.Shared.Interfaces.Reflection;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Utility;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Client.GameObjects
|
namespace Content.Client.GameObjects.Components.Mobs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A character UI component which shows the current damage state of the mob (living/dead)
|
/// A character UI component which shows the current damage state of the mob (living/dead)
|
||||||
@@ -21,48 +24,33 @@ namespace Content.Client.GameObjects
|
|||||||
[ComponentReference(typeof(SharedOverlayEffectsComponent))]
|
[ComponentReference(typeof(SharedOverlayEffectsComponent))]
|
||||||
public sealed class ClientOverlayEffectsComponent : SharedOverlayEffectsComponent//, ICharacterUI
|
public sealed class ClientOverlayEffectsComponent : SharedOverlayEffectsComponent//, ICharacterUI
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An enum representing the current state being applied to the user
|
/// An enum representing the current state being applied to the user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ScreenEffects _currentEffect = ScreenEffects.None;
|
private readonly List<OverlayContainer> _currentEffects = new List<OverlayContainer>();
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public List<OverlayContainer> ActiveOverlays
|
||||||
|
{
|
||||||
|
get => _currentEffects;
|
||||||
|
set => SetEffects(value);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
// Required dependencies
|
// Required dependencies
|
||||||
[Dependency] private readonly IOverlayManager _overlayManager;
|
[Dependency] private readonly IOverlayManager _overlayManager;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager;
|
[Dependency] private readonly IReflectionManager _reflectionManager;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Holds the screen effects that can be applied mapped ot their relevant overlay
|
|
||||||
/// </summary>
|
|
||||||
private Dictionary<ScreenEffects, Overlay> _effectsDictionary;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allows calculating if we need to act due to this component being controlled by the current mob
|
|
||||||
/// </summary>
|
|
||||||
private bool CurrentlyControlled => _playerManager.LocalPlayer.ControlledEntity == Owner;
|
|
||||||
|
|
||||||
public override void OnAdd()
|
|
||||||
{
|
|
||||||
base.OnAdd();
|
|
||||||
|
|
||||||
_effectsDictionary = new Dictionary<ScreenEffects, Overlay>()
|
|
||||||
{
|
|
||||||
{ ScreenEffects.CircleMask, new CircleMaskOverlay() },
|
|
||||||
{ ScreenEffects.GradientCircleMask, new GradientCircleMask() }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent component)
|
public override void HandleMessage(ComponentMessage message, IComponent component)
|
||||||
{
|
{
|
||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
case PlayerAttachedMsg _:
|
case PlayerAttachedMsg _:
|
||||||
SetOverlay(_currentEffect);
|
SetEffects(ActiveOverlays);
|
||||||
break;
|
break;
|
||||||
case PlayerDetachedMsg _:
|
case PlayerDetachedMsg _:
|
||||||
RemoveOverlay();
|
ActiveOverlays = new List<OverlayContainer>();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,42 +58,77 @@ namespace Content.Client.GameObjects
|
|||||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||||
{
|
{
|
||||||
base.HandleComponentState(curState, nextState);
|
base.HandleComponentState(curState, nextState);
|
||||||
if (!(curState is OverlayEffectComponentState state) || _currentEffect == state.ScreenEffect) return;
|
if (!(curState is OverlayEffectComponentState state) || ActiveOverlays.Equals(state.Overlays))
|
||||||
SetOverlay(state.ScreenEffect);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetOverlay(ScreenEffects effect)
|
|
||||||
{
|
|
||||||
RemoveOverlay();
|
|
||||||
|
|
||||||
_currentEffect = effect;
|
|
||||||
|
|
||||||
ApplyOverlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveOverlay()
|
|
||||||
{
|
|
||||||
if (CurrentlyControlled && _currentEffect != ScreenEffects.None)
|
|
||||||
{
|
{
|
||||||
var appliedEffect = _effectsDictionary[_currentEffect];
|
return;
|
||||||
_overlayManager.RemoveOverlay(appliedEffect.ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentEffect = ScreenEffects.None;
|
ActiveOverlays = state.Overlays;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyOverlay()
|
private void SetEffects(List<OverlayContainer> newOverlays)
|
||||||
{
|
{
|
||||||
if (CurrentlyControlled && _currentEffect != ScreenEffects.None)
|
foreach (var container in ActiveOverlays.ShallowClone())
|
||||||
{
|
{
|
||||||
var overlay = _effectsDictionary[_currentEffect];
|
if (!newOverlays.Contains(container))
|
||||||
if (_overlayManager.HasOverlay(overlay.ID))
|
|
||||||
{
|
{
|
||||||
return;
|
RemoveOverlay(container);
|
||||||
}
|
}
|
||||||
_overlayManager.AddOverlay(overlay);
|
|
||||||
Logger.InfoS("overlay", $"Changed overlay to {overlay}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var container in newOverlays)
|
||||||
|
{
|
||||||
|
if (!ActiveOverlays.Contains(container))
|
||||||
|
{
|
||||||
|
AddOverlay(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveOverlay(OverlayContainer container)
|
||||||
|
{
|
||||||
|
ActiveOverlays.Remove(container);
|
||||||
|
_overlayManager.RemoveOverlay(container.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOverlay(OverlayContainer container)
|
||||||
|
{
|
||||||
|
ActiveOverlays.Add(container);
|
||||||
|
if (TryCreateOverlay(container, out var overlay))
|
||||||
|
{
|
||||||
|
_overlayManager.AddOverlay(overlay);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.ErrorS("overlay", $"Could not add overlay {container.ID}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryCreateOverlay(OverlayContainer container, out Overlay overlay)
|
||||||
|
{
|
||||||
|
var overlayTypes = _reflectionManager.GetAllChildren<Overlay>();
|
||||||
|
var foundType = overlayTypes.FirstOrDefault(t => t.Name == container.ID);
|
||||||
|
|
||||||
|
if (foundType != null)
|
||||||
|
{
|
||||||
|
overlay = Activator.CreateInstance(foundType) as Overlay;
|
||||||
|
var configurable = foundType
|
||||||
|
.GetInterfaces()
|
||||||
|
.FirstOrDefault(type =>
|
||||||
|
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IConfigurable<>)
|
||||||
|
&& type.GenericTypeArguments.First() == container.GetType());
|
||||||
|
|
||||||
|
if (configurable != null)
|
||||||
|
{
|
||||||
|
var method = overlay?.GetType().GetMethod("Configure");
|
||||||
|
method?.Invoke(overlay, new []{ container });
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay = default;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Robust.Client.Graphics.Drawing;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Robust.Client.Graphics.Drawing;
|
||||||
using Robust.Client.Graphics.Overlays;
|
using Robust.Client.Graphics.Overlays;
|
||||||
using Robust.Client.Graphics.Shaders;
|
using Robust.Client.Graphics.Shaders;
|
||||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||||
@@ -17,7 +18,7 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||||
|
|
||||||
public CircleMaskOverlay() : base(nameof(CircleMaskOverlay))
|
public CircleMaskOverlay() : base(nameof(OverlayType.CircleMaskOverlay))
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
Shader = _prototypeManager.Index<ShaderPrototype>("CircleMask").Instance();
|
Shader = _prototypeManager.Index<ShaderPrototype>("CircleMask").Instance();
|
||||||
|
|||||||
73
Content.Client/Graphics/Overlays/FlashOverlay.cs
Normal file
73
Content.Client/Graphics/Overlays/FlashOverlay.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using System.Net.Mime;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Graphics.Drawing;
|
||||||
|
using Robust.Client.Graphics.Overlays;
|
||||||
|
using Robust.Client.Graphics.Shaders;
|
||||||
|
using Robust.Client.Interfaces.Graphics;
|
||||||
|
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||||
|
using Robust.Shared.Interfaces.Timing;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using Color = Robust.Shared.Maths.Color;
|
||||||
|
|
||||||
|
namespace Content.Client.Graphics.Overlays
|
||||||
|
{
|
||||||
|
public class FlashOverlay : Overlay, IConfigurable<TimedOverlayContainer>
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||||
|
[Dependency] private readonly IClyde _displayManager;
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||||
|
private double _startTime;
|
||||||
|
private int lastsFor = 5000;
|
||||||
|
private Texture _screenshotTexture;
|
||||||
|
|
||||||
|
public FlashOverlay() : base(nameof(OverlayType.FlashOverlay))
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
Shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").Instance().Duplicate();
|
||||||
|
|
||||||
|
_startTime = _gameTiming.CurTime.TotalMilliseconds;
|
||||||
|
_displayManager.Screenshot(ScreenshotType.BeforeUI, image =>
|
||||||
|
{
|
||||||
|
var rgba32Image = image.CloneAs<Rgba32>(Configuration.Default);
|
||||||
|
_screenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleBase handle)
|
||||||
|
{
|
||||||
|
var percentComplete = (float) ((_gameTiming.CurTime.TotalMilliseconds - _startTime) / lastsFor);
|
||||||
|
Shader?.SetParameter("percentComplete", percentComplete);
|
||||||
|
|
||||||
|
var screenSpaceHandle = handle as DrawingHandleScreen;
|
||||||
|
var screenSize = UIBox2.FromDimensions((0, 0), _displayManager.ScreenSize);
|
||||||
|
|
||||||
|
if (_screenshotTexture != null)
|
||||||
|
{
|
||||||
|
screenSpaceHandle?.DrawTextureRect(_screenshotTexture, screenSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
_screenshotTexture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(TimedOverlayContainer parameters)
|
||||||
|
{
|
||||||
|
lastsFor = parameters.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Robust.Client.Graphics.Drawing;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Robust.Client.Graphics.Drawing;
|
||||||
using Robust.Client.Graphics.Overlays;
|
using Robust.Client.Graphics.Overlays;
|
||||||
using Robust.Client.Graphics.Shaders;
|
using Robust.Client.Graphics.Shaders;
|
||||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||||
@@ -8,7 +9,7 @@ using Robust.Shared.Prototypes;
|
|||||||
|
|
||||||
namespace Content.Client.Graphics.Overlays
|
namespace Content.Client.Graphics.Overlays
|
||||||
{
|
{
|
||||||
public class GradientCircleMask : Overlay
|
public class GradientCircleMaskOverlay : Overlay
|
||||||
{
|
{
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||||
@@ -16,7 +17,7 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||||
|
|
||||||
public GradientCircleMask() : base(nameof(GradientCircleMask))
|
public GradientCircleMaskOverlay() : base(nameof(OverlayType.GradientCircleMaskOverlay))
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
Shader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").Instance();
|
Shader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").Instance();
|
||||||
|
|||||||
@@ -69,21 +69,24 @@ namespace Content.Server.GameObjects
|
|||||||
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Health,
|
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||||
"/Textures/Mob/UI/Human/human" + modifier + ".png");
|
"/Textures/Mob/UI/Human/human" + modifier + ".png");
|
||||||
|
|
||||||
overlayComponent?.ChangeOverlay(ScreenEffects.None);
|
overlayComponent?.RemoveOverlay(OverlayType.GradientCircleMaskOverlay);
|
||||||
|
overlayComponent?.RemoveOverlay(OverlayType.CircleMaskOverlay);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
case ThresholdType.Critical:
|
case ThresholdType.Critical:
|
||||||
statusEffectsComponent?.ChangeStatusEffectIcon(
|
statusEffectsComponent?.ChangeStatusEffectIcon(
|
||||||
StatusEffect.Health,
|
StatusEffect.Health,
|
||||||
"/Textures/Mob/UI/Human/humancrit-0.png");
|
"/Textures/Mob/UI/Human/humancrit-0.png");
|
||||||
overlayComponent?.ChangeOverlay(ScreenEffects.GradientCircleMask);
|
overlayComponent?.ClearOverlays();
|
||||||
|
overlayComponent?.AddOverlay(OverlayType.GradientCircleMaskOverlay);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
case ThresholdType.Death:
|
case ThresholdType.Death:
|
||||||
statusEffectsComponent?.ChangeStatusEffectIcon(
|
statusEffectsComponent?.ChangeStatusEffectIcon(
|
||||||
StatusEffect.Health,
|
StatusEffect.Health,
|
||||||
"/Textures/Mob/UI/Human/humandead.png");
|
"/Textures/Mob/UI/Human/humandead.png");
|
||||||
overlayComponent?.ChangeOverlay(ScreenEffects.CircleMask);
|
overlayComponent?.ClearOverlays();
|
||||||
|
overlayComponent?.AddOverlay(OverlayType.CircleMaskOverlay);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Timers;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Mobs
|
namespace Content.Server.GameObjects.Components.Mobs
|
||||||
{
|
{
|
||||||
@@ -7,20 +12,49 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
[ComponentReference(typeof(SharedOverlayEffectsComponent))]
|
[ComponentReference(typeof(SharedOverlayEffectsComponent))]
|
||||||
public sealed class ServerOverlayEffectsComponent : SharedOverlayEffectsComponent
|
public sealed class ServerOverlayEffectsComponent : SharedOverlayEffectsComponent
|
||||||
{
|
{
|
||||||
private ScreenEffects _currentOverlay = ScreenEffects.None;
|
private readonly List<OverlayContainer> _currentOverlays = new List<OverlayContainer>();
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
private List<OverlayContainer> ActiveOverlays => _currentOverlays;
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
public override ComponentState GetComponentState()
|
||||||
{
|
{
|
||||||
return new OverlayEffectComponentState(_currentOverlay);
|
return new OverlayEffectComponentState(_currentOverlays);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeOverlay(ScreenEffects effect)
|
public void AddOverlay(OverlayContainer container)
|
||||||
{
|
{
|
||||||
if (effect == _currentOverlay)
|
if (!ActiveOverlays.Contains(container))
|
||||||
{
|
{
|
||||||
return;
|
ActiveOverlays.Add(container);
|
||||||
|
Dirty();
|
||||||
}
|
}
|
||||||
_currentOverlay = effect;
|
}
|
||||||
|
|
||||||
|
public void AddOverlay(string id) => AddOverlay(new OverlayContainer(id));
|
||||||
|
public void AddOverlay(OverlayType type) => AddOverlay(new OverlayContainer(type));
|
||||||
|
|
||||||
|
public void RemoveOverlay(OverlayContainer container)
|
||||||
|
{
|
||||||
|
if (ActiveOverlays.RemoveAll(c => c.Equals(container)) > 0)
|
||||||
|
{
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveOverlay(string id)
|
||||||
|
{
|
||||||
|
if (ActiveOverlays.RemoveAll(container => container.ID == id) > 0)
|
||||||
|
{
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveOverlay(OverlayType type) => RemoveOverlay(type.ToString());
|
||||||
|
|
||||||
|
public void ClearOverlays()
|
||||||
|
{
|
||||||
|
ActiveOverlays.Clear();
|
||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ namespace Content.Server.GameObjects
|
|||||||
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Health);
|
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Health);
|
||||||
|
|
||||||
Owner.TryGetComponent(out ServerOverlayEffectsComponent overlayEffectsComponent);
|
Owner.TryGetComponent(out ServerOverlayEffectsComponent overlayEffectsComponent);
|
||||||
overlayEffectsComponent?.ChangeOverlay(ScreenEffects.None);
|
overlayEffectsComponent?.ClearOverlays();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IActionBlocker.CanMove()
|
bool IActionBlocker.CanMove()
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Interfaces;
|
||||||
|
using Content.Shared.Chat;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Network;
|
||||||
|
using Robust.Shared.Interfaces.Random;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Timer = Robust.Shared.Timers.Timer;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Weapon.Melee
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class FlashComponent : MeleeWeaponComponent, IUse, IExamine
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager;
|
||||||
|
[Dependency] private readonly ISharedNotifyManager _notifyManager;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
public override string Name => "Flash";
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private int _flashDuration = 5000;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private float _flashFalloffExp = 8f;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private int _uses = 5;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private float _range = 3f;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private int _aoeFlashDuration = 5000 / 3;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private float _slowTo = 0.75f;
|
||||||
|
private bool _flashing;
|
||||||
|
|
||||||
|
private int Uses
|
||||||
|
{
|
||||||
|
get => _uses;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_uses = value;
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasUses => _uses > 0;
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
serializer.DataField(ref _flashDuration, "duration", 5000);
|
||||||
|
serializer.DataField(ref _flashFalloffExp, "flashFalloffExp", 8f);
|
||||||
|
serializer.DataField(ref _uses, "uses", 5);
|
||||||
|
serializer.DataField(ref _range, "range", 3f);
|
||||||
|
serializer.DataField(ref _aoeFlashDuration, "aoeFlashDuration", _flashDuration / 3);
|
||||||
|
serializer.DataField(ref _slowTo, "slowTo", 0.75f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHitEntities(IReadOnlyList<IEntity> entities, AttackEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (entities.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Use(eventArgs.User))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
Flash(entity, eventArgs.User);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseEntity(UseEntityEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (!Use(eventArgs.User))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entity in _entityManager.GetEntitiesInRange(Owner.Transform.GridPosition, _range))
|
||||||
|
{
|
||||||
|
Flash(entity, eventArgs.User, _aoeFlashDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Use(IEntity user)
|
||||||
|
{
|
||||||
|
if (HasUses)
|
||||||
|
{
|
||||||
|
var sprite = Owner.GetComponent<SpriteComponent>();
|
||||||
|
if (--Uses == 0)
|
||||||
|
{
|
||||||
|
sprite.LayerSetState(0, "burnt");
|
||||||
|
|
||||||
|
_notifyManager.PopupMessage(Owner, user, "The flash burns out!");
|
||||||
|
}
|
||||||
|
else if (!_flashing)
|
||||||
|
{
|
||||||
|
int animLayer = sprite.AddLayerWithState("flashing");
|
||||||
|
_flashing = true;
|
||||||
|
|
||||||
|
Timer.Spawn(400, () =>
|
||||||
|
{
|
||||||
|
sprite.RemoveLayer(animLayer);
|
||||||
|
_flashing = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/weapons/flash.ogg", Owner.Transform.GridPosition,
|
||||||
|
AudioParams.Default);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Flash(IEntity entity, IEntity user)
|
||||||
|
{
|
||||||
|
Flash(entity, user, _flashDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if target can be flashed (e.g. things like sunglasses would block a flash)
|
||||||
|
private void Flash(IEntity entity, IEntity user, int flashDuration)
|
||||||
|
{
|
||||||
|
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlayEffectsComponent))
|
||||||
|
{
|
||||||
|
var container = new TimedOverlayContainer(nameof(OverlayType.FlashOverlay), flashDuration);
|
||||||
|
overlayEffectsComponent.AddOverlay(container);
|
||||||
|
container.StartTimer(() => overlayEffectsComponent.RemoveOverlay(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.TryGetComponent(out StunnableComponent stunnableComponent))
|
||||||
|
{
|
||||||
|
stunnableComponent.Slowdown(flashDuration / 1000f, _slowTo, _slowTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity != user)
|
||||||
|
{
|
||||||
|
_notifyManager.PopupMessage(user, entity, $"{user.Name} blinds you with the {Owner.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Examine(FormattedMessage message, bool inDetailsRange)
|
||||||
|
{
|
||||||
|
if (!HasUses)
|
||||||
|
{
|
||||||
|
message.AddText("It's burnt out.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inDetailsRange)
|
||||||
|
{
|
||||||
|
message.AddMarkup(_localizationManager.GetString(
|
||||||
|
$"The flash has [color=green]{Uses}[/color] uses remaining."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,9 +81,9 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
|
|||||||
serializer.DataField(ref _cooldownTime, "cooldownTime", 1f);
|
serializer.DataField(ref _cooldownTime, "cooldownTime", 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool OnHitEntities(IReadOnlyList<IEntity> entities)
|
protected virtual bool OnHitEntities(IReadOnlyList<IEntity> entities, AttackEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IAttack.Attack(AttackEventArgs eventArgs)
|
void IAttack.Attack(AttackEventArgs eventArgs)
|
||||||
@@ -112,7 +112,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(OnHitEntities(hitEntities)) return;
|
if(!OnHitEntities(hitEntities, eventArgs)) return;
|
||||||
|
|
||||||
var audioSystem = EntitySystem.Get<AudioSystem>();
|
var audioSystem = EntitySystem.Get<AudioSystem>();
|
||||||
var emitter = hitEntities.Count == 0 ? eventArgs.User : hitEntities[0];
|
var emitter = hitEntities.Count == 0 ? eventArgs.User : hitEntities[0];
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
|
|||||||
serializer.DataField(ref _slowdownTime, "slowdownTime", 5f);
|
serializer.DataField(ref _slowdownTime, "slowdownTime", 5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnHitEntities(IReadOnlyList<IEntity> entities)
|
protected override bool OnHitEntities(IReadOnlyList<IEntity> entities, AttackEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
var cell = Cell;
|
var cell = Cell;
|
||||||
if (!Activated || entities.Count == 0 || cell == null)
|
if (!Activated || entities.Count == 0 || cell == null)
|
||||||
@@ -118,7 +118,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
|
|||||||
TurnOff();
|
TurnOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ToggleStatus(IEntity user)
|
private bool ToggleStatus(IEntity user)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
using Content.Server.Mobs.Roles;
|
using Content.Server.Mobs.Roles;
|
||||||
using Content.Server.Players;
|
using Content.Server.Players;
|
||||||
using Content.Shared.Jobs;
|
using Content.Shared.Jobs;
|
||||||
@@ -117,4 +118,52 @@ namespace Content.Server.Mobs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AddOverlayCommand : IClientCommand
|
||||||
|
{
|
||||||
|
public string Command => "addoverlay";
|
||||||
|
public string Description => "Adds an overlay by its ID";
|
||||||
|
public string Help => "addoverlay <id>";
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
shell.SendText(player, "Expected 1 argument.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player?.AttachedEntity != null)
|
||||||
|
{
|
||||||
|
if (player.AttachedEntity.TryGetComponent(out ServerOverlayEffectsComponent overlayEffectsComponent))
|
||||||
|
{
|
||||||
|
overlayEffectsComponent.AddOverlay(args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoveOverlayCommand : IClientCommand
|
||||||
|
{
|
||||||
|
public string Command => "rmoverlay";
|
||||||
|
public string Description => "Removes an overlay by its ID";
|
||||||
|
public string Help => "rmoverlay <id>";
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
shell.SendText(player, "Expected 1 argument.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player?.AttachedEntity != null)
|
||||||
|
{
|
||||||
|
if (player.AttachedEntity.TryGetComponent(out ServerOverlayEffectsComponent overlayEffectsComponent))
|
||||||
|
{
|
||||||
|
overlayEffectsComponent.RemoveOverlay(args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Timers;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
using Component = Robust.Shared.GameObjects.Component;
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components.Mobs
|
namespace Content.Shared.GameObjects.Components.Mobs
|
||||||
{
|
{
|
||||||
@@ -13,21 +21,72 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
|||||||
public sealed override uint? NetID => ContentNetIDs.OVERLAYEFFECTS;
|
public sealed override uint? NetID => ContentNetIDs.OVERLAYEFFECTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScreenEffects
|
[Serializable, NetSerializable]
|
||||||
|
public class OverlayContainer
|
||||||
{
|
{
|
||||||
None,
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
CircleMask,
|
public string ID { get; }
|
||||||
GradientCircleMask,
|
|
||||||
|
public OverlayContainer([NotNull] string id)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverlayContainer(OverlayType type) : this(type.ToString())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is OverlayContainer container)
|
||||||
|
{
|
||||||
|
return container.ID == ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is string idString)
|
||||||
|
{
|
||||||
|
return idString == ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return (ID != null ? ID.GetHashCode() : 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class OverlayEffectComponentState : ComponentState
|
public class OverlayEffectComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public ScreenEffects ScreenEffect;
|
public List<OverlayContainer> Overlays;
|
||||||
|
|
||||||
public OverlayEffectComponentState(ScreenEffects screenEffect) : base(ContentNetIDs.OVERLAYEFFECTS)
|
public OverlayEffectComponentState(List<OverlayContainer> overlays) : base(ContentNetIDs.OVERLAYEFFECTS)
|
||||||
{
|
{
|
||||||
ScreenEffect = screenEffect;
|
Overlays = overlays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class TimedOverlayContainer : OverlayContainer
|
||||||
|
{
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public int Length { get; }
|
||||||
|
|
||||||
|
public TimedOverlayContainer(string id, int length) : base(id)
|
||||||
|
{
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartTimer(Action finished) => Timer.Spawn(Length, finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum OverlayType
|
||||||
|
{
|
||||||
|
GradientCircleMaskOverlay,
|
||||||
|
CircleMaskOverlay,
|
||||||
|
FlashOverlay
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
Content.Shared/Interfaces/IConfigurable.cs
Normal file
7
Content.Shared/Interfaces/IConfigurable.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Content.Shared.Interfaces
|
||||||
|
{
|
||||||
|
public interface IConfigurable<in T>
|
||||||
|
{
|
||||||
|
public void Configure(T parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Resources/Audio/weapons/flash.ogg
Normal file
BIN
Resources/Audio/weapons/flash.ogg
Normal file
Binary file not shown.
@@ -74,6 +74,8 @@
|
|||||||
- mindinfo
|
- mindinfo
|
||||||
- addrole
|
- addrole
|
||||||
- rmrole
|
- rmrole
|
||||||
|
- addoverlay
|
||||||
|
- rmoverlay
|
||||||
- showtime
|
- showtime
|
||||||
- group
|
- group
|
||||||
- addai
|
- addai
|
||||||
@@ -121,6 +123,8 @@
|
|||||||
- mindinfo
|
- mindinfo
|
||||||
- addrole
|
- addrole
|
||||||
- rmrole
|
- rmrole
|
||||||
|
- addoverlay
|
||||||
|
- rmoverlay
|
||||||
- srvpopupmsg
|
- srvpopupmsg
|
||||||
- group
|
- group
|
||||||
- showtime
|
- showtime
|
||||||
|
|||||||
@@ -23,3 +23,30 @@
|
|||||||
HeldPrefix: off
|
HeldPrefix: off
|
||||||
|
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: flash
|
||||||
|
parent: BaseItem
|
||||||
|
id: Flash
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Melee/flash.rsi
|
||||||
|
state: flash
|
||||||
|
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Melee/flash.rsi
|
||||||
|
state: flash
|
||||||
|
|
||||||
|
- type: Flash
|
||||||
|
damage: 0
|
||||||
|
cooldownTime: 1
|
||||||
|
arc: smash
|
||||||
|
hitSound: /Audio/weapons/flash.ogg
|
||||||
|
slowTo: 0.7
|
||||||
|
|
||||||
|
- type: Item
|
||||||
|
Size: 2
|
||||||
|
sprite: Objects/Melee/flash.rsi
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
|
||||||
|
|||||||
@@ -7,3 +7,8 @@
|
|||||||
id: GradientCircleMask
|
id: GradientCircleMask
|
||||||
kind: source
|
kind: source
|
||||||
path: "/Shaders/gradient_circle_mask.swsl"
|
path: "/Shaders/gradient_circle_mask.swsl"
|
||||||
|
|
||||||
|
- type: shader
|
||||||
|
id: FlashedEffect
|
||||||
|
kind: source
|
||||||
|
path: "/Shaders/flashed_effect.swsl"
|
||||||
|
|||||||
18
Resources/Shaders/flashed_effect.swsl
Normal file
18
Resources/Shaders/flashed_effect.swsl
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
uniform float percentComplete;
|
||||||
|
uniform float fadeFalloffExp = 8;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
// Higher exponent -> stronger blinding effect
|
||||||
|
float remaining = -pow(percentComplete, fadeFalloffExp) + 1;
|
||||||
|
|
||||||
|
// Two ghost textures that spin around the character
|
||||||
|
vec4 tex1 = texture(TEXTURE, vec2(UV.x + (0.02) * sin(TIME * 3), UV.y + (0.02) * cos(TIME * 3)));
|
||||||
|
vec4 tex2 = texture(TEXTURE, vec2(UV.x + (0.01) * sin(TIME * 2), UV.y + (0.01) * cos(TIME * 2)));
|
||||||
|
|
||||||
|
vec4 textureMix = mix(tex1, tex2, 0.5);
|
||||||
|
|
||||||
|
// Gradually mixes between the texture mix and a full-white texture, causing the "blinding" effect
|
||||||
|
vec4 mixed = mix(vec4(1, 1, 1, 1), textureMix, percentComplete);
|
||||||
|
|
||||||
|
COLOR = vec4(mixed.rgb, remaining);
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/Melee/flash.rsi/burnt.png
Normal file
BIN
Resources/Textures/Objects/Melee/flash.rsi/burnt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 354 B |
BIN
Resources/Textures/Objects/Melee/flash.rsi/flash.png
Normal file
BIN
Resources/Textures/Objects/Melee/flash.rsi/flash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 368 B |
BIN
Resources/Textures/Objects/Melee/flash.rsi/flashing.png
Normal file
BIN
Resources/Textures/Objects/Melee/flash.rsi/flashing.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 385 B |
BIN
Resources/Textures/Objects/Melee/flash.rsi/inhand-left.png
Normal file
BIN
Resources/Textures/Objects/Melee/flash.rsi/inhand-left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 280 B |
BIN
Resources/Textures/Objects/Melee/flash.rsi/inhand-right.png
Normal file
BIN
Resources/Textures/Objects/Melee/flash.rsi/inhand-right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 296 B |
76
Resources/Textures/Objects/Melee/flash.rsi/meta.json
Normal file
76
Resources/Textures/Objects/Melee/flash.rsi/meta.json
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"license": "CC BY-SA 3.0",
|
||||||
|
"copyright": "Taken from CEV Eris",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "flash",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flashing",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.3
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "burnt",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inhand-right",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inhand-left",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user