diff --git a/Source/Data/AchievementSet.cs b/Source/Data/AchievementSet.cs index 721ce46a..4c699d48 100644 --- a/Source/Data/AchievementSet.cs +++ b/Source/Data/AchievementSet.cs @@ -9,5 +9,10 @@ public class AchievementSet : AssetBase /// Gets or sets the type classification of the achievement set. /// public AchievementSetType Type { get; set; } + + /// + /// Gets the name of the badge for the achievement set. + /// + public string BadgeName { get; set; } } } diff --git a/Source/Data/Leaderboard.cs b/Source/Data/Leaderboard.cs index 11544a64..caa26180 100644 --- a/Source/Data/Leaderboard.cs +++ b/Source/Data/Leaderboard.cs @@ -241,7 +241,12 @@ public enum ValueFormat Value, /// - /// Generic value followed by "Points" - %06d Points + /// Generic unsigned value - %01u + /// + Unsigned, + + /// + /// Generic value padded with 0s to 6 digits - %06d Points /// Score, diff --git a/Source/Parser/PublishedAssets.cs b/Source/Parser/PublishedAssets.cs index b97d5d6a..3229f363 100644 --- a/Source/Parser/PublishedAssets.cs +++ b/Source/Parser/PublishedAssets.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; namespace RATools.Parser @@ -129,6 +130,7 @@ private void Read() OwnerGameId = GameId, Title = Title, Type = AchievementSetType.Core, + BadgeName = ExtractBadgeName(publishedData.GetField("ImageIconUrl").StringValue), }); var publishedAchievements = publishedData.GetField("Achievements"); @@ -168,6 +170,16 @@ private static AchievementSetType ConvertType(string type) } } + private static string ExtractBadgeName(string url) + { + if (url == null) + return null; + + var index = url.LastIndexOf('/'); + var filename = url.Substring(index + 1); + return Path.GetFileNameWithoutExtension(filename); + } + private void ReadSets(JsonField sets) { foreach (var set in sets.ObjectArrayValue) @@ -182,6 +194,7 @@ private void ReadSets(JsonField sets) OwnerGameId = gameId, Title = set.GetField("Title").StringValue ?? Title, Type = ConvertType(set.GetField("Type").StringValue), + BadgeName = ExtractBadgeName(set.GetField("ImageIconUrl").StringValue), }); var publishedAchievements = set.GetField("Achievements"); diff --git a/Source/ViewModels/AchievementViewModel.cs b/Source/ViewModels/AchievementViewModel.cs index 27aa4b1b..950056c1 100644 --- a/Source/ViewModels/AchievementViewModel.cs +++ b/Source/ViewModels/AchievementViewModel.cs @@ -184,5 +184,13 @@ private static string GetMeasuredTarget(IEnumerable requirements) return null; } + + public bool BelongsToSet(AchievementSet achievementSet) + { + if (OwnerSetId == 0) + return (achievementSet == null || achievementSet.Type == AchievementSetType.Core); + + return (achievementSet == null || achievementSet.Id == OwnerSetId); + } } } diff --git a/Source/ViewModels/AchievementsListViewModel.cs b/Source/ViewModels/AchievementsListViewModel.cs new file mode 100644 index 00000000..6a30675a --- /dev/null +++ b/Source/ViewModels/AchievementsListViewModel.cs @@ -0,0 +1,183 @@ +using Jamiras.Commands; +using Jamiras.DataModels; +using Jamiras.ViewModels; +using RATools.Data; +using RATools.ViewModels.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace RATools.ViewModels +{ + public class AchievementsListViewModel : ViewerViewModelBase + { + public AchievementsListViewModel(GameViewModel owner, AchievementSet achievementSet, IEnumerable navigationNodes) + : base(owner) + { + AchievementViewModel[] achievements; + + if (navigationNodes != null) + { + achievements = navigationNodes.OfType() + .Select(vm => vm.Editor as AchievementViewModel).ToArray(); + } + else + { + achievements = owner.Editors.OfType() + .Where(a => a.BelongsToSet(achievementSet)).ToArray(); + } + + Achievements = achievements; + + Title = achievementSet.Title; + CountAchievements(achievements); + _badgeName = achievementSet?.BadgeName ?? owner.BadgeName; + + _id = (achievementSet != null) ? achievementSet.Id : 0; + + ExportCommand = new DelegateCommand(Export); + } + + private readonly int _id; + + private void CountAchievements(AchievementViewModel[] achievements) + { + var description = new StringBuilder(); + description.AppendFormat("{0} achievements", achievements.Length); + var initialDescriptionLength = description.Length; + description.Append(" ("); + + var points = new StringBuilder(); + points.AppendFormat("{0} points", achievements.Sum(a => a.Points)); + var initialPointsLength = points.Length; + points.Append(" ("); + + var published = achievements.Where(a => !a.Published?.Asset?.IsUnofficial ?? false); + if (published.Any()) + { + description.AppendFormat("{0} published, ", published.Count()); + points.AppendFormat("{0} published, ", published.Sum(a => a.Points)); + } + + var unpublished = achievements.Where(a => a.Published?.Asset?.IsUnofficial ?? false); + if (unpublished.Any()) + { + description.AppendFormat("{0} unpublished, ", unpublished.Count()); + points.AppendFormat("{0} unpublished, ", unpublished.Sum(a => a.Points)); + } + + var local = achievements.Where(a => a.Published?.Asset == null); + if (local.Any()) + { + description.AppendFormat("{0} local, ", local.Count()); + points.AppendFormat("{0} local, ", local.Sum(a => a.Points)); + } + + description.Length -= 2; + description.Append(')'); + + points.Length -= 2; + points.Append(')'); + + bool hasComma = false; + for (int i = initialDescriptionLength; i < description.Length; i++) + { + if (description[i] == ',') + { + hasComma = true; + break; + } + } + if (!hasComma) + { + description.Length = initialDescriptionLength; + points.Length = initialPointsLength; + } + + Description = description.ToString(); + PointsSummary = points.ToString(); + } + + private readonly string _badgeName; + + public override string ViewerType => "AchievementList"; + + public override int ViewerId { get { return _id; } } + + public static readonly ModelProperty BadgeProperty = ModelProperty.RegisterDependant(typeof(AchievementsListViewModel), "Badge", typeof(ImageSource), new ModelProperty[0], GetBadge); + public ImageSource Badge + { + get { return (ImageSource)GetValue(BadgeProperty); } + } + + private static ImageSource GetBadge(ModelBase model) + { + var vm = (AchievementsListViewModel)model; + if (!String.IsNullOrEmpty(vm._badgeName) && vm._badgeName != "0") + { + if (String.IsNullOrEmpty(vm._owner.RACacheDirectory)) + return null; + + var name = "i" + vm._badgeName; + if (Path.GetExtension(name) == "") + name += ".png"; + var path = Path.Combine(Path.Combine(vm._owner.RACacheDirectory, "..\\Badge"), name); + if (File.Exists(path)) + { + var image = new BitmapImage(new Uri(path)); + image.Freeze(); + return image; + } + } + + return null; + } + + public static readonly ModelProperty PointsSummaryProperty = ModelProperty.Register(typeof(AchievementsListViewModel), "PointsSummary", typeof(string), String.Empty); + public string PointsSummary + { + get { return (string)GetValue(PointsSummaryProperty); } + protected set { SetValue(PointsSummaryProperty, value); } + } + + /// + /// Gets the list of achievements. + /// + public IEnumerable Achievements { get; private set; } + + public int Points { get { return Achievements.Sum(a => a.Points); } } + + public CommandBase ExportCommand { get; private set; } + + private void Export() + { + var filename = Path.GetFileNameWithoutExtension(_owner.Script.Filename) ?? "achievements"; + + var vm = new FileDialogViewModel(); + vm.DialogTitle = "Export achievement information"; + vm.Filters["CSV file"] = "*.csv"; + vm.FileNames = new[] { filename + ".csv" }; + vm.OverwritePrompt = true; + + if (vm.ShowSaveFileDialog() == DialogResult.Ok) + { + using (var file = File.CreateText(vm.FileNames[0])) + { + file.WriteLine("Id,Title,Description,Points"); + + foreach (var achievement in Achievements) + { + file.Write("{0},\"{1}\",", achievement.Id, achievement.Title.Replace("\"", "\\\"")); + file.Write("\"{0}\",", achievement.Description.Replace("\"", "\\\"")); + file.Write("{0}", achievement.Points); + file.WriteLine(); + } + } + } + } + } +} diff --git a/Source/ViewModels/AssetViewModelBase.cs b/Source/ViewModels/AssetViewModelBase.cs index 03401d77..115eb65d 100644 --- a/Source/ViewModels/AssetViewModelBase.cs +++ b/Source/ViewModels/AssetViewModelBase.cs @@ -36,6 +36,8 @@ public AssetViewModelBase(GameViewModel owner) } } + public override int ViewerId { get { return this.Id; } } + /// /// The asset generated by the script. /// diff --git a/Source/ViewModels/GameViewModel.cs b/Source/ViewModels/GameViewModel.cs index 86eb3518..d8dba841 100644 --- a/Source/ViewModels/GameViewModel.cs +++ b/Source/ViewModels/GameViewModel.cs @@ -134,20 +134,7 @@ public void NavigateBack() private void NavigateTo(NavigationItem item) { - ViewerViewModelBase editor = null; - foreach (var scan in Editors) - { - if (scan.ViewerType != item.EditorType) - continue; - - var assetEditor = scan as AssetViewModelBase; - if (assetEditor != null && assetEditor.Id != item.EditorId) - continue; - - editor = scan; - break; - } - + ViewerViewModelBase editor = FindEditor(NavigationNodes, item); if (editor == null) return; @@ -199,10 +186,7 @@ public void CaptureNavigationLocation() private static NavigationItem CaptureNavigationItem(ViewerViewModelBase editor) { - var item = new NavigationItem { EditorType = editor.ViewerType }; - var assetEditor = editor as AssetViewModelBase; - if (assetEditor != null) - item.EditorId = assetEditor.Id; + var item = new NavigationItem { EditorType = editor.ViewerType, EditorId = editor.ViewerId }; var scriptEditor = editor as ScriptViewModel; if (scriptEditor != null) @@ -211,6 +195,28 @@ private static NavigationItem CaptureNavigationItem(ViewerViewModelBase editor) return item; } + private static ViewerViewModelBase FindEditor(IEnumerable nodes, NavigationItem item) + { + foreach (var node in nodes.OfType()) + { + var editor = node.Editor; + if (editor.ViewerType == item.EditorType && editor.ViewerId == item.EditorId) + return editor; + } + + foreach (var node in nodes) + { + if (node.Children != null && node.Children.Count > 0) + { + var child = FindEditor(node.Children, item); + if (child != null) + return child; + } + } + + return null; + } + internal void PopulateEditorList(AchievementScriptInterpreter interpreter) { if (interpreter != null) @@ -227,9 +233,48 @@ internal void PopulateEditorList(AchievementScriptInterpreter interpreter) var navigation = new NavigationListViewModel(this, _publishedAssets, _localAssets, _editors); NavigationNodes = navigation.Merge(interpreter); - SelectedNavigationNode = FindEditorNavigationNode(NavigationNodes, SelectedEditor); + UpdateSelectedNavigationNode(SelectedNavigationNode); } + internal void UpdateSelectedNavigationNode(NavigationViewModelBase searchNode) + { + var newNode = searchNode != null + ? FindNavigationNode(NavigationNodes, searchNode) + : FindEditorNavigationNode(NavigationNodes, Script); + if (!ReferenceEquals(searchNode, newNode)) + { + SelectedNavigationNode = newNode; + + if (newNode != null) + newNode.IsSelected = true; + + if (searchNode != null) + searchNode.IsSelected = false; + } + } + + internal static NavigationViewModelBase FindNavigationNode(IEnumerable nodes, NavigationViewModelBase searchNode) + { + foreach (var node in nodes) + { + if (node == searchNode) + return node; + } + + foreach (var node in nodes) + { + if (node.Children != null && node.Children.Count > 0) + { + var child = FindNavigationNode(node.Children, searchNode); + if (child != null) + return child; + } + } + + return null; + } + + public int CompileProgress { get; internal set; } public int CompileProgressLine { get; internal set; } @@ -264,7 +309,7 @@ public NavigationViewModelBase SelectedNavigationNode private static void OnSelectedNavigationNodeChanged(object sender, ModelPropertyChangedEventArgs e) { - var editorNavigationViewModel = e.NewValue as EditorNavigationViewModelBase; + var editorNavigationViewModel = e.NewValue as IEditorNavigationViewModel; if (editorNavigationViewModel != null) ((GameViewModel)sender).SelectedEditor = editorNavigationViewModel.Editor; } @@ -307,10 +352,10 @@ private static void OnSelectedEditorChanged(object sender, ModelPropertyChangedE private static NavigationViewModelBase FindEditorNavigationNode(IEnumerable nodes, ViewerViewModelBase editor) { - foreach (var node in nodes.OfType()) + foreach (var node in nodes.OfType()) { if (ReferenceEquals(node.Editor, editor)) - return node; + return (NavigationViewModelBase)node; } foreach (var node in nodes) @@ -522,13 +567,20 @@ private void HandleLocalAssetChange(AssetBase asset, LocalAssets.LocalAssetChang } } - public static readonly ModelProperty TitleProperty = ModelProperty.Register(typeof(MainWindowViewModel), "Title", typeof(string), String.Empty); + public static readonly ModelProperty TitleProperty = ModelProperty.Register(typeof(GameViewModel), "Title", typeof(string), String.Empty); public string Title { get { return (string)GetValue(TitleProperty); } private set { SetValue(TitleProperty, value); } } + public static readonly ModelProperty BadgeNameProperty = ModelProperty.Register(typeof(GameViewModel), "BadgeName", typeof(string), String.Empty); + public string BadgeName + { + get { return (string)GetValue(BadgeNameProperty); } + set { SetValue(BadgeNameProperty, value); } + } + public IEnumerable PublishedSets { get @@ -537,49 +589,49 @@ public IEnumerable PublishedSets } } - public static readonly ModelProperty GeneratedAchievementCountProperty = ModelProperty.Register(typeof(MainWindowViewModel), "GeneratedAchievementCount", typeof(int), 0); + public static readonly ModelProperty GeneratedAchievementCountProperty = ModelProperty.Register(typeof(GameViewModel), "GeneratedAchievementCount", typeof(int), 0); public int GeneratedAchievementCount { get { return (int)GetValue(GeneratedAchievementCountProperty); } private set { SetValue(GeneratedAchievementCountProperty, value); } } - public static readonly ModelProperty CoreAchievementCountProperty = ModelProperty.Register(typeof(MainWindowViewModel), "CoreAchievementCount", typeof(int), 0); + public static readonly ModelProperty CoreAchievementCountProperty = ModelProperty.Register(typeof(GameViewModel), "CoreAchievementCount", typeof(int), 0); public int CoreAchievementCount { get { return (int)GetValue(CoreAchievementCountProperty); } private set { SetValue(CoreAchievementCountProperty, value); } } - public static readonly ModelProperty CoreAchievementPointsProperty = ModelProperty.Register(typeof(MainWindowViewModel), "CoreAchievementPoints", typeof(int), 0); + public static readonly ModelProperty CoreAchievementPointsProperty = ModelProperty.Register(typeof(GameViewModel), "CoreAchievementPoints", typeof(int), 0); public int CoreAchievementPoints { get { return (int)GetValue(CoreAchievementPointsProperty); } private set { SetValue(CoreAchievementPointsProperty, value); } } - public static readonly ModelProperty UnofficialAchievementCountProperty = ModelProperty.Register(typeof(MainWindowViewModel), "UnofficialAchievementCount", typeof(int), 0); + public static readonly ModelProperty UnofficialAchievementCountProperty = ModelProperty.Register(typeof(GameViewModel), "UnofficialAchievementCount", typeof(int), 0); public int UnofficialAchievementCount { get { return (int)GetValue(UnofficialAchievementCountProperty); } private set { SetValue(UnofficialAchievementCountProperty, value); } } - public static readonly ModelProperty UnofficialAchievementPointsProperty = ModelProperty.Register(typeof(MainWindowViewModel), "UnofficialAchievementPoints", typeof(int), 0); + public static readonly ModelProperty UnofficialAchievementPointsProperty = ModelProperty.Register(typeof(GameViewModel), "UnofficialAchievementPoints", typeof(int), 0); public int UnofficialAchievementPoints { get { return (int)GetValue(UnofficialAchievementPointsProperty); } private set { SetValue(UnofficialAchievementPointsProperty, value); } } - public static readonly ModelProperty LocalAchievementCountProperty = ModelProperty.Register(typeof(MainWindowViewModel), "LocalAchievementCount", typeof(int), 0); + public static readonly ModelProperty LocalAchievementCountProperty = ModelProperty.Register(typeof(GameViewModel), "LocalAchievementCount", typeof(int), 0); public int LocalAchievementCount { get { return (int)GetValue(LocalAchievementCountProperty); } private set { SetValue(LocalAchievementCountProperty, value); } } - public static readonly ModelProperty LocalAchievementPointsProperty = ModelProperty.Register(typeof(MainWindowViewModel), "LocalAchievementPoints", typeof(int), 0); + public static readonly ModelProperty LocalAchievementPointsProperty = ModelProperty.Register(typeof(GameViewModel), "LocalAchievementPoints", typeof(int), 0); public int LocalAchievementPoints { get { return (int)GetValue(LocalAchievementPointsProperty); } diff --git a/Source/ViewModels/MainWindowViewModel.cs b/Source/ViewModels/MainWindowViewModel.cs index deb00b03..7ff3143b 100644 --- a/Source/ViewModels/MainWindowViewModel.cs +++ b/Source/ViewModels/MainWindowViewModel.cs @@ -4,12 +4,14 @@ using Jamiras.Services; using Jamiras.ViewModels; using RATools.Services; +using RATools.ViewModels.Navigation; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Xml.Linq; namespace RATools.ViewModels { @@ -28,7 +30,6 @@ public MainWindowViewModel() DragDropScriptCommand = new DelegateCommand(DragDropFile, CanDragDropFile); UpdateLocalCommand = DisabledCommand.Instance; - ViewAchievementsCommand = DisabledCommand.Instance; GameBadgesCommand = new DelegateCommand(GameBadges); GameStatsCommand = new DelegateCommand(GameStats); @@ -154,12 +155,10 @@ private static void OnGameChanged(object sender, ModelPropertyChangedEventArgs e vm.SaveScriptAsCommand = new DelegateCommand(() => vm.SaveScriptAs()); vm.RefreshScriptCommand = new DelegateCommand(vm.RefreshScript); vm.UpdateLocalCommand = new DelegateCommand(vm.UpdateLocal); - vm.ViewAchievementsCommand = new DelegateCommand(vm.ViewAchievements); vm.OnPropertyChanged(() => vm.SaveScriptCommand); vm.OnPropertyChanged(() => vm.SaveScriptAsCommand); vm.OnPropertyChanged(() => vm.RefreshScriptCommand); vm.OnPropertyChanged(() => vm.UpdateLocalCommand); - vm.OnPropertyChanged(() => vm.ViewAchievementsCommand); } } @@ -214,7 +213,7 @@ private void OpenFile(string filename) int line = 1; int column = 1; - string selectedEditor = null; + NavigationViewModelBase selectedNavigationNode = null; if (Game != null && Game.Script.Filename == filename) { if (Game.Script.CompareState == GeneratedCompareState.LocalDiffers) @@ -226,8 +225,7 @@ private void OpenFile(string filename) // capture current location so we can restore it after refreshing line = Game.Script.Editor.CursorLine; column = Game.Script.Editor.CursorColumn; - if (Game.SelectedEditor != null) - selectedEditor = Game.SelectedEditor.Title; + selectedNavigationNode = Game.SelectedNavigationNode; } else if (!CloseEditor()) { @@ -359,7 +357,7 @@ private void OpenFile(string filename) existingViewModel.Script.SetContent(content); viewModel = existingViewModel; - existingViewModel.SelectedEditor = existingViewModel.Editors.FirstOrDefault(e => e.Title == selectedEditor); + existingViewModel.UpdateSelectedNavigationNode(selectedNavigationNode); existingViewModel.Script.Editor.MoveCursorTo(line, column, Jamiras.ViewModels.CodeEditor.CodeEditorViewModel.MoveCursorFlags.None); } else @@ -483,20 +481,6 @@ private void UpdateLocal() dialog.ShowDialog(); } - public CommandBase ViewAchievementsCommand { get; private set; } - private void ViewAchievements() - { - var game = Game; - if (game == null) - { - TaskDialogViewModel.ShowErrorMessage("No game loaded", "Cannot view achievements if game not loaded."); - return; - } - - var dialog = new ViewAchievementsViewModel(game); - dialog.ShowDialog(); - } - public static readonly ModelProperty ShowHexValuesProperty = ModelProperty.Register(typeof(MainWindowViewModel), "ShowHexValues", typeof(bool), false, OnShowHexValuesChanged); public bool ShowHexValues { diff --git a/Source/ViewModels/Navigation/AchievementSetFolderNavigationViewModel.cs b/Source/ViewModels/Navigation/AchievementSetFolderNavigationViewModel.cs index 7d0144a1..7ce16830 100644 --- a/Source/ViewModels/Navigation/AchievementSetFolderNavigationViewModel.cs +++ b/Source/ViewModels/Navigation/AchievementSetFolderNavigationViewModel.cs @@ -11,5 +11,16 @@ public AchievementSetFolderNavigationViewModel(AchievementSet achievementSet) } public AchievementSet AchievementSet { get; private set; } + + public override bool Equals(object obj) + { + var that = obj as AchievementSetFolderNavigationViewModel; + return (that != null && this.AchievementSet.Id == that.AchievementSet.Id); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } } } diff --git a/Source/ViewModels/Navigation/AchievementsFolderNavigationViewModel.cs b/Source/ViewModels/Navigation/AchievementsFolderNavigationViewModel.cs new file mode 100644 index 00000000..763b2a50 --- /dev/null +++ b/Source/ViewModels/Navigation/AchievementsFolderNavigationViewModel.cs @@ -0,0 +1,28 @@ +using RATools.Data; +using System.Linq; + +namespace RATools.ViewModels.Navigation +{ + internal class AchievementsFolderNavigationViewModel : AssetFolderNavigationViewModel, IEditorNavigationViewModel + { + public AchievementsFolderNavigationViewModel(GameViewModel game, AchievementSet achievementSet) + : base("Achievements") + { + _game = game; + _achievementSet = achievementSet; + } + + private readonly GameViewModel _game; + private readonly AchievementSet _achievementSet; + private AchievementsListViewModel _editor; + + public ViewerViewModelBase Editor + { + get + { + _editor ??= new AchievementsListViewModel(_game, _achievementSet, Children.OfType()); + return _editor; + } + } + } +} diff --git a/Source/ViewModels/Navigation/EditorNavigationViewModelBase.cs b/Source/ViewModels/Navigation/EditorNavigationViewModelBase.cs index 6310df6f..1faf10f1 100644 --- a/Source/ViewModels/Navigation/EditorNavigationViewModelBase.cs +++ b/Source/ViewModels/Navigation/EditorNavigationViewModelBase.cs @@ -7,7 +7,7 @@ namespace RATools.ViewModels.Navigation { - public abstract class EditorNavigationViewModelBase : NavigationViewModelBase + public abstract class EditorNavigationViewModelBase : NavigationViewModelBase, IEditorNavigationViewModel { public EditorNavigationViewModelBase() { @@ -125,5 +125,28 @@ public bool IsNodeFor(AssetViewModelBase editor) return false; } + public override bool Equals(object obj) + { + var that = obj as EditorNavigationViewModelBase; + if (that == null || this.Editor?.ViewerType != that.Editor?.ViewerType) + return false; + + var assetEditorViewModel = this.Editor as AssetViewModelBase; + if (assetEditorViewModel != null) + return assetEditorViewModel.Id == ((AssetViewModelBase)that.Editor).Id; + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + public interface IEditorNavigationViewModel + { + public ViewerViewModelBase Editor { get; } } + } diff --git a/Source/ViewModels/Navigation/NavigationListViewModel.cs b/Source/ViewModels/Navigation/NavigationListViewModel.cs index 014f2b19..1ee060a6 100644 --- a/Source/ViewModels/Navigation/NavigationListViewModel.cs +++ b/Source/ViewModels/Navigation/NavigationListViewModel.cs @@ -363,7 +363,7 @@ private void UpdateNavigationNodes(IEnumerable navigati bool hasSubsets = false; foreach (var achievementSetNode in navigationNodes.OfType()) { - var achievementsFolder = achievementSetNode.Children.OfType().First(n => n.Label == "Achievements"); + var achievementsFolder = achievementSetNode.Children.OfType().First(); var leaderboardsFolder = achievementSetNode.Children.OfType().First(n => n.Label == "Leaderboards"); UpdateAchievementSetNodes(achievementsFolder, leaderboardsFolder, achievementSetNode.AchievementSet); hasSubsets = true; @@ -371,7 +371,7 @@ private void UpdateNavigationNodes(IEnumerable navigati if (!hasSubsets) { - var achievementsFolder = navigationNodes.OfType().First(n => n.Label == "Achievements"); + var achievementsFolder = navigationNodes.OfType().First(); var leaderboardsFolder = navigationNodes.OfType().First(n => n.Label == "Leaderboards"); UpdateAchievementSetNodes(achievementsFolder, leaderboardsFolder, null); } @@ -382,11 +382,8 @@ private void UpdateAchievementSetNodes(AssetFolderNavigationViewModel achievemen var achievementNodes = achievementsFolder.Children.OfType().ToList(); foreach (var achievement in _editors.OfType()) { - if (achievementSet != null && achievement.OwnerSetId != achievementSet.Id) - { - if (achievement.OwnerSetId != 0 || achievementSet.Type != AchievementSetType.Core) - continue; - } + if (!achievement.BelongsToSet(achievementSet)) + continue; if (achievement.Generated.Asset == null && achievement.Local.Asset == null && achievement.Published.Asset == null) { @@ -513,7 +510,7 @@ public IEnumerable Merge(AchievementScriptInterpreter i if (sets.Count < 2) { - newNavigationNodes.Add(new AssetFolderNavigationViewModel("Achievements")); + newNavigationNodes.Add(new AchievementsFolderNavigationViewModel(_gameViewModel, sets[0])); newNavigationNodes.Add(new AssetFolderNavigationViewModel("Leaderboards")); } else @@ -521,7 +518,7 @@ public IEnumerable Merge(AchievementScriptInterpreter i foreach (var achievementSet in sets) { var setFolderNode = new AchievementSetFolderNavigationViewModel(achievementSet); - setFolderNode.AddChild(new AssetFolderNavigationViewModel("Achievements")); + setFolderNode.AddChild(new AchievementsFolderNavigationViewModel(_gameViewModel, achievementSet)); setFolderNode.AddChild(new AssetFolderNavigationViewModel("Leaderboards")); newNavigationNodes.Add(setFolderNode); } @@ -539,11 +536,13 @@ public IEnumerable Merge(AchievementScriptInterpreter i MergePublished(); } - if (_localAssets != null) - MergeLocal(); - if (interpreter != null) + { + if (_localAssets != null) + MergeLocal(); + MergeGenerated(interpreter); + } UpdateTemporaryIds(); diff --git a/Source/ViewModels/Navigation/NavigationViewModelBase.cs b/Source/ViewModels/Navigation/NavigationViewModelBase.cs index 9ca2d11b..e332ba64 100644 --- a/Source/ViewModels/Navigation/NavigationViewModelBase.cs +++ b/Source/ViewModels/Navigation/NavigationViewModelBase.cs @@ -96,6 +96,13 @@ internal void UpdateCompareState() CompareState = compareState; } + public static readonly ModelProperty IsSelectedProperty = ModelProperty.Register(typeof(NavigationViewModelBase), "IsSelected", typeof(bool), false); + public bool IsSelected + { + get { return (bool)GetValue(IsSelectedProperty); } + set { SetValue(IsSelectedProperty, value); } + } + public static readonly ModelProperty IsExpandedProperty = ModelProperty.Register(typeof(NavigationViewModelBase), "IsExpanded", typeof(bool), true); public bool IsExpanded { @@ -122,5 +129,37 @@ public void AddChild(NavigationViewModelBase child) } public IEnumerable ContextMenu { get; protected set; } + + public static bool operator ==(NavigationViewModelBase left, NavigationViewModelBase right) + { + if (ReferenceEquals(left, right)) + return true; + if (left is null || right is null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(NavigationViewModelBase left, NavigationViewModelBase right) + { + if (ReferenceEquals(left, right)) + return false; + if (left is null || right is null) + return true; + + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + var that = obj as NavigationViewModelBase; + + return that != null && this.ImageName == that.ImageName && this.Label == that.Label; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } } } diff --git a/Source/ViewModels/ViewAchievementsViewModel.cs b/Source/ViewModels/ViewAchievementsViewModel.cs deleted file mode 100644 index 52fb2eea..00000000 --- a/Source/ViewModels/ViewAchievementsViewModel.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Jamiras.Commands; -using Jamiras.ViewModels; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace RATools.ViewModels -{ - public class ViewAchievementsViewModel : DialogViewModelBase - { - public ViewAchievementsViewModel(GameViewModel game) - { - _game = game; - - DialogTitle = "Achievements - " + game.Title; - CanClose = true; - CancelButtonText = null; - ExtraButtonCommand = new DelegateCommand(Export); - - Achievements = game.Editors.OfType().ToArray(); - } - - private readonly GameViewModel _game; - - /// - /// Gets the list of achievements. - /// - public IEnumerable Achievements { get; private set; } - - public string ExtraButtonText { get { return "Export"; } } - public CommandBase ExtraButtonCommand { get; private set; } - - private void Export() - { - var filename = Path.GetFileNameWithoutExtension(_game.Script.Filename) ?? "achievements"; - - var vm = new FileDialogViewModel(); - vm.DialogTitle = "Export achievement information"; - vm.Filters["CSV file"] = "*.csv"; - vm.FileNames = new[] { filename + ".csv" }; - vm.OverwritePrompt = true; - - if (vm.ShowSaveFileDialog() == DialogResult.Ok) - { - using (var file = File.CreateText(vm.FileNames[0])) - { - file.WriteLine("Id,Title,Description,Points"); - - foreach (var achievement in Achievements) - { - file.Write("{0},\"{1}\",", achievement.Id, achievement.Title.Replace("\"", "\\\"")); - file.Write("\"{0}\",", achievement.Description.Replace("\"", "\\\"")); - file.Write("{0}", achievement.Points); - file.WriteLine(); - } - } - } - } - } -} diff --git a/Source/ViewModels/ViewerViewModelBase.cs b/Source/ViewModels/ViewerViewModelBase.cs index 727b5f09..a868855b 100644 --- a/Source/ViewModels/ViewerViewModelBase.cs +++ b/Source/ViewModels/ViewerViewModelBase.cs @@ -23,6 +23,8 @@ public virtual string ViewerImage get { return String.Format("/RATools;component/Resources/{0}.png", ViewerType.ToLower()); } } + public virtual int ViewerId { get { return 0; } } + public CommandBase UpdateLocalCommand { get; protected set; } internal int SortOrder { get; set; } diff --git a/Source/Views/AchievementsListViewer.xaml b/Source/Views/AchievementsListViewer.xaml new file mode 100644 index 00000000..94199289 --- /dev/null +++ b/Source/Views/AchievementsListViewer.xaml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Views/GameViewer.xaml b/Source/Views/GameViewer.xaml index f3394b9b..35b79cd9 100644 --- a/Source/Views/GameViewer.xaml +++ b/Source/Views/GameViewer.xaml @@ -5,6 +5,7 @@ + @@ -156,6 +157,7 @@ + diff --git a/Source/Views/MainWindow.xaml b/Source/Views/MainWindow.xaml index 059034fc..5a353afd 100644 --- a/Source/Views/MainWindow.xaml +++ b/Source/Views/MainWindow.xaml @@ -54,8 +54,6 @@ - - diff --git a/Source/Views/MainWindow.xaml.cs b/Source/Views/MainWindow.xaml.cs index 508ca812..0b9d1de9 100644 --- a/Source/Views/MainWindow.xaml.cs +++ b/Source/Views/MainWindow.xaml.cs @@ -58,7 +58,6 @@ protected override void OnInitialized(EventArgs e) dialogService.RegisterDialogHandler(typeof(NewScriptDialogViewModel), vm => new NewScriptDialog()); dialogService.RegisterDialogHandler(typeof(OptionsDialogViewModel), vm => new OkCancelView(new OptionsDialog())); dialogService.RegisterDialogHandler(typeof(UpdateLocalViewModel), vm => new OkCancelView(new UpdateLocalDialog())); - dialogService.RegisterDialogHandler(typeof(ViewAchievementsViewModel), vm => new OkCancelView(new ViewAchievementsDialog())); dialogService.RegisterDialogHandler(typeof(GameBadgesViewModel), vm => new GameBadgesDialog()); dialogService.RegisterDialogHandler(typeof(GameStatsViewModel), vm => new GameStatsDialog()); dialogService.RegisterDialogHandler(typeof(GameStatsViewModel.UserHistoryViewModel), vm => new UserHistoryDialog()); diff --git a/Source/Views/ViewAchievementsDialog.xaml b/Source/Views/ViewAchievementsDialog.xaml deleted file mode 100644 index cc18a140..00000000 --- a/Source/Views/ViewAchievementsDialog.xaml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Source/Views/ViewAchievementsDialog.xaml.cs b/Source/Views/ViewAchievementsDialog.xaml.cs deleted file mode 100644 index c6644f8f..00000000 --- a/Source/Views/ViewAchievementsDialog.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows.Controls; - -namespace RATools.Views -{ - /// - /// Interaction logic for ViewAchievementsDialog.xaml - /// - public partial class ViewAchievementsDialog : UserControl - { - public ViewAchievementsDialog() - { - InitializeComponent(); - } - } -} diff --git a/Tests/ViewModels/Nagivation/NavigationListViewModelTests.cs b/Tests/ViewModels/Nagivation/NavigationListViewModelTests.cs index 87a6c103..c0d62f0d 100644 --- a/Tests/ViewModels/Nagivation/NavigationListViewModelTests.cs +++ b/Tests/ViewModels/Nagivation/NavigationListViewModelTests.cs @@ -165,8 +165,15 @@ public void TestMergeLocalAchievement() { var harness = new NavigationListViewModelHarness(); var achievement = harness.AddLocalAchievement("Test Achievement"); + + // if generated assets aren't provided, local assets shouldn't be loaded. this prevents them + // from appearing as "Local, but not generated" while the script is initially being processed. harness.Merge(null); + Assert.AreEqual("Achievements", harness.NavigationNodes[2].Label); + Assert.AreEqual(0, harness.NavigationNodes[2].Children?.Count ?? 0); + // when generated assets are provided, local assets should be loaded. + harness.Merge(new AchievementScriptInterpreter()); Assert.AreEqual("Achievements", harness.NavigationNodes[2].Label); Assert.AreEqual(1, harness.NavigationNodes[2].Children?.Count ?? 0); @@ -218,7 +225,7 @@ public void TestMergeLocalAndPublishedAchievementIdentical() publishedAchievement.Id = 12345; var localAchievement = harness.AddLocalAchievement("Test Achievement"); localAchievement.Id = 12345; - harness.Merge(null); + harness.Merge(new AchievementScriptInterpreter()); Assert.AreEqual("Achievements", harness.NavigationNodes[2].Label); Assert.AreEqual(1, harness.NavigationNodes[2].Children?.Count ?? 0); @@ -247,7 +254,7 @@ public void TestMergeLocalAndPublishedAchievementSameId() publishedAchievement.Id = 12345; var localAchievement = harness.AddLocalAchievement("Test Achievement 2"); localAchievement.Id = 12345; - harness.Merge(null); + harness.Merge(new AchievementScriptInterpreter()); Assert.AreEqual("Achievements", harness.NavigationNodes[2].Label); Assert.AreEqual(1, harness.NavigationNodes[2].Children?.Count ?? 0); @@ -276,7 +283,7 @@ public void TestMergeLocalAndPublishedAchievementDifferentId() publishedAchievement.Id = 12345; var localAchievement = harness.AddLocalAchievement("Test Achievement 2"); localAchievement.Id = 12346; - harness.Merge(null); + harness.Merge(new AchievementScriptInterpreter()); Assert.AreEqual("Achievements", harness.NavigationNodes[2].Label); Assert.AreEqual(2, harness.NavigationNodes[2].Children?.Count ?? 0);