* #272 add bordered panel for effects bar * #272 avoid mouse overlapping tooltip when near edges, change tooltip colors to match mockups * #272 WIP defining status effect states as YML and sending them as encoded integers * #272 refactor to use new alert system * #272 refactor to use new alert system * #272 fix various bugs with new alert system and update alerts to have color * #272 WIP * #272 rename status effects to alerts * #272 WIP reworking alert internals to avoid code dup and eliminate enum * #272 refactor alerts to use categories and fix various bugs * #272 more alert bugfixes * #272 alert ordering * #272 callback-based approach for alert clicks * #272 add debug commands for alerts * #272 utilize new GridContainer capabilities for sizing of alerts tab * #272 scale alerts height based on window size * #272 fix tooltip flicker * #272 transparent alert panel * #272 adjust styles to match injazz mockups more, add cooldown info in tooltip * #272 adjust styles to match injazz mockups more, add cooldown info in tooltip * #272 alert prototype tests * #272 alert manager tests * #272 alert order tests * #272 simple unit test for alerts component * #272 integration test for alerts * #272 rework alerts to use enums instead of id / category * #272 various cleanups for PR * #272 use byte for more compact alert messages * #272 rename StatusEffects folder to Alerts, add missing NetSerializable
207 lines
6.9 KiB
C#
207 lines
6.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Content.Client.UserInterface.Stylesheets;
|
|
using Content.Shared.AI;
|
|
using Robust.Client.Interfaces.Graphics.ClientEye;
|
|
using Robust.Client.Interfaces.UserInterface;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Shared.GameObjects.Systems;
|
|
using Robust.Shared.Interfaces.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Maths;
|
|
|
|
namespace Content.Client.GameObjects.EntitySystems.AI
|
|
{
|
|
#if DEBUG
|
|
public class ClientAiDebugSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
|
|
|
private AiDebugMode _tooltips = AiDebugMode.None;
|
|
private readonly Dictionary<IEntity, PanelContainer> _aiBoxes = new Dictionary<IEntity,PanelContainer>();
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
if (_tooltips == 0)
|
|
{
|
|
if (_aiBoxes.Count > 0)
|
|
{
|
|
foreach (var (_, panel) in _aiBoxes)
|
|
{
|
|
panel.Dispose();
|
|
}
|
|
|
|
_aiBoxes.Clear();
|
|
}
|
|
return;
|
|
}
|
|
|
|
var deletedEntities = new List<IEntity>(0);
|
|
foreach (var (entity, panel) in _aiBoxes)
|
|
{
|
|
if (entity.Deleted)
|
|
{
|
|
deletedEntities.Add(entity);
|
|
continue;
|
|
}
|
|
|
|
if (!_eyeManager.GetWorldViewport().Contains(entity.Transform.WorldPosition))
|
|
{
|
|
panel.Visible = false;
|
|
continue;
|
|
}
|
|
|
|
var (x, y) = _eyeManager.CoordinatesToScreen(entity.Transform.Coordinates).Position;
|
|
var offsetPosition = new Vector2(x - panel.Width / 2, y - panel.Height - 50f);
|
|
panel.Visible = true;
|
|
|
|
LayoutContainer.SetPosition(panel, offsetPosition);
|
|
}
|
|
|
|
foreach (var entity in deletedEntities)
|
|
{
|
|
_aiBoxes.Remove(entity);
|
|
}
|
|
}
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
SubscribeNetworkEvent<SharedAiDebug.UtilityAiDebugMessage>(HandleUtilityAiDebugMessage);
|
|
SubscribeNetworkEvent<SharedAiDebug.AStarRouteMessage>(HandleAStarRouteMessage);
|
|
SubscribeNetworkEvent<SharedAiDebug.JpsRouteMessage>(HandleJpsRouteMessage);
|
|
}
|
|
|
|
private void HandleUtilityAiDebugMessage(SharedAiDebug.UtilityAiDebugMessage message)
|
|
{
|
|
if ((_tooltips & AiDebugMode.Thonk) != 0)
|
|
{
|
|
// I guess if it's out of range we don't know about it?
|
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
var entity = entityManager.GetEntity(message.EntityUid);
|
|
TryCreatePanel(entity);
|
|
|
|
// Probably shouldn't access by index but it's a debugging tool so eh
|
|
var label = (Label) _aiBoxes[entity].GetChild(0).GetChild(0);
|
|
label.Text = $"Current Task: {message.FoundTask}\n" +
|
|
$"Task score: {message.ActionScore}\n" +
|
|
$"Planning time (ms): {message.PlanningTime * 1000:0.0000}\n" +
|
|
$"Considered {message.ConsideredTaskCount} tasks";
|
|
}
|
|
}
|
|
|
|
private void HandleAStarRouteMessage(SharedAiDebug.AStarRouteMessage message)
|
|
{
|
|
if ((_tooltips & AiDebugMode.Paths) != 0)
|
|
{
|
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
var entity = entityManager.GetEntity(message.EntityUid);
|
|
TryCreatePanel(entity);
|
|
|
|
var label = (Label) _aiBoxes[entity].GetChild(0).GetChild(1);
|
|
label.Text = $"Pathfinding time (ms): {message.TimeTaken * 1000:0.0000}\n" +
|
|
$"Nodes traversed: {message.CameFrom.Count}\n" +
|
|
$"Nodes per ms: {message.CameFrom.Count / (message.TimeTaken * 1000)}";
|
|
}
|
|
}
|
|
|
|
private void HandleJpsRouteMessage(SharedAiDebug.JpsRouteMessage message)
|
|
{
|
|
if ((_tooltips & AiDebugMode.Paths) != 0)
|
|
{
|
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
var entity = entityManager.GetEntity(message.EntityUid);
|
|
TryCreatePanel(entity);
|
|
|
|
var label = (Label) _aiBoxes[entity].GetChild(0).GetChild(1);
|
|
label.Text = $"Pathfinding time (ms): {message.TimeTaken * 1000:0.0000}\n" +
|
|
$"Jump Nodes: {message.JumpNodes.Count}\n" +
|
|
$"Jump Nodes per ms: {message.JumpNodes.Count / (message.TimeTaken * 1000)}";
|
|
}
|
|
}
|
|
|
|
public void Disable()
|
|
{
|
|
foreach (var tooltip in _aiBoxes.Values)
|
|
{
|
|
tooltip.Dispose();
|
|
}
|
|
_aiBoxes.Clear();
|
|
_tooltips = AiDebugMode.None;
|
|
}
|
|
|
|
|
|
private void EnableTooltip(AiDebugMode tooltip)
|
|
{
|
|
_tooltips |= tooltip;
|
|
}
|
|
|
|
private void DisableTooltip(AiDebugMode tooltip)
|
|
{
|
|
_tooltips &= ~tooltip;
|
|
}
|
|
|
|
public void ToggleTooltip(AiDebugMode tooltip)
|
|
{
|
|
if ((_tooltips & tooltip) != 0)
|
|
{
|
|
DisableTooltip(tooltip);
|
|
}
|
|
else
|
|
{
|
|
EnableTooltip(tooltip);
|
|
}
|
|
}
|
|
|
|
private bool TryCreatePanel(IEntity entity)
|
|
{
|
|
if (!_aiBoxes.ContainsKey(entity))
|
|
{
|
|
var userInterfaceManager = IoCManager.Resolve<IUserInterfaceManager>();
|
|
|
|
var actionLabel = new Label
|
|
{
|
|
MouseFilter = Control.MouseFilterMode.Ignore,
|
|
};
|
|
|
|
var pathfindingLabel = new Label
|
|
{
|
|
MouseFilter = Control.MouseFilterMode.Ignore,
|
|
};
|
|
|
|
var vBox = new VBoxContainer()
|
|
{
|
|
SeparationOverride = 15,
|
|
Children = {actionLabel, pathfindingLabel},
|
|
};
|
|
|
|
var panel = new PanelContainer
|
|
{
|
|
StyleClasses = { StyleNano.StyleClassTooltipPanel },
|
|
Children = {vBox},
|
|
MouseFilter = Control.MouseFilterMode.Ignore,
|
|
ModulateSelfOverride = Color.White.WithAlpha(0.75f),
|
|
};
|
|
|
|
userInterfaceManager.StateRoot.AddChild(panel);
|
|
|
|
_aiBoxes[entity] = panel;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
public enum AiDebugMode : byte
|
|
{
|
|
None = 0,
|
|
Paths = 1 << 1,
|
|
Thonk = 1 << 2,
|
|
}
|
|
#endif
|
|
}
|