Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/UX/UXMap.cs

477 lines
16 KiB
C#

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using BITKit;
using BITKit.Entities;
using BITKit.Mod;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using MemoryPack;
using Net.BITKit.Localization;
using Net.BITKit.UX;
using Net.Project.B.Item;
using Net.Project.B.Mark;
using Net.Project.B.Quest;
using Net.Project.B.World;
using Net.Project.B.WorldNode;
using Project.B.UX;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace Net.Project.B.UX
{
public static class UXMapUtils{
public static Vector2 GetPosition(Vector2 size,float mapSize,Vector2 iconSize , Vector2 offset,Vector3 position,float scale ,Vector3 uiOffset)
{
position -= new Vector3(offset.x,0,offset.y);
position.x /= mapSize*2;
position.z /= mapSize*2;
//size *= scale;
var result = new Vector2(position.x * size.x + size.x / 2 - iconSize.x / 2,
size.y - (position.z * size.y + size.y / 2) - iconSize.y / 2);
result.x += uiOffset.x;
result.y += uiOffset.y;
return new((int)result.x, (int)result.y);
}
}
public class UXMap<TPanel> : UIToolkitSubPanel<TPanel>,IDisposable, IUXMap where TPanel : IUXPanel
{
private readonly ILocalizationService _localizationService;
private readonly IWorldMinimapService _minimapService;
private readonly IFixedTicker _ticker;
private readonly IManagedItemService _itemService;
private readonly IMarkService _markService;
private readonly IEntitiesService _entitiesService;
[UXBindPath("map-container")]
private VisualElement _mapContainer;
[UXBindPath("map-position")]
private VisualElement _mapPosition;
private VisualTreeAsset _markTemplate;
private readonly ConcurrentDictionary<int, VisualElement> _markPoints = new();
private readonly HashSet<int> _fixedPoints = new();
private bool _isInitialized;
private bool _isUiInitialized;
private bool _isDragging;
private float _scale=1;
private Vector3 _offset;
private readonly Dictionary<int, IEntity> _registerQueue = new();
public UXMap(IServiceProvider serviceProvider, IWorldMinimapService minimapService, IFixedTicker ticker, IMarkService markService, IEntitiesService entitiesService, IManagedItemService itemService, ILocalizationService localizationService) : base(serviceProvider)
{
_minimapService = minimapService;
_ticker = ticker;
_markService = markService;
_entitiesService = entitiesService;
_itemService = itemService;
_localizationService = localizationService;
_minimapService.OnMinimapInitialized += OnMinimapInitialized;
_entitiesService.OnAdd += OnAdd;
_entitiesService.OnRemove += OnRemove;
_ticker.Add(OnFixedTick);
}
private void OnRemove(IEntity obj)
{
_registerQueue.TryRemove(obj.Id);
if (_markPoints.ContainsKey(obj.Id))
{
_fixedPoints.Remove(obj.Id);
OnMark(obj.Id,false);
}
}
private void OnAdd(IEntity obj)
{
if (_isUiInitialized is false)
{
_registerQueue[obj.Id] = obj;
return;
}
switch (obj.ServiceProvider)
{
case not null when obj.ServiceProvider.QueryComponents(out LandMark landMark) && string.IsNullOrEmpty(landMark.IconName):
{
var label = _mapContainer.parent.Create<Label>();
label.AddToClassList(UXLocalization.USS);
label.viewDataKey = landMark.Name;
label.text =_localizationService.GetLocalizedString( landMark.Name);
label.style.position = new StyleEnum<Position>(Position.Absolute);
label.pickingMode = PickingMode.Ignore;
_markPoints[obj.Id]=label;
}
return;
}
UXMarkUtils.SetUp(obj.Id,AddPoint);
return;
VisualElement AddPoint()
{
_fixedPoints.Add(obj.Id);
var container = _mapContainer.parent.Create(_markTemplate);
container.style.position = new StyleEnum<Position>(Position.Absolute);
_markPoints[obj.Id] =container;
container.RegisterCallback<PointerDownEvent>(x =>
{
if(x.button is not (1 or 2))return;
if (_markService.InMarking.Contains(obj.Id))
{
_markService.CancelMark(obj.Id);
}
else
{
_markService.Mark(obj.Id);
}
});
container.RegisterCallback<WheelEvent>(x =>
{
x.StopPropagation();
OnWheel(x);
});
return container;
}
}
private void OnMark(int arg1, bool arg2)
{
try
{
if (_fixedPoints.Contains(arg1) && _markPoints.TryGetValue(arg1, out var ve))
{
if (arg2)
{
ve.AddToClassList("selected");
}
else
{
ve.RemoveFromClassList("selected");
}
return;
}
if (arg2)
{
UXMarkUtils.SetUp(arg1,Create,true);
VisualElement Create()
{
var container = _mapContainer.parent.Create(_markTemplate);
container.style.position = new StyleEnum<Position>(Position.Absolute);
_markPoints.TryAdd(arg1, container);
container.RegisterCallback<PointerDownEvent>(x =>
{
if (x.button is not 0) return;
_markService.CancelMark(arg1);
});
container.RegisterCallback<WheelEvent>(x =>
{
x.StopPropagation();
OnWheel(x);
});
return container;
}
}
else if (_markPoints.TryRemove(arg1, out ve))
{
ve.RemoveFromHierarchy();
}
}
finally
{
if (_markPoints.TryGetValue(arg1, out var ve))
{
if (_entitiesService.Entities.ContainsKey(arg1) is false)
{
_markPoints.TryRemove(arg1);
ve.RemoveFromHierarchy();
}
}
}
}
protected override void OnInitiated()
{
base.OnInitiated();
_mapContainer.RegisterCallback<WheelEvent>(OnWheel);
_mapContainer.RegisterCallback<PointerMoveEvent>(OnPointerMove);
_mapContainer.RegisterCallback<PointerDownEvent>(OnPointerDown);
_mapContainer.RegisterCallback<PointerUpEvent>(OnPointerUp);
_mapContainer.parent.RegisterCallback<GeometryChangedEvent>(x =>
{
IsVisible.SetElements(64,x.newRect.x>0);
});
_mapContainer.parent.style.overflow = new StyleEnum<Overflow>(Overflow.Hidden);
_isUiInitialized = true;
if (_isInitialized)
{
OnMinimapInitialized();
}
foreach (var entity in _registerQueue.Values.ToArray())
{
OnAdd(entity);
}
_registerQueue.Clear();
_markService.OnMark += OnMark;
foreach (var id in _markService.InMarking)
{
OnMark(id,true);
}
}
protected override async UniTask OnInitiatedAsync()
{
await base.OnInitiatedAsync();
_markTemplate = await ModService.LoadAsset<VisualTreeAsset>("ux_mark-template");
}
private void OnPointerUp(PointerUpEvent evt)
{
_isDragging = false;
_mapContainer.ReleasePointer(evt.pointerId);
if (Vector2.Distance(_pointStartPos, evt.position) is 0)
{
var position = GetMinimapPointAsWorldPoint(evt.position);
foreach (var (entity, _, _, transform) in _entitiesService
.QueryComponents<IEntity, OwnedByLocalPlayer, PositionMark, Transform>())
{
position.y = 512;
if (Physics.Raycast(position, Vector3.down, out var hit, 512, ~0))
{
position = hit.point;
}
else
{
position.y = 0;
}
transform.position = position;
_markService.Mark(entity.Id);
}
}
}
private Vector2 _pointStartPos;
private void OnPointerDown(PointerDownEvent evt)
{
_pointStartPos = evt.position;
_isDragging = true;
_mapContainer.CapturePointer(evt.pointerId);
}
private Vector3 GetMinimapPointAsWorldPoint(Vector2 mousePosition)
{
var worldPercentage = GetMousePositionPercentage(_mapContainer);
var worldSize = _minimapService.Size;
var worldPos = new Vector3
{
x = math.lerp(-worldSize, worldSize, worldPercentage.x) + _minimapService.Offset.x,
z = -math.lerp(-worldSize, worldSize, worldPercentage.y) + _minimapService.Offset.y
};
//GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = worldPos;
return worldPos;
Vector2 GetMousePositionPercentage(VisualElement element)
{
var layout = element.layout;
var localPos = element.WorldToLocal(mousePosition);
return new Vector2(
Mathf.Clamp01(localPos.x / layout.width),
Mathf.Clamp01(localPos.y / layout.height)
);
}
}
private void OnPointerMove(PointerMoveEvent evt)
{
if( _isDragging is false)return;
var currentPosition = new Vector3(_mapContainer.style.left.value.value,_mapContainer.style.top.value.value);
var newPosition = currentPosition ;
_offset += evt.deltaPosition;
_mapContainer.style.left = newPosition.x;
_mapContainer.style.top = newPosition.y;
ClampSize();
}
private void OnWheel(WheelEvent evt)
{
evt.StopPropagation();
var sourceRect = _mapContainer.parent.layout.size;
var currentPosition = UXMapUtils.GetPosition(sourceRect * _scale, _minimapService.Size, _mapPosition.layout.size,
_minimapService.Offset, GetMinimapPointAsWorldPoint(evt.mousePosition),_scale,_offset);
_scale -= evt.delta.y*Time.deltaTime * 12;
_scale = math.clamp(_scale, 1, 8);
var newSize = _mapContainer.parent.layout.size.x * _scale;
_mapContainer.style.width = newSize;
_mapContainer.style.height = newSize;
var afterPosition = UXMapUtils.GetPosition(sourceRect * _scale, _minimapService.Size, _mapPosition.layout.size,
_minimapService.Offset, GetMinimapPointAsWorldPoint(evt.mousePosition),_scale,_offset);
_offset.x-=afterPosition.x-currentPosition.x;
_offset.y-=afterPosition.y-currentPosition.y;
ClampSize();
}
private void ClampSize()
{
var currentPosition = _offset;
var standardSize = _mapContainer.parent.layout.size;
var negative = (_scale - 1) * standardSize.x;
currentPosition.x = math.clamp(currentPosition.x,-negative,0);
currentPosition.y = math.clamp(currentPosition.y, -negative,0);
_offset = currentPosition;
_mapContainer.style.left = currentPosition.x;
_mapContainer.style.top = currentPosition.y;
}
private void OnMinimapInitialized()
{
_isInitialized = true;
if(_isUiInitialized is false)return;
switch (_minimapService.MinimapObject)
{
case RenderTexture renderTexture:
{
_mapContainer.style.backgroundImage = new StyleBackground(Background.FromRenderTexture(renderTexture));
}
break;
case Texture2D texture2D:
{
_mapContainer.style.backgroundImage = new StyleBackground(texture2D);
}
break;
case Sprite sprite:
{
_mapContainer.style.backgroundImage = new StyleBackground(sprite);
}
break;
}
}
private void OnFixedTick(float obj)
{
//if(IsVisible.Allow is false)return;
if(Panel.Root is null)return;
if(_isInitialized is false)return;
foreach (var (id,visualElement) in _markPoints)
{
if(_entitiesService.Entities.TryGetValue(id,out var markEntity) is false)continue;
if(markEntity.ServiceProvider.QueryComponents(out Transform transform) is false)continue;
if (!transform)
{
_markPoints.TryRemove(id, out _);
break;
}
var pos = UXMapUtils.GetPosition(_mapContainer.layout.size, _minimapService.Size, visualElement.layout.size,
_minimapService.Offset, transform.position,_scale,_offset);
visualElement.style.left = pos.x;
visualElement.style.top = pos.y;
}
UpdatePlayerPosition();
}
public void Dispose()
{
_markService.OnMark -= OnMark;
_ticker.Remove(OnFixedTick);
}
void UpdatePlayerPosition()
{
var camera = Camera.main;
if(!camera)return;
var pos = UXMapUtils.GetPosition(_mapContainer.layout.size, _minimapService.Size, _mapPosition.layout.size,
_minimapService.Offset, camera.transform.position,_scale,_offset);
_mapPosition.style.left = pos.x;
_mapPosition.style.top = pos.y;
_mapPosition.style.rotate = new StyleRotate(new Rotate(camera.transform.eulerAngles.y));
}
}
}