Visualize melee weapon cooldowns in HUD.

This commit is contained in:
Pieter-Jan Briers
2019-11-12 01:43:11 +01:00
parent 7198a7c78d
commit 841bb101c5
20 changed files with 279 additions and 10 deletions

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Content.Tests")]

View File

@@ -1,10 +1,11 @@
using Content.Client.GameObjects;
using System;
using Content.Client.GameObjects;
using Content.Client.GameObjects.EntitySystems;
using Content.Client.Interfaces.GameObjects;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Input;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Player;
@@ -12,15 +13,18 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
namespace Content.Client.UserInterface
{
public class HandsGui : Control
{
private const int CooldownLevels = 8;
private const int BoxSpacing = 0;
private const int BoxSize = 64;
@@ -28,11 +32,13 @@ namespace Content.Client.UserInterface
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly ILocalizationManager _loc;
[Dependency] private readonly IGameTiming _gameTiming;
#pragma warning restore 0649
private Texture TextureHandLeft;
private Texture TextureHandRight;
private Texture TextureHandActive;
private readonly Texture TextureHandLeft;
private readonly Texture TextureHandRight;
private readonly Texture TextureHandActive;
private readonly Texture[] TexturesCooldownOverlay;
private IEntity LeftHand;
private IEntity RightHand;
@@ -43,6 +49,9 @@ namespace Content.Client.UserInterface
private readonly SpriteView RightSpriteView;
private readonly TextureRect ActiveHandRect;
private readonly TextureRect CooldownCircleLeft;
private readonly TextureRect CooldownCircleRight;
public HandsGui()
{
IoCManager.InjectDependencies(this);
@@ -58,6 +67,13 @@ namespace Content.Client.UserInterface
TextureHandRight = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/hand_r.png");
TextureHandActive = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/hand_active.png");
TexturesCooldownOverlay = new Texture[CooldownLevels];
for (var i = 0; i < CooldownLevels; i++)
{
TexturesCooldownOverlay[i] =
_resourceCache.GetTexture($"/Textures/UserInterface/Inventory/cooldown-{i}.png");
}
AddChild(new TextureRect
{
MouseFilter = MouseFilterMode.Ignore,
@@ -102,6 +118,24 @@ namespace Content.Client.UserInterface
AddChild(RightSpriteView);
RightSpriteView.Size = _handR.Size;
RightSpriteView.Position = _handR.TopLeft;
// Cooldown circles.
AddChild(CooldownCircleLeft = new TextureRect
{
MouseFilter = MouseFilterMode.Ignore,
Position = _handL.TopLeft + (8, 8),
TextureScale = (2, 2),
Visible = false,
});
AddChild(CooldownCircleRight = new TextureRect
{
MouseFilter = MouseFilterMode.Ignore,
Position = _handR.TopLeft + (8, 8),
TextureScale = (2, 2),
Visible = false
});
}
protected override Vector2 CalculateMinimumSize()
@@ -210,8 +244,8 @@ namespace Content.Client.UserInterface
return;
}
var leftHandContains = _handL.Contains((Vector2i)args.RelativePosition);
var rightHandContains = _handR.Contains((Vector2i)args.RelativePosition);
var leftHandContains = _handL.Contains((Vector2i) args.RelativePosition);
var rightHandContains = _handR.Contains((Vector2i) args.RelativePosition);
string handIndex;
if (leftHandContains)
@@ -262,8 +296,55 @@ namespace Content.Client.UserInterface
}
var esm = IoCManager.Resolve<IEntitySystemManager>();
esm.GetEntitySystem<VerbSystem>().OpenContextMenu(entity, new ScreenCoordinates(args.PointerLocation.Position));
esm.GetEntitySystem<VerbSystem>()
.OpenContextMenu(entity, new ScreenCoordinates(args.PointerLocation.Position));
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateCooldown(CooldownCircleLeft, LeftHand);
UpdateCooldown(CooldownCircleRight, RightHand);
}
private void UpdateCooldown(TextureRect cooldownTexture, IEntity entity)
{
if (entity != null
&& entity.TryGetComponent(out ItemCooldownComponent cooldown)
&& cooldown.CooldownStart.HasValue
&& cooldown.CooldownEnd.HasValue)
{
var start = cooldown.CooldownStart.Value;
var end = cooldown.CooldownEnd.Value;
var length = (end - start).TotalSeconds;
var progress = (_gameTiming.CurTime - start).TotalSeconds;
var ratio = (float)(progress / length);
var textureIndex = CalculateCooldownLevel(ratio);
if (textureIndex == CooldownLevels)
{
cooldownTexture.Visible = false;
}
else
{
cooldownTexture.Visible = true;
cooldownTexture.Texture = TexturesCooldownOverlay[textureIndex];
}
}
else
{
cooldownTexture.Visible = false;
}
}
internal static int CalculateCooldownLevel(float cooldownValue)
{
var val = cooldownValue.Clamp(0, 1);
val *= CooldownLevels;
return (int) Math.Floor(val);
}
}
}

View File

@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
@@ -112,7 +111,14 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
var sys = _entitySystemManager.GetEntitySystem<MeleeWeaponSystem>();
sys.SendAnimation(Arc, angle, eventArgs.User, hitEntities);
}
_lastAttackTime = IoCManager.Resolve<IGameTiming>().CurTime;
if (Owner.TryGetComponent(out ItemCooldownComponent cooldown))
{
cooldown.CooldownStart = _lastAttackTime;
cooldown.CooldownEnd = _lastAttackTime + TimeSpan.FromSeconds(_cooldownTime);
}
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Items
{
/// <summary>
/// Stores a visual "cooldown" for items, that gets displayed in the hands GUI.
/// </summary>
[RegisterComponent]
public sealed class ItemCooldownComponent : Component
{
public override string Name => "ItemCooldown";
public override Type StateType => typeof(ItemCooldownComponentState);
public override uint? NetID => ContentNetIDs.ITEMCOOLDOWN;
private TimeSpan? _cooldownEnd;
private TimeSpan? _cooldownStart;
/// <summary>
/// The time when this cooldown ends.
/// </summary>
/// <remarks>
/// If null, no cooldown is displayed.
/// </remarks>
[ViewVariables]
public TimeSpan? CooldownEnd
{
get => _cooldownEnd;
set
{
_cooldownEnd = value;
Dirty();
}
}
/// <summary>
/// The time when this cooldown started.
/// </summary>
/// <remarks>
/// If null, no cooldown is displayed.
/// </remarks>
[ViewVariables]
public TimeSpan? CooldownStart
{
get => _cooldownStart;
set
{
_cooldownStart = value;
Dirty();
}
}
public override ComponentState GetComponentState()
{
return new ItemCooldownComponentState
{
CooldownEnd = CooldownEnd,
CooldownStart = CooldownStart
};
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (ItemCooldownComponentState) curState;
CooldownStart = cast.CooldownStart;
CooldownEnd = cast.CooldownEnd;
}
[Serializable, NetSerializable]
private sealed class ItemCooldownComponentState : ComponentState
{
public TimeSpan? CooldownStart { get; set; }
public TimeSpan? CooldownEnd { get; set; }
public ItemCooldownComponentState() : base(ContentNetIDs.ITEMCOOLDOWN)
{
}
}
}
}

View File

@@ -31,5 +31,6 @@
public const uint STATUSEFFECTS = 1026;
public const uint OVERLAYEFFECTS = 1027;
public const uint STOMACH = 1028;
public const uint ITEMCOOLDOWN = 1029;
}
}

View File

@@ -0,0 +1,17 @@
using Content.Client.UserInterface;
using NUnit.Framework;
namespace Content.Tests.Client.UserInterface
{
[TestFixture]
public class HandsGuiTest
{
[Test]
public void TestCalculateCooldownLevel()
{
Assert.AreEqual(HandsGui.CalculateCooldownLevel(0.5f), 4);
Assert.AreEqual(HandsGui.CalculateCooldownLevel(1), 8);
Assert.AreEqual(HandsGui.CalculateCooldownLevel(0), 0);
}
}
}

View File

@@ -6,6 +6,7 @@
Capacity: 60
- type: Item
Size: 9999
- type: ItemCooldown
- type: MeleeWeapon
hitSound: "/Audio/weapons/smash.ogg"

View File

@@ -9,6 +9,7 @@
texture: Objects/Tools/wirecutter.png
- type: Icon
texture: Objects/Tools/wirecutter.png
- type: ItemCooldown
- type: MeleeWeapon
- type: entity
@@ -22,6 +23,7 @@
texture: Objects/Tools/screwdriver.png
- type: Icon
texture: Objects/Tools/screwdriver.png
- type: ItemCooldown
- type: MeleeWeapon
- type: entity
@@ -42,6 +44,7 @@
- type: Icon
sprite: Objects/Tools/welder.rsi
state: welder
- type: ItemCooldown
- type: MeleeWeapon
- type: entity
@@ -55,6 +58,7 @@
texture: Objects/Tools/wrench.png
- type: Icon
texture: Objects/Tools/wrench.png
- type: ItemCooldown
- type: MeleeWeapon
- type: entity
@@ -68,6 +72,7 @@
texture: Objects/Tools/crowbar.png
- type: Icon
texture: Objects/Tools/crowbar.png
- type: ItemCooldown
- type: MeleeWeapon
- type: entity

View File

@@ -13,6 +13,7 @@
sprite: Objects/Melee/cleaver.rsi
state: butch
- type: ItemCooldown
- type: MeleeWeapon
- type: Item
Size: 10

View File

@@ -11,6 +11,7 @@
sprite: Objects/Melee/pickaxe.rsi
state: pickaxe
- type: Pickaxe
- type: ItemCooldown
- type: MeleeWeapon
damage: 25
- type: Item

View File

@@ -21,6 +21,8 @@
sprite: Objects/Melee/spear.rsi
prefix: inhand
- type: ItemCooldown
- type: MeleeWeaponAnimation
id: spear
state: spear

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 6.3499999 6.3500002"
version="1.1"
id="svg8"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="cooldown.svg"
inkscape:export-filename="/ssdhome/pj/Projects/space-station-14-content/Resources/Textures/UserInterface/Inventory/cooldown-7.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="8.9236949"
inkscape:cy="12.417176"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:pagecheckerboard="true"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="circle"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-290.64999)">
<path
style="opacity:1;vector-effect:none;fill:#4a4666;fill-opacity:0.50196078;fill-rule:nonzero;stroke:none;stroke-width:1.99293923;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 12,0 A 12,12 0 0 0 3.5195312,3.5195312 L 12,12 Z"
transform="matrix(0.26458334,0,0,0.26458334,0,290.64999)"
id="circle"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB