mirror of
https://github.com/4sval/FModel.git
synced 2026-03-22 01:34:37 -05:00
fixes
This commit is contained in:
parent
ee9f3feb09
commit
53bfbedefa
|
|
@ -1 +1 @@
|
|||
Subproject commit 1f6ce1632134663c1dcc9467bdf05c0b486110ea
|
||||
Subproject commit 03adf9bfeddeda725cbbdfa7951e51ff61f0801d
|
||||
|
|
@ -52,7 +52,7 @@ public class BaseIconStats : BaseIcon
|
|||
foreach (var poi in challengeMapPoiData)
|
||||
{
|
||||
if (!poi.TryGetValue(out FStructFallback locationTag, "LocationTag") || !locationTag.TryGetValue(out FName tagName, "TagName") ||
|
||||
!tagName.Text.Equals(location.Text, StringComparison.OrdinalIgnoreCase) || !poi.TryGetValue(out FText text, "Text")) continue;
|
||||
tagName != location.TagName || !poi.TryGetValue(out FText text, "Text")) continue;
|
||||
|
||||
locationName = text.Text;
|
||||
break;
|
||||
|
|
@ -92,7 +92,11 @@ public class BaseIconStats : BaseIcon
|
|||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "35D04D1B45737BEA25B69686D9E085B9", "Damage"), dmgPb * multiplier, 200));
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"))
|
||||
if (weaponRowValue.TryGetValue(out float mdpc, "MaxDamagePerCartridge") && mdpc >= 0f)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), mdpc, 200));
|
||||
}
|
||||
else if (weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"))
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 200));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ public class Typefaces
|
|||
private const string _BURBANK_BIG_REGULAR_BOLD = "BurbankBigRegular-Bold"; // official fortnite ig
|
||||
private const string _BURBANK_SMALL_MEDIUM = "BurbankSmall-Medium";
|
||||
private const string _DROID_SANS_FORTNITE_SUBSET = "DroidSans-Fortnite-Subset";
|
||||
private const string _NIS_JYAU = "NIS_JYAU"; // japanese fortnite
|
||||
private const string _NOTO_COLOR_EMOJI = "NotoColorEmoji";
|
||||
private const string _NOTO_SANS_BOLD = "NotoSans-Bold";
|
||||
private const string _NOTO_SANS_FORTNITE_BOLD = "NotoSans-Fortnite-Bold";
|
||||
|
|
@ -32,7 +31,7 @@ public class Typefaces
|
|||
private const string _NOTO_SANS_ARABIC_BLACK = "NotoSansArabic-Black"; // arabic fortnite
|
||||
private const string _NOTO_SANS_ARABIC_BOLD = "NotoSansArabic-Bold";
|
||||
private const string _NOTO_SANS_ARABIC_REGULAR = "NotoSansArabic-Regular";
|
||||
private const string _NOTO_SANS_JP_BOLD = "NotoSansJP-Bold";
|
||||
private const string _NOTO_SANS_JP_BOLD = "NotoSansJP-Bold"; // japanese fortnite
|
||||
private const string _NOTO_SANS_KR_REGULAR = "NotoSansKR-Regular";
|
||||
private const string _NOTO_SANS_SC_BLACK = "NotoSansSC-Black"; // simplified chinese fortnite
|
||||
private const string _NOTO_SANS_SC_REGULAR = "NotoSansSC-Regular";
|
||||
|
|
@ -77,7 +76,7 @@ public class Typefaces
|
|||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
|
||||
ELanguage.Japanese => _NIS_JYAU,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
|
|
@ -113,7 +112,7 @@ public class Typefaces
|
|||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
|
||||
ELanguage.Japanese => _NIS_JYAU,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
|
|
@ -125,7 +124,7 @@ public class Typefaces
|
|||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
|
||||
ELanguage.Japanese => _NIS_JYAU,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
|
|
@ -136,7 +135,7 @@ public class Typefaces
|
|||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Japanese => _NIS_JYAU,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
|
|
@ -147,7 +146,7 @@ public class Typefaces
|
|||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Japanese => _NIS_JYAU,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
|
|
|
|||
|
|
@ -126,25 +126,6 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<!-- <MenuItem Header="Map Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_MapViewer" IsEnabled="{Binding Status.IsReady}"> -->
|
||||
<!-- <MenuItem.Icon> -->
|
||||
<!-- <Viewbox Width="16" Height="16"> -->
|
||||
<!-- <Canvas Width="24" Height="24"> -->
|
||||
<!-- <Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource MapIcon}" /> -->
|
||||
<!-- </Canvas> -->
|
||||
<!-- </Viewbox> -->
|
||||
<!-- </MenuItem.Icon> -->
|
||||
<!-- <MenuItem.Style> -->
|
||||
<!-- <Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}"> -->
|
||||
<!-- <Setter Property="Visibility" Value="Collapsed"/> -->
|
||||
<!-- <Style.Triggers> -->
|
||||
<!-- <DataTrigger Binding="{Binding CUE4Parse.InternalGameName, Converter={x:Static converters:CaseInsensitiveStringEqualsConverter.Instance}, ConverterParameter='FortniteGame'}" Value="True"> -->
|
||||
<!-- <Setter Property="Visibility" Value="Visible" /> -->
|
||||
<!-- </DataTrigger> -->
|
||||
<!-- </Style.Triggers> -->
|
||||
<!-- </Style> -->
|
||||
<!-- </MenuItem.Style> -->
|
||||
<!-- </MenuItem> -->
|
||||
<MenuItem Header="Image Merger" Command="{Binding MenuCommand}" CommandParameter="Views_ImageMerger">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
case "Views_AudioPlayer":
|
||||
Helper.OpenWindow<AdonisWindow>("Audio Player", () => new AudioPlayer().Show());
|
||||
break;
|
||||
case "Views_MapViewer":
|
||||
Helper.OpenWindow<AdonisWindow>("Map Viewer", () => new MapViewer().Show());
|
||||
break;
|
||||
case "Views_ImageMerger":
|
||||
Helper.OpenWindow<AdonisWindow>("Image Merger", () => new ImageMerger().Show());
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,876 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.GameplayTags;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Creator;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.ViewModels;
|
||||
|
||||
public class MapLayer
|
||||
{
|
||||
public SKBitmap Layer;
|
||||
public bool IsEnabled;
|
||||
}
|
||||
|
||||
public enum EWaypointType
|
||||
{
|
||||
Parkour,
|
||||
TimeTrials
|
||||
}
|
||||
|
||||
public class MapViewerViewModel : ViewModel
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
private DiscordHandler _discordHandler => DiscordService.DiscordHandler;
|
||||
|
||||
#region BINDINGS
|
||||
|
||||
private bool _brPois;
|
||||
public bool BrPois
|
||||
{
|
||||
get => _brPois;
|
||||
set => SetProperty(ref _brPois, value, "ApolloGameplay_MapPois");
|
||||
}
|
||||
|
||||
private bool _brLandmarks;
|
||||
public bool BrLandmarks
|
||||
{
|
||||
get => _brLandmarks;
|
||||
set => SetProperty(ref _brLandmarks, value, "ApolloGameplay_MapLandmarks");
|
||||
}
|
||||
|
||||
private bool _brTagsLocation;
|
||||
public bool BrTagsLocation
|
||||
{
|
||||
get => _brTagsLocation;
|
||||
set => SetProperty(ref _brTagsLocation, value, "ApolloGameplay_TagsLocation");
|
||||
}
|
||||
|
||||
private bool _brPatrolsPath;
|
||||
public bool BrPatrolsPath
|
||||
{
|
||||
get => _brPatrolsPath;
|
||||
set => SetProperty(ref _brPatrolsPath, value, "ApolloGameplay_PatrolsPath");
|
||||
}
|
||||
|
||||
private bool _brUpgradeBenches;
|
||||
public bool BrUpgradeBenches
|
||||
{
|
||||
get => _brUpgradeBenches;
|
||||
set => SetProperty(ref _brUpgradeBenches, value, "ApolloGameplay_UpgradeBenches");
|
||||
}
|
||||
|
||||
private bool _brPhonebooths;
|
||||
public bool BrPhonebooths
|
||||
{
|
||||
get => _brPhonebooths;
|
||||
set => SetProperty(ref _brPhonebooths, value, "ApolloGameplay_Phonebooths");
|
||||
}
|
||||
|
||||
private bool _brVendingMachines;
|
||||
public bool BrVendingMachines
|
||||
{
|
||||
get => _brVendingMachines;
|
||||
set => SetProperty(ref _brVendingMachines, value, "ApolloGameplay_VendingMachines");
|
||||
}
|
||||
|
||||
private bool _brBountyBoards;
|
||||
public bool BrBountyBoards
|
||||
{
|
||||
get => _brBountyBoards;
|
||||
set => SetProperty(ref _brBountyBoards, value, "ApolloGameplay_BountyBoards");
|
||||
}
|
||||
|
||||
private bool _prLandmarks;
|
||||
public bool PrLandmarks
|
||||
{
|
||||
get => _prLandmarks;
|
||||
set => SetProperty(ref _prLandmarks, value, "PapayaGameplay_MapLandmarks");
|
||||
}
|
||||
|
||||
private bool _prCannonball;
|
||||
public bool PrCannonball
|
||||
{
|
||||
get => _prCannonball;
|
||||
set => SetProperty(ref _prCannonball, value, "PapayaGameplay_CannonballGame");
|
||||
}
|
||||
|
||||
private bool _prSkydive;
|
||||
public bool PrSkydive
|
||||
{
|
||||
get => _prSkydive;
|
||||
set => SetProperty(ref _prSkydive, value, "PapayaGameplay_SkydiveGame");
|
||||
}
|
||||
|
||||
private bool _prShootingTargets;
|
||||
public bool PrShootingTargets
|
||||
{
|
||||
get => _prShootingTargets;
|
||||
set => SetProperty(ref _prShootingTargets, value, "PapayaGameplay_ShootingTargets");
|
||||
}
|
||||
|
||||
private bool _prParkour;
|
||||
public bool PrParkour
|
||||
{
|
||||
get => _prParkour;
|
||||
set => SetProperty(ref _prParkour, value, "PapayaGameplay_ParkourGame");
|
||||
}
|
||||
|
||||
private bool _prTimeTrials;
|
||||
public bool PrTimeTrials
|
||||
{
|
||||
get => _prTimeTrials;
|
||||
set => SetProperty(ref _prTimeTrials, value, "PapayaGameplay_TimeTrials");
|
||||
}
|
||||
|
||||
private bool _prVendingMachines;
|
||||
public bool PrVendingMachines
|
||||
{
|
||||
get => _prVendingMachines;
|
||||
set => SetProperty(ref _prVendingMachines, value, "PapayaGameplay_VendingMachines");
|
||||
}
|
||||
|
||||
private bool _prMusicBlocks;
|
||||
public bool PrMusicBlocks
|
||||
{
|
||||
get => _prMusicBlocks;
|
||||
set => SetProperty(ref _prMusicBlocks, value, "PapayaGameplay_MusicBlocks");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BITMAP IMAGES
|
||||
|
||||
private BitmapImage _brMiniMapImage;
|
||||
private BitmapImage _prMiniMapImage;
|
||||
private BitmapImage _mapImage;
|
||||
public BitmapImage MapImage
|
||||
{
|
||||
get => _mapImage;
|
||||
set => SetProperty(ref _mapImage, value);
|
||||
}
|
||||
|
||||
private BitmapImage _brLayerImage;
|
||||
private BitmapImage _prLayerImage;
|
||||
private BitmapImage _layerImage;
|
||||
public BitmapImage LayerImage
|
||||
{
|
||||
get => _layerImage;
|
||||
set => SetProperty(ref _layerImage, value);
|
||||
}
|
||||
|
||||
private const int _widthHeight = 2048;
|
||||
private const int _brRadius = 160000;
|
||||
private const int _prRadius = 51000;
|
||||
private int _mapIndex;
|
||||
public int MapIndex // 0 is BR, 1 is PR
|
||||
{
|
||||
get => _mapIndex;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _mapIndex, value);
|
||||
TriggerChange();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private const string _FIRST_BITMAP = "MapCheck";
|
||||
private readonly Dictionary<string, MapLayer>[] _bitmaps; // first bitmap is the displayed map, others are overlays of the map
|
||||
private readonly CUE4ParseViewModel _cue4Parse;
|
||||
|
||||
public MapViewerViewModel(CUE4ParseViewModel cue4Parse)
|
||||
{
|
||||
_bitmaps = new[]
|
||||
{
|
||||
new Dictionary<string, MapLayer>(),
|
||||
new Dictionary<string, MapLayer>()
|
||||
};
|
||||
_cue4Parse = cue4Parse;
|
||||
}
|
||||
|
||||
public async void Initialize()
|
||||
{
|
||||
Utils.Typefaces ??= new Typefaces(_cue4Parse);
|
||||
_textPaint.Typeface = _fillPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName;
|
||||
await LoadBrMiniMap();
|
||||
await LoadPrMiniMap();
|
||||
TriggerChange();
|
||||
}
|
||||
|
||||
public BitmapImage GetImageToSave() => GetImageSource(GetLayerBitmap(true));
|
||||
|
||||
private SKBitmap GetLayerBitmap(bool withMap)
|
||||
{
|
||||
var ret = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
foreach (var (key, value) in _bitmaps[MapIndex])
|
||||
{
|
||||
if (!value.IsEnabled || !withMap && key == _FIRST_BITMAP)
|
||||
continue;
|
||||
|
||||
c.DrawBitmap(value.Layer, new SKRect(0, 0, _widthHeight, _widthHeight));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override bool SetProperty<T>(ref T storage, T value, string propertyName = null) // don't delete, else nothing will update for some reason
|
||||
{
|
||||
var ret = base.SetProperty(ref storage, value, propertyName);
|
||||
if (bool.TryParse(value.ToString(), out var b)) GenericToggle(propertyName, b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private async void GenericToggle(string key, bool enabled)
|
||||
{
|
||||
if (_bitmaps[MapIndex].TryGetValue(key, out var layer) && layer.Layer != null)
|
||||
{
|
||||
layer.IsEnabled = enabled;
|
||||
}
|
||||
else if (enabled) // load layer
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "ApolloGameplay_MapPois":
|
||||
case "ApolloGameplay_MapLandmarks":
|
||||
case "PapayaGameplay_MapLandmarks":
|
||||
await LoadQuestIndicatorData();
|
||||
break;
|
||||
case "ApolloGameplay_TagsLocation":
|
||||
await LoadTagsLocation();
|
||||
break;
|
||||
case "ApolloGameplay_PatrolsPath":
|
||||
await LoadPatrolsPath();
|
||||
break;
|
||||
case "ApolloGameplay_UpgradeBenches":
|
||||
await LoadUpgradeBenches();
|
||||
break;
|
||||
case "ApolloGameplay_Phonebooths":
|
||||
await LoadPhonebooths();
|
||||
break;
|
||||
case "ApolloGameplay_VendingMachines":
|
||||
await LoadBrVendingMachines();
|
||||
break;
|
||||
case "ApolloGameplay_BountyBoards":
|
||||
await LoadBountyBoards();
|
||||
break;
|
||||
case "PapayaGameplay_CannonballGame":
|
||||
await LoadCannonballGame();
|
||||
break;
|
||||
case "PapayaGameplay_SkydiveGame":
|
||||
await LoadSkydiveGame();
|
||||
break;
|
||||
case "PapayaGameplay_ShootingTargets":
|
||||
await LoadShootingTargets();
|
||||
break;
|
||||
case "PapayaGameplay_ParkourGame":
|
||||
await LoadWaypoint(EWaypointType.Parkour);
|
||||
break;
|
||||
case "PapayaGameplay_TimeTrials":
|
||||
await LoadWaypoint(EWaypointType.TimeTrials);
|
||||
break;
|
||||
case "PapayaGameplay_VendingMachines":
|
||||
await LoadPrVendingMachines();
|
||||
break;
|
||||
case "PapayaGameplay_MusicBlocks":
|
||||
await LoadMusicBlocks();
|
||||
break;
|
||||
}
|
||||
|
||||
_bitmaps[MapIndex][key].IsEnabled = true;
|
||||
}
|
||||
|
||||
switch (MapIndex)
|
||||
{
|
||||
case 0:
|
||||
_brLayerImage = GetImageSource(GetLayerBitmap(false));
|
||||
break;
|
||||
case 1:
|
||||
_prLayerImage = GetImageSource(GetLayerBitmap(false));
|
||||
break;
|
||||
}
|
||||
|
||||
TriggerChange();
|
||||
}
|
||||
|
||||
private BitmapImage GetImageSource(SKBitmap bitmap)
|
||||
{
|
||||
if (bitmap == null) return null;
|
||||
using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100);
|
||||
using var stream = data.AsStream();
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.StreamSource = stream;
|
||||
image.EndInit();
|
||||
image.Freeze();
|
||||
return image;
|
||||
}
|
||||
|
||||
private void TriggerChange()
|
||||
{
|
||||
var layerCount = _bitmaps[_mapIndex].Count(x => x.Value.IsEnabled);
|
||||
var layerString = $"{layerCount} Layer{(layerCount > 1 ? "s" : "")}";
|
||||
switch (_mapIndex)
|
||||
{
|
||||
case 0:
|
||||
_discordHandler.UpdateButDontSavePresence(null, $"Map Viewer: Battle Royale ({layerString})");
|
||||
_mapImage = _brMiniMapImage;
|
||||
_layerImage = _brLayerImage;
|
||||
break;
|
||||
case 1:
|
||||
_discordHandler.UpdateButDontSavePresence(null, $"Map Viewer: Party Royale ({layerString})");
|
||||
_mapImage = _prMiniMapImage;
|
||||
_layerImage = _prLayerImage;
|
||||
break;
|
||||
}
|
||||
|
||||
RaisePropertyChanged(nameof(MapImage));
|
||||
RaisePropertyChanged(nameof(LayerImage));
|
||||
}
|
||||
|
||||
private readonly SKPaint _textPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Color = SKColors.White, TextAlign = SKTextAlign.Center, TextSize = 26
|
||||
};
|
||||
|
||||
private readonly SKPaint _fillPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
IsStroke = true, Color = SKColors.Black, TextSize = 26,
|
||||
TextAlign = SKTextAlign.Center
|
||||
};
|
||||
|
||||
private readonly SKPaint _pathPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, IsStroke = true,
|
||||
Style = SKPaintStyle.Stroke, StrokeWidth = 5, Color = SKColors.Red,
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(4, 4, 8, 8, SKColors.Black)
|
||||
};
|
||||
|
||||
private FVector2D GetMapPosition(FVector vector, int mapRadius)
|
||||
{
|
||||
const int wh = 2048 + 128 + 32;
|
||||
var nx = (vector.Y + mapRadius) / (mapRadius * 2) * wh;
|
||||
var ny = (1 - (vector.X + mapRadius) / (mapRadius * 2)) * wh;
|
||||
return new FVector2D(nx, ny);
|
||||
}
|
||||
|
||||
private async Task LoadBrMiniMap()
|
||||
{
|
||||
if (_bitmaps[0].TryGetValue(_FIRST_BITMAP, out var brMap) && brMap.Layer != null)
|
||||
return; // if map already loaded
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/UI/IngameMap/UIMapManagerBR.Default__UIMapManagerBR_C", out UObject mapManager) ||
|
||||
!mapManager.TryGetValue(out UMaterial mapMaterial, "MapMaterial")) return;
|
||||
var midTex = mapMaterial.GetFirstTexture();
|
||||
if ((midTex?.Name ?? string.Empty).Contains("Mask"))
|
||||
midTex = mapMaterial.GetTextureAtIndex(1);
|
||||
|
||||
if (midTex is not UTexture2D tex) return;
|
||||
_bitmaps[0][_FIRST_BITMAP] = new MapLayer { Layer = Utils.GetBitmap(tex), IsEnabled = true };
|
||||
_brMiniMapImage = GetImageSource(_bitmaps[0][_FIRST_BITMAP].Layer);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadPrMiniMap()
|
||||
{
|
||||
if (_bitmaps[1].TryGetValue(_FIRST_BITMAP, out var prMap) && prMap.Layer != null)
|
||||
return; // if map already loaded
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/UI/IngameMap/UIMapManagerPapaya.Default__UIMapManagerPapaya_C", out UObject mapManager) ||
|
||||
!mapManager.TryGetValue(out UMaterial mapMaterial, "MapMaterial") || mapMaterial.GetFirstTexture() is not UTexture2D tex) return;
|
||||
|
||||
_bitmaps[1][_FIRST_BITMAP] = new MapLayer { Layer = Utils.GetBitmap(tex), IsEnabled = true };
|
||||
_prMiniMapImage = GetImageSource(_bitmaps[1][_FIRST_BITMAP].Layer);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadQuestIndicatorData()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
var poisBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
var brLandmarksBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
var prLandmarksBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var pois = new SKCanvas(poisBitmap);
|
||||
using var brLandmarks = new SKCanvas(brLandmarksBitmap);
|
||||
using var prLandmarks = new SKCanvas(prLandmarksBitmap);
|
||||
|
||||
if (Utils.TryLoadObject("FortniteGame/Content/Quests/QuestIndicatorData", out UObject indicatorData) &&
|
||||
indicatorData.TryGetValue(out FStructFallback[] challengeMapPoiData, "ChallengeMapPoiData"))
|
||||
{
|
||||
foreach (var poiData in challengeMapPoiData)
|
||||
{
|
||||
if (!poiData.TryGetValue(out FSoftObjectPath discoveryQuest, "DiscoveryQuest") ||
|
||||
!poiData.TryGetValue(out FText text, "Text") || string.IsNullOrEmpty(text.Text) ||
|
||||
!poiData.TryGetValue(out FVector worldLocation, "WorldLocation") ||
|
||||
!poiData.TryGetValue(out FName discoverBackend, "DiscoverObjectiveBackendName")) continue;
|
||||
|
||||
var shaper = new CustomSKShaper(_textPaint.Typeface);
|
||||
if (discoverBackend.Text.Contains("papaya", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var vector = GetMapPosition(worldLocation, _prRadius);
|
||||
prLandmarks.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
prLandmarks.DrawShapedText(shaper, text.Text, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
prLandmarks.DrawShapedText(shaper, text.Text, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
else if (discoveryQuest.AssetPathName.Text.Contains("landmarks", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var vector = GetMapPosition(worldLocation, _brRadius);
|
||||
brLandmarks.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
brLandmarks.DrawShapedText(shaper, text.Text, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
brLandmarks.DrawShapedText(shaper, text.Text, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
_fillPaint.StrokeWidth = 10;
|
||||
var vector = GetMapPosition(worldLocation, _brRadius);
|
||||
pois.DrawShapedText(shaper, text.Text.ToUpperInvariant(), vector.X, vector.Y, _fillPaint);
|
||||
pois.DrawShapedText(shaper, text.Text.ToUpperInvariant(), vector.X, vector.Y, _textPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_MapPois"] = new MapLayer { Layer = poisBitmap, IsEnabled = false };
|
||||
_bitmaps[0]["ApolloGameplay_MapLandmarks"] = new MapLayer { Layer = brLandmarksBitmap, IsEnabled = false };
|
||||
_bitmaps[1]["PapayaGameplay_MapLandmarks"] = new MapLayer { Layer = prLandmarksBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadPatrolsPath()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var patrolsPathBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(patrolsPathBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Artemis_Overlay_S22_NPCLibrary");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("FortAthenaPatrolPath", StringComparison.OrdinalIgnoreCase) ||
|
||||
!export.TryGetValue(out FPackageIndex[] patrolPoints, "PatrolPoints")) continue;
|
||||
|
||||
var displayName = export.Name["FortAthenaPatrolPath_".Length..];
|
||||
if (export.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags") && gameplayTags.GameplayTags.Length > 0)
|
||||
displayName = gameplayTags.GameplayTags[0].Text["Athena.AI.SpawnLocation.Tandem.".Length..];
|
||||
|
||||
if (!Utils.TryGetPackageIndexExport(patrolPoints[0], out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var path = new SKPath();
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
path.MoveTo(vector.X, vector.Y);
|
||||
|
||||
for (var i = 1; i < patrolPoints.Length; i++)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(patrolPoints[i], out uObject) ||
|
||||
!uObject.TryGetValue(out rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
!uObject.TryGetValue(out relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
path.LineTo(vector.X, vector.Y);
|
||||
}
|
||||
|
||||
c.DrawPath(path, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_PatrolsPath"] = new MapLayer { Layer = patrolsPathBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadCannonballGame()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var cannonballBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(cannonballBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_CannonballGame");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("BP_CannonballGame_Target_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("CannonballGame_VehicleSpawner_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var displayName = Utils.GetLocalizedResource("", "D998BEF44F051E0885C6C58565934BEA", "Cannonball");
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[1]["PapayaGameplay_CannonballGame"] = new MapLayer { Layer = cannonballBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadSkydiveGame()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var skydiveBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(skydiveBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_SkydiveGame");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("BP_Waypoint_Papaya_Skydive_Start_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!export.TryGetValue(out FText minigameActivityName, "MinigameActivityName") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(minigameActivityName.Text, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(minigameActivityName.Text, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[1]["PapayaGameplay_SkydiveGame"] = new MapLayer { Layer = skydiveBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadShootingTargets()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var shootingTargetsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(shootingTargetsBitmap);
|
||||
|
||||
var bDone = false;
|
||||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_ShootingTargets");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("PapayaShootingTarget_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (bDone) continue;
|
||||
|
||||
bDone = true;
|
||||
c.DrawText("Shooting Target", vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText("Shooting Target", vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[1]["PapayaGameplay_ShootingTargets"] = new MapLayer { Layer = shootingTargetsBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadWaypoint(EWaypointType type)
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var waypointBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(waypointBitmap);
|
||||
|
||||
string file;
|
||||
string name;
|
||||
switch (type)
|
||||
{
|
||||
case EWaypointType.Parkour:
|
||||
file = "PapayaGameplay_ParkourGame";
|
||||
name = "Parkour";
|
||||
break;
|
||||
case EWaypointType.TimeTrials:
|
||||
file = "PapayaGameplay_TimeTrials";
|
||||
name = "Time Trials";
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
|
||||
var path = new SKPath();
|
||||
var exports = Utils.LoadExports($"/PapayaGameplay/LevelOverlays/{file}");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("BP_Waypoint_Parent_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject root) ||
|
||||
!root.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
if (path.IsEmpty || export.TryGetValue(out bool startsTrial, "StartsTrial") && startsTrial)
|
||||
{
|
||||
path.MoveTo(vector.X, vector.Y);
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
else if (export.TryGetValue(out bool endsTrial, "EndsTrial") && endsTrial)
|
||||
{
|
||||
path.LineTo(vector.X, vector.Y);
|
||||
c.DrawPath(path, _pathPaint);
|
||||
path = new SKPath();
|
||||
}
|
||||
else path.LineTo(vector.X, vector.Y);
|
||||
}
|
||||
|
||||
_bitmaps[1][file] = new MapLayer { Layer = waypointBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadPrVendingMachines()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var set = new HashSet<string>();
|
||||
var timeTrialsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(timeTrialsBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_VendingMachines");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("B_Papaya_VendingMachine_Boat_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_BoogieBomb_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Burger_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_CrashPad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_FishingPole_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Grappler_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Jetpack_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_PlungerBow_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Quad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Papaya_VendingMachine_Tomato_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject root) ||
|
||||
!root.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var name = export.ExportType.SubstringAfter("B_Papaya_VendingMachine_").SubstringBeforeLast("_C");
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (!set.Add(name)) continue;
|
||||
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[1]["PapayaGameplay_VendingMachines"] = new MapLayer { Layer = timeTrialsBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadMusicBlocks()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var shootingTargetsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(shootingTargetsBitmap);
|
||||
|
||||
var bDone = false;
|
||||
var exports = Utils.LoadExports("/PapayaGameplay/LevelOverlays/PapayaGameplay_MusicBlocks");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("MusicBlock_Piano3_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (bDone) continue;
|
||||
|
||||
bDone = true;
|
||||
c.DrawText("Music Blocks", vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText("Music Blocks", vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[1]["PapayaGameplay_MusicBlocks"] = new MapLayer { Layer = shootingTargetsBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadUpgradeBenches()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var upgradeBenchesBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(upgradeBenchesBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Artemis_Overlay_S19_UpgradeBenches");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("B_Athena_Spawner_UpgradeStation_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = export.Name["B_Athena_Spawner_".Length..];
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_UpgradeBenches"] = new MapLayer { Layer = upgradeBenchesBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadPhonebooths()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var phoneboothsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(phoneboothsBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Apollo_Terrain_NPCLibrary_Stations_Phonebooths");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("B_Athena_Spawner_Payphone_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = export.Name["B_Athena_Spawner_".Length..];
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_Phonebooths"] = new MapLayer { Layer = phoneboothsBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadBrVendingMachines()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var vendingMachinesBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(vendingMachinesBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Artemis_Overlay_S19_ServiceStations");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("B_Athena_Spawner_VendingMachine_MendingOnly_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!export.ExportType.Equals("B_Athena_Spawner_VendingMachine_Random_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = $"{(export.ExportType.Contains("Mending") ? "MM" : "WOM")}_{export.Name["B_Athena_Spawner_VendingMachine_Random".Length..]}";
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_VendingMachines"] = new MapLayer { Layer = vendingMachinesBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadBountyBoards()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var bountyBoardsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(bountyBoardsBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/Bounties/Maps/BB_Overlay_ServiceStations");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!export.ExportType.Equals("B_Bounties_Spawner_BountyBoard_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = $"BountyBoard_{export.Name["BP_BountyBoard_C_".Length..]}";
|
||||
|
||||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_BountyBoards"] = new MapLayer { Layer = bountyBoardsBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadTagsLocation()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Quests/QuestTagToLocationDataRows.QuestTagToLocationDataRows", out UDataTable locationData))
|
||||
return;
|
||||
|
||||
var tagsLocationBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(tagsLocationBitmap);
|
||||
|
||||
foreach (var (key, uObject) in locationData.RowMap)
|
||||
{
|
||||
if (key.Text.StartsWith("Athena.Location.POI", StringComparison.OrdinalIgnoreCase) ||
|
||||
key.Text.StartsWith("Athena.Location.Unnamed", StringComparison.OrdinalIgnoreCase) ||
|
||||
key.Text.Contains(".Tandem.", StringComparison.OrdinalIgnoreCase) ||
|
||||
!uObject.TryGetValue(out FVector worldLocation, "WorldLocation")) continue;
|
||||
|
||||
var parts = key.Text.Split('.');
|
||||
var displayName = parts[^2];
|
||||
if (!int.TryParse(parts[^1], out var _))
|
||||
displayName += " " + parts[^1];
|
||||
|
||||
var vector = GetMapPosition(worldLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_TagsLocation"] = new MapLayer { Layer = tagsLocationBitmap, IsEnabled = false };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<adonisControls:AdonisWindow x:Class="FModel.Views.MapViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:FModel"
|
||||
xmlns:controls="clr-namespace:FModel.Views.Resources.Controls"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" SizeToContent="Width" ResizeMode="CanMinimize" Closing="OnClosing"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.75'}"
|
||||
MinWidth="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}">
|
||||
<adonisControls:AdonisWindow.Style>
|
||||
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
|
||||
<Setter Property="Title" Value="Map Viewer" />
|
||||
</Style>
|
||||
</adonisControls:AdonisWindow.Style>
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<controls:OnTagDataTemplateSelector x:Key="TagTemplateSelector" />
|
||||
<DataTemplate x:Key="BrTemplate">
|
||||
<StackPanel VerticalAlignment="Center" Margin="25 0">
|
||||
<CheckBox Content="Points Of Interest" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrPois}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Landmarks" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrLandmarks}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<!-- <CheckBox Content="Tags Location" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrTagsLocation}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
<!-- <CheckBox Content="Patrols Path" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrPatrolsPath}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
<!-- <CheckBox Content="Upgrade Benches" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrUpgradeBenches}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
<!-- <CheckBox Content="Phonebooths" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrPhonebooths}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
<!-- <CheckBox Content="Weapon-o-matic/Mending Machines" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrVendingMachines}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
<!-- <CheckBox Content="Bounty Boards" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrBountyBoards}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="PrTemplate">
|
||||
<StackPanel VerticalAlignment="Center" Margin="25 0">
|
||||
<CheckBox Content="Landmarks" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrLandmarks}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Cannonball" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrCannonball}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Skydive" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrSkydive}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Shooting Targets" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrShootingTargets}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Parkour" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrParkour}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Time Trials" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrTimeTrials}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<CheckBox Content="Vending Machines" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrVendingMachines}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" />
|
||||
<!-- <CheckBox Content="Music Blocks" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.PrMusicBlocks}" -->
|
||||
<!-- DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> -->
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="300" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TreeView Grid.Row="0" Grid.RowSpan="3" x:Name="MapTree" SelectedItemChanged="OnSelectedItemChanged"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}">
|
||||
<TreeViewItem Tag="BrTemplate" IsSelected="True">
|
||||
<TreeViewItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center" Margin="-20 4 7.5 4">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource GliderIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
<TextBlock Text="Battle Royale" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Tag="PrTemplate">
|
||||
<TreeViewItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center" Margin="-20 4 7.5 4">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource AnchorIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
<TextBlock Text="Party Royale" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
</TreeView>
|
||||
|
||||
<Grid Grid.Row="1" HorizontalAlignment="Stretch">
|
||||
<ContentControl ContentTemplateSelector="{StaticResource TagTemplateSelector}" Content="{Binding SelectedItem.Tag, ElementName=MapTree}" />
|
||||
</Grid>
|
||||
|
||||
<Button Grid.Row="2" Content="Save Image" Margin="5" IsEnabled="{Binding Status.IsReady}" VerticalAlignment="Bottom" Click="OnClick" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Column="1" HorizontalAlignment="Stretch">
|
||||
<controls:MagnifierManager.Magnifier>
|
||||
<controls:Magnifier Radius="200" ZoomFactor=".4" BorderBrush="{DynamicResource {x:Static adonisUi:Brushes.AccentBrush}}" BorderThickness="1" />
|
||||
</controls:MagnifierManager.Magnifier>
|
||||
|
||||
<TextBlock Text="Minimap is loading, please wait..." HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Image UseLayoutRounding="True" Source="{Binding MapViewer.MapImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Visibility="{Binding Status.IsReady, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<Image UseLayoutRounding="True" Source="{Binding MapViewer.LayerImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Visibility="{Binding Status.IsReady, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using Microsoft.Win32;
|
||||
using Serilog;
|
||||
|
||||
namespace FModel.Views;
|
||||
|
||||
public partial class MapViewer
|
||||
{
|
||||
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
|
||||
public MapViewer()
|
||||
{
|
||||
DataContext = _applicationView;
|
||||
_applicationView.MapViewer.Initialize();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnClosing(object sender, CancelEventArgs e) => DiscordService.DiscordHandler.UpdateToSavedPresence();
|
||||
|
||||
private void OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_applicationView.MapViewer.MapImage == null) return;
|
||||
var path = Path.Combine(UserSettings.Default.TextureDirectory, "MiniMap.png");
|
||||
|
||||
var saveFileDialog = new SaveFileDialog
|
||||
{
|
||||
Title = "Save MiniMap",
|
||||
FileName = "MiniMap.png",
|
||||
InitialDirectory = path.SubstringBeforeLast('\\'),
|
||||
Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*"
|
||||
};
|
||||
|
||||
if (!saveFileDialog.ShowDialog().GetValueOrDefault()) return;
|
||||
path = saveFileDialog.FileName;
|
||||
|
||||
using var fileStream = new FileStream(path, FileMode.Create);
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(_applicationView.MapViewer.GetImageToSave()));
|
||||
encoder.Save(fileStream);
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Log.Information("MiniMap.png successfully saved");
|
||||
FLogger.Append(ELog.Information, () => FLogger.Text("Successfully saved 'MiniMap.png'", Constants.WHITE, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("MiniMap.png could not be saved");
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text("Could not save 'MiniMap.png'", Constants.WHITE, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var item in MapTree.Items)
|
||||
{
|
||||
if (item is not TreeViewItem { IsSelected: true })
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
_applicationView.MapViewer.MapIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user