diff --git a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs index 9fdf91b3..c9254c63 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs @@ -415,6 +415,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private double cursorX, cursorY, deltaX, deltaY; public event EventHandler AutoscrollBlocks; + private bool showHighlightCursor; + public bool ShowHighlightCursor { get => showHighlightCursor; set => Set(ref showHighlightCursor, value); } + private double highlightCursorX, highlightCursorY, highlightCursorWidth, highlightCursorHeight; public double HighlightCursorX { get => highlightCursorX; set => Set(ref highlightCursorX, value); } public double HighlightCursorY { get => highlightCursorY; set => Set(ref highlightCursorY, value); } @@ -458,6 +461,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var (prevW, prevH) = (highlightCursorWidth, highlightCursorHeight); var border = primaryMap.GetBorderThickness(); + ShowHighlightCursor = true; HighlightCursorX = (left + border.West + width / 2.0) * 16 * primaryMap.SpriteScale + primaryMap.LeftEdge; HighlightCursorY = (top + border.North + height / 2.0) * 16 * primaryMap.SpriteScale + primaryMap.TopEdge; HighlightCursorWidth = width * 16 * primaryMap.SpriteScale + 4; @@ -479,7 +483,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (map != null) UpdatePrimaryMap(map); } - public void DragMove(double x, double y) { + public void DragMove(double x, double y, bool isMiddleClickMap) { deltaX += x - cursorX; deltaY += y - cursorY; var (intX, intY) = ((int)deltaX, (int)deltaY); @@ -487,8 +491,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { deltaY -= intY; (cursorX, cursorY) = (x, y); Pan(intX, intY); - if (intX != 0 || intY != 0) Tutorials.Complete(Tutorial.MiddleClick_PanMap); - Hover(x, y); + if ((intX != 0 || intY != 0) && isMiddleClickMap) Tutorials.Complete(Tutorial.MiddleClick_PanMap); + if (isMiddleClickMap) Hover(x, y); } public void DragUp(double x, double y) { } @@ -594,6 +598,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { signpost.Pointer < model.Count ) { viewPort.Goto.Execute(signpost.Pointer); + Tutorials.Complete(Tutorial.DoubleClickEvent_SeeScript); } else { Tutorials.Complete(Tutorial.LeftClick_SelectEvent); interactionType = PrimaryInteractionType.Event; diff --git a/src/HexManiac.Core/ViewModels/Map/MapSlider.cs b/src/HexManiac.Core/ViewModels/Map/MapSlider.cs index d4ffd2f5..fea6368c 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapSlider.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapSlider.cs @@ -9,6 +9,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public const int SliderSize = 20; protected readonly int id; + public abstract string Tooltip { get; } + private MapSliderIcons icon; public MapSliderIcons Icon { get => icon; set => SetEnum(ref icon, value); } @@ -57,6 +59,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private Action notify; private ConnectionModel connection, inverse; + public override string Tooltip => "Drag to adjust the connection between the maps."; + public ConnectionSlider(ConnectionModel connection, (int group, int num) sourceMapInfo, Action notify, int id, MapSliderIcons icon, MapTutorialsViewModel tutorials, int left = int.MinValue, int top = int.MinValue, int right = int.MinValue, int bottom = int.MinValue) : base(id, icon, left, top, right, bottom) { (this.notify, this.connection) = (notify, connection); this.tutorials = tutorials; @@ -103,6 +107,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public class ExpansionSlider : MapSlider { private Action resize; + public override string Tooltip => $"Drag to change the {(Icon == MapSliderIcons.LeftRight ? "width" : "height")} of the map"; + public ExpansionSlider(Action resize, int id, MapSliderIcons icon, IEnumerable contextItems, int left = int.MinValue, int top = int.MinValue, int right = int.MinValue, int bottom = int.MinValue) : base(id, icon, left, top, right, bottom) { foreach (var item in contextItems) ContextItems.Add(item); this.resize = resize; diff --git a/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs b/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs index d280ae52..aaf25837 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs @@ -58,32 +58,34 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { double completed = Tutorials.Sum(tut => tut.Incomplete ? 0 : 1); return $"{completed/Tutorials.Count:p}"; } - } + } + + public bool SeeMore => Tutorials.Count(tut => tut.Incomplete) > VisibleCount; public MapTutorialsViewModel() { // general navigation { - Tutorials.Add(new(MiddleClick, "Pan Map", "Middle-Click and drag to move the maps around on the screen.")); - Tutorials.Add(new("ArrowKeys", "Pan Map", "Use the arrow keys to move the maps around on the screen.")); + Tutorials.Add(new(MiddleClick, "Move Map", "Middle-Click and drag to move the maps around on the screen.")); + Tutorials.Add(new("ArrowKeys", "Move Map", "Use the arrow keys to move the maps around on the screen.")); Tutorials.Add(new(MiddleClick, "Zoom Map", "Use the scroll wheel to zoom in and out of the map. Zooming in will hide the border blocks.")); } // select block / draw / paint { - Tutorials.Add(new(LeftClick, "Select Block", "Click a block in the block panel to select it.")); + Tutorials.Add(new(LeftClick, "Select Block", "Click a block in the Blocks panel to select it.")); Tutorials.Add(new(LeftClick, "Draw Block", "Click/Drag over the map to draw with the selected block.")); Tutorials.Add(new("UndoArrow", "Undo", "Use the Undo button in the toolbar to undo any mistakes.")); - Tutorials.Add(new("LeftMouseDoubleClick", "Paint Block", "Double-Click over the map to paint with the selected block.")); + Tutorials.Add(new("FillBucket", "Paint Blocks", "Double-Click over the map to paint with the selected block.")); Tutorials.Add(new("EscapeKey", "Unselect Block", "Press the escape key to unselect a block.")); Tutorials.Add(new(LeftClick, "Select Blocks", "Left-Click and drag on the block panel to select multiple blocks. When multiple blocks are selected, you can draw, but not paint.")); } // editing blocks { - Tutorials.Add(new("RightAngleArrow", "Edit Blocks", "Select a block, then click this button in the block panel to show the tile panel.")); - Tutorials.Add(new(LeftClick, "Select Tile", "Click a tile to select it.")); - Tutorials.Add(new(LeftClick, "Draw Tile", "Click on a block's tile at the top of the block panel to replace it with your selected tile/palette.")); - Tutorials.Add(new("ArrowsLeftRight", "Flip Tile", "Click the arrows next to a tile to flip that tile vertically or horizontally.")); + Tutorials.Add(new("RightAngleArrow", "Edit Block", "Blocks are made of Tiles in a 2x2 grid, with foreground and background layers. Select a block, then click 'Edit Block' in the block panel to show the tile panel.")); + Tutorials.Add(new(LeftClick, "Select Tile", "Click a tile in the Tile panel to select it.")); + Tutorials.Add(new(LeftClick, "Draw Tile", "Click on a block's foreground or background tile at the top of the block panel to replace it with your selected tile/palette.")); + Tutorials.Add(new("ArrowsLeftRight", "Flip Tile", "Click the arrows next to the selected block's foreground/background tile to flip that tile vertically or horizontally.")); } // right-click map @@ -94,8 +96,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { // editing events { - Tutorials.Add(new(LeftClick, "Select Event", "Click an event in the map to select it. Selecting an event will unselect any active blocks.")); - Tutorials.Add(new("LeftMouseDoubleClick", "Follow Warp", "Double-Click on a warp to go to the map it references.")); + Tutorials.Add(new(LeftClick, "Select Event", "Click an event (character, blue warp, green script, or red signpost) in the map to select it. Selecting an event will unselect any active blocks.")); + Tutorials.Add(new("LeftMouseDoubleClick", "Follow Warp", "Double-Click on a warp event to go to the map it references.")); Tutorials.Add(new("LeftArrow", "Go Back", "Use the back arrow in the toolbar to return to the previous map or data.")); Tutorials.Add(new("FourDirectionArrows", "Move Event", "Drag an event to move it on the map.")); Tutorials.Add(new(LeftClick, "Unselect Event", "Click on any part of the map to unselect the event.")); @@ -118,12 +120,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { Tutorials.Add(new("Selection", "Create Object", "Drag the character sprite from the top toolbar to create object events.")); Tutorials.Add(new("Selection", "Create Event", "Drag the Blue/Green/Red squares from the top toolbar to create warp events, script events, and signpost events.")); Tutorials.Add(new(RightClick, "Warp New Map", "Right-Click a warp to create a new map to warp to.")); - Tutorials.Add(new("Settings", "Configure Object", "Click the icon next to the character sprite to choose what type of object you want to add.")); + Tutorials.Add(new("Settings", "Configure Object", "Click the gear icon next to the character sprite to choose what type of object you want to add.")); } Reset(); - foreach (var tut in Tutorials) { - tut.RequestClose += (sender, e) => Complete((Tutorial)Tutorials.IndexOf((MapTutorialViewModel)sender)); + for(int i = 0; i < Tutorials.Count; i++) { + Tutorials[i].RequestClose += (sender, e) => Complete((Tutorial)Tutorials.IndexOf((MapTutorialViewModel)sender)); + Tutorials[i].Index = i + 1; } } @@ -167,7 +170,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { Tutorials[i].TargetPosition -= 1; if (Tutorials[i].Incomplete && Tutorials[i].TargetPosition < VisibleCount) Tutorials[i].TriggerAnimation(); } - NotifyPropertyChanged(nameof(CompletionPercent)); + NotifyPropertiesChanged(nameof(CompletionPercent), nameof(SeeMore)); } } @@ -181,6 +184,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public bool Incomplete { get => incomplete; set => PropertyChanged.TryUpdate(this, ref incomplete, value); } public double TargetPosition { get => target; set => PropertyChanged.TryUpdate(this, ref target, value); } public double TopEdge => TargetPosition * 90; + public int Index { get; set; } + public string IndexText => $"{Index} of 32"; public void TriggerAnimation() => AnimateMovement.Raise(this); diff --git a/src/HexManiac.WPF/Controls/MapTab.xaml b/src/HexManiac.WPF/Controls/MapTab.xaml index 187ef5ee..0a30fa16 100644 --- a/src/HexManiac.WPF/Controls/MapTab.xaml +++ b/src/HexManiac.WPF/Controls/MapTab.xaml @@ -25,7 +25,7 @@ - + @@ -60,7 +60,7 @@ - + @@ -694,7 +694,7 @@ + MouseDown="ButtonDown" MouseMove="ButtonMove" MouseUp="ButtonUp" MouseLeave="ButtonLeave" MouseWheel="Wheel"> @@ -764,6 +764,9 @@ + + + @@ -776,7 +779,7 @@ - diff --git a/src/HexManiac.WPF/Controls/MapTab.xaml.cs b/src/HexManiac.WPF/Controls/MapTab.xaml.cs index 37d34b34..034381f9 100644 --- a/src/HexManiac.WPF/Controls/MapTab.xaml.cs +++ b/src/HexManiac.WPF/Controls/MapTab.xaml.cs @@ -130,7 +130,7 @@ namespace HavenSoft.HexManiac.WPF.Controls { if (withinMapInteraction == MouseButton.Left) { vm.PrimaryMove(p.X, p.Y); } else if (withinMapInteraction == MouseButton.Middle) { - vm.DragMove(p.X, p.Y); + vm.DragMove(p.X, p.Y, true); } else if (withinMapInteraction == MouseButton.Right) { vm.SelectMove(p.X, p.Y); } @@ -156,6 +156,56 @@ namespace HavenSoft.HexManiac.WPF.Controls { } } + private void ButtonLeave(object sender, MouseEventArgs e) { + UpdateTooltipContent(NoTooltip); + ViewModel.ShowHighlightCursor = false; + } + + private void BackgroundDown(object sender, MouseButtonEventArgs e) { + if (e.ChangedButton == MouseButton.XButton1 && ViewModel.Back.CanExecute(null)) { + ViewModel.Back.Execute(); + return; + } + if (e.ChangedButton == MouseButton.XButton2 && ViewModel.Forward.CanExecute(null)) { + ViewModel.Forward.Execute(); + return; + } + if (withinMapInteraction != MouseButton.XButton1) return; + UpdateTooltipContent(NoTooltip); + Focus(); + e.Handled = true; + var element = (FrameworkElement)sender; + var vm = ViewModel; + var p = GetCoordinates(element, e); + element.CaptureMouse(); + withinMapInteraction = MouseButton.Middle; + vm.DragDown(p.X, p.Y); + } + + private void BackgroundMove(object sender, MouseEventArgs e) { + var element = (FrameworkElement)sender; + var vm = ViewModel; + if (vm == null) return; + var p = GetCoordinates(element, e); + e.Handled = true; + if (withinMapInteraction == MouseButton.XButton1) return; + vm.DragMove(p.X, p.Y, false); + } + + private void BackgroundUp(object sender, MouseButtonEventArgs e) { + e.Handled = true; + var element = (FrameworkElement)sender; + var previousInteraction = withinMapInteraction; + withinMapInteraction = MouseButton.XButton1; + element.ReleaseMouseCapture(); + if (previousInteraction == MouseButton.XButton1) return; + if (e.ChangedButton != previousInteraction) return; + var vm = ViewModel; + if (vm == null) return; + var p = GetCoordinates(element, e); + vm.DragUp(p.X, p.Y); + } + private void Wheel(object sender, MouseWheelEventArgs e) { var element = (FrameworkElement)sender; var vm = ViewModel; diff --git a/src/HexManiac.WPF/Controls/TutorialControl.xaml b/src/HexManiac.WPF/Controls/TutorialControl.xaml index 40735e40..5135e764 100644 --- a/src/HexManiac.WPF/Controls/TutorialControl.xaml +++ b/src/HexManiac.WPF/Controls/TutorialControl.xaml @@ -17,10 +17,11 @@ - + + @@ -44,6 +45,7 @@ + @@ -52,5 +54,20 @@ + + + + + + Complete actions to clear tutorials. + + Additional tutorials will show until you've completed them all. + + + + + + diff --git a/src/HexManiac.WPF/Controls/TutorialControl.xaml.cs b/src/HexManiac.WPF/Controls/TutorialControl.xaml.cs index 2c1f7793..90e2c70b 100644 --- a/src/HexManiac.WPF/Controls/TutorialControl.xaml.cs +++ b/src/HexManiac.WPF/Controls/TutorialControl.xaml.cs @@ -1,11 +1,9 @@ using HavenSoft.HexManiac.Core.ViewModels.Map; using System; -using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Animation; -using System.Xml.Linq; namespace HavenSoft.HexManiac.WPF.Controls { public partial class TutorialControl {