mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-15 07:43:20 +08:00
新增小地图遮罩,在小地图上展示资源点位 (#2830)
This commit is contained in:
@@ -14,6 +14,9 @@ namespace BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
public class MapAssets : BaseAssets<MapAssets>
|
||||
{
|
||||
public Rect MimiMapRect { get; }
|
||||
|
||||
public static Rect MimiMapRect1080P = new Rect(62, 19,212,212);
|
||||
|
||||
|
||||
public MapAssets()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,18 @@ public partial class MapMaskConfig : ObservableObject
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// 小地图遮罩是否启用
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _miniMapMaskEnabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// 自动记录路径功能是否启用
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _pathAutoRecordEnabled = false;
|
||||
|
||||
private MapPointApiProvider _mapPointApiProvider = MapPointApiProvider.MihoyoMap;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using BetterGenshinImpact.GameTask.AutoPathing;
|
||||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Common.Map.Maps;
|
||||
using BetterGenshinImpact.GameTask.Common.Map.Maps.Base;
|
||||
using BetterGenshinImpact.GameTask.Common.Map.Maps.Layer;
|
||||
@@ -9,6 +10,8 @@ using BetterGenshinImpact.Helpers;
|
||||
using BetterGenshinImpact.View;
|
||||
using BetterGenshinImpact.ViewModel;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenCvSharp;
|
||||
using Rect = System.Windows.Rect;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.MapMask;
|
||||
|
||||
@@ -23,8 +26,8 @@ public class MapMaskTrigger : ITaskTrigger
|
||||
public bool IsEnabled { get; set; }
|
||||
public int Priority => 1; // 低优先级
|
||||
public bool IsExclusive => false;
|
||||
|
||||
public GameUiCategory SupportedGameUiCategory => GameUiCategory.BigMap;
|
||||
|
||||
public GameUiCategory SupportedGameUiCategory => GameUiCategory.Unknown;
|
||||
|
||||
private readonly MapMaskConfig _config = TaskContext.Instance().Config.MapMaskConfig;
|
||||
private readonly string _mapMatchingMethod = TaskContext.Instance().Config.PathingConditionConfig.MapMatchingMethod;
|
||||
@@ -41,10 +44,12 @@ public class MapMaskTrigger : ITaskTrigger
|
||||
|
||||
private const int RectDebounceThreshold = 3;
|
||||
|
||||
private readonly NavigationInstance _navigationInstance = new();
|
||||
|
||||
public void Init()
|
||||
{
|
||||
IsEnabled = _config.Enabled;
|
||||
|
||||
|
||||
// 关闭时隐藏UI
|
||||
if (!IsEnabled)
|
||||
{
|
||||
@@ -108,8 +113,8 @@ public class MapMaskTrigger : ITaskTrigger
|
||||
_prevRect = default;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// if (_prevRect != default)
|
||||
// {
|
||||
// var dx = Math.Abs(rect256.X - _prevRect.X);
|
||||
@@ -130,6 +135,38 @@ public class MapMaskTrigger : ITaskTrigger
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((_config.MiniMapMaskEnabled || _config.PathAutoRecordEnabled) && Bv.IsInMainUi(region))
|
||||
{
|
||||
// 主界面上展示小地图
|
||||
if (_config.MiniMapMaskEnabled)
|
||||
{
|
||||
var miniPoint = _navigationInstance.GetPositionStable(region, nameof(MapTypes.Teyvat), TaskContext.Instance().Config.PathingConditionConfig.MapMatchingMethod);
|
||||
if (miniPoint != default)
|
||||
{
|
||||
// 展示窗口是 212
|
||||
double viewportSize = MapAssets.MimiMapRect1080P.Width / 3.0 * 10;
|
||||
UIDispatcherHelper.Invoke(() =>
|
||||
{
|
||||
MaskWindow.Instance().MiniMapPointsCanvasControl.UpdateViewport(
|
||||
miniPoint.X - viewportSize / 2.0,
|
||||
miniPoint.Y - viewportSize / 2.0,
|
||||
viewportSize,
|
||||
viewportSize);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
UIDispatcherHelper.Invoke(() => { MaskWindow.Instance().MiniMapPointsCanvasControl.UpdateViewport(0, 0, 0, 0); });
|
||||
}
|
||||
|
||||
// 自动记录路径
|
||||
if (_config.PathAutoRecordEnabled)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_prevRect = default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using BetterGenshinImpact.GameTask;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterGenshinImpact.Helpers;
|
||||
@@ -18,7 +19,16 @@ public class DpiHelper
|
||||
if (Environment.OSVersion.Version >= new Version(6, 3)
|
||||
&& UIDispatcherHelper.MainWindow != null)
|
||||
{
|
||||
HWND hWnd = new WindowInteropHelper(Application.Current?.MainWindow).Handle;
|
||||
HWND hWnd = HWND.NULL;
|
||||
if (TaskContext.Instance().IsInitialized)
|
||||
{
|
||||
hWnd = TaskContext.Instance().GameHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
hWnd = new WindowInteropHelper(Application.Current?.MainWindow).Handle;
|
||||
}
|
||||
|
||||
HMONITOR hMonitor = User32.MonitorFromWindow(hWnd, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST);
|
||||
SHCore.GetDpiForMonitor(hMonitor, SHCore.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out _, out uint dpiY);
|
||||
return dpiY / 96f;
|
||||
|
||||
@@ -19,12 +19,12 @@ public class MaskMapPoint
|
||||
public double GameY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 游戏图像地图的坐标 X
|
||||
/// 2048级别游戏图像地图的坐标 X
|
||||
/// </summary>
|
||||
public double ImageX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 游戏图像地图的坐标 Y
|
||||
/// 2048级别游戏图像地图的坐标 Y
|
||||
/// </summary>
|
||||
public double ImageY { get; set; }
|
||||
|
||||
|
||||
355
BetterGenshinImpact/View/Controls/MiniMapPointsCanvas.cs
Normal file
355
BetterGenshinImpact/View/Controls/MiniMapPointsCanvas.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using BetterGenshinImpact.Model.MaskMap;
|
||||
using BetterGenshinImpact.ViewModel;
|
||||
|
||||
namespace BetterGenshinImpact.View.Controls;
|
||||
|
||||
public sealed class MiniMapPointsCanvas : FrameworkElement
|
||||
{
|
||||
public static readonly DependencyProperty PointsSourceProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PointsSource),
|
||||
typeof(ObservableCollection<MaskMapPoint>),
|
||||
typeof(MiniMapPointsCanvas),
|
||||
new PropertyMetadata(null, OnPointsSourceChanged));
|
||||
|
||||
public static readonly DependencyProperty LabelsSourceProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(LabelsSource),
|
||||
typeof(IEnumerable<MaskMapPointLabel>),
|
||||
typeof(MiniMapPointsCanvas),
|
||||
new PropertyMetadata(null, OnLabelsSourceChanged));
|
||||
|
||||
private readonly VisualCollection _children;
|
||||
private readonly DrawingVisual _drawingVisual;
|
||||
private readonly Dictionary<string, Brush> _colorBrushCache;
|
||||
private int _refreshQueued;
|
||||
|
||||
private ObservableCollection<MaskMapPoint>? _points;
|
||||
private List<MaskMapPoint> _allPoints = new();
|
||||
private Dictionary<string, MaskMapPointLabel> _labelMap = new();
|
||||
private Rect _viewportRect = Rect.Empty;
|
||||
|
||||
public ObservableCollection<MaskMapPoint>? PointsSource
|
||||
{
|
||||
get => (ObservableCollection<MaskMapPoint>?)GetValue(PointsSourceProperty);
|
||||
set => SetValue(PointsSourceProperty, value);
|
||||
}
|
||||
|
||||
public IEnumerable<MaskMapPointLabel>? LabelsSource
|
||||
{
|
||||
get => (IEnumerable<MaskMapPointLabel>?)GetValue(LabelsSourceProperty);
|
||||
set => SetValue(LabelsSourceProperty, value);
|
||||
}
|
||||
|
||||
public MiniMapPointsCanvas()
|
||||
{
|
||||
_children = new VisualCollection(this);
|
||||
_drawingVisual = new DrawingVisual();
|
||||
_children.Add(_drawingVisual);
|
||||
_colorBrushCache = new Dictionary<string, Brush>();
|
||||
|
||||
IsHitTestVisible = false;
|
||||
|
||||
MapIconImageCache.ImageUpdated += PointImageCacheManagerOnImageUpdated;
|
||||
}
|
||||
|
||||
private static void OnPointsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var canvas = (MiniMapPointsCanvas)d;
|
||||
canvas.UpdatePoints(e.NewValue as ObservableCollection<MaskMapPoint>);
|
||||
}
|
||||
|
||||
private static void OnLabelsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var canvas = (MiniMapPointsCanvas)d;
|
||||
canvas.UpdateLabels(e.NewValue as IEnumerable<MaskMapPointLabel>);
|
||||
}
|
||||
|
||||
protected override void OnVisualParentChanged(DependencyObject oldParent)
|
||||
{
|
||||
base.OnVisualParentChanged(oldParent);
|
||||
if (VisualParent == null)
|
||||
{
|
||||
MapIconImageCache.ImageUpdated -= PointImageCacheManagerOnImageUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private void PointImageCacheManagerOnImageUpdated(object? sender, string e)
|
||||
{
|
||||
if (Interlocked.Exchange(ref _refreshQueued, 1) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
Interlocked.Exchange(ref _refreshQueued, 0);
|
||||
Refresh();
|
||||
}, System.Windows.Threading.DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount => _children.Count;
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
if (index < 0 || index >= _children.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
return _children[index];
|
||||
}
|
||||
|
||||
private void OnPointsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.OldItems != null)
|
||||
{
|
||||
foreach (MaskMapPoint point in e.OldItems)
|
||||
{
|
||||
UnsubscribePoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
foreach (MaskMapPoint point in e.NewItems)
|
||||
{
|
||||
SubscribePoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
_allPoints = _points?.ToList() ?? new List<MaskMapPoint>();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void SubscribePoint(MaskMapPoint point)
|
||||
{
|
||||
if (point is INotifyPropertyChanged notifyPoint)
|
||||
{
|
||||
notifyPoint.PropertyChanged += OnPointPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribePoint(MaskMapPoint point)
|
||||
{
|
||||
if (point is INotifyPropertyChanged notifyPoint)
|
||||
{
|
||||
notifyPoint.PropertyChanged -= OnPointPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void RenderPoints()
|
||||
{
|
||||
using var dc = _drawingVisual.RenderOpen();
|
||||
if (_allPoints.Count == 0 || _viewportRect.IsEmpty || _viewportRect.Width == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var aw = ActualWidth;
|
||||
var ah = ActualHeight;
|
||||
if (aw <= 0 || ah <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var side = Math.Min(aw, ah);
|
||||
if (side <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var clipRect = new Rect((aw - side) / 2.0, (ah - side) / 2.0, side, side);
|
||||
var clip = new EllipseGeometry(clipRect);
|
||||
dc.PushClip(clip);
|
||||
|
||||
var expandedViewport = _viewportRect;
|
||||
expandedViewport.Inflate(MaskMapPointStatic.Width, MaskMapPointStatic.Height);
|
||||
|
||||
var scaleX = side / _viewportRect.Width;
|
||||
var scaleY = side / _viewportRect.Height;
|
||||
|
||||
var pointSide = Math.Max(8, Math.Min(16, side / 12.0));
|
||||
|
||||
foreach (var point in _allPoints)
|
||||
{
|
||||
if (!expandedViewport.Contains(point.ImageX, point.ImageY))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var localX = clipRect.X + (point.ImageX - _viewportRect.X) * scaleX;
|
||||
var localY = clipRect.Y + (point.ImageY - _viewportRect.Y) * scaleY;
|
||||
DrawPoint(dc, point, localX, localY, pointSide, pointSide);
|
||||
}
|
||||
|
||||
dc.Pop();
|
||||
}
|
||||
|
||||
private void DrawPoint(DrawingContext dc, MaskMapPoint point, double centerX, double centerY, double width, double height)
|
||||
{
|
||||
var radius = width / 2.0;
|
||||
const double strokeThickness = 2.0;
|
||||
|
||||
var circleCenter = new Point(centerX, centerY);
|
||||
|
||||
var fillBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#323947"));
|
||||
fillBrush.Freeze();
|
||||
|
||||
var borderBrush = new SolidColorBrush(Color.FromRgb(0xD3, 0xBC, 0x8E));
|
||||
borderBrush.Freeze();
|
||||
|
||||
var borderPen = new Pen(borderBrush, strokeThickness);
|
||||
borderPen.Freeze();
|
||||
|
||||
var shadowBrush = new SolidColorBrush(Color.FromArgb(30, 0, 0, 0));
|
||||
shadowBrush.Freeze();
|
||||
|
||||
var shadowOffset = new Point(2, 2);
|
||||
|
||||
var shadowCircleGeometry = new EllipseGeometry(
|
||||
new Point(circleCenter.X + shadowOffset.X, circleCenter.Y + shadowOffset.Y),
|
||||
radius, radius);
|
||||
dc.DrawGeometry(shadowBrush, null, shadowCircleGeometry);
|
||||
|
||||
var circleGeometry = new EllipseGeometry(circleCenter, radius, radius);
|
||||
dc.DrawGeometry(fillBrush, borderPen, circleGeometry);
|
||||
|
||||
if (_labelMap.TryGetValue(point.LabelId, out var label))
|
||||
{
|
||||
var image = MapIconImageCache.TryGet(label.IconUrl);
|
||||
if (image != null)
|
||||
{
|
||||
var imageRect = new Rect(circleCenter.X - radius, circleCenter.Y - radius, width, height);
|
||||
dc.PushClip(circleGeometry);
|
||||
dc.DrawImage(image, imageRect);
|
||||
dc.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = MapIconImageCache.GetAsync(label.IconUrl, CancellationToken.None);
|
||||
|
||||
var brush = GetColorBrush(label);
|
||||
dc.DrawEllipse(brush, null, new Point(centerX, centerY), width / 2.0, height / 2.0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var brush = new SolidColorBrush(GenerateRandomColor(point.Id));
|
||||
brush.Freeze();
|
||||
dc.DrawEllipse(brush, null, new Point(centerX, centerY), width / 2.0, height / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
private Brush GetColorBrush(MaskMapPointLabel label)
|
||||
{
|
||||
if (_colorBrushCache.TryGetValue(label.LabelId, out var cachedBrush))
|
||||
{
|
||||
return cachedBrush;
|
||||
}
|
||||
|
||||
Color color;
|
||||
if (label.Color.HasValue)
|
||||
{
|
||||
var c = label.Color.Value;
|
||||
color = Color.FromArgb(c.A, c.R, c.G, c.B);
|
||||
}
|
||||
else
|
||||
{
|
||||
color = GenerateRandomColor(label.LabelId);
|
||||
}
|
||||
|
||||
var brush = new SolidColorBrush(color);
|
||||
brush.Freeze();
|
||||
_colorBrushCache[label.LabelId] = brush;
|
||||
return brush;
|
||||
}
|
||||
|
||||
private static Color GenerateRandomColor(string seed)
|
||||
{
|
||||
var hash = seed?.GetHashCode() ?? 0;
|
||||
var random = new Random(hash);
|
||||
return Color.FromRgb(
|
||||
(byte)random.Next(80, 256),
|
||||
(byte)random.Next(80, 256),
|
||||
(byte)random.Next(80, 256));
|
||||
}
|
||||
|
||||
public void UpdatePoints(ObservableCollection<MaskMapPoint>? points)
|
||||
{
|
||||
if (_points != null)
|
||||
{
|
||||
_points.CollectionChanged -= OnPointsCollectionChanged;
|
||||
foreach (var point in _points)
|
||||
{
|
||||
UnsubscribePoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
_points = points;
|
||||
|
||||
if (_points != null)
|
||||
{
|
||||
_points.CollectionChanged += OnPointsCollectionChanged;
|
||||
foreach (var point in _points)
|
||||
{
|
||||
SubscribePoint(point);
|
||||
}
|
||||
|
||||
_allPoints = _points.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_allPoints.Clear();
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void UpdateLabels(IEnumerable<MaskMapPointLabel>? labels)
|
||||
{
|
||||
if (labels != null)
|
||||
{
|
||||
_labelMap = labels.ToDictionary(l => l.LabelId, l => l);
|
||||
_colorBrushCache.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_labelMap.Clear();
|
||||
_colorBrushCache.Clear();
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void UpdateViewport(double x, double y, double width, double height)
|
||||
{
|
||||
var newRect = new Rect(x, y, width, height);
|
||||
if (newRect.Equals(_viewportRect))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_viewportRect = newRect;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
RenderPoints();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class PointsCanvas : FrameworkElement
|
||||
private int _refreshQueued;
|
||||
|
||||
// 私有字段
|
||||
private ObservableCollection<MaskMapPoint> _points;
|
||||
private ObservableCollection<MaskMapPoint>? _points;
|
||||
private List<MaskMapPoint> _allPoints = new();
|
||||
private Dictionary<string, MaskMapPointLabel> _labelMap = new();
|
||||
private Rect _viewportRect = Rect.Empty;
|
||||
@@ -37,6 +37,20 @@ public class PointsCanvas : FrameworkElement
|
||||
|
||||
#region 依赖属性
|
||||
|
||||
public static readonly DependencyProperty PointsSourceProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PointsSource),
|
||||
typeof(ObservableCollection<MaskMapPoint>),
|
||||
typeof(PointsCanvas),
|
||||
new PropertyMetadata(null, OnPointsSourceChanged));
|
||||
|
||||
public static readonly DependencyProperty LabelsSourceProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(LabelsSource),
|
||||
typeof(IEnumerable<MaskMapPointLabel>),
|
||||
typeof(PointsCanvas),
|
||||
new PropertyMetadata(null, OnLabelsSourceChanged));
|
||||
|
||||
public static readonly DependencyProperty PointClickCommandProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PointClickCommand),
|
||||
@@ -67,6 +81,18 @@ public class PointsCanvas : FrameworkElement
|
||||
set => SetValue(PointClickCommandProperty, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<MaskMapPoint>? PointsSource
|
||||
{
|
||||
get => (ObservableCollection<MaskMapPoint>?)GetValue(PointsSourceProperty);
|
||||
set => SetValue(PointsSourceProperty, value);
|
||||
}
|
||||
|
||||
public IEnumerable<MaskMapPointLabel>? LabelsSource
|
||||
{
|
||||
get => (IEnumerable<MaskMapPointLabel>?)GetValue(LabelsSourceProperty);
|
||||
set => SetValue(LabelsSourceProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 右键点击命令
|
||||
/// </summary>
|
||||
@@ -87,6 +113,18 @@ public class PointsCanvas : FrameworkElement
|
||||
|
||||
#endregion
|
||||
|
||||
private static void OnPointsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var canvas = (PointsCanvas)d;
|
||||
canvas.UpdatePoints(e.NewValue as ObservableCollection<MaskMapPoint>);
|
||||
}
|
||||
|
||||
private static void OnLabelsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var canvas = (PointsCanvas)d;
|
||||
canvas.UpdateLabels(e.NewValue as IEnumerable<MaskMapPointLabel>);
|
||||
}
|
||||
|
||||
public PointsCanvas()
|
||||
{
|
||||
_children = new VisualCollection(this);
|
||||
@@ -508,7 +546,7 @@ public class PointsCanvas : FrameworkElement
|
||||
/// <summary>
|
||||
/// 更新点位数据
|
||||
/// </summary>
|
||||
public void UpdatePoints(ObservableCollection<MaskMapPoint> points)
|
||||
public void UpdatePoints(ObservableCollection<MaskMapPoint>? points)
|
||||
{
|
||||
// 取消订阅旧集合
|
||||
if (_points != null)
|
||||
@@ -541,7 +579,7 @@ public class PointsCanvas : FrameworkElement
|
||||
/// <summary>
|
||||
/// 更新标签数据
|
||||
/// </summary>
|
||||
public void UpdateLabels(IEnumerable<MaskMapPointLabel> labels)
|
||||
public void UpdateLabels(IEnumerable<MaskMapPointLabel>? labels)
|
||||
{
|
||||
if (labels != null)
|
||||
{
|
||||
|
||||
@@ -86,6 +86,37 @@
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="6"
|
||||
ClipToBounds="True">
|
||||
<controls:MiniMapPointsCanvas x:Name="MiniMapPointsCanvasControl"
|
||||
Visibility="{Binding Config.MapMaskConfig.MiniMapMaskEnabled, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
PointsSource="{Binding MapPoints}"
|
||||
LabelsSource="{Binding MapPointLabels}"
|
||||
IsHitTestVisible="False">
|
||||
<Canvas.Left>
|
||||
<MultiBinding Converter="{StaticResource OverlayRelativeOrAbsoluteConverter}">
|
||||
<Binding Path="MiniMapOverlayLeftRatio" />
|
||||
<Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="ActualWidth" />
|
||||
</MultiBinding>
|
||||
</Canvas.Left>
|
||||
<Canvas.Top>
|
||||
<MultiBinding Converter="{StaticResource OverlayRelativeOrAbsoluteConverter}">
|
||||
<Binding Path="MiniMapOverlayTopRatio" />
|
||||
<Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="ActualHeight" />
|
||||
</MultiBinding>
|
||||
</Canvas.Top>
|
||||
<controls:MiniMapPointsCanvas.Width>
|
||||
<MultiBinding Converter="{StaticResource OverlayRelativeOrAbsoluteConverter}">
|
||||
<Binding Path="MiniMapOverlaySizeRatio" />
|
||||
<Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="ActualHeight" />
|
||||
</MultiBinding>
|
||||
</controls:MiniMapPointsCanvas.Width>
|
||||
<controls:MiniMapPointsCanvas.Height>
|
||||
<MultiBinding Converter="{StaticResource OverlayRelativeOrAbsoluteConverter}">
|
||||
<Binding Path="MiniMapOverlaySizeRatio" />
|
||||
<Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="ActualHeight" />
|
||||
</MultiBinding>
|
||||
</controls:MiniMapPointsCanvas.Height>
|
||||
</controls:MiniMapPointsCanvas>
|
||||
|
||||
<overlay:AdjustableOverlayItem x:Name="StatusWrapper"
|
||||
Padding="4,2"
|
||||
Background="#00000000"
|
||||
@@ -428,6 +459,8 @@
|
||||
x:Name="PointsCanvasControl"
|
||||
Visibility="{Binding IsInBigMapUi, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
PointsSource="{Binding MapPoints}"
|
||||
LabelsSource="{Binding MapPointLabels}"
|
||||
PointClickCommand="{Binding PointClickCommand}"
|
||||
PointRightClickCommand="{Binding PointRightClickCommand}"
|
||||
PointHoverCommand="{Binding PointHoverCommand}" />
|
||||
|
||||
@@ -191,11 +191,6 @@ public partial class MaskWindow : Window
|
||||
|
||||
RefreshPosition();
|
||||
PrintSystemInfo();
|
||||
if (_viewModel != null)
|
||||
{
|
||||
PointsCanvasControl.UpdateLabels(_viewModel.MapPointLabels);
|
||||
PointsCanvasControl.UpdatePoints(_viewModel.MapPoints);
|
||||
}
|
||||
|
||||
PointsCanvasControl.ViewportChanged += PointsCanvasControlOnViewportChanged;
|
||||
}
|
||||
@@ -258,27 +253,6 @@ public partial class MaskWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
if (e.PropertyName == nameof(MaskWindowViewModel.MapPointLabels))
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (_viewModel != null)
|
||||
{
|
||||
PointsCanvasControl.UpdateLabels(_viewModel.MapPointLabels);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (e.PropertyName == nameof(MaskWindowViewModel.MapPoints))
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (_viewModel != null)
|
||||
{
|
||||
PointsCanvasControl.UpdatePoints(_viewModel.MapPoints);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateClickThroughState()
|
||||
|
||||
@@ -975,16 +975,20 @@
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<ui:CardControl Margin="0,0,0,12">
|
||||
<ui:CardControl.Icon>
|
||||
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
|
||||
<ui:CardExpander.Icon>
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
</ui:CardControl.Icon>
|
||||
<ui:CardControl.Header>
|
||||
</ui:CardExpander.Icon>
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
@@ -996,10 +1000,41 @@
|
||||
TextWrapping="Wrap">
|
||||
在遮罩窗口中显示大地图位置与标点信息
|
||||
</ui:TextBlock>
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,24,0"
|
||||
IsChecked="{Binding Config.MapMaskConfig.Enabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</ui:CardControl.Header>
|
||||
<ui:ToggleSwitch Margin="0,0,40,0" IsChecked="{Binding Config.MapMaskConfig.Enabled, Mode=TwoWay}" />
|
||||
</ui:CardControl>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel IsEnabled="{Binding Config.MapMaskConfig.Enabled}">
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用小地图遮罩"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="在小地图上显示点位"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,8,0"
|
||||
IsChecked="{Binding Config.MapMaskConfig.MiniMapMaskEnabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<!-- 冷却提示 -->
|
||||
<ui:CardExpander
|
||||
|
||||
@@ -9,26 +9,19 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using LazyCache;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PresentMonFps;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.Model.MaskMap;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Vanara.PInvoke;
|
||||
using MaskMapPoint = BetterGenshinImpact.Model.MaskMap.MaskMapPoint;
|
||||
using MaskMapPointLabel = BetterGenshinImpact.Model.MaskMap.MaskMapPointLabel;
|
||||
@@ -78,6 +71,12 @@ namespace BetterGenshinImpact.ViewModel
|
||||
|
||||
[ObservableProperty] private string _mapPointsLoadingText = "正在加载点位...";
|
||||
|
||||
public double MiniMapOverlayLeftRatio => MapAssets.MimiMapRect1080P.X / 1920d;
|
||||
|
||||
public double MiniMapOverlayTopRatio => MapAssets.MimiMapRect1080P.Y / 1080d;
|
||||
|
||||
public double MiniMapOverlaySizeRatio => MapAssets.MimiMapRect1080P.Width / 1080d;
|
||||
|
||||
public sealed record MapPointApiProviderOption(MapPointApiProvider Provider, string DisplayName);
|
||||
|
||||
public IReadOnlyList<MapPointApiProviderOption> MapPointApiProviderOptions { get; } =
|
||||
|
||||
Reference in New Issue
Block a user