477 lines
16 KiB
C#
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));
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|