mirror of
https://github.com/4sval/FModel.git
synced 2026-08-27 20:04:25 -05:00
Merge pull request #187 from iAmAsval/dev
new map viewer, mesh support, other stuff
This commit is contained in:
27
.github/workflows/main.yml
vendored
27
.github/workflows/main.yml
vendored
@@ -1,7 +1,12 @@
|
||||
name: Artifact Generator
|
||||
name: FModel Builder
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
appVersion:
|
||||
description: 'FModel Version And Release Tag'
|
||||
required: true
|
||||
default: '4.0.X.X'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -12,7 +17,6 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
token: ${{ secrets.PAT_TOKEN }}
|
||||
|
||||
- name: .NET 5 Setup
|
||||
uses: actions/setup-dotnet@v1
|
||||
@@ -23,10 +27,19 @@ jobs:
|
||||
run: dotnet restore FModel
|
||||
|
||||
- name: .NET Publish
|
||||
run: dotnet publish FModel -c Release -f net5.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false --no-self-contained -r win-x64
|
||||
run: dotnet publish FModel -c Release -f net5.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }} --no-self-contained -r win-x64
|
||||
|
||||
- name: EXE Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
- name: ZIP File
|
||||
uses: papeloto/action-zip@v1
|
||||
with:
|
||||
name: FModel
|
||||
path: D:\a\FModel\FModel\FModel\bin\Publish\
|
||||
files: ./FModel/bin/Publish/FModel.exe
|
||||
dest: FModel.zip # will end up in working directory not the Publish folder
|
||||
|
||||
- name: GIT Release
|
||||
uses: marvinpinto/action-automatic-releases@latest
|
||||
with:
|
||||
title: "FModel v${{ github.event.inputs.appVersion }}"
|
||||
automatic_release_tag: ${{ github.event.inputs.appVersion }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
prerelease: false
|
||||
files: FModel.zip
|
||||
Submodule CUE4Parse updated: 70d417009d...a520ada39b
@@ -23,8 +23,14 @@ namespace FModel
|
||||
/// </summary>
|
||||
public partial class App
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool AttachConsole(int dwProcessId);
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
#if DEBUG
|
||||
AttachConsole(-1);
|
||||
#endif
|
||||
base.OnStartup(e);
|
||||
|
||||
try
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
|
||||
namespace FModel
|
||||
@@ -23,41 +22,5 @@ namespace FModel
|
||||
|
||||
public const string _FN_LIVE_TRIGGER = "fortnite-live.manifest";
|
||||
public const string _VAL_LIVE_TRIGGER = "valorant-live.manifest";
|
||||
|
||||
public static string GetRandomColor()
|
||||
{
|
||||
return _randomColors[_random.Next(0, 255)];
|
||||
}
|
||||
|
||||
private static readonly Random _random = new(Environment.TickCount);
|
||||
private static readonly string[] _randomColors =
|
||||
{
|
||||
"F44336", "FFEBEE", "FFCDD2", "EF9A9A", "E57373", "EF5350", "E53935", "D32F2F", "C62828", "B71C1C",
|
||||
"FF8A80", "FF5252", "FF1744", "D50000", "FCE4EC", "F8BBD0", "F48FB1", "F06292", "EC407A", "E91E63",
|
||||
"D81B60", "C2185B", "AD1457", "880E4F", "FF80AB", "FF4081", "F50057", "C51162", "F3E5F5", "E1BEE7",
|
||||
"CE93D8", "BA68C8", "AB47BC", "9C27B0", "8E24AA", "7B1FA2", "6A1B9A", "4A148C", "EA80FC", "E040FB",
|
||||
"D500F9", "AA00FF", "EDE7F6", "D1C4E9", "B39DDB", "9575CD", "7E57C2", "673AB7", "5E35B1", "512DA8",
|
||||
"4527A0", "311B92", "B388FF", "7C4DFF", "651FFF", "6200EA", "E8EAF6", "C5CAE9", "9FA8DA", "7986CB",
|
||||
"5C6BC0", "3F51B5", "3949AB", "303F9F", "283593", "1A237E", "8C9EFF", "536DFE", "3D5AFE", "304FFE",
|
||||
"E3F2FD", "BBDEFB", "90CAF9", "64B5F6", "42A5F5", "2196F3", "1E88E5", "1976D2", "1565C0", "0D47A1",
|
||||
"82B1FF", "448AFF", "2979FF", "2962FF", "E1F5FE", "B3E5FC", "81D4FA", "4FC3F7", "29B6F6", "03A9F4",
|
||||
"039BE5", "0288D1", "0277BD", "01579B", "80D8FF", "40C4FF", "00B0FF", "0091EA", "E0F7FA", "B2EBF2",
|
||||
"80DEEA", "4DD0E1", "26C6DA", "00BCD4", "00ACC1", "0097A7", "00838F", "006064", "84FFFF", "18FFFF",
|
||||
"00E5FF", "00B8D4", "E0F2F1", "B2DFDB", "80CBC4", "4DB6AC", "26A69A", "009688", "00897B", "00796B",
|
||||
"00695C", "004D40", "A7FFEB", "64FFDA", "1DE9B6", "00BFA5", "E8F5E9", "C8E6C9", "A5D6A7", "81C784",
|
||||
"66BB6A", "4CAF50", "43A047", "388E3C", "2E7D32", "1B5E20", "B9F6CA", "69F0AE", "00E676", "00C853",
|
||||
"F1F8E9", "DCEDC8", "C5E1A5", "AED581", "9CCC65", "8BC34A", "7CB342", "689F38", "558B2F", "33691E",
|
||||
"CCFF90", "B2FF59", "76FF03", "64DD17", "F9FBE7", "F0F4C3", "E6EE9C", "DCE775", "D4E157", "CDDC39",
|
||||
"C0CA33", "AFB42B", "9E9D24", "827717", "F4FF81", "EEFF41", "C6FF00", "AEEA00", "FFFDE7", "FFF9C4",
|
||||
"FFF59D", "FFF176", "FFEE58", "FFEB3B", "FDD835", "FBC02D", "F9A825", "F57F17", "FFFF8D", "FFFF00",
|
||||
"FFEA00", "FFD600", "FFF8E1", "FFECB3", "FFE082", "FFD54F", "FFCA28", "FFC107", "FFB300", "FFA000",
|
||||
"FF8F00", "FF6F00", "FFE57F", "FFD740", "FFC400", "FFAB00", "FFF3E0", "FFE0B2", "FFCC80", "FFB74D",
|
||||
"FFA726", "FF9800", "FB8C00", "F57C00", "EF6C00", "E65100", "FFD180", "FFAB40", "FF9100", "FF6D00",
|
||||
"FBE9E7", "FFCCBC", "FFAB91", "FF8A65", "FF7043", "FF5722", "F4511E", "E64A19", "D84315", "BF360C",
|
||||
"FF9E80", "FF6E40", "FF3D00", "DD2C00", "EFEBE9", "D7CCC8", "BCAAA4", "A1887F", "8D6E63", "795548",
|
||||
"6D4C41", "5D4037", "4E342E", "3E2723", "FAFAFA", "F5F5F5", "EEEEEE", "E0E0E0", "BDBDBD", "9E9E9E",
|
||||
"757575", "616161", "424242", "212121", "ECEFF1", "CFD8DC", "B0BEC5", "90A4AE", "78909C", "607D8B",
|
||||
"546E7A", "455A64", "37474F", "263238", "000000",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -88,8 +88,8 @@ namespace FModel.Creator.Bases.FN
|
||||
if (_design.DrawSeason && gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
|
||||
_season = GetCosmeticSeason(season.Text, _design.DrawSeasonShort);
|
||||
|
||||
GetUserFacingFlags(gameplayTags.GetAllGameplayTags(
|
||||
"Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."));
|
||||
var triggers = _design.GameplayTags.DrawCustomOnly ? new[] {"Cosmetics.UserFacingFlags."} : new[] {"Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."};
|
||||
GetUserFacingFlags(gameplayTags.GetAllGameplayTags(triggers));
|
||||
}
|
||||
|
||||
private string GetCosmeticSet(string setName, bool bShort)
|
||||
@@ -290,7 +290,7 @@ namespace FModel.Creator.Bases.FN
|
||||
|
||||
private void DrawUserFacingFlags(SKCanvas c, bool customOnly)
|
||||
{
|
||||
if (UserFacingFlags == null || UserFacingFlags.Length < 1) return;
|
||||
if (UserFacingFlags == null || UserFacingFlags.Count < 1) return;
|
||||
if (customOnly)
|
||||
{
|
||||
c.DrawBitmap(_design.GameplayTags.Custom, 0, 0, ImagePaint);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace FModel.Creator.Bases.FN
|
||||
public SKBitmap SeriesBackground { get; protected set; }
|
||||
protected string ShortDescription { get; set; }
|
||||
protected string CosmeticSource { get; set; }
|
||||
protected SKBitmap[] UserFacingFlags { get; set; }
|
||||
protected Dictionary<string, SKBitmap> UserFacingFlags { get; set; }
|
||||
|
||||
public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
@@ -269,24 +269,24 @@ namespace FModel.Creator.Bases.FN
|
||||
if (!itemCategories.TryGetValue(out FStructFallback[] tertiaryCategories, "TertiaryCategories"))
|
||||
return;
|
||||
|
||||
UserFacingFlags = new SKBitmap[userFacingFlags.Count];
|
||||
for (var i = 0; i < UserFacingFlags.Length; i++)
|
||||
UserFacingFlags = new Dictionary<string, SKBitmap>(userFacingFlags.Count);
|
||||
foreach (var flag in userFacingFlags)
|
||||
{
|
||||
if (userFacingFlags[i].Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase))
|
||||
if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Object.ExportType.Equals("AthenaPetCarrierItemDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
UserFacingFlags[i] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream);
|
||||
else UserFacingFlags[i] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream);
|
||||
UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream);
|
||||
else UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var category in tertiaryCategories)
|
||||
{
|
||||
if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(userFacingFlags[i], out _) &&
|
||||
if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(flag, out _) &&
|
||||
category.TryGetValue(out FStructFallback categoryBrush, "CategoryBrush") && categoryBrush.TryGetValue(out FStructFallback brushXxs, "Brush_XXS") &&
|
||||
brushXxs.TryGetValue(out FPackageIndex resourceObject, "ResourceObject") && Utils.TryGetPackageIndexExport(resourceObject, out UTexture2D texture))
|
||||
{
|
||||
UserFacingFlags[i] = Utils.GetBitmap(texture);
|
||||
UserFacingFlags[flag] = Utils.GetBitmap(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,7 +299,7 @@ namespace FModel.Creator.Bases.FN
|
||||
|
||||
const int size = 25;
|
||||
var x = Margin * (int) 2.5;
|
||||
foreach (var flag in UserFacingFlags)
|
||||
foreach (var flag in UserFacingFlags.Values)
|
||||
{
|
||||
if (flag == null) continue;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace FModel.Creator.Bases.FN
|
||||
{
|
||||
if (Object is not UMaterialInstanceConstant material) return;
|
||||
|
||||
texture_finding:
|
||||
foreach (var textureParameter in material.TextureParameterValues) // get texture from base material
|
||||
{
|
||||
if (textureParameter.ParameterValue is not UTexture2D texture || Preview != null) continue;
|
||||
@@ -45,6 +46,9 @@ namespace FModel.Creator.Bases.FN
|
||||
if (material == null) return;
|
||||
}
|
||||
|
||||
if (Preview == null)
|
||||
goto texture_finding;
|
||||
|
||||
foreach (var vectorParameter in material.VectorParameterValues)
|
||||
{
|
||||
if (vectorParameter.ParameterValue == null) continue;
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace FModel.Creator.Bases.FN
|
||||
}
|
||||
else if (!_unauthorizedReward.Contains(name.Text))
|
||||
{
|
||||
_reward = new Reward(quantity, primaryAssetName);
|
||||
_reward = new Reward(quantity, $"{name}:{primaryAssetName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,4 +256,4 @@ namespace FModel.Creator.Bases.FN
|
||||
_reward.DrawQuest(c, new SKRect(Height, outY + 25, Width - 20, y + Height - 25));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
@@ -10,10 +10,17 @@ using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN
|
||||
{
|
||||
public class Page
|
||||
{
|
||||
public int LevelsNeededForUnlock;
|
||||
public int RewardsNeededForUnlock;
|
||||
public Reward[] RewardEntryList;
|
||||
}
|
||||
|
||||
public class BaseSeason : UCreator
|
||||
{
|
||||
private Reward _firstWinReward;
|
||||
private Dictionary<int, List<Reward>> _bookXpSchedule;
|
||||
private Page[] _bookXpSchedule;
|
||||
private const int _headerHeight = 150;
|
||||
// keep the list because rewards are ordered by least to most important
|
||||
// we only care about the most but we also have filters so we can't just take the last reward
|
||||
@@ -27,8 +34,8 @@ namespace FModel.Creator.Bases.FN
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
_bookXpSchedule = new Dictionary<int, List<Reward>>();
|
||||
|
||||
_bookXpSchedule = Array.Empty<Page>();
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text.ToUpperInvariant();
|
||||
|
||||
@@ -45,56 +52,48 @@ namespace FModel.Creator.Bases.FN
|
||||
}
|
||||
}
|
||||
|
||||
var freeLevels = Array.Empty<FStructFallback>();
|
||||
var paidLevels = Array.Empty<FStructFallback>();
|
||||
if (Object.TryGetValue(out FPackageIndex[] additionalSeasonData, "AdditionalSeasonData") &&
|
||||
additionalSeasonData.Length > 0 && Utils.TryGetPackageIndexExport(additionalSeasonData[0], out UObject data) &&
|
||||
data.TryGetValue(out FStructFallback battlePassXpScheduleFree, "BattlePassXpScheduleFree") &&
|
||||
battlePassXpScheduleFree.TryGetValue(out freeLevels, "Levels") &&
|
||||
data.TryGetValue(out FStructFallback battlePassXpSchedulePaid, "BattlePassXpSchedulePaid") &&
|
||||
battlePassXpSchedulePaid.TryGetValue(out paidLevels, "Levels"))
|
||||
if (Object.TryGetValue(out FPackageIndex[] additionalSeasonData, "AdditionalSeasonData"))
|
||||
{
|
||||
// we got them boys
|
||||
}
|
||||
else if (Object.TryGetValue(out FStructFallback bookXpScheduleFree, "BookXpScheduleFree") &&
|
||||
bookXpScheduleFree.TryGetValue(out freeLevels, "Levels") &&
|
||||
Object.TryGetValue(out FStructFallback bookXpSchedulePaid, "BookXpSchedulePaid") &&
|
||||
bookXpSchedulePaid.TryGetValue(out paidLevels, "Levels"))
|
||||
{
|
||||
// we got them boys
|
||||
}
|
||||
|
||||
for (var i = 0; i < freeLevels.Length; i++)
|
||||
{
|
||||
_bookXpSchedule[i] = new List<Reward>();
|
||||
if (!freeLevels[i].TryGetValue(out rewards, "Rewards")) continue;
|
||||
|
||||
foreach (var reward in rewards)
|
||||
foreach (var data in additionalSeasonData)
|
||||
{
|
||||
if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
|
||||
itemDefinition.AssetPathName.Text.Contains("/Items/Tokens/") ||
|
||||
!Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
|
||||
|
||||
_bookXpSchedule[i].Add(new Reward(uObject));
|
||||
if (!Utils.TryGetPackageIndexExport(data, out UObject packageIndex) ||
|
||||
!packageIndex.TryGetValue(out FStructFallback[] pageList, "PageList")) continue;
|
||||
|
||||
var i = 0;
|
||||
_bookXpSchedule = new Page[pageList.Length];
|
||||
foreach (var page in pageList)
|
||||
{
|
||||
if (!page.TryGetValue(out int levelsNeededForUnlock, "LevelsNeededForUnlock") ||
|
||||
!page.TryGetValue(out int rewardsNeededForUnlock, "RewardsNeededForUnlock") ||
|
||||
!page.TryGetValue(out FPackageIndex[] rewardEntryList, "RewardEntryList"))
|
||||
continue;
|
||||
|
||||
var p = new Page
|
||||
{
|
||||
LevelsNeededForUnlock = levelsNeededForUnlock,
|
||||
RewardsNeededForUnlock = rewardsNeededForUnlock,
|
||||
RewardEntryList = new Reward[rewardEntryList.Length]
|
||||
};
|
||||
|
||||
for (var j = 0; j < p.RewardEntryList.Length; j++)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(rewardEntryList[j], out packageIndex) ||
|
||||
!packageIndex.TryGetValue(out FStructFallback battlePassOffer, "BattlePassOffer") ||
|
||||
!battlePassOffer.TryGetValue(out FStructFallback rewardItem, "RewardItem") ||
|
||||
!rewardItem.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
|
||||
!Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
|
||||
|
||||
p.RewardEntryList[j] = new Reward(uObject);
|
||||
}
|
||||
|
||||
_bookXpSchedule[i++] = p;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < paidLevels.Length; i++)
|
||||
{
|
||||
if (!paidLevels[i].TryGetValue(out rewards, "Rewards")) continue;
|
||||
|
||||
foreach (var reward in rewards)
|
||||
{
|
||||
if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
|
||||
!Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
|
||||
|
||||
_bookXpSchedule[i].Add(new Reward(uObject));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Height += 100 * _bookXpSchedule.Count / 10;
|
||||
Height += 100 * _bookXpSchedule.Sum(x => x.RewardEntryList.Length) / _bookXpSchedule.Length;
|
||||
}
|
||||
|
||||
public override SKImage Draw()
|
||||
@@ -151,23 +150,15 @@ namespace FModel.Creator.Bases.FN
|
||||
{
|
||||
var x = 20;
|
||||
var y = _headerHeight + 50;
|
||||
foreach (var (index, reward) in _bookXpSchedule)
|
||||
foreach (var page in _bookXpSchedule)
|
||||
{
|
||||
if (index == 0 || reward.Count == 0 || !reward[0].HasReward())
|
||||
continue;
|
||||
|
||||
c.DrawText(index.ToString(), new SKPoint(x + _DEFAULT_AREA_SIZE / 2, y - 5), _bookPaint);
|
||||
reward[0].DrawSeason(c, x, y, _DEFAULT_AREA_SIZE);
|
||||
|
||||
if (index != 1 && index % 10 == 0)
|
||||
{
|
||||
y += _DEFAULT_AREA_SIZE + 20;
|
||||
x = 20;
|
||||
}
|
||||
else
|
||||
foreach (var reward in page.RewardEntryList)
|
||||
{
|
||||
reward.DrawSeason(c, x, y, _DEFAULT_AREA_SIZE);
|
||||
x += _DEFAULT_AREA_SIZE + 20;
|
||||
}
|
||||
y += _DEFAULT_AREA_SIZE + 20;
|
||||
x = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,13 +120,13 @@ namespace FModel.Creator.Bases.FN
|
||||
_theReward = new BaseIcon(null, EIconStyle.Default);
|
||||
_theReward.Border[0] = SKColor.Parse("FFDB67");
|
||||
_theReward.Background[0] = SKColor.Parse("8F4A20");
|
||||
_theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-FNBR-BattlePoints.T-FNBR-BattlePoints");
|
||||
_theReward.Preview = Utils.GetBitmap("FortniteGame/Content/Athena/UI/Frontend/Art/T_UI_BP_BattleStar_L.T_UI_BP_BattleStar_L");
|
||||
break;
|
||||
case "athenaseasonalxp":
|
||||
_theReward = new BaseIcon(null, EIconStyle.Default);
|
||||
_theReward.Border[0] = SKColor.Parse("E6FDB1");
|
||||
_theReward.Background[0] = SKColor.Parse("51830F");
|
||||
_theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-FNBR-XPMedium.T-FNBR-XPMedium");
|
||||
_theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-FNBR-XPUncommon-L.T-FNBR-XPUncommon-L");
|
||||
break;
|
||||
case "mtxgiveaway":
|
||||
_theReward = new BaseIcon(null, EIconStyle.Default);
|
||||
|
||||
98
FModel/Creator/Bases/SOD2/BaseDecayIcon.cs
Normal file
98
FModel/Creator/Bases/SOD2/BaseDecayIcon.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.SOD2
|
||||
{
|
||||
public class BaseDecayIcon : BaseIcon
|
||||
{
|
||||
private int _maxStackCount;
|
||||
private readonly SKBitmap _backgroundOverlay;
|
||||
|
||||
public BaseDecayIcon(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Margin = 0;
|
||||
Background = new[] {SKColor.Parse("000000"), SKColor.Parse("B24E18")};
|
||||
_backgroundOverlay = Utils.GetBitmap("StateOfDecay2/Content/Art/UI/settings/icon_sod_eagle_09_square.icon_sod_eagle_09_square").Resize(512);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FStructFallback stackingInfo, "StackingInfo") &&
|
||||
stackingInfo.TryGetValue(out int maxStackCount, "MaxStackCount"))
|
||||
_maxStackCount = maxStackCount;
|
||||
|
||||
if (Object.Class.SuperStruct != null && Utils.TryGetPackageIndexExport(Object.Class.SuperStruct, out UObject t))
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback displayInfo, "DisplayInfo"))
|
||||
{
|
||||
if (displayInfo.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
if (displayInfo.TryGetValue(out FText displayDescription, "DisplayDescription"))
|
||||
Description = displayDescription.Text;
|
||||
|
||||
if (displayInfo.TryGetValue(out string iconFills, "IconFills"))
|
||||
Preview = Utils.GetBitmap(iconFills);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
//
|
||||
// ExtraLargeBackpackBase - for extra large
|
||||
// SmallBackpackBase - for small
|
||||
// ExtraSmallBackpackBase - for extra small
|
||||
// MediumBackpackBase - for medium
|
||||
// LargeBackpackBase - for large
|
||||
|
||||
public override SKImage Draw()
|
||||
{
|
||||
using var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
|
||||
return SKImage.FromBitmap(ret);
|
||||
}
|
||||
|
||||
private new void DrawPreview(SKCanvas c)
|
||||
{
|
||||
c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, Margin, Width - Margin, Height - Margin), new SKPaint
|
||||
{
|
||||
// BlendMode = SKBlendMode.SrcATop -- Need Asval's assistance.
|
||||
});
|
||||
}
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, Height / 2), Width / 5 * 2,
|
||||
new[] {Background[0], Background[1]},
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
|
||||
for (var i = 0; i < _backgroundOverlay.Width; i++)
|
||||
for (var j = 0; j < _backgroundOverlay.Height; j++)
|
||||
if (_backgroundOverlay.GetPixel(i, j) == SKColors.Black)
|
||||
{
|
||||
_backgroundOverlay.SetPixel(i, j, SKColors.Transparent);
|
||||
}
|
||||
|
||||
c.DrawBitmap(_backgroundOverlay, new SKRect(Margin, Margin, Width - Margin, Height - Margin), new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
ColorFilter = SKColorFilter.CreateBlendMode(SKColors.Black.WithAlpha(150), SKBlendMode.DstIn),
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0))
|
||||
});
|
||||
c.DrawColor(SKColors.Black.WithAlpha(125), SKBlendMode.DstIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using FModel.Creator.Bases;
|
||||
using FModel.Creator.Bases.BB;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using FModel.Creator.Bases.SB;
|
||||
using FModel.Creator.Bases.SOD2;
|
||||
|
||||
namespace FModel.Creator
|
||||
{
|
||||
@@ -125,7 +126,8 @@ namespace FModel.Creator
|
||||
case "MaterialInstanceConstant"
|
||||
when _object.Owner != null &&
|
||||
(_object.Owner.Name.EndsWith($"/MI_OfferImages/{_object.Name}") ||
|
||||
_object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}")):
|
||||
_object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}") ||
|
||||
_object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}")):
|
||||
creator = new BaseMaterialInstance(_object, _style);
|
||||
return true;
|
||||
case "FortMtxOfferData":
|
||||
@@ -225,10 +227,38 @@ namespace FModel.Creator
|
||||
case "GLeagueDivision":
|
||||
creator = new BaseDivision(_object, EIconStyle.Default);
|
||||
return true;
|
||||
// TODO: Draw this properly
|
||||
// case "GGameModeInfo":
|
||||
// creator = new BaseGameModeInfo(_object, EIconStyle.Default);
|
||||
// return true;
|
||||
// State of Decay 2
|
||||
case "CureItem":
|
||||
case "AmmoItem":
|
||||
case "Pro_Brake_C":
|
||||
case "BackpackItem":
|
||||
case "MagicAmmoItem":
|
||||
case "ConsumableItem":
|
||||
case "MeleeWeaponItem":
|
||||
case "CloseCombatItem":
|
||||
case "FacilityModItem":
|
||||
case "RangedWeaponItem":
|
||||
case "MiscellaneousItem":
|
||||
case "RepairVehicleItem":
|
||||
case "ResourceItemBase_C":
|
||||
case "FuelResourceBase_C":
|
||||
case "MedsResourceBase_C":
|
||||
case "PartsResourceBase_C":
|
||||
case "RangedWeaponModItem":
|
||||
case "VehicleDeliveryItem":
|
||||
case "ConsumableBase_BP_C":
|
||||
case "SmallBackpackBase_C":
|
||||
case "LargeBackpackBase_C":
|
||||
case "MediumBackpackBase_C":
|
||||
case "ConsumableMedsBase_BP_C":
|
||||
case "MaterialsResourceBase_C":
|
||||
case "ExtraLargeBackpackBase_C":
|
||||
case "ExtraSmallBackpackBase_C":
|
||||
case "ConsumableStimsBase_BP_C":
|
||||
case "Consumable_TimedStatBuff_Base_BP_C":
|
||||
case "Consumable_SuspendFatigue_Base_BP_C":
|
||||
creator = new BaseDecayIcon(_object, EIconStyle.Default);
|
||||
return true;
|
||||
default:
|
||||
creator = null;
|
||||
return false;
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Text.RegularExpressions;
|
||||
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.UObject;
|
||||
using CUE4Parse.Utils;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
@@ -38,12 +37,6 @@ namespace FModel.Creator
|
||||
|
||||
public static bool TryGetDisplayAsset(UObject uObject, out SKBitmap preview)
|
||||
{
|
||||
if (uObject.TryGetValue(out FSoftObjectPath displayAsset, "DisplayAssetPath"))
|
||||
{
|
||||
preview = GetDisplayAsset(displayAsset);
|
||||
return preview != null;
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
|
||||
{
|
||||
preview = GetBitmap(sidePanelIcon);
|
||||
@@ -61,35 +54,22 @@ namespace FModel.Creator
|
||||
return preview != null;
|
||||
}
|
||||
|
||||
public static SKBitmap GetDisplayAsset(FSoftObjectPath path)
|
||||
{
|
||||
if (!TryLoadObject(path.AssetPathName.Text, out UObject obj)) return null;
|
||||
|
||||
if (obj.TryGetValue(out FStructFallback type, "DetailsImage") &&
|
||||
type.TryGetValue(out FPackageIndex resource, "ResourceObject") && resource.ResolvedObject?.Outer != null &&
|
||||
!resource.ResolvedObject.Outer.Name.Text.Contains("FortniteGame/Content/Athena/Prototype/Textures/"))
|
||||
{
|
||||
return GetBitmap(resource);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static SKBitmap GetBitmap(FPackageIndex packageIndex)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!TryGetPackageIndexExport(packageIndex, out UExport export)) return null;
|
||||
if (!TryGetPackageIndexExport(packageIndex, out UObject export)) return null;
|
||||
switch (export)
|
||||
{
|
||||
case UTexture2D texture:
|
||||
return GetBitmap(texture);
|
||||
case UMaterialInstanceConstant material:
|
||||
return GetBitmap(material);
|
||||
case UObject uObject:
|
||||
default:
|
||||
{
|
||||
if (uObject.TryGetValue(out FSoftObjectPath previewImage, "LargePreviewImage", "SmallPreviewImage")) return GetBitmap(previewImage);
|
||||
if (uObject.TryGetValue(out string largePreview, "LargePreviewImage")) return GetBitmap(largePreview);
|
||||
if (uObject.TryGetValue(out FPackageIndex smallPreview, "SmallPreviewImage"))
|
||||
if (export.TryGetValue(out FSoftObjectPath previewImage, "LargePreviewImage", "SmallPreviewImage")) return GetBitmap(previewImage);
|
||||
if (export.TryGetValue(out string largePreview, "LargePreviewImage")) return GetBitmap(largePreview);
|
||||
if (export.TryGetValue(out FPackageIndex smallPreview, "SmallPreviewImage"))
|
||||
{
|
||||
packageIndex = smallPreview;
|
||||
continue;
|
||||
@@ -97,8 +77,6 @@ namespace FModel.Creator
|
||||
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +117,7 @@ namespace FModel.Creator
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public static bool TryGetPackageIndexExport<T>(FPackageIndex packageIndex, out T export) where T : UExport
|
||||
public static bool TryGetPackageIndexExport<T>(FPackageIndex packageIndex, out T export) where T : UObject
|
||||
{
|
||||
if (packageIndex.ResolvedObject == null)
|
||||
{
|
||||
@@ -172,19 +150,19 @@ namespace FModel.Creator
|
||||
}
|
||||
|
||||
// fullpath must be either without any extension or with the export objectname
|
||||
public static bool TryLoadObject<T>(string fullPath, out T export) where T : UExport
|
||||
public static bool TryLoadObject<T>(string fullPath, out T export) where T : UObject
|
||||
{
|
||||
return _applicationView.CUE4Parse.Provider.TryLoadObject(fullPath, out export);
|
||||
}
|
||||
|
||||
public static IEnumerable<UExport> LoadExports(string fullPath)
|
||||
public static IEnumerable<UObject> LoadExports(string fullPath)
|
||||
{
|
||||
return _applicationView.CUE4Parse.Provider.LoadObjectExports(fullPath);
|
||||
}
|
||||
|
||||
public static string GetLocalizedResource(string namespacee, string key, string defaultValue)
|
||||
public static string GetLocalizedResource(string @namespace, string key, string defaultValue)
|
||||
{
|
||||
return _applicationView.CUE4Parse.Provider.GetLocalizedString(namespacee, key, defaultValue);
|
||||
return _applicationView.CUE4Parse.Provider.GetLocalizedString(@namespace, key, defaultValue);
|
||||
}
|
||||
|
||||
public static string GetFullPath(string partialPath)
|
||||
|
||||
@@ -88,7 +88,9 @@ namespace FModel
|
||||
[Description("Star Wars: Jedi Fallen Order")]
|
||||
SwGame,
|
||||
[Description("Core")]
|
||||
Platform
|
||||
Platform,
|
||||
[Description("Days Gone")]
|
||||
BendGame
|
||||
}
|
||||
|
||||
public enum ELoadingMode
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>FModel.ico</ApplicationIcon>
|
||||
<Version>4.0.0</Version>
|
||||
<AssemblyVersion>4.0.0.1</AssemblyVersion>
|
||||
<FileVersion>4.0.0.1</FileVersion>
|
||||
<AssemblyVersion>4.0.1.0</AssemblyVersion>
|
||||
<FileVersion>4.0.1.0</FileVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsPublishable>true</IsPublishable>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<StartupObject>FModel.App</StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@@ -71,14 +72,13 @@
|
||||
<None Remove="Resources\Cpp.xshd" />
|
||||
<None Remove="Resources\unix.png" />
|
||||
<None Remove="Resources\linux.png" />
|
||||
<None Remove="Resources\stateofdecay2.png" />
|
||||
<None Remove="Resources\T_Placeholder_Item_Image.png" />
|
||||
<None Remove="Resources\T_ClipSize_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T_DamagePerBullet_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T_ReloadTime_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T-Icon-Pets-64.png" />
|
||||
<None Remove="Resources\T-Icon-Quests-64.png" />
|
||||
<None Remove="Resources\city_pin.png" />
|
||||
<None Remove="Resources\pin.png" />
|
||||
<None Remove="Resources\Default.png" />
|
||||
<None Remove="Resources\NoBackground.png" />
|
||||
<None Remove="Resources\NoText.png" />
|
||||
@@ -162,14 +162,13 @@
|
||||
<Resource Include="Resources\athena.png" />
|
||||
<Resource Include="Resources\unix.png" />
|
||||
<Resource Include="Resources\linux.png" />
|
||||
<Resource Include="Resources\stateofdecay2.png" />
|
||||
<Resource Include="Resources\T_Placeholder_Item_Image.png" />
|
||||
<Resource Include="Resources\T_ClipSize_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T_DamagePerBullet_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T_ReloadTime_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T-Icon-Pets-64.png" />
|
||||
<Resource Include="Resources\T-Icon-Quests-64.png" />
|
||||
<Resource Include="Resources\city_pin.png" />
|
||||
<Resource Include="Resources\pin.png" />
|
||||
<Resource Include="Resources\Default.png" />
|
||||
<Resource Include="Resources\NoBackground.png" />
|
||||
<Resource Include="Resources\NoText.png" />
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FModel.Framework
|
||||
|
||||
protected ViewModelCommand(TContextViewModel contextViewModel)
|
||||
{
|
||||
ContextViewModel = contextViewModel ?? throw new ArgumentNullException(nameof(contextViewModel));
|
||||
ContextViewModel = contextViewModel /*?? throw new ArgumentNullException(nameof(contextViewModel))*/;
|
||||
}
|
||||
|
||||
public sealed override void Execute(object parameter)
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
|
||||
<Setter Property="Title" Value="FModel" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger
|
||||
Binding="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, Converter={x:Static converters:IsNullToBoolReversedConverter.Instance}}"
|
||||
Value="True">
|
||||
<Setter Property="Title"
|
||||
Value="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, StringFormat={}FModel - {0}}" />
|
||||
<DataTrigger Binding="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, Converter={x:Static converters:IsNullToBoolReversedConverter.Instance}}" Value="True">
|
||||
<Setter Property="Title" Value="{Binding DataContext.TitleExtra, RelativeSource={RelativeSource Self}, StringFormat={}FModel - {0}}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
@@ -132,12 +129,12 @@
|
||||
<MenuItem Header="Save Textures" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<!--<MenuItem Header="Save Meshes" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />-->
|
||||
<MenuItem Header="Save Materials" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Save Meshes" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
@@ -438,9 +435,10 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Separator Grid.Row="0" Style="{StaticResource CustomSeparator}"
|
||||
Tag="{Binding SelectedItem.Header, ElementName=AssetsFolderName, FallbackValue='ASSETS LIST', Converter={x:Static converters:FolderToSeparatorTagConverter.Instance}}" />
|
||||
|
||||
<controls:Breadcrumb Grid.Row="0" MaxWidth="{Binding ActualWidth, ElementName=AssetsSearchName}" HorizontalAlignment="Left" Margin="0 5 0 5"
|
||||
DataContext="{Binding SelectedItem.PathAtThisPoint, ElementName=AssetsFolderName, FallbackValue='No/Directory/Detected/In/Folder'}"/>
|
||||
|
||||
<ListBox Grid.Row="1" x:Name="AssetsListName" Style="{StaticResource AssetsListBox}" PreviewMouseDoubleClick="OnAssetsListMouseDoubleClick">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
@@ -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:
|
||||
@@ -236,7 +238,7 @@ namespace FModel
|
||||
|
||||
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not ListBox listBox) return;
|
||||
if (!_applicationView.IsReady || sender is not ListBox listBox) return;
|
||||
UserSettings.Default.LoadingMode = ELoadingMode.Multiple;
|
||||
_applicationView.LoadingModes.LoadCommand.Execute(listBox.SelectedItems);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
BIN
FModel/Resources/stateofdecay2.png
Normal file
BIN
FModel/Resources/stateofdecay2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -22,7 +22,7 @@ namespace FModel.Services
|
||||
|
||||
private readonly Assets _staticAssets = new()
|
||||
{
|
||||
LargeImageKey = "official_logo", SmallImageKey = "verified", SmallImageText = $"v{Constants.APP_VERSION[..5]}"
|
||||
LargeImageKey = "official_logo", SmallImageKey = "verified", SmallImageText = $"v{Constants.APP_VERSION}"
|
||||
};
|
||||
|
||||
private readonly Button[] _buttons =
|
||||
@@ -38,7 +38,7 @@ namespace FModel.Services
|
||||
Assets = _staticAssets,
|
||||
Timestamps = _timestamps,
|
||||
Buttons = _buttons,
|
||||
State = $"{game.GetDescription()} - Idling"
|
||||
Details = $"{game.GetDescription()} - Idling"
|
||||
};
|
||||
|
||||
_client.OnReady += (_, args) => Log.Information("{Username}#{Discriminator} ({UserId}) is now ready", args.User.Username, args.User.Discriminator, args.User.ID);
|
||||
@@ -60,7 +60,7 @@ namespace FModel.Services
|
||||
_client.Invoke();
|
||||
}
|
||||
|
||||
public void UpdateButDontSavePresence(string details, string state)
|
||||
public void UpdateButDontSavePresence(string details = null, string state = null)
|
||||
{
|
||||
if (!_client.IsInitialized) return;
|
||||
_client.SetPresence(new RichPresence
|
||||
@@ -68,8 +68,8 @@ namespace FModel.Services
|
||||
Assets = _staticAssets,
|
||||
Timestamps = _timestamps,
|
||||
Buttons = _buttons,
|
||||
Details = details,
|
||||
State = state
|
||||
Details = details ?? _currentPresence.Details,
|
||||
State = state ?? _currentPresence.State
|
||||
});
|
||||
_client.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows.Input;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.Framework;
|
||||
using FModel.ViewModels;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
@@ -132,7 +134,7 @@ namespace FModel.Settings
|
||||
set => SetProperty(ref _loadingMode, value);
|
||||
}
|
||||
|
||||
private EUpdateMode _updateMode = EUpdateMode.Stable;
|
||||
private EUpdateMode _updateMode = EUpdateMode.Beta;
|
||||
public EUpdateMode UpdateMode
|
||||
{
|
||||
get => _updateMode;
|
||||
@@ -205,12 +207,13 @@ namespace FModel.Settings
|
||||
{FGame.Dungeons, EGame.GAME_UE4_LATEST},
|
||||
{FGame.WorldExplorers, EGame.GAME_UE4_LATEST},
|
||||
{FGame.g3, EGame.GAME_UE4_22},
|
||||
{FGame.StateOfDecay2, EGame.GAME_UE4_LATEST},
|
||||
{FGame.StateOfDecay2, EGame.GAME_SOD2},
|
||||
{FGame.Prospect, EGame.GAME_UE4_LATEST},
|
||||
{FGame.Indiana, EGame.GAME_UE4_LATEST},
|
||||
{FGame.RogueCompany, EGame.GAME_UE4_LATEST},
|
||||
{FGame.SwGame, EGame.GAME_UE4_LATEST},
|
||||
{FGame.Platform, EGame.GAME_UE4_24}
|
||||
{FGame.Platform, EGame.GAME_UE4_25},
|
||||
{FGame.BendGame, EGame.GAME_UE4_11}
|
||||
};
|
||||
public IDictionary<FGame, EGame> OverridedGame
|
||||
{
|
||||
@@ -233,7 +236,8 @@ namespace FModel.Settings
|
||||
{FGame.Indiana, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.RogueCompany, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.SwGame, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.Platform, UE4Version.VER_UE4_DETERMINE_BY_GAME}
|
||||
{FGame.Platform, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.BendGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}
|
||||
};
|
||||
public IDictionary<FGame, UE4Version> OverridedUEVersion
|
||||
{
|
||||
@@ -292,7 +296,8 @@ namespace FModel.Settings
|
||||
{FGame.Indiana, new List<CustomDirectory>()},
|
||||
{FGame.RogueCompany, new List<CustomDirectory>()},
|
||||
{FGame.SwGame, new List<CustomDirectory>()},
|
||||
{FGame.Platform, new List<CustomDirectory>()}
|
||||
{FGame.Platform, new List<CustomDirectory>()},
|
||||
{FGame.BendGame, new List<CustomDirectory>()}
|
||||
};
|
||||
public IDictionary<FGame, IList<CustomDirectory>> CustomDirectories
|
||||
{
|
||||
@@ -300,7 +305,7 @@ namespace FModel.Settings
|
||||
set => SetProperty(ref _customDirectories, value);
|
||||
}
|
||||
|
||||
private DateTime _lastAesReload = DateTime.Today;
|
||||
private DateTime _lastAesReload = DateTime.Today.AddDays(-1);
|
||||
public DateTime LastAesReload
|
||||
{
|
||||
get => _lastAesReload;
|
||||
@@ -425,5 +430,26 @@ namespace FModel.Settings
|
||||
get => _nextAudio;
|
||||
set => SetProperty(ref _nextAudio, value);
|
||||
}
|
||||
|
||||
private EMeshFormat _meshExportFormat = EMeshFormat.ActorX;
|
||||
public EMeshFormat MeshExportFormat
|
||||
{
|
||||
get => _meshExportFormat;
|
||||
set => SetProperty(ref _meshExportFormat, value);
|
||||
}
|
||||
|
||||
private ELodFormat _lodExportFormat = ELodFormat.FirstLod;
|
||||
public ELodFormat LodExportFormat
|
||||
{
|
||||
get => _lodExportFormat;
|
||||
set => SetProperty(ref _lodExportFormat, value);
|
||||
}
|
||||
|
||||
private ETextureFormat _textureExportFormat = ETextureFormat.Png;
|
||||
public ETextureFormat TextureExportFormat
|
||||
{
|
||||
get => _textureExportFormat;
|
||||
set => SetProperty(ref _textureExportFormat, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,28 +64,35 @@ namespace FModel.ViewModels
|
||||
|
||||
_keysFromSettings.MainKey = key;
|
||||
}
|
||||
else if (!_keysFromSettings.HasDynamicKeys)
|
||||
{
|
||||
HasChange = true;
|
||||
_keysFromSettings.DynamicKeys = new List<DynamicKey>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Key = key,
|
||||
FileName = collection[e.CollectionIndex].Name,
|
||||
Guid = collection[e.CollectionIndex].Guid.ToString()
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (_keysFromSettings.DynamicKeys.FirstOrDefault(x => x.Guid == collection[e.CollectionIndex].Guid.ToString()) is { } d)
|
||||
{
|
||||
if (!HasChange)
|
||||
HasChange = FixKey(d.Key) != key;
|
||||
|
||||
d.Key = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_keysFromSettings.HasDynamicKeys)
|
||||
HasChange = true;
|
||||
_keysFromSettings.DynamicKeys.Add(new DynamicKey
|
||||
{
|
||||
HasChange = true;
|
||||
_keysFromSettings.DynamicKeys = new[]
|
||||
{
|
||||
new DynamicKey
|
||||
{
|
||||
Key = key,
|
||||
FileName = collection[e.CollectionIndex].Name,
|
||||
Guid = collection[e.CollectionIndex].Guid.ToString()
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (_keysFromSettings.DynamicKeys.FirstOrDefault(x => x.Guid == collection[e.CollectionIndex].Guid.ToString()) is { } d)
|
||||
{
|
||||
if (!HasChange)
|
||||
HasChange = FixKey(d.Key) != key;
|
||||
|
||||
d.Key = key;
|
||||
}
|
||||
Key = key,
|
||||
FileName = collection[e.CollectionIndex].Name,
|
||||
Guid = collection[e.CollectionIndex].Guid.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AutoUpdaterDotNET;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
@@ -107,13 +108,16 @@ namespace FModel.ViewModels.ApiEndpoints
|
||||
|
||||
private void CheckForUpdateEvent(UpdateInfoEventArgs args)
|
||||
{
|
||||
if (args != null)
|
||||
if (args is {CurrentVersion: { }})
|
||||
{
|
||||
if (!args.IsUpdateAvailable) return;
|
||||
var currentVersion = new Version(args.CurrentVersion);
|
||||
if (currentVersion == args.InstalledVersion) return;
|
||||
|
||||
var downgrade = currentVersion < args.InstalledVersion;
|
||||
var messageBox = new MessageBoxModel
|
||||
{
|
||||
Text = $"FModel {args.CurrentVersion} is available. You are using version {args.InstalledVersion}. Do you want to update the application now?",
|
||||
Caption = "Update Available",
|
||||
Text = $"The latest version of FModel {UserSettings.Default.UpdateMode} is {args.CurrentVersion}. You are using version {args.InstalledVersion}. Do you want to {(downgrade ? "downgrade" : "update")} the application now?",
|
||||
Caption = $"{(downgrade ? "Downgrade" : "Update")} Available",
|
||||
Icon = MessageBoxImage.Question,
|
||||
Buttons = MessageBoxButtons.YesNo(),
|
||||
IsSoundEnabled = false
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using J = Newtonsoft.Json.JsonPropertyAttribute;
|
||||
|
||||
namespace FModel.ViewModels.ApiEndpoints.Models
|
||||
@@ -8,9 +9,9 @@ namespace FModel.ViewModels.ApiEndpoints.Models
|
||||
{
|
||||
[J("version")] public string Version { get; private set; }
|
||||
[J("mainKey")] public string MainKey { get; set; }
|
||||
[J("dynamicKeys")] public DynamicKey[] DynamicKeys { get; set; }
|
||||
[J("dynamicKeys")] public List<DynamicKey> DynamicKeys { get; set; }
|
||||
|
||||
public bool HasDynamicKeys => DynamicKeys is {Length: > 0};
|
||||
public bool HasDynamicKeys => DynamicKeys is {Count: > 0};
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{" + nameof(Key) + "}")]
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace FModel.ViewModels
|
||||
set
|
||||
{
|
||||
SetProperty(ref _status, value);
|
||||
RaisePropertyChanged(nameof(TitleExtra));
|
||||
IsReady = Status != EStatusKind.Loading && Status != EStatusKind.Stopping;
|
||||
}
|
||||
}
|
||||
@@ -105,6 +104,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();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Windows.Data;
|
||||
using CSCore.CoreAudioAPI;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.Commands;
|
||||
using FModel.Views.Resources.Controls;
|
||||
@@ -154,6 +155,7 @@ namespace FModel.ViewModels
|
||||
|
||||
public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
||||
{
|
||||
private DiscordHandler _discordHandler => DiscordService.DiscordHandler;
|
||||
private static IWaveSource _waveSource;
|
||||
private static ISoundOut _soundOut;
|
||||
private Timer _sourceTimer;
|
||||
@@ -385,6 +387,7 @@ namespace FModel.ViewModels
|
||||
public void Play()
|
||||
{
|
||||
if (_soundOut == null || IsPlaying) return;
|
||||
_discordHandler.UpdateButDontSavePresence(null, $"Audio Player: {PlayedFile.FileName} ({PlayedFile.Duration:g})");
|
||||
_soundOut.Play();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using AdonisUI.Controls;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
@@ -15,13 +16,19 @@ using CUE4Parse.MappingsProvider;
|
||||
using CUE4Parse.UE4.AssetRegistry;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Sound;
|
||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.Localization;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Oodle.Objects;
|
||||
using CUE4Parse.UE4.Shaders;
|
||||
using CUE4Parse.UE4.Wwise;
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse_Conversion.Materials;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Sounds;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using EpicManifestParser.Objects;
|
||||
@@ -81,9 +88,18 @@ namespace FModel.ViewModels
|
||||
}
|
||||
default:
|
||||
{
|
||||
Game = gameDirectory.SubstringBeforeLast("\\Content\\Paks").SubstringAfterLast("\\").ToEnum(FGame.Unknown);
|
||||
Provider = new DefaultFileProvider(gameDirectory, SearchOption.TopDirectoryOnly, true,
|
||||
UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game]);
|
||||
Game = gameDirectory.SubstringBeforeLast("\\Content").SubstringAfterLast("\\").ToEnum(FGame.Unknown);
|
||||
|
||||
if (Game == FGame.StateOfDecay2)
|
||||
Provider = new DefaultFileProvider(new DirectoryInfo(gameDirectory), new List<DirectoryInfo>
|
||||
{
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\Paks"),
|
||||
new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\DisabledPaks")
|
||||
},
|
||||
SearchOption.AllDirectories, true, UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game]);
|
||||
else
|
||||
Provider = new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, true, UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -202,7 +218,7 @@ namespace FModel.ViewModels
|
||||
foreach (var file in GameDirectory.DirectoryFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!(Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is { } vfs))
|
||||
if (Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is not { } vfs)
|
||||
continue;
|
||||
|
||||
file.IsEnabled = true;
|
||||
@@ -232,7 +248,7 @@ namespace FModel.ViewModels
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
var aes = _apiEndpointView.BenbotApi.GetAesKeys(cancellationToken);
|
||||
if (aes == null) return;
|
||||
if (aes?.MainKey == null && aes?.DynamicKeys == null && aes?.Version == null) return;
|
||||
|
||||
UserSettings.Default.AesKeys[Game] = aes;
|
||||
});
|
||||
@@ -381,6 +397,7 @@ namespace FModel.ViewModels
|
||||
{
|
||||
case "ini":
|
||||
case "txt":
|
||||
case "log":
|
||||
case "po":
|
||||
case "bat":
|
||||
case "xml":
|
||||
@@ -466,14 +483,15 @@ namespace FModel.ViewModels
|
||||
TabControl.SelectedTab.Image = null;
|
||||
if (Provider.TryCreateReader(fullPath, out var archive))
|
||||
{
|
||||
var header = new FDictionaryHeader(archive);
|
||||
var header = new FOodleDictionaryArchive(archive).Header;
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case "png":
|
||||
case "jpg":
|
||||
case "bmp":
|
||||
{
|
||||
if (Provider.TrySaveAsset(fullPath, out var data))
|
||||
{
|
||||
@@ -488,8 +506,18 @@ namespace FModel.ViewModels
|
||||
FLogger.AppendText($"Export '{fullPath.SubstringAfterLast('/')}' and change its extension if you want it to be an installable font file", Constants.WHITE, true);
|
||||
break;
|
||||
case "ushaderbytecode":
|
||||
case "ushadercode":
|
||||
{
|
||||
TabControl.SelectedTab.Image = null;
|
||||
|
||||
if (Provider.TryCreateReader(fullPath, out var archive))
|
||||
{
|
||||
var ar = new FShaderCodeArchive(archive);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), bulkSave);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var exports = Provider.LoadObjectExports(fullPath);
|
||||
@@ -522,13 +550,19 @@ namespace FModel.ViewModels
|
||||
ExportData(fullPath);
|
||||
}
|
||||
|
||||
private bool CheckExport(UExport export) // return if this loads an image
|
||||
private bool CheckExport(UObject export) // return if this loads an image
|
||||
{
|
||||
switch (export)
|
||||
{
|
||||
case UTexture2D texture:
|
||||
{
|
||||
SetImage(texture.Decode());
|
||||
var bNearest = false;
|
||||
if (texture.TryGetValue(out FName trigger, "LODGroup", "Filter") && !trigger.IsNone)
|
||||
bNearest = trigger.Text.EndsWith("TEXTUREGROUP_Pixels2D", StringComparison.OrdinalIgnoreCase) ||
|
||||
trigger.Text.EndsWith("TF_Nearest", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
TabControl.SelectedTab.ImageRender = bNearest ? BitmapScalingMode.NearestNeighbor : BitmapScalingMode.Linear;
|
||||
SetImage(texture.Decode(bNearest));
|
||||
return true;
|
||||
}
|
||||
case UAkMediaAssetData:
|
||||
@@ -542,30 +576,30 @@ namespace FModel.ViewModels
|
||||
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
|
||||
return false;
|
||||
}
|
||||
case UMaterialInterface material when UserSettings.Default.IsAutoSaveMaterials:
|
||||
case UMaterialInterface when UserSettings.Default.IsAutoSaveMaterials:
|
||||
case UStaticMesh when UserSettings.Default.IsAutoSaveMeshes:
|
||||
case USkeletalMesh when UserSettings.Default.IsAutoSaveMeshes:
|
||||
{
|
||||
var mat = new MaterialExporter(material);
|
||||
if (mat.TryWriteTo(
|
||||
Path.Combine(UserSettings.Default.OutputDirectory, "Saves"),
|
||||
Path.Combine(UserSettings.Default.OutputDirectory, "Textures"),
|
||||
out var savedFileName))
|
||||
var toSave = new Exporter(export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat);
|
||||
var toSaveDirectory = new DirectoryInfo(Path.Combine(UserSettings.Default.OutputDirectory, "Saves"));
|
||||
if (toSave.TryWriteToDir(toSaveDirectory, out var savedFileName))
|
||||
{
|
||||
Log.Information("{FileName} successfully saved", savedFileName);
|
||||
Log.Information("Successfully saved {FileName}", savedFileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved '{savedFileName}'", Constants.WHITE, true);
|
||||
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", savedFileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not saved '{savedFileName}'", Constants.WHITE, true);
|
||||
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
case UObject uObject:
|
||||
default:
|
||||
{
|
||||
using var package = new CreatorPackage(uObject, UserSettings.Default.CosmeticStyle);
|
||||
using var package = new CreatorPackage(export, UserSettings.Default.CosmeticStyle);
|
||||
if (!package.TryConstructCreator(out var creator)) return false;
|
||||
|
||||
creator.ParseForInfo();
|
||||
@@ -573,8 +607,6 @@ namespace FModel.ViewModels
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetImage(SKImage img)
|
||||
@@ -635,7 +667,7 @@ namespace FModel.ViewModels
|
||||
Directory.CreateDirectory(path.SubstringBeforeLast('/'));
|
||||
File.WriteAllBytes(path, kvp.Value);
|
||||
}
|
||||
|
||||
|
||||
Log.Information("{FileName} successfully exported", fileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully exported '{fileName}'", Constants.WHITE, true);
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace FModel.ViewModels.Commands
|
||||
{
|
||||
foreach (var folder in root)
|
||||
{
|
||||
if (!folder.Header.Equals(folders[i])) continue;
|
||||
if (!folder.Header.Equals(folders[i], i == 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
folder.IsExpanded = true; // folder found = expand
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Framework;
|
||||
using FModel.Views.Resources.Controls;
|
||||
@@ -21,14 +22,16 @@ namespace FModel.ViewModels.Commands
|
||||
{
|
||||
Helper.OpenWindow<AdonisWindow>(contextViewModel.Header + " (Image)", () =>
|
||||
{
|
||||
new ImagePopout
|
||||
var popout = new ImagePopout
|
||||
{
|
||||
Title = contextViewModel.Header + " (Image)",
|
||||
Width = contextViewModel.Image.Width,
|
||||
Height = contextViewModel.Image.Height,
|
||||
WindowState = contextViewModel.Image.Height > 1000 ? WindowState.Maximized : WindowState.Normal,
|
||||
ImageCtrl = {Source = contextViewModel.Image}
|
||||
}.Show();
|
||||
};
|
||||
RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, contextViewModel.ImageRender);
|
||||
popout.Show();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ namespace FModel.ViewModels.Commands
|
||||
|
||||
public override async void Execute(LoadingModesViewModel contextViewModel, object parameter)
|
||||
{
|
||||
if (_applicationView.CUE4Parse.GameDirectory.HasNoFile) return;
|
||||
if (_applicationView.CUE4Parse.Game == FGame.FortniteGame &&
|
||||
_applicationView.CUE4Parse.Provider.MappingsContainer == null)
|
||||
{
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText("Mappings could not get pulled, extracting assets might not work properly. If so, press F12 or please restart.", Constants.WHITE, true);
|
||||
}
|
||||
#if DEBUG
|
||||
var loadingTime = Stopwatch.StartNew();
|
||||
#endif
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace FModel.ViewModels
|
||||
|
||||
public class GameDirectoryViewModel : ViewModel
|
||||
{
|
||||
public bool HasNoFile => DirectoryFiles.Count < 1;
|
||||
public readonly ObservableCollection<FileItem> DirectoryFiles;
|
||||
public ICollectionView DirectoryFilesView { get; }
|
||||
|
||||
|
||||
@@ -55,11 +55,12 @@ namespace FModel.ViewModels
|
||||
yield return GetUnrealEngineGame("Rosemallow", "\\Indiana\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("Catnip", "\\OakGame\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("AzaleaAlpha", "\\Prospect\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("WorldExplorersLive", "\\WorldExplorers\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("WorldExplorersLive", "\\WorldExplorers\\Content");
|
||||
yield return GetUnrealEngineGame("Newt", "\\g3\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("shoebill", "\\SwGame\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("Snoek", "\\StateOfDecay2\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508", "\\Core\\Platform\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("Nebula", "\\BendGame\\Content");
|
||||
yield return GetRiotGame("VALORANT", "ShooterGame\\Content\\Paks");
|
||||
yield return new DetectedGame {GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER};
|
||||
yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks");
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
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;
|
||||
@@ -18,118 +21,278 @@ 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;
|
||||
|
||||
private bool _showCities;
|
||||
public bool ShowCities
|
||||
#region BINDINGS
|
||||
|
||||
private bool _brPois;
|
||||
public bool BrPois
|
||||
{
|
||||
get => _showCities;
|
||||
set => SetProperty(ref _showCities, value, nameof(ShowCities));
|
||||
get => _brPois;
|
||||
set => SetProperty(ref _brPois, value, "ApolloGameplay_MapPois");
|
||||
}
|
||||
|
||||
private bool _showLandmarks;
|
||||
public bool ShowLandmarks
|
||||
private bool _brLandmarks;
|
||||
public bool BrLandmarks
|
||||
{
|
||||
get => _showLandmarks;
|
||||
set => SetProperty(ref _showLandmarks, value, nameof(ShowLandmarks));
|
||||
get => _brLandmarks;
|
||||
set => SetProperty(ref _brLandmarks, value, "ApolloGameplay_MapLandmarks");
|
||||
}
|
||||
|
||||
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 _showPatrolPaths;
|
||||
public bool ShowPatrolPaths
|
||||
private bool _brTagsLocation;
|
||||
public bool BrTagsLocation
|
||||
{
|
||||
get => _showPatrolPaths;
|
||||
set => SetProperty(ref _showPatrolPaths, value, nameof(ShowPatrolPaths));
|
||||
get => _brTagsLocation;
|
||||
set => SetProperty(ref _brTagsLocation, value, "ApolloGameplay_TagsLocation");
|
||||
}
|
||||
|
||||
private bool _brAlienArtifacts;
|
||||
public bool BrAlienArtifacts
|
||||
{
|
||||
get => _brAlienArtifacts;
|
||||
set => SetProperty(ref _brAlienArtifacts, value, "ApolloGameplay_AlienArtifacts");
|
||||
}
|
||||
|
||||
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 _citiesImage;
|
||||
public BitmapImage CitiesImage
|
||||
{
|
||||
get => _citiesImage;
|
||||
set => SetProperty(ref _citiesImage, value);
|
||||
}
|
||||
|
||||
private BitmapImage _landmarksImage;
|
||||
public BitmapImage LandmarksImage
|
||||
private BitmapImage _brLayerImage;
|
||||
private BitmapImage _prLayerImage;
|
||||
private BitmapImage _layerImage;
|
||||
public BitmapImage LayerImage
|
||||
{
|
||||
get => _landmarksImage;
|
||||
set => SetProperty(ref _landmarksImage, value);
|
||||
get => _layerImage;
|
||||
set => SetProperty(ref _layerImage, value);
|
||||
}
|
||||
|
||||
private BitmapImage _patrolPathImage;
|
||||
public BitmapImage PatrolPathImage
|
||||
private const int _widthHeight = 2048;
|
||||
private const int _brRadius = 135000;
|
||||
private const int _prRadius = 51000;
|
||||
private int _mapIndex;
|
||||
public int MapIndex // 0 is BR, 1 is PR
|
||||
{
|
||||
get => _patrolPathImage;
|
||||
set => SetProperty(ref _patrolPathImage, value);
|
||||
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);
|
||||
if (MapImage == null && _mapBitmap == null)
|
||||
{
|
||||
await LoadMiniMap();
|
||||
}
|
||||
_textPaint.Typeface = _fillPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName;
|
||||
await LoadBrMiniMap();
|
||||
await LoadPrMiniMap();
|
||||
TriggerChange();
|
||||
}
|
||||
|
||||
public BitmapImage GetImageToSave()
|
||||
public BitmapImage GetImageToSave() => GetImageSource(GetLayerBitmap(true));
|
||||
private SKBitmap GetLayerBitmap(bool withMap)
|
||||
{
|
||||
var ret = new SKBitmap(_mapBitmap.Width, _mapBitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
var ret = new SKBitmap(_widthHeight, _widthHeight, 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);
|
||||
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 GetImageSource(ret);
|
||||
}
|
||||
|
||||
protected override bool SetProperty<T>(ref T storage, T value, string propertyName = null)
|
||||
{
|
||||
var ret = base.SetProperty(ref storage, value, propertyName);
|
||||
CheckForStuffToDraw(propertyName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private async void CheckForStuffToDraw(string propertyName = null)
|
||||
protected override bool SetProperty<T>(ref T storage, T value, string propertyName = null) // don't delete, else nothing will update for some reason
|
||||
{
|
||||
switch (propertyName)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
layer.IsEnabled = enabled;
|
||||
}
|
||||
else if (enabled) // load layer
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "ApolloGameplay_MapPois":
|
||||
case "ApolloGameplay_MapLandmarks":
|
||||
case "PapayaGameplay_MapLandmarks":
|
||||
await LoadQuestIndicatorData();
|
||||
break;
|
||||
case "ApolloGameplay_PatrolsPath":
|
||||
await LoadPatrolsPath();
|
||||
break;
|
||||
case "ApolloGameplay_UpgradeBenches":
|
||||
await LoadUpgradeBenches();
|
||||
break;
|
||||
case "ApolloGameplay_Phonebooths":
|
||||
await LoadPhonebooths();
|
||||
break;
|
||||
case "ApolloGameplay_AlienArtifacts":
|
||||
await LoadAlienArtifacts();
|
||||
break;
|
||||
case "ApolloGameplay_TagsLocation":
|
||||
await LoadTagsLocation();
|
||||
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 LoadVendingMachines();
|
||||
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)
|
||||
@@ -145,37 +308,58 @@ namespace FModel.ViewModels
|
||||
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()
|
||||
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 = 25,
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(4, 4, 8, 8, SKColors.Black)
|
||||
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 = 10, Color = SKColors.Red,
|
||||
Style = SKPaintStyle.Stroke, StrokeWidth = 5, Color = SKColors.Red,
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(4, 4, 8, 8, SKColors.Black)
|
||||
};
|
||||
|
||||
private FVector2D GetMapPosition(FVector vector)
|
||||
private FVector2D GetMapPosition(FVector vector, int mapRadius)
|
||||
{
|
||||
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) * _widthHeight;
|
||||
var ny = (1 - (vector.X + mapRadius) / (mapRadius * 2)) * _widthHeight;
|
||||
return new FVector2D(nx, ny);
|
||||
}
|
||||
|
||||
private async Task LoadMiniMap()
|
||||
|
||||
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) ||
|
||||
@@ -183,21 +367,39 @@ 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][_FIRST_BITMAP] = new MapLayer{Layer = Utils.GetBitmap(textureValues[0]), IsEnabled = true};
|
||||
_brMiniMapImage = GetImageSource(_bitmaps[0][_FIRST_BITMAP].Layer);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadCities()
|
||||
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.ReferencedTextures.Count < 1) return;
|
||||
|
||||
_bitmaps[1][_FIRST_BITMAP] = new MapLayer{Layer = Utils.GetBitmap(mapMaterial.ReferencedTextures[0] as UTexture2D), IsEnabled = true};
|
||||
_prMiniMapImage = GetImageSource(_bitmaps[1][_FIRST_BITMAP].Layer);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadQuestIndicatorData()
|
||||
{
|
||||
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);
|
||||
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"))
|
||||
{
|
||||
@@ -205,86 +407,435 @@ namespace FModel.ViewModels
|
||||
{
|
||||
if (!poiData.TryGetValue(out FSoftObjectPath discoveryQuest, "DiscoveryQuest") ||
|
||||
!poiData.TryGetValue(out FText text, "Text") || string.IsNullOrEmpty(text.Text) ||
|
||||
!poiData.TryGetValue(out FVector worldLocation, "WorldLocation")) continue;
|
||||
!poiData.TryGetValue(out FVector worldLocation, "WorldLocation") ||
|
||||
!poiData.TryGetValue(out FName discoverBackend, "DiscoverObjectiveBackendName")) continue;
|
||||
|
||||
var shaper = new CustomSKShaper(_imagePaint.Typeface);
|
||||
var shapedText = shaper.Shape(text.Text, _imagePaint);
|
||||
var vector = GetMapPosition(worldLocation);
|
||||
var shaper = new CustomSKShaper(_textPaint.Typeface);
|
||||
var shapedText = shaper.Shape(text.Text, _textPaint);
|
||||
|
||||
if (discoveryQuest.AssetPathName.Text.Contains("landmarks", StringComparison.OrdinalIgnoreCase))
|
||||
if (discoverBackend.Text.Contains("papaya", 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);
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var vector = GetMapPosition(worldLocation, _prRadius);
|
||||
prLandmarks.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
prLandmarks.DrawShapedText(shaper, text.Text, vector.X - shapedText.Points[^1].X / 2, vector.Y - 12.5F, _fillPaint);
|
||||
prLandmarks.DrawShapedText(shaper, text.Text, vector.X - shapedText.Points[^1].X / 2, 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 - shapedText.Points[^1].X / 2, vector.Y - 12.5F, _fillPaint);
|
||||
brLandmarks.DrawShapedText(shaper, text.Text, vector.X - shapedText.Points[^1].X / 2, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
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);
|
||||
_fillPaint.StrokeWidth = 10;
|
||||
var vector = GetMapPosition(worldLocation, _brRadius);
|
||||
pois.DrawShapedText(shaper, text.Text.ToUpperInvariant(), vector.X - shapedText.Points[^1].X / 2, vector.Y, _fillPaint);
|
||||
pois.DrawShapedText(shaper, text.Text.ToUpperInvariant(), vector.X - shapedText.Points[^1].X / 2, vector.Y, _textPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CitiesImage = GetImageSource(_citiesBitmap);
|
||||
LandmarksImage = GetImageSource(_landmarksBitmap);
|
||||
_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 LoadPatrolPaths()
|
||||
|
||||
private async Task LoadPatrolsPath()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_patrolPathBitmap = new SKBitmap(_mapBitmap.Width, _mapBitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(_patrolPathBitmap);
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var patrolsPathBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(patrolsPathBitmap);
|
||||
|
||||
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)
|
||||
var exports = Utils.LoadExports("/NPCLibrary/LevelOverlays/Apollo_Terrain_NPCLibraryS17_Overlay");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (!overlay.TryGetValue(out FSoftObjectPath overlayWorld, "OverlayWorld"))
|
||||
continue;
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("FortAthenaPatrolPath", StringComparison.OrdinalIgnoreCase) ||
|
||||
!uObject.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags") ||
|
||||
!uObject.TryGetValue(out FPackageIndex[] patrolPoints, "PatrolPoints")) continue;
|
||||
|
||||
var exports = Utils.LoadExports(overlayWorld.AssetPathName.Text.SubstringBeforeLast("."));
|
||||
foreach (var export in exports)
|
||||
var displayName = gameplayTags.GameplayTags[0].Text.SubstringAfterLast(".");
|
||||
if (displayName.Equals("Generic", StringComparison.OrdinalIgnoreCase)) 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, _brRadius);
|
||||
path.MoveTo(vector.X, vector.Y);
|
||||
|
||||
for (var i = 1; i < patrolPoints.Length; i++)
|
||||
{
|
||||
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") ||
|
||||
if (!Utils.TryGetPackageIndexExport(patrolPoints[i], out uObject) ||
|
||||
!uObject.TryGetValue(out rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out uObject) ||
|
||||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
!uObject.TryGetValue(out 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);
|
||||
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);
|
||||
}
|
||||
|
||||
PatrolPathImage = GetImageSource(_patrolPathBitmap);
|
||||
_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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_CannonballGame_Target_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("CannonballGame_VehicleSpawner_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_Waypoint_Papaya_Skydive_Start_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!uObject.TryGetValue(out FText minigameActivityName, "MinigameActivityName") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("PapayaShootingTarget_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_Waypoint_Parent_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.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 || uObject.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 (uObject.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 LoadVendingMachines()
|
||||
{
|
||||
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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("B_Papaya_VendingMachine_Boat_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_BoogieBomb_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Burger_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_CrashPad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_FishingPole_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Grappler_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Jetpack_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintGrenade_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Blue_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PaintLauncher_Red_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_PlungerBow_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Quad_C", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uObject.ExportType.Equals("B_Papaya_VendingMachine_Tomato_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject root) ||
|
||||
!root.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var name = uObject.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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("MusicBlock_Piano3_Papaya_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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/Apollo_Terrain_NPCLibrary_Stations_UpgradeBenches");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("B_Athena_Spawner_UpgradeStation_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = uObject.Name["B_Athena_Spawner_".Length..];
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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 is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("B_Athena_Spawner_Payphone_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = uObject.Name["B_Athena_Spawner_".Length..];
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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 LoadAlienArtifacts()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var alienArtifactsBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(alienArtifactsBitmap);
|
||||
|
||||
var exports = Utils.LoadExports("/BattlepassS17/Maps/Apollo_ItemCollect_S17_Overlay");
|
||||
foreach (var export in exports)
|
||||
{
|
||||
if (export is not { } uObject) continue;
|
||||
if (!uObject.ExportType.Equals("BP_S17_AlienArtifact_Variant1_C", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
var displayName = uObject.Name;
|
||||
|
||||
if (!uObject.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out 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_AlienArtifacts"] = new MapLayer {Layer = alienArtifactsBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadTagsLocation()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
var tagsLocationBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(tagsLocationBitmap);
|
||||
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Quests/QuestTagToLocationDataRows.QuestTagToLocationDataRows", out UDataTable locationData))
|
||||
return;
|
||||
|
||||
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};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
@@ -82,6 +84,27 @@ namespace FModel.ViewModels
|
||||
get => _selectedCosmeticDisplayAsset;
|
||||
set => SetProperty(ref _selectedCosmeticDisplayAsset, value);
|
||||
}
|
||||
|
||||
private EMeshFormat _selectedMeshExportFormat;
|
||||
public EMeshFormat SelectedMeshExportFormat
|
||||
{
|
||||
get => _selectedMeshExportFormat;
|
||||
set => SetProperty(ref _selectedMeshExportFormat, value);
|
||||
}
|
||||
|
||||
private ELodFormat _selectedLodExportFormat;
|
||||
public ELodFormat SelectedLodExportFormat
|
||||
{
|
||||
get => _selectedLodExportFormat;
|
||||
set => SetProperty(ref _selectedLodExportFormat, value);
|
||||
}
|
||||
|
||||
private ETextureFormat _selectedTextureExportFormat;
|
||||
public ETextureFormat SelectedTextureExportFormat
|
||||
{
|
||||
get => _selectedTextureExportFormat;
|
||||
set => SetProperty(ref _selectedTextureExportFormat, value);
|
||||
}
|
||||
|
||||
public ReadOnlyObservableCollection<EUpdateMode> UpdateModes { get; private set; }
|
||||
public ReadOnlyObservableCollection<EGame> UeGames { get; private set; }
|
||||
@@ -93,6 +116,9 @@ namespace FModel.ViewModels
|
||||
public ReadOnlyObservableCollection<ECompressedAudio> CompressedAudios { get; private set; }
|
||||
public ReadOnlyObservableCollection<EIconStyle> CosmeticStyles { get; private set; }
|
||||
public ReadOnlyObservableCollection<EEnabledDisabled> CosmeticDisplayAssets { get; private set; }
|
||||
public ReadOnlyObservableCollection<EMeshFormat> MeshExportFormats { get; private set; }
|
||||
public ReadOnlyObservableCollection<ELodFormat> LodExportFormats { get; private set; }
|
||||
public ReadOnlyObservableCollection<ETextureFormat> TextureExportFormats { get; private set; }
|
||||
|
||||
private readonly FGame _game;
|
||||
private string _outputSnapshot;
|
||||
@@ -105,6 +131,9 @@ namespace FModel.ViewModels
|
||||
private ECompressedAudio _compressedAudioSnapshot;
|
||||
private EIconStyle _cosmeticStyleSnapshot;
|
||||
private EEnabledDisabled _cosmeticDisplayAssetSnapshot;
|
||||
private EMeshFormat _meshExportFormatSnapshot;
|
||||
private ELodFormat _lodExportFormatSnapshot;
|
||||
private ETextureFormat _textureExportFormatSnapshot;
|
||||
|
||||
public SettingsViewModel(FGame game)
|
||||
{
|
||||
@@ -123,6 +152,9 @@ namespace FModel.ViewModels
|
||||
_compressedAudioSnapshot = UserSettings.Default.CompressedAudioMode;
|
||||
_cosmeticStyleSnapshot = UserSettings.Default.CosmeticStyle;
|
||||
_cosmeticDisplayAssetSnapshot = UserSettings.Default.CosmeticDisplayAsset;
|
||||
_meshExportFormatSnapshot = UserSettings.Default.MeshExportFormat;
|
||||
_lodExportFormatSnapshot = UserSettings.Default.LodExportFormat;
|
||||
_textureExportFormatSnapshot = UserSettings.Default.TextureExportFormat;
|
||||
|
||||
SelectedUpdateMode = _updateModeSnapshot;
|
||||
SelectedUeGame = _ueGameSnapshot;
|
||||
@@ -132,6 +164,9 @@ namespace FModel.ViewModels
|
||||
SelectedCompressedAudio = _compressedAudioSnapshot;
|
||||
SelectedCosmeticStyle = _cosmeticStyleSnapshot;
|
||||
SelectedCosmeticDisplayAsset = _cosmeticDisplayAssetSnapshot;
|
||||
SelectedMeshExportFormat = _meshExportFormatSnapshot;
|
||||
SelectedLodExportFormat = _lodExportFormatSnapshot;
|
||||
SelectedTextureExportFormat = _textureExportFormatSnapshot;
|
||||
SelectedAesReload = UserSettings.Default.AesReload;
|
||||
SelectedDiscordRpc = UserSettings.Default.DiscordRpc;
|
||||
|
||||
@@ -145,6 +180,9 @@ namespace FModel.ViewModels
|
||||
CompressedAudios = new ReadOnlyObservableCollection<ECompressedAudio>(new ObservableCollection<ECompressedAudio>(EnumerateCompressedAudios()));
|
||||
CosmeticStyles = new ReadOnlyObservableCollection<EIconStyle>(new ObservableCollection<EIconStyle>(EnumerateCosmeticStyles()));
|
||||
CosmeticDisplayAssets = new ReadOnlyObservableCollection<EEnabledDisabled>(new ObservableCollection<EEnabledDisabled>(EnumerateEnabledDisabled()));
|
||||
MeshExportFormats = new ReadOnlyObservableCollection<EMeshFormat>(new ObservableCollection<EMeshFormat>(EnumerateMeshExportFormat()));
|
||||
LodExportFormats = new ReadOnlyObservableCollection<ELodFormat>(new ObservableCollection<ELodFormat>(EnumerateLodExportFormat()));
|
||||
TextureExportFormats = new ReadOnlyObservableCollection<ETextureFormat>(new ObservableCollection<ETextureFormat>(EnumerateTextureExportFormat()));
|
||||
}
|
||||
|
||||
public SettingsOut Save()
|
||||
@@ -167,6 +205,9 @@ namespace FModel.ViewModels
|
||||
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;
|
||||
UserSettings.Default.CosmeticStyle = SelectedCosmeticStyle;
|
||||
UserSettings.Default.CosmeticDisplayAsset = SelectedCosmeticDisplayAsset;
|
||||
UserSettings.Default.MeshExportFormat = SelectedMeshExportFormat;
|
||||
UserSettings.Default.LodExportFormat = SelectedLodExportFormat;
|
||||
UserSettings.Default.TextureExportFormat = SelectedTextureExportFormat;
|
||||
UserSettings.Default.AesReload = SelectedAesReload;
|
||||
UserSettings.Default.DiscordRpc = SelectedDiscordRpc;
|
||||
|
||||
@@ -185,5 +226,8 @@ namespace FModel.ViewModels
|
||||
private IEnumerable<ECompressedAudio> EnumerateCompressedAudios() => Enum.GetValues(SelectedCompressedAudio.GetType()).Cast<ECompressedAudio>();
|
||||
private IEnumerable<EIconStyle> EnumerateCosmeticStyles() => Enum.GetValues(SelectedCosmeticStyle.GetType()).Cast<EIconStyle>();
|
||||
private IEnumerable<EEnabledDisabled> EnumerateEnabledDisabled() => Enum.GetValues(SelectedCosmeticDisplayAsset.GetType()).Cast<EEnabledDisabled>();
|
||||
private IEnumerable<EMeshFormat> EnumerateMeshExportFormat() => Enum.GetValues(SelectedMeshExportFormat.GetType()).Cast<EMeshFormat>();
|
||||
private IEnumerable<ELodFormat> EnumerateLodExportFormat() => Enum.GetValues(SelectedLodExportFormat.GetType()).Cast<ELodFormat>();
|
||||
private IEnumerable<ETextureFormat> EnumerateTextureExportFormat() => Enum.GetValues(SelectedTextureExportFormat.GetType()).Cast<ETextureFormat>();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using FModel.Extensions;
|
||||
using FModel.Settings;
|
||||
@@ -125,6 +126,13 @@ namespace FModel.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapScalingMode _imageRender = BitmapScalingMode.Linear;
|
||||
public BitmapScalingMode ImageRender
|
||||
{
|
||||
get => _imageRender;
|
||||
set => SetProperty(ref _imageRender, value);
|
||||
}
|
||||
|
||||
public bool HasImage => Image != null;
|
||||
public bool ShouldScroll => !string.IsNullOrEmpty(ScrollTrigger);
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ namespace FModel.ViewModels
|
||||
CurrentCancellationTokenSource = null; // kill token
|
||||
|
||||
Log.Error("{Exception}", e);
|
||||
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText(e.Message, Constants.WHITE, true);
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<adonisControls:AdonisWindow x:Class="FModel.Views.About"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:FModel"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
|
||||
IconVisibility="Collapsed" Width="500" SizeToContent="Height">
|
||||
@@ -18,7 +19,7 @@
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<StackPanel Margin="30 10">
|
||||
<StackPanel HorizontalAlignment="Center" Margin="0 0 0 30">
|
||||
<TextBlock Text="404FMode!" FontSize="15" FontWeight="500" Foreground="#9DA3DD" FontStretch="Expanded" />
|
||||
<TextBlock Text="{Binding Source={x:Static local:Constants.APP_VERSION}, StringFormat={}FModel {0}}" FontSize="15" FontWeight="500" Foreground="#9DA3DD" FontStretch="Expanded" />
|
||||
<TextBlock Text="Made with ♥ by Asval" FontSize="30" FontWeight="700" Foreground="#DAE5F2" HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace FModel.Views
|
||||
{
|
||||
_applicationView.AudioPlayer.Stop();
|
||||
_applicationView.AudioPlayer.Dispose();
|
||||
DiscordService.DiscordHandler.UpdateToSavedPresence();
|
||||
}
|
||||
|
||||
private void OnDeviceSwap(object sender, SelectionChangedEventArgs e)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" IconVisibility="Collapsed" SizeToContent="Height" Loaded="OnLoaded"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.35'}">
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.225'}">
|
||||
<adonisControls:AdonisWindow.Style>
|
||||
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
|
||||
<Setter Property="Title" Value="Backup Manager" />
|
||||
@@ -19,68 +19,60 @@
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<GroupBox adonisExtensions:LayerExtension.Layer="2" Margin="10" Padding="{adonisUi:Space 0}" Background="Transparent">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" TextAlignment="Center" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Text="Backups hold an old state of your game. They are used to show new or modified files between the old and current state of your game. When loading files in those modes, make sure to always select a backup older than what your game currently is, else you will simply see nothing." />
|
||||
|
||||
<Grid Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="5" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Click="OnCreateBackupClick"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}" Content="Create" />
|
||||
|
||||
<Separator Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" Tag="PRE-CREATED BACKUP FILES" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding SelectedBackup.FileSize, Converter={x:Static converters:SizeToStringConverter.Instance}, FallbackValue='Backup', StringFormat={}Backup ({0})}" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{Binding BackupsView}" SelectedItem="{Binding SelectedBackup, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding FileName}" TextTrimming="CharacterEllipsis" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
<Setter Property="IsEnabled" Value="True" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding BackupsView.Count}" Value="0">
|
||||
<Setter Property="IsEnabled" Value="False" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
</ComboBox>
|
||||
<Button Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" Content="Download" Click="OnDownloadClick">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="IsEnabled" Value="True" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding BackupsView.Count}" Value="0">
|
||||
<Setter Property="IsEnabled" Value="False" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<Grid Column="2" adonisExtensions:LayerExtension.Layer="2" Margin="10" Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="5" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" TextAlignment="Center" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Text="Because it looks like it's pretty hard to understand, you don't load backups, you load your files and compare them with the backup. Also, by clicking the download button you don't create your own backup you download a pre-made one. Kinda crazy we have to explain this, who will even read anyway..." />
|
||||
|
||||
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Click="OnCreateBackupClick"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}" Content="Create" />
|
||||
|
||||
<Separator Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" Tag="PRE-CREATED BACKUP FILES" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding SelectedBackup.FileSize, Converter={x:Static converters:SizeToStringConverter.Instance}, FallbackValue='Backup', StringFormat={}Backup ({0})}" />
|
||||
<ComboBox Grid.Row="4" Grid.Column="2" ItemsSource="{Binding BackupsView}" SelectedItem="{Binding SelectedBackup, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding FileName}" TextTrimming="CharacterEllipsis" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
<Setter Property="IsEnabled" Value="True" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding BackupsView.Count}" Value="0">
|
||||
<Setter Property="IsEnabled" Value="False" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
</ComboBox>
|
||||
<Button Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="3" Content="Download" Click="OnDownloadClick">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="IsEnabled" Value="True" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding BackupsView.Count}" Value="0">
|
||||
<Setter Property="IsEnabled" Value="False" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<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"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" SizeToContent="Width" ResizeMode="CanMinimize" Closing="OnClosing"
|
||||
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 +19,106 @@
|
||||
<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 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 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 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 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 IsReady}" />
|
||||
<CheckBox Content="Alien Artifacts" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrAlienArtifacts}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding 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 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 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 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 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 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 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 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 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 IsReady}" />
|
||||
</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}}" />
|
||||
<Image UseLayoutRounding="True" Source="{Binding MapViewer.LayerImage}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Visibility="{Binding IsReady, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.IO;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
@@ -23,6 +25,8 @@ namespace FModel.Views
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnClosing(object sender, CancelEventArgs e) => DiscordService.DiscordHandler.UpdateToSavedPresence();
|
||||
|
||||
private void OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_applicationView.MapViewer.MapImage == null) return;
|
||||
@@ -57,5 +61,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
FModel/Views/Resources/Controls/Aed/BraceFoldingStrategy.cs
Normal file
121
FModel/Views/Resources/Controls/Aed/BraceFoldingStrategy.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.Collections.Generic;
|
||||
using ICSharpCode.AvalonEdit;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Folding;
|
||||
|
||||
namespace FModel.Views.Resources.Controls
|
||||
{
|
||||
public class JsonFoldingStrategies
|
||||
{
|
||||
private readonly BraceFoldingStrategy _strategy;
|
||||
private readonly FoldingManager _foldingManager;
|
||||
|
||||
public JsonFoldingStrategies(TextEditor avalonEditor)
|
||||
{
|
||||
_foldingManager = FoldingManager.Install(avalonEditor.TextArea);
|
||||
_strategy = new BraceFoldingStrategy(avalonEditor);
|
||||
}
|
||||
|
||||
public void UpdateFoldings(TextDocument document)
|
||||
{
|
||||
_foldingManager.UpdateFoldings(_strategy.UpdateFoldings(document), -1);
|
||||
}
|
||||
|
||||
public void UnfoldAll()
|
||||
{
|
||||
if (_foldingManager.AllFoldings == null)
|
||||
return;
|
||||
|
||||
foreach (var folding in _foldingManager.AllFoldings)
|
||||
{
|
||||
folding.IsFolded = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void FoldToggle(int offset)
|
||||
{
|
||||
if (_foldingManager.AllFoldings == null)
|
||||
return;
|
||||
|
||||
var foldSection = _foldingManager.GetFoldingsContaining(offset);
|
||||
if (foldSection.Count > 0)
|
||||
foldSection[^1].IsFolded = !foldSection[^1].IsFolded;
|
||||
}
|
||||
|
||||
public void FoldToggleAtLevel(int level = 0)
|
||||
{
|
||||
if (_foldingManager.AllFoldings == null)
|
||||
return;
|
||||
|
||||
foreach (var folding in _foldingManager.AllFoldings)
|
||||
{
|
||||
if (folding.Tag is not CustomNewFolding realFolding) continue;
|
||||
if (realFolding.Level == level) folding.IsFolded = !folding.IsFolded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BraceFoldingStrategy
|
||||
{
|
||||
public BraceFoldingStrategy(TextEditor editor)
|
||||
{
|
||||
UpdateFoldings(editor.Document);
|
||||
}
|
||||
|
||||
public IEnumerable<CustomNewFolding> UpdateFoldings(TextDocument document)
|
||||
{
|
||||
return CreateNewFoldings(document);
|
||||
}
|
||||
|
||||
public IEnumerable<CustomNewFolding> CreateNewFoldings(ITextSource document)
|
||||
{
|
||||
var newFoldings = new List<CustomNewFolding>();
|
||||
var startOffsets = new Stack<int>();
|
||||
var lastNewLineOffset = 0;
|
||||
var level = -1;
|
||||
|
||||
for (var i = 0; i < document.TextLength; i++)
|
||||
{
|
||||
var c = document.GetCharAt(i);
|
||||
switch (c)
|
||||
{
|
||||
case '{' or '[':
|
||||
level++;
|
||||
startOffsets.Push(i);
|
||||
break;
|
||||
case '}' or ']' when startOffsets.Count > 0:
|
||||
{
|
||||
var startOffset = startOffsets.Pop();
|
||||
if (startOffset < lastNewLineOffset)
|
||||
{
|
||||
newFoldings.Add(new CustomNewFolding(startOffset, i + 1, level));
|
||||
}
|
||||
level--;
|
||||
break;
|
||||
}
|
||||
case '\n' or '\r':
|
||||
lastNewLineOffset = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
|
||||
return newFoldings;
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomNewFolding : NewFolding
|
||||
{
|
||||
public int Level { get; }
|
||||
|
||||
public CustomNewFolding(int start, int end, int level) : base(start, end)
|
||||
{
|
||||
Level = level;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{Level}] {StartOffset} -> {EndOffset}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,9 @@ namespace FModel.Views.Resources.Controls
|
||||
|
||||
public AvalonEditor()
|
||||
{
|
||||
CommandBindings.Add(new CommandBinding(NavigationCommands.Search, (s, e) => FindNext(e.Parameter != null)));
|
||||
|
||||
CommandBindings.Add(new CommandBinding(NavigationCommands.Search, (_, e) => FindNext(e.Parameter != null)));
|
||||
InitializeComponent();
|
||||
|
||||
YesWeEditor = MyAvalonEditor;
|
||||
YesWeSearch = WpfSuckMyDick;
|
||||
MyAvalonEditor.TextArea.TextView.ElementGenerators.Add(new GamePathElementGenerator());
|
||||
@@ -81,7 +81,7 @@ namespace FModel.Views.Resources.Controls
|
||||
if (sender is not TextEditor avalonEditor || DataContext is not TabItem tabItem ||
|
||||
avalonEditor.Document == null || string.IsNullOrEmpty(avalonEditor.Document.Text))
|
||||
return;
|
||||
|
||||
|
||||
avalonEditor.Document.FileName = tabItem.Directory + '/' + tabItem.Header.SubstringBeforeLast('.');
|
||||
if (!tabItem.ShouldScroll) return;
|
||||
|
||||
@@ -95,9 +95,8 @@ namespace FModel.Views.Resources.Controls
|
||||
{
|
||||
if (DataContext is not TabItem tabItem || Keyboard.Modifiers != ModifierKeys.Control)
|
||||
return;
|
||||
|
||||
|
||||
var fontSize = tabItem.FontSize + e.Delta / 50.0;
|
||||
|
||||
tabItem.FontSize = fontSize switch
|
||||
{
|
||||
< 6 => 6,
|
||||
|
||||
13
FModel/Views/Resources/Controls/Breadcrumb.xaml
Normal file
13
FModel/Views/Resources/Controls/Breadcrumb.xaml
Normal file
@@ -0,0 +1,13 @@
|
||||
<UserControl x:Class="FModel.Views.Resources.Controls.Breadcrumb"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
DataContextChanged="OnDataContextChanged">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="../Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<StackPanel x:Name="InMeDaddy" Orientation="Horizontal" HorizontalAlignment="Right" Height="24" />
|
||||
</UserControl>
|
||||
70
FModel/Views/Resources/Controls/Breadcrumb.xaml.cs
Normal file
70
FModel/Views/Resources/Controls/Breadcrumb.xaml.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using FModel.Services;
|
||||
|
||||
namespace FModel.Views.Resources.Controls
|
||||
{
|
||||
public partial class Breadcrumb
|
||||
{
|
||||
private const string _NAVIGATE_NEXT = "M9.31 6.71c-.39.39-.39 1.02 0 1.41L13.19 12l-3.88 3.88c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41L10.72 6.7c-.38-.38-1.02-.38-1.41.01z";
|
||||
|
||||
public Breadcrumb()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is not string pathAtThisPoint) return;
|
||||
InMeDaddy.Children.Clear();
|
||||
|
||||
var folders = pathAtThisPoint.Split('/');
|
||||
for (var i = 0; i < folders.Length; i++)
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = folders[i],
|
||||
Background = Brushes.Transparent,
|
||||
Cursor = Cursors.Hand,
|
||||
Tag = i + 1,
|
||||
Margin = new Thickness(0, 3, 0, 0)
|
||||
};
|
||||
textBlock.MouseUp += OnMouseClick;
|
||||
|
||||
InMeDaddy.Children.Add(textBlock);
|
||||
if (i >= folders.Length - 1) continue;
|
||||
|
||||
InMeDaddy.Children.Add(new Viewbox
|
||||
{
|
||||
Width = 16,
|
||||
Height = 16,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Child = new Canvas
|
||||
{
|
||||
Width = 24,
|
||||
Height = 24,
|
||||
Children = { new Path
|
||||
{
|
||||
Fill = Brushes.White,
|
||||
Data = Geometry.Parse(_NAVIGATE_NEXT)
|
||||
}}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not TextBlock {DataContext: string pathAtThisPoint, Tag: int index}) return;
|
||||
|
||||
var directory = string.Join('/', pathAtThisPoint.Split('/').Take(index));
|
||||
if (pathAtThisPoint.Equals(directory)) return;
|
||||
|
||||
ApplicationService.ApplicationView.CustomDirectories.GoToCommand.JumpTo(directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed"
|
||||
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" PreviewKeyDown="OnPreviewKeyDown"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.40'}">
|
||||
<adonisControls:AdonisWindow.Resources>
|
||||
<ResourceDictionary>
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace FModel.Views.Resources.Controls
|
||||
private readonly Regex _hexColorRegex = new("\"Hex\": \"(?'target'[0-9A-Fa-f]{3,8})\"$",
|
||||
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
private readonly System.Windows.Controls.ToolTip _toolTip = new();
|
||||
private JsonFoldingStrategies _manager;
|
||||
|
||||
public PropertiesPopout(TabItem contextViewModel)
|
||||
{
|
||||
@@ -30,6 +31,8 @@ namespace FModel.Views.Resources.Controls
|
||||
MyAvalonEditor.ScrollToVerticalOffset(contextViewModel.ScrollPosition);
|
||||
MyAvalonEditor.TextArea.TextView.ElementGenerators.Add(new GamePathElementGenerator());
|
||||
MyAvalonEditor.TextArea.TextView.ElementGenerators.Add(new HexColorElementGenerator());
|
||||
_manager = new JsonFoldingStrategies(MyAvalonEditor);
|
||||
_manager.UpdateFoldings(MyAvalonEditor.Document);
|
||||
}
|
||||
|
||||
private void OnMouseHover(object sender, MouseEventArgs e)
|
||||
@@ -64,6 +67,22 @@ namespace FModel.Views.Resources.Controls
|
||||
_ => fontSize
|
||||
};
|
||||
}
|
||||
|
||||
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.J when Keyboard.IsKeyDown(Key.K) && Keyboard.Modifiers.HasFlag(ModifierKeys.Control):
|
||||
_manager.UnfoldAll();
|
||||
break;
|
||||
case Key.L when Keyboard.IsKeyDown(Key.K) && Keyboard.Modifiers.HasFlag(ModifierKeys.Control):
|
||||
_manager.FoldToggle(MyAvalonEditor.CaretOffset);
|
||||
break;
|
||||
case >= Key.D0 and <= Key.D9 when Keyboard.IsKeyDown(Key.K) && Keyboard.Modifiers.HasFlag(ModifierKeys.Control):
|
||||
_manager.FoldToggleAtLevel(int.Parse(e.Key.ToString()[1].ToString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseHoverStopped(object sender, MouseEventArgs e)
|
||||
{
|
||||
|
||||
@@ -12,14 +12,16 @@ namespace FModel.Views.Resources.Converters
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
"Newt" => "Spellbreak",
|
||||
"Nebula" => "Days Gone",
|
||||
"Fortnite" => "Fortnite",
|
||||
"VALORANT" => "Valorant",
|
||||
"Pewee" => "Rogue Company",
|
||||
"Rosemallow" => "The Outer Worlds",
|
||||
"Catnip" => "Borderlands 3",
|
||||
"AzaleaAlpha" => "The Cycle",
|
||||
"Snoek" => "State of Decay 2",
|
||||
"Rosemallow" => "The Outer Worlds",
|
||||
"WorldExplorersLive" => "Battle Breakers",
|
||||
"Newt" => "Spellbreak",
|
||||
"VALORANT" => "Valorant",
|
||||
"MinecraftDungeons" => "Minecraft Dungeons",
|
||||
"shoebill" => "Star Wars: Jedi Fallen Order",
|
||||
"a99769d95d8f400baad1f67ab5dfe508" => "Core",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns:audioControls="clr-namespace:FModel.Views.Resources.Controls.Aup"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||
xmlns:folding="clr-namespace:ICSharpCode.AvalonEdit.Folding;assembly=ICSharpCode.AvalonEdit"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisConverters="clr-namespace:AdonisUI.Converters;assembly=AdonisUI"
|
||||
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
|
||||
@@ -57,6 +58,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">
|
||||
@@ -382,6 +385,9 @@
|
||||
<DataTrigger Binding="{Binding Header}" Value="Athena">
|
||||
<Setter TargetName="TreeImage" Property="Source" Value="/FModel;component/Resources/athena.png" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Header, Converter={x:Static converters:CaseInsensitiveStringEqualsConverter.Instance}, ConverterParameter='StateOfDecay2'}" Value="True">
|
||||
<Setter TargetName="TreeImage" Property="Source" Value="/FModel;component/Resources/stateofdecay2.png" />
|
||||
</DataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</Setter.Value>
|
||||
@@ -642,7 +648,7 @@
|
||||
<controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" Grid.ColumnSpan="3" DataContext="{Binding SelectedItem, ElementName=TabControlName}" />
|
||||
<Image Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform"
|
||||
Visibility="{Binding SelectedItem.HasImage, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Source="{Binding SelectedItem.Image, ElementName=TabControlName}">
|
||||
Source="{Binding SelectedItem.Image, ElementName=TabControlName}" RenderOptions.BitmapScalingMode="{Binding SelectedItem.ImageRender, ElementName=TabControlName}">
|
||||
<Image.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding SelectedItem.ImageCommand, ElementName=TabControlName}" CommandParameter="Open"/>
|
||||
</Image.InputBindings>
|
||||
@@ -1630,4 +1636,11 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type folding:FoldingMargin}">
|
||||
<Setter Property="FoldingMarkerBrush" Value="#262630" />
|
||||
<Setter Property="FoldingMarkerBackgroundBrush" Value="#262630" />
|
||||
<Setter Property="SelectedFoldingMarkerBrush" Value="#808080" />
|
||||
<Setter Property="SelectedFoldingMarkerBackgroundBrush" Value="#2A2B34" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -38,6 +38,8 @@ namespace FModel.Views
|
||||
MainWindow.YesWeCats.AssetsListName.ItemsSource = null;
|
||||
var folder = _applicationView.CustomDirectories.GoToCommand.JumpTo(assetItem.FullPath.SubstringBeforeLast('/'));
|
||||
if (folder == null) return;
|
||||
|
||||
MainWindow.YesWeCats.Activate();
|
||||
|
||||
do { await Task.Delay(100); } while (MainWindow.YesWeCats.AssetsListName.Items.Count < folder.AssetsList.Assets.Count);
|
||||
MainWindow.YesWeCats.LeftTabControl.SelectedIndex = 2; // assets tab
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -45,11 +46,11 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Output Directory *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBox Grid.Row="0" Grid.Column="2" Text="{Binding OutputDirectory, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 0 0 5" />
|
||||
<TextBox Grid.Row="0" Grid.Column="2" Text="{Binding OutputDirectory, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 5" />
|
||||
<Button Grid.Row="0" Grid.Column="4" Content="..." HorizontalAlignment="Right" Click="OnBrowseOutput" Margin="0 0 0 5" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Game Directory *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding GameDirectory, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 0 0 5" />
|
||||
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding GameDirectory, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 5" />
|
||||
<Button Grid.Row="1" Grid.Column="4" Content="..." HorizontalAlignment="Right" Click="OnBrowseDirectories" Margin="0 0 0 5" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Update Mode" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Receive updates each time a new release is pushed to GitHub Receive updates each time a new commit is pushed to GitHub" />
|
||||
@@ -62,8 +63,8 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="UE4 Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeGames}" SelectedItem="{Binding SettingsView.SelectedUeGame, Mode=TwoWay}"
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Assets Language" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Language used and shown when parsing assets" />
|
||||
<ComboBox Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AssetLanguages}" SelectedItem="{Binding SettingsView.SelectedAssetLanguage, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -72,8 +73,8 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="UE4 Sub-Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 sub-version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeVersions}" SelectedItem="{Binding SettingsView.SelectedUeVersion, Mode=TwoWay}"
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Discord Rich Presence" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DiscordRpcs}" SelectedItem="{Binding SettingsView.SelectedDiscordRpc, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -82,8 +83,10 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="5" Grid.Column="0" Text="Assets Language" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Language used and shown when parsing assets" />
|
||||
<ComboBox Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AssetLanguages}" SelectedItem="{Binding SettingsView.SelectedAssetLanguage, Mode=TwoWay}"
|
||||
<Separator Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="5" Style="{StaticResource CustomSeparator}" Tag="ADVANCED"></Separator>
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="UE4 Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeGames}" SelectedItem="{Binding SettingsView.SelectedUeGame, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -92,8 +95,8 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Keep Directory Structure" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Auto Export & Save assets inside their game directory" />
|
||||
<ComboBox Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DirectoryStructures}" SelectedItem="{Binding SettingsView.SelectedDirectoryStructure, Mode=TwoWay}"
|
||||
<TextBlock Grid.Row="7" Grid.Column="0" Text="UE4 Sub-Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 sub-version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeVersions}" SelectedItem="{Binding SettingsView.SelectedUeVersion, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -102,8 +105,8 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="7" Grid.Column="0" Text="Compressed Audio" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="What to do when encountering a compressed audio file" />
|
||||
<ComboBox Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.CompressedAudios}" SelectedItem="{Binding SettingsView.SelectedCompressedAudio, Mode=TwoWay}"
|
||||
<TextBlock Grid.Row="8" Grid.Column="0" Text="Keep Directory Structure" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Auto Export & Save assets inside their game directory" />
|
||||
<ComboBox Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DirectoryStructures}" SelectedItem="{Binding SettingsView.SelectedDirectoryStructure, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -112,7 +115,17 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="8" Grid.Column="0" Text="AES Reload at Launch" VerticalAlignment="Center" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Compressed Audio" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="What to do when encountering a compressed audio file" />
|
||||
<ComboBox Grid.Row="9" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.CompressedAudios}" SelectedItem="{Binding SettingsView.SelectedCompressedAudio, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="AES Reload at Launch" VerticalAlignment="Center" Margin="0 0 0 5"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
@@ -125,7 +138,7 @@
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<ComboBox Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AesReloads}" SelectedItem="{Binding SettingsView.SelectedAesReload, Mode=TwoWay}"
|
||||
<ComboBox Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AesReloads}" SelectedItem="{Binding SettingsView.SelectedAesReload, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
@@ -143,16 +156,6 @@
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Discord Rich Presence" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="9" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DiscordRpcs}" SelectedItem="{Binding SettingsView.SelectedDiscordRpc, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="CreatorTemplate">
|
||||
@@ -225,6 +228,58 @@
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="ModelsTemplate">
|
||||
<Grid adonisExtensions:LayerExtension.Layer="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Mesh Export Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding SettingsView.MeshExportFormats}" SelectedItem="{Binding SettingsView.SelectedMeshExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Level Of Detail Export Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding SettingsView.LodExportFormats}" SelectedItem="{Binding SettingsView.SelectedLodExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Texture Export Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{Binding SettingsView.TextureExportFormats}" SelectedItem="{Binding SettingsView.SelectedTextureExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="KeybindingsTemplate">
|
||||
<Grid adonisExtensions:LayerExtension.Layer="2">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -287,12 +342,12 @@
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Auto Save Textures *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="10" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<!-- <TextBlock Grid.Row="11" Grid.Column="0" Text="Auto Save Meshes *" VerticalAlignment="Center" Margin="0 0 0 5" /> -->
|
||||
<!-- <controls:HotkeyTextBox Grid.Row="11" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5" -->
|
||||
<!-- HotKey="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" /> -->
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Save Materials *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Auto Save Materials *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="11" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Save Meshes *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
@@ -362,6 +417,22 @@
|
||||
</Style>
|
||||
</TreeViewItem.Style>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Tag="ModelsTemplate">
|
||||
<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="M2,6c0.55,0,1-0.45,1-1V4c0-0.55,0.45-1,1-1h1c0.55,0,1-0.45,1-1S5.55,1,5,1H4C2.34,1,1,2.34,1,4v1C1,5.55,1.45,6,2,6z" />
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M5,21H4c-0.55,0-1-0.45-1-1v-1c0-0.55-0.45-1-1-1c-0.55,0-1,0.45-1,1v1c0,1.66,1.34,3,3,3h1c0.55,0,1-0.45,1-1 S5.55,21,5,21z" />
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M20,1h-1c-0.55,0-1,0.45-1,1s0.45,1,1,1h1c0.55,0,1,0.45,1,1v1c0,0.55,0.45,1,1,1c0.55,0,1-0.45,1-1V4 C23,2.34,21.66,1,20,1z" />
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M22,18c-0.55,0-1,0.45-1,1v1c0,0.55-0.45,1-1,1h-1c-0.55,0-1,0.45-1,1s0.45,1,1,1h1c1.66,0,3-1.34,3-3v-1 C23,18.45,22.55,18,22,18z" />
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M19,14.87V9.13c0-0.72-0.38-1.38-1-1.73l-5-2.88c-0.31-0.18-0.65-0.27-1-0.27s-0.69,0.09-1,0.27L6,7.39 C5.38,7.75,5,8.41,5,9.13v5.74c0,0.72,0.38,1.38,1,1.73l5,2.88c0.31,0.18,0.65,0.27,1,0.27s0.69-0.09,1-0.27l5-2.88 C18.62,16.25,19,15.59,19,14.87z M11,17.17l-4-2.3v-4.63l4,2.33V17.17z M12,10.84L8.04,8.53L12,6.25l3.96,2.28L12,10.84z M17,14.87l-4,2.3v-4.6l4-2.33V14.87z" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
<TextBlock Text="Models" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Tag="KeybindingsTemplate">
|
||||
<TreeViewItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace FModel.Views
|
||||
{
|
||||
var whatShouldIDo = _applicationView.SettingsView.Save();
|
||||
if (whatShouldIDo == SettingsOut.Restart)
|
||||
_applicationView.Restart();
|
||||
_applicationView.RestartWithWarning();
|
||||
|
||||
Close();
|
||||
|
||||
|
||||
@@ -18,4 +18,13 @@ To use FModel, you need to have **[.NET 5](https://dotnet.microsoft.com/download
|
||||
- [@GMatrix](https://github.com/GMatrixGames)
|
||||
- [@Tiger](https://github.com/XTigerHyperX)
|
||||
|
||||
## Support
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EP9SSWG8MW4UC&source=url)
|
||||
|
||||
<p>This project is supported by:</p>
|
||||
<p>
|
||||
<a href="https://www.jetbrains.com/">
|
||||
<img src="https://cdn.fmodel.app/i/svg/jetbrains.svg" width="256px">
|
||||
</a>
|
||||
</p>
|
||||
Reference in New Issue
Block a user