Files

285 lines
8.2 KiB
C#

using System;
using System.Collections.Generic;
using BriarQueen.Framework.Coordinators.Events;
using BriarQueen.Framework.Events.UI;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
namespace BriarQueen.Framework.Managers.UI
{
public sealed class UICursorService : MonoBehaviour
{
public enum CursorStyle
{
Default,
Interact,
Talk,
Pickup,
Inspect,
UseItem,
Travel,
Knife,
}
[Header("System Cursor")]
[SerializeField]
private bool _enableCustomCursor = true;
[SerializeField]
private bool _restoreDefaultCursorOnDisable = true;
[Header("Software Cursor")]
[SerializeField]
private RectTransform _virtualCursorTransform;
[SerializeField]
private Image _virtualCursorImage;
[Header("Styles")]
[SerializeField]
private CursorStyle _startingStyle = CursorStyle.Default;
[SerializeField]
private List<CursorStyleEntry> _styles = new();
private readonly Dictionary<CursorStyle, CursorStyleEntry> _styleMap = new();
private EventCoordinator _eventCoordinator;
private CursorStyle _currentStyle;
private CursorStyle _currentStyleOverride = CursorStyle.Default;
private bool _isStyleOverridden;
private Texture2D _currentTexture;
private Vector2 _currentHotspot;
private CursorMode _currentMode;
private bool _useVirtualCursor;
public CursorStyleEntry CurrentStyleEntry => _styleMap[_currentStyle];
[Inject]
private void Construct(EventCoordinator eventCoordinator)
{
_eventCoordinator = eventCoordinator;
}
private void Awake()
{
BuildStyleMap();
_currentStyle = _startingStyle;
_currentStyleOverride = CursorStyle.Default;
_isStyleOverridden = false;
}
private void OnEnable()
{
_eventCoordinator?.Subscribe<CursorStyleChangeEvent>(OnCursorStyleChangeEvent);
_eventCoordinator?.Subscribe<OverrideCursorStyleChangeEvent>(OnOverrideCursorStyleChangeEvent);
ApplyCurrentEffectiveStyle();
ApplyCursorModeVisuals();
}
private void OnDisable()
{
_eventCoordinator?.Unsubscribe<CursorStyleChangeEvent>(OnCursorStyleChangeEvent);
_eventCoordinator?.Unsubscribe<OverrideCursorStyleChangeEvent>(OnOverrideCursorStyleChangeEvent);
if (_restoreDefaultCursorOnDisable)
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
}
private void OnValidate()
{
BuildStyleMap();
}
public void SetUseVirtualCursor(bool useVirtualCursor)
{
if (_useVirtualCursor == useVirtualCursor)
return;
_useVirtualCursor = useVirtualCursor;
ApplyCursorModeVisuals();
ApplyCurrentEffectiveStyle();
}
public void SetVirtualCursorPosition(Vector2 screenPosition)
{
if (!_useVirtualCursor || _virtualCursorTransform == null)
return;
_virtualCursorTransform.position = screenPosition;
}
public void SetStyle(CursorStyle style)
{
_currentStyle = style;
ApplyCurrentEffectiveStyle();
}
public void SetOverrideStyle(CursorStyle style)
{
if (style == CursorStyle.Default)
{
ClearOverrideStyle();
return;
}
_isStyleOverridden = true;
_currentStyleOverride = style;
ApplyCurrentEffectiveStyle();
}
public void ClearOverrideStyle()
{
_isStyleOverridden = false;
_currentStyleOverride = CursorStyle.Default;
ApplyCurrentEffectiveStyle();
}
public void ResetToDefault()
{
_currentStyle = CursorStyle.Default;
ClearOverrideStyle();
}
private void OnCursorStyleChangeEvent(CursorStyleChangeEvent evt)
{
_currentStyle = evt.Style;
ApplyCurrentEffectiveStyle();
}
private void OnOverrideCursorStyleChangeEvent(OverrideCursorStyleChangeEvent evt)
{
if (evt.Style == CursorStyle.Default)
{
ClearOverrideStyle();
return;
}
_isStyleOverridden = true;
_currentStyleOverride = evt.Style;
ApplyCurrentEffectiveStyle();
}
private void ApplyCursorModeVisuals()
{
Cursor.visible = !_useVirtualCursor;
if (_virtualCursorImage != null)
_virtualCursorImage.enabled = _useVirtualCursor;
}
private void ApplyCurrentEffectiveStyle()
{
var style = GetEffectiveStyle();
if (_useVirtualCursor)
{
ApplyVirtualCursorStyle(style);
}
else
{
ApplySystemCursorStyle(style);
}
}
private CursorStyle GetEffectiveStyle()
{
return _isStyleOverridden ? _currentStyleOverride : _currentStyle;
}
private void ApplyVirtualCursorStyle(CursorStyle style)
{
if (_virtualCursorImage == null)
return;
if (!_styleMap.TryGetValue(style, out var entry) || entry.Texture == null)
{
if (!_styleMap.TryGetValue(CursorStyle.Default, out entry) || entry.Texture == null)
return;
}
var rect = new Rect(0, 0, entry.Texture.width, entry.Texture.height);
var pivot = new Vector2(0.5f, 0.5f);
_virtualCursorImage.sprite = Sprite.Create(entry.Texture, rect, pivot);
}
private void ApplySystemCursorStyle(CursorStyle style)
{
if (!_enableCustomCursor)
return;
if (!_styleMap.TryGetValue(style, out var entry) || entry.Texture == null)
{
if (_styleMap.TryGetValue(CursorStyle.Default, out var defaultEntry) && defaultEntry.Texture != null)
{
entry = defaultEntry;
}
else
{
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
CacheApplied(null, Vector2.zero, CursorMode.Auto);
return;
}
}
var hotspot = entry.Hotspot;
if (entry.Texture != null && entry.HotspotIsNormalized)
{
hotspot = new Vector2(
hotspot.x * entry.Texture.width,
hotspot.y * entry.Texture.height);
}
if (IsSameAsCurrent(entry.Texture, hotspot, entry.Mode))
return;
Cursor.SetCursor(entry.Texture, hotspot, entry.Mode);
CacheApplied(entry.Texture, hotspot, entry.Mode);
}
private bool IsSameAsCurrent(Texture2D texture, Vector2 hotspot, CursorMode mode)
{
return _currentTexture == texture &&
_currentHotspot == hotspot &&
_currentMode == mode;
}
private void CacheApplied(Texture2D texture, Vector2 hotspot, CursorMode mode)
{
_currentTexture = texture;
_currentHotspot = hotspot;
_currentMode = mode;
}
private void BuildStyleMap()
{
_styleMap.Clear();
if (_styles == null)
return;
for (var i = 0; i < _styles.Count; i++)
{
var entry = _styles[i];
_styleMap[entry.Style] = entry;
}
}
[Serializable]
public struct CursorStyleEntry
{
public CursorStyle Style;
public Texture2D Texture;
public Vector2 Hotspot;
public CursorMode Mode;
public bool HotspotIsNormalized;
public Vector2 TooltipOffset;
}
}
}