base map viewer v2

This commit is contained in:
iAmAsval 2021-06-01 00:10:30 +02:00
parent 9a79bd608d
commit e575b5fb3b
12 changed files with 167 additions and 249 deletions

View File

@ -12,7 +12,6 @@ jobs:
uses: actions/checkout@v2
with:
submodules: 'true'
token: ${{ secrets.PAT_TOKEN }}
- name: .NET 5 Setup
uses: actions/setup-dotnet@v1

@ -1 +1 @@
Subproject commit bfc78f61b6e0dfcf8cba8e227eae21947c72f077
Subproject commit 83465934b69a1d4b339d05f0396ed1164ee77a6f

View File

@ -57,8 +57,10 @@ namespace FModel
private async void OnLoaded(object sender, RoutedEventArgs e)
{
#if !DEBUG
ApplicationService.ApiEndpointView.FModelApi.CheckForUpdates(UserSettings.Default.UpdateMode);
#endif
switch (UserSettings.Default.AesReload)
{
case EAesReload.Always:

View File

@ -109,7 +109,7 @@ namespace FModel.ViewModels.ApiEndpoints
{
if (args != null)
{
if (!args.IsUpdateAvailable) return;
if (new Version(args.CurrentVersion) == args.InstalledVersion) return;
var messageBox = new MessageBoxModel
{
Text = $"FModel {args.CurrentVersion} is available. You are using version {args.InstalledVersion}. Do you want to update the application now?",

View File

@ -105,6 +105,11 @@ namespace FModel.ViewModels
UserSettings.Default.GameDirectory = gameLauncherViewModel.SelectedDetectedGame.GameDirectory;
if (!bAlreadyLaunched || gameDirectory.Equals(gameLauncherViewModel.SelectedDetectedGame.GameDirectory)) return;
RestartWithWarning();
}
public void RestartWithWarning()
{
MessageBox.Show("It looks like you just changed something.\nFModel will restart to apply your changes.", "Uh oh, a restart is needed", MessageBoxButton.OK, MessageBoxImage.Warning);
Restart();
}

View File

@ -539,9 +539,7 @@ namespace FModel.ViewModels
{
case UTexture2D texture:
{
var filter = texture.GetOrDefault<FName>("Filter");
var lodGroup = texture.GetOrDefault<FName>("LODGroup");
SetImage(texture.Decode(), filter.IsNone ? null : filter.Text, lodGroup.IsNone ? null : lodGroup.Text);
SetImage(texture.Decode());
return true;
}
case UAkMediaAssetData:
@ -590,45 +588,17 @@ namespace FModel.ViewModels
return false;
}
private void SetImage(SKImage img, string filter = null, string lodGroup = null)
private void SetImage(SKImage img)
{
const int UPSCALE_SIZE = 512;
SKData data;
if ((filter != null && filter.EndsWith("TF_Nearest", StringComparison.Ordinal) ||
lodGroup != null && lodGroup.EndsWith("TEXTUREGROUP_Pixels2D", StringComparison.Ordinal)) &&
img.Width < UPSCALE_SIZE && img.Height < UPSCALE_SIZE)
{
var width = img.Width;
var heigth = img.Height;
while (width < UPSCALE_SIZE && heigth < UPSCALE_SIZE)
{
width *= 2;
heigth *= 2;
}
using var bitmap = SKBitmap.FromImage(img);
using var resized = bitmap.Resize(new SKImageInfo(width, heigth), SKFilterQuality.None);
data = resized.Encode(SKEncodedImageFormat.Png, 100); // maybe dispose 'img' at this point?
}
else
{
data = img.Encode(SKEncodedImageFormat.Png, 100);
}
using (data)
{
using var stream = data.AsStream(false);
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
TabControl.SelectedTab.Image = image;
}
using var stream = img.Encode().AsStream();
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
TabControl.SelectedTab.Image = image;
if (UserSettings.Default.IsAutoSaveTextures)
TabControl.SelectedTab.SaveImage(true);
}

View File

@ -1,20 +1,15 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.UE4.Assets.Exports;
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
{
@ -22,27 +17,31 @@ namespace FModel.ViewModels
{
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
private bool _showCities;
public bool ShowCities
private int _mapIndex = -1;
public int MapIndex // 0 is BR, 1 is PR
{
get => _showCities;
set => SetProperty(ref _showCities, value, nameof(ShowCities));
}
private bool _showLandmarks;
public bool ShowLandmarks
{
get => _showLandmarks;
set => SetProperty(ref _showLandmarks, value, nameof(ShowLandmarks));
}
private bool _showPatrolPaths;
public bool ShowPatrolPaths
{
get => _showPatrolPaths;
set => SetProperty(ref _showPatrolPaths, value, nameof(ShowPatrolPaths));
get => _mapIndex;
set
{
switch (value)
{
case 0:
_mapRadius = 135000;
_mapImage = _brMiniMapImage;
break;
case 1:
_mapRadius = 51000;
_mapImage = _prMiniMapImage;
break;
}
SetProperty(ref _mapIndex, value);
}
}
private int _mapRadius;
private BitmapImage _brMiniMapImage;
private BitmapImage _prMiniMapImage;
private BitmapImage _mapImage;
public BitmapImage MapImage
{
@ -50,55 +49,30 @@ namespace FModel.ViewModels
set => SetProperty(ref _mapImage, value);
}
private BitmapImage _citiesImage;
public BitmapImage CitiesImage
{
get => _citiesImage;
set => SetProperty(ref _citiesImage, value);
}
private BitmapImage _landmarksImage;
public BitmapImage LandmarksImage
{
get => _landmarksImage;
set => SetProperty(ref _landmarksImage, value);
}
private BitmapImage _patrolPathImage;
public BitmapImage PatrolPathImage
{
get => _patrolPathImage;
set => SetProperty(ref _patrolPathImage, value);
}
private readonly List<SKBitmap>[] _bitmaps; // first bitmap is the displayed map, others are overlays of the map
private readonly CUE4ParseViewModel _cue4Parse;
public MapViewerViewModel(CUE4ParseViewModel cue4Parse)
{
_bitmaps = new[] {new List<SKBitmap>(), new List<SKBitmap>()};
_cue4Parse = cue4Parse;
}
public async void Initialize()
{
Utils.Typefaces ??= new Typefaces(_cue4Parse);
if (MapImage == null && _mapBitmap == null)
{
await LoadMiniMap();
}
await LoadBrMiniMap();
await LoadPrMiniMap();
MapIndex = 1; // don't forget br is selected by default
MapIndex = 0; // this will trigger the br map to be shown
}
public BitmapImage GetImageToSave()
{
var ret = new SKBitmap(_mapBitmap.Width, _mapBitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
var ret = new SKBitmap(_bitmaps[MapIndex][0].Width, _bitmaps[MapIndex][0].Height, SKColorType.Rgba8888, SKAlphaType.Premul);
using var c = new SKCanvas(ret);
c.DrawBitmap(_mapBitmap, 0, 0);
if (ShowCities)
c.DrawBitmap(_citiesBitmap, 0, 0);
if (ShowLandmarks)
c.DrawBitmap(_landmarksBitmap, 0, 0);
if (ShowPatrolPaths)
c.DrawBitmap(_patrolPathBitmap, 0, 0);
c.DrawBitmap(_bitmaps[MapIndex][0], 0, 0);
return GetImageSource(ret);
}
@ -114,21 +88,7 @@ namespace FModel.ViewModels
{
switch (propertyName)
{
case nameof(ShowCities) when _citiesBitmap == null && _landmarksBitmap == null && _mapBitmap != null:
{
await LoadCities();
break;
}
case nameof(ShowLandmarks) when _landmarksBitmap == null && _citiesBitmap == null && _mapBitmap != null:
{
await LoadCities();
break;
}
case nameof(ShowPatrolPaths) when _patrolPathBitmap == null && _mapBitmap != null:
{
await LoadPatrolPaths();
break;
}
}
}
@ -144,16 +104,7 @@ namespace FModel.ViewModels
image.Freeze();
return image;
}
private const int WorldRadius = 135000;
private SKBitmap _mapBitmap;
private SKBitmap _citiesBitmap;
private SKBitmap _landmarksBitmap;
private SKBitmap _patrolPathBitmap;
private readonly SKBitmap _pinBitmap =
SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/pin.png"))?.Stream);
private readonly SKBitmap _cityPinBitmap =
SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/city_pin.png"))?.Stream);
private readonly SKPaint _imagePaint = new()
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
@ -169,13 +120,14 @@ namespace FModel.ViewModels
private FVector2D GetMapPosition(FVector vector)
{
var nx = (vector.Y + WorldRadius) / (WorldRadius * 2) * _mapBitmap.Width;
var ny = (1 - (vector.X + WorldRadius) / (WorldRadius * 2)) * _mapBitmap.Height;
var nx = (vector.Y + _mapRadius) / (_mapRadius * 2) * _bitmaps[MapIndex][0].Width;
var ny = (1 - (vector.X + _mapRadius) / (_mapRadius * 2)) * _bitmaps[MapIndex][0].Height;
return new FVector2D(nx, ny);
}
private async Task LoadMiniMap()
private async Task LoadBrMiniMap()
{
if (_bitmaps[0].Count > 0) return;
await _threadWorkerView.Begin(_ =>
{
if (!Utils.TryLoadObject("FortniteGame/Content/UI/IngameMap/UIMapManagerBR.Default__UIMapManagerBR_C", out UObject mapManager) ||
@ -183,109 +135,23 @@ namespace FModel.ViewModels
!mapMaterial.TryGetValue(out FStructFallback cachedExpressionData, "CachedExpressionData") ||
!cachedExpressionData.TryGetValue(out FStructFallback parameters, "Parameters") ||
!parameters.TryGetValue(out UTexture2D[] textureValues, "TextureValues")) return;
_imagePaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName;
_mapBitmap = Utils.GetBitmap(textureValues[0]);
MapImage = GetImageSource(_mapBitmap);
_bitmaps[0].Add(Utils.GetBitmap(textureValues[0]));
_brMiniMapImage = GetImageSource(_bitmaps[0][0]);
});
}
private async Task LoadCities()
private async Task LoadPrMiniMap()
{
if (_bitmaps[1].Count > 0) return;
await _threadWorkerView.Begin(_ =>
{
_citiesBitmap = new SKBitmap(_mapBitmap.Width, _mapBitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
_landmarksBitmap = new SKBitmap(_mapBitmap.Width, _mapBitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
using var cities = new SKCanvas(_citiesBitmap);
using var landmarks = new SKCanvas(_landmarksBitmap);
if (Utils.TryLoadObject("FortniteGame/Content/Quests/QuestIndicatorData.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") ||
discoverBackend.Text.Contains("papaya", StringComparison.OrdinalIgnoreCase)) continue;
var shaper = new CustomSKShaper(_imagePaint.Typeface);
var shapedText = shaper.Shape(text.Text, _imagePaint);
var vector = GetMapPosition(worldLocation);
if (!Utils.TryLoadObject("FortniteGame/Content/UI/IngameMap/UIMapManagerPapaya.Default__UIMapManagerPapaya_C", out UObject mapManager) ||
!mapManager.TryGetValue(out UMaterial mapMaterial, "MapMaterial") ||
mapMaterial.ReferencedTextures.Count < 1) return;
if (discoveryQuest.AssetPathName.Text.Contains("landmarks", StringComparison.OrdinalIgnoreCase))
{
landmarks.DrawPoint(vector.X, vector.Y, _pathPaint);
landmarks.DrawShapedText(shaper, text.Text, vector.X - shapedText.Points[^1].X / 2, vector.Y - 12.5F, _imagePaint);
}
else
{
cities.DrawPoint(vector.X, vector.Y, _pathPaint);
cities.DrawBitmap(_cityPinBitmap, vector.X - 50, vector.Y - 90, _imagePaint);
cities.DrawShapedText(shaper, text.Text, vector.X - shapedText.Points[^1].X / 2, vector.Y - 12.5F, _imagePaint);
}
}
}
CitiesImage = GetImageSource(_citiesBitmap);
LandmarksImage = GetImageSource(_landmarksBitmap);
});
}
private async Task LoadPatrolPaths()
{
await _threadWorkerView.Begin(_ =>
{
_patrolPathBitmap = new SKBitmap(_mapBitmap.Width, _mapBitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
using var c = new SKCanvas(_patrolPathBitmap);
if (!Utils.TryLoadObject("FortniteGame/Plugins/GameFeatures/NPCLibrary/Content/GameFeatureData.GameFeatureData", out UObject gameFeatureData) ||
!gameFeatureData.TryGetValue(out FPackageIndex levelOverlayConfig, "LevelOverlayConfig") ||
!Utils.TryGetPackageIndexExport(levelOverlayConfig, out UObject npcLibrary) ||
!npcLibrary.TryGetValue(out FStructFallback[] overlayList, "OverlayList"))
return;
foreach (var overlay in overlayList)
{
if (!overlay.TryGetValue(out FSoftObjectPath overlayWorld, "OverlayWorld"))
continue;
var exports = Utils.LoadExports(overlayWorld.AssetPathName.Text.SubstringBeforeLast("."));
foreach (var export in exports)
{
if (!(export is UObject uObject)) continue;
if (!uObject.ExportType.Equals("FortAthenaPatrolPath", StringComparison.OrdinalIgnoreCase) ||
!uObject.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags") ||
!uObject.TryGetValue(out FPackageIndex[] patrolPoints, "PatrolPoints")) continue;
if (!Utils.TryGetPackageIndexExport(patrolPoints[0], out 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);
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);
path.LineTo(vector.X, vector.Y);
}
path.Close();
c.DrawPath(path, _pathPaint);
c.DrawBitmap(_pinBitmap, vector.X - 50, vector.Y - 90, _imagePaint);
c.DrawText(gameplayTags.GameplayTags[0].Text.SubstringAfterLast("."), vector.X, vector.Y - 12.5F, _imagePaint);
}
}
PatrolPathImage = GetImageSource(_patrolPathBitmap);
_bitmaps[1].Add(Utils.GetBitmap(mapMaterial.ReferencedTextures[0] as UTexture2D));
_prMiniMapImage = GetImageSource(_bitmaps[1][0]);
});
}
}

View File

@ -5,9 +5,9 @@
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"
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" SizeToContent="Width" ResizeMode="CanMinimize"
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.75'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}">
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" />
@ -18,37 +18,87 @@
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<controls:OnTagDataTemplateSelector x:Key="TagTemplateSelector" />
<DataTemplate x:Key="BrTemplate">
<StackPanel VerticalAlignment="Center" Margin="50">
<CheckBox Content="Show Cities" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="Show Landmarks" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="Show NPCs Path" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="PrTemplate">
<StackPanel VerticalAlignment="Center" Margin="25 0">
<CheckBox Content="PapayaGameplay_CannonballGame" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaGameplay_SkydiveGame" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaGameplay_ShootingTargets" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaGameplay_ParkourGame" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaGameplay_TimeTrials" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaGameplay_VendingMachines" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaGameplay_MusicBlocks" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaEffects_Concert" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaAudio_StageVolumes" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="PapayaEffects_Directionals" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</adonisControls:AdonisWindow.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" MinWidth="300" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center" Margin="50">
<CheckBox Content="Show Cities" IsEnabled="{Binding IsReady}" IsChecked="{Binding MapViewer.ShowCities, Mode=TwoWay}"
Margin="0 0 0 10" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="Show Landmarks" IsEnabled="{Binding IsReady}" IsChecked="{Binding MapViewer.ShowLandmarks, Mode=TwoWay}"
Margin="0 0 0 10" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<CheckBox Content="Show Patrol Paths" IsEnabled="{Binding IsReady}" IsChecked="{Binding MapViewer.ShowPatrolPaths, Mode=TwoWay}"
Margin="0 0 0 10" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
<Button Content="Save Image" IsEnabled="{Binding IsReady}" Click="OnClick" />
</StackPanel>
<Grid Grid.Column="1">
<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 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="Map Viewer is loading, please wait..." HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="Minimap is loading, please wait..." HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image UseLayoutRounding="True" Source="{Binding MapViewer.MapImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
Visibility="{Binding IsReady, Converter={StaticResource BoolToVisibilityConverter}}" />
<Image UseLayoutRounding="True" Source="{Binding MapViewer.CitiesImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
Visibility="{Binding MapViewer.ShowCities, Converter={StaticResource BoolToVisibilityConverter}}" />
<Image UseLayoutRounding="True" Source="{Binding MapViewer.LandmarksImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
Visibility="{Binding MapViewer.ShowLandmarks, Converter={StaticResource BoolToVisibilityConverter}}" />
<Image UseLayoutRounding="True" Source="{Binding MapViewer.PatrolPathImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
Visibility="{Binding MapViewer.ShowPatrolPaths, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
</Grid>
</adonisControls:AdonisWindow>

View File

@ -1,5 +1,6 @@
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using FModel.Extensions;
using FModel.Services;
@ -57,5 +58,21 @@ namespace FModel.Views
FLogger.AppendText("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;
}
}
}
}

View File

@ -0,0 +1,7 @@
namespace FModel.Views.Resources.Controls
{
public class Breadcrumb
{
// https://trello.com/c/sUFv5fZM/63-breadcrumb-above-asset-list
}
}

View File

@ -57,6 +57,8 @@
<Geometry x:Key="AddAudioIcon">M13 10H3c-.55 0-1 .45-1 1s.45 1 1 1h10c.55 0 1-.45 1-1s-.45-1-1-1zm0-4H3c-.55 0-1 .45-1 1s.45 1 1 1h10c.55 0 1-.45 1-1s-.45-1-1-1zm5 8v-3c0-.55-.45-1-1-1s-1 .45-1 1v3h-3c-.55 0-1 .45-1 1s.45 1 1 1h3v3c0 .55.45 1 1 1s1-.45 1-1v-3h3c.55 0 1-.45 1-1s-.45-1-1-1h-3zM3 16h6c.55 0 1-.45 1-1s-.45-1-1-1H3c-.55 0-1 .45-1 1s.45 1 1 1z</Geometry>
<Geometry x:Key="SavePlaylistIcon">M14 6H4c-.55 0-1 .45-1 1s.45 1 1 1h10c.55 0 1-.45 1-1s-.45-1-1-1zm0 4H4c-.55 0-1 .45-1 1s.45 1 1 1h10c.55 0 1-.45 1-1s-.45-1-1-1zM4 16h6c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1zM19 6c-1.1 0-2 .9-2 2v6.18c-.31-.11-.65-.18-1-.18-1.84 0-3.28 1.64-2.95 3.54.21 1.21 1.2 2.2 2.41 2.41 1.9.33 3.54-1.11 3.54-2.95V8h2c.55 0 1-.45 1-1s-.45-1-1-1h-2z</Geometry>
<Geometry x:Key="ImageMergerIcon">M10 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-4 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z</Geometry>
<Geometry x:Key="GliderIcon">M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z M17.95,14c-0.52,0-0.94,0.4-0.99,0.92 c-0.2,2.03-1.05,2.68-1.48,3.02C14.68,18.54,14,19,12,19s-2.68-0.46-3.48-1.06c-0.43-0.34-1.28-0.99-1.48-3.02 C6.99,14.4,6.57,14,6.05,14c-0.59,0-1.06,0.51-1,1.09c0.22,2.08,1.07,3.47,2.24,4.41c0.5,0.4,1.1,0.7,1.7,0.9L9,24h6v-3.6 c0.6-0.2,1.2-0.5,1.7-0.9c1.17-0.94,2.03-2.32,2.24-4.41C19.01,14.51,18.53,14,17.95,14z M12,0C5.92,0,1,1.9,1,4.25v3.49 C1,8.55,1.88,9,2.56,8.57C2.7,8.48,2.84,8.39,3,8.31L5,13h2l1.5-6.28C9.6,6.58,10.78,6.5,12,6.5s2.4,0.08,3.5,0.22L17,13h2l2-4.69 c0.16,0.09,0.3,0.17,0.44,0.26C22.12,9,23,8.55,23,7.74V4.25C23,1.9,18.08,0,12,0z M5.88,11.24L4.37,7.69 c0.75-0.28,1.6-0.52,2.53-0.71L5.88,11.24z M18.12,11.24L17.1,6.98c0.93,0.19,1.78,0.43,2.53,0.71L18.12,11.24z</Geometry>
<Geometry x:Key="AnchorIcon">M13,9V7.82C14.16,7.4,15,6.3,15,5c0-1.65-1.35-3-3-3S9,3.35,9,5c0,1.3,0.84,2.4,2,2.82V9H9c-0.55,0-1,0.45-1,1v0 c0,0.55,0.45,1,1,1h2v8.92c-2.22-0.33-4.59-1.68-5.55-3.37l1.14-1.14c0.22-0.22,0.19-0.57-0.05-0.75L3.8,12.6 C3.47,12.35,3,12.59,3,13v2c0,3.88,4.92,7,9,7s9-3.12,9-7v-2c0-0.41-0.47-0.65-0.8-0.4l-2.74,2.05c-0.24,0.18-0.27,0.54-0.05,0.75 l1.14,1.14c-0.96,1.69-3.33,3.04-5.55,3.37V11h2c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H13z M12,4c0.55,0,1,0.45,1,1s-0.45,1-1,1 s-1-0.45-1-1S11.45,4,12,4z</Geometry>
<Style x:Key="TabItemFillSpace" TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="Width">

View File

@ -31,7 +31,7 @@ namespace FModel.Views
{
var whatShouldIDo = _applicationView.SettingsView.Save();
if (whatShouldIDo == SettingsOut.Restart)
_applicationView.Restart();
_applicationView.RestartWithWarning();
Close();