diff --git a/NewWorldCompanion.Constants/EmguConstants.cs b/NewWorldCompanion.Constants/EmguConstants.cs index aefe3ae..3a09279 100644 --- a/NewWorldCompanion.Constants/EmguConstants.cs +++ b/NewWorldCompanion.Constants/EmguConstants.cs @@ -7,8 +7,8 @@ public class EmguConstants public const int Default1600900AreaUpper = 7000; public const int Default19201080AreaLower = 6000; public const int Default19201080AreaUpper = 8000; - public const int Default25601440AreaLower = 10000; - public const int Default25601440AreaUpper = 15000; + public const int Default25601440AreaLower = 13000; + public const int Default25601440AreaUpper = 15500; public const int Default38402160AreaLower = 22600; public const int Default38402160AreaUpper = 26600; diff --git a/NewWorldCompanion.Entities/ItemDefinition.cs b/NewWorldCompanion.Entities/ItemDefinition.cs index b691e85..0065ac0 100644 --- a/NewWorldCompanion.Entities/ItemDefinition.cs +++ b/NewWorldCompanion.Entities/ItemDefinition.cs @@ -12,5 +12,7 @@ public class ItemDefinition public string Name { get; set; } = string.Empty; /// Used to define if an item is tradable public bool BindOnPickup { get; set; } = false; + public bool BindOnEquip { get; set; } = false; + } } diff --git a/NewWorldCompanion.Entities/SettingsNWC.cs b/NewWorldCompanion.Entities/SettingsNWC.cs index 99f0af5..203b455 100644 --- a/NewWorldCompanion.Entities/SettingsNWC.cs +++ b/NewWorldCompanion.Entities/SettingsNWC.cs @@ -14,6 +14,7 @@ public class SettingsNWC // Overlay public bool TooltipEnabled { get; set; } = true; public bool ExtendedTooltipEnabled { get; set; } = false; + public bool NamedItemsTooltipEnabled { get; set; } = false; public int PriceServerId { get; set; } = 1; // Shape detection @@ -28,5 +29,19 @@ public class SettingsNWC public int EmguThresholdMaxR { get; set; } = 200; public int EmguThresholdMaxG { get; set; } = 235; public int EmguThresholdMaxB { get; set; } = 255; + + // Named items + public bool NamedItemsFilterTier2 { get; set; } = true; + public bool NamedItemsFilterTier3 { get; set; } = true; + public bool NamedItemsFilterTier4 { get; set; } = true; + public bool NamedItemsFilterTier5 { get; set; } = true; + public bool NamedItemsFilterItemClassArmor { get; set; } = true; + public bool NamedItemsFilterItemClassJewelry { get; set; } = true; + public bool NamedItemsFilterItemClassWeapon { get; set; } = true; + public bool NamedItemsFilterStorageCollected { get; set; } = true; + public bool NamedItemsFilterStorageMissing { get; set; } = true; + public bool NamedItemsFilterStorageDuplicates { get; set; } = true; + public bool NamedItemsFilterBindOnEquip { get; set; } = true; + public bool NamedItemsFilterBindOnPickup { get; set; } = true; } } diff --git a/NewWorldCompanion.Events/ScreenCaptureEvent.cs b/NewWorldCompanion.Events/ScreenCaptureEvent.cs index bd41e53..0110e11 100644 --- a/NewWorldCompanion.Events/ScreenCaptureEvent.cs +++ b/NewWorldCompanion.Events/ScreenCaptureEvent.cs @@ -9,4 +9,9 @@ public class ScreenCaptureReadyEvent : PubSubEvent public class MouseCoordinatesUpdatedEvent : PubSubEvent { } + + public class MouseDeltaUpdatedEvent : PubSubEvent + { + + } } diff --git a/NewWorldCompanion.Events/StorageManagerEvents.cs b/NewWorldCompanion.Events/StorageManagerEvents.cs new file mode 100644 index 0000000..4e860d4 --- /dev/null +++ b/NewWorldCompanion.Events/StorageManagerEvents.cs @@ -0,0 +1,8 @@ +using Prism.Events; + +namespace NewWorldCompanion.Events +{ + public class StorageManagerUpdated : PubSubEvent + { + } +} \ No newline at end of file diff --git a/NewWorldCompanion.Interfaces/INewWorldDataStore.cs b/NewWorldCompanion.Interfaces/INewWorldDataStore.cs index fb61c83..6d0db46 100644 --- a/NewWorldCompanion.Interfaces/INewWorldDataStore.cs +++ b/NewWorldCompanion.Interfaces/INewWorldDataStore.cs @@ -14,14 +14,18 @@ public interface INewWorldDataStore string LoadStatusCraftingRecipes { get; } string LoadStatusHouseItems { get; } string LoadStatusLocalisation { get; } + string LoadStatusLocalisationNamed { get; } List GetCraftingRecipes(); bool IsBindOnPickup(string itemName); + bool IsNamedItem(string itemName); string GetItemId(string itemName); ItemDefinition? GetItem(string itemId); string GetLevenshteinItemName(string itemName); List GetOverlayResources(); + List GetNamedItems(); string GetItemLocalisation(string itemMasterName); + string GetNamedItemLocalisation(string itemMasterName); List GetRelatedRecipes(string itemId); CraftingRecipeJson GetCraftingRecipeDetails(string itemId); void UpdateStoreData(); diff --git a/NewWorldCompanion.Interfaces/IStorageManager.cs b/NewWorldCompanion.Interfaces/IStorageManager.cs index 8d55461..07825e2 100644 --- a/NewWorldCompanion.Interfaces/IStorageManager.cs +++ b/NewWorldCompanion.Interfaces/IStorageManager.cs @@ -15,6 +15,7 @@ ObservableCollection Items get; } + string GetItemStorageInfo(string itemName); void SaveStorage(); } } \ No newline at end of file diff --git a/NewWorldCompanion.Services/NewWorldDataStore.cs b/NewWorldCompanion.Services/NewWorldDataStore.cs index ba7dc89..cc43930 100644 --- a/NewWorldCompanion.Services/NewWorldDataStore.cs +++ b/NewWorldCompanion.Services/NewWorldDataStore.cs @@ -1,10 +1,12 @@ -using NewWorldCompanion.Constants; +using Microsoft.Extensions.Logging; +using NewWorldCompanion.Constants; using NewWorldCompanion.Entities; using NewWorldCompanion.Events; using NewWorldCompanion.Helpers; using NewWorldCompanion.Interfaces; using Prism.Events; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -21,11 +23,14 @@ namespace NewWorldCompanion.Services public class NewWorldDataStore : INewWorldDataStore { private readonly IEventAggregator _eventAggregator; + private readonly ILogger _logger; private List _masterItemDefinitionsJson = new List(); + private List _masterItemDefinitionsJson_Named = new List(); private List _craftingRecipesJson = new List(); private List _houseItemsJson = new List(); - private Dictionary _itemDefinitionsLocalisation = new Dictionary(); + private ConcurrentDictionary _itemDefinitionsLocalisation = new ConcurrentDictionary(50, 15000); + private ConcurrentDictionary _namedItemDefinitionsLocalisation = new ConcurrentDictionary(50, 15000); private bool _available = false; @@ -33,17 +38,21 @@ public class NewWorldDataStore : INewWorldDataStore private string _loadStatusCraftingRecipes = string.Empty; private string _loadStatusHouseItems = string.Empty; private string _loadStatusLocalisation = string.Empty; + private string _loadStatusLocalisationNamed = string.Empty; // Start of Constructor region #region Constructor - public NewWorldDataStore(IEventAggregator eventAggregator) + public NewWorldDataStore(IEventAggregator eventAggregator, ILogger logger) { // Init IEventAggregator _eventAggregator = eventAggregator; + // Init logger + _logger = logger; + // Init store data Task.Run(() => UpdateStoreData()); } @@ -59,6 +68,7 @@ public NewWorldDataStore(IEventAggregator eventAggregator) public string LoadStatusCraftingRecipes { get => _loadStatusCraftingRecipes; set => _loadStatusCraftingRecipes = value; } public string LoadStatusHouseItems { get => _loadStatusHouseItems; set => _loadStatusHouseItems = value; } public string LoadStatusLocalisation { get => _loadStatusLocalisation; set => _loadStatusLocalisation = value; } + public string LoadStatusLocalisationNamed { get => _loadStatusLocalisationNamed; set => _loadStatusLocalisationNamed = value; } #endregion @@ -73,8 +83,10 @@ public void UpdateStoreData() _loadStatusItemDefinitions = $"ItemDefinitions: 0. Loading common items"; _eventAggregator.GetEvent().Publish(); - // MasterItemDefinitions Common _masterItemDefinitionsJson.Clear(); + _masterItemDefinitionsJson_Named.Clear(); + + // MasterItemDefinitions Common var masterItemDefinitionsJson = new List(); resourcePath = @".\Data\MasterItemDefinitions_Common.json"; using (FileStream? stream = File.OpenRead(resourcePath)) @@ -164,6 +176,7 @@ public void UpdateStoreData() masterItemDefinitionsJson = JsonSerializer.Deserialize>(stream, options) ?? new List(); _masterItemDefinitionsJson.AddRange(masterItemDefinitionsJson); + _masterItemDefinitionsJson_Named.AddRange(masterItemDefinitionsJson); } } @@ -232,6 +245,7 @@ public void UpdateStoreData() _loadStatusHouseItems = $"HouseItems: {_houseItemsJson.Count}"; _loadStatusLocalisation = $"Localisation: 0. Loading localisations"; + _loadStatusLocalisationNamed = $"Localisation (named): 0. Loading localisations"; _eventAggregator.GetEvent().Publish(); // ItemDefinitionsLocalisation - Itemdefinitions @@ -246,7 +260,8 @@ public void UpdateStoreData() where loc.Name.LocalName == "string" select loc; - foreach (var loc in query) + Parallel.ForEach(query, new ParallelOptions { MaxDegreeOfParallelism = 50 }, + loc => { string key = loc.Attribute("key")?.Value ?? string.Empty; string value = loc.Value; @@ -255,22 +270,31 @@ public void UpdateStoreData() // MasterItemDefinitions_Common.json // MasterItemDefinitions_Crafting.json // MasterItemDefinitions_Loot.json + // MasterItemDefinitions_Named.json // MasterItemDefinitions_Quest.json if (_masterItemDefinitionsJson.Any(d => d.Name?.Equals("@" + key, StringComparison.OrdinalIgnoreCase) ?? false)) { _itemDefinitionsLocalisation.TryAdd(key.ToLower(), value); } + if (_masterItemDefinitionsJson_Named.Any(d => (d.Name?.Equals("@" + key, StringComparison.OrdinalIgnoreCase) ?? false) && d.ItemClass.Contains("Named"))) + { + _namedItemDefinitionsLocalisation.TryAdd(key.ToLower(), value); + } _loadStatusLocalisation = $"Localisation data: {_itemDefinitionsLocalisation.Count}"; + _loadStatusLocalisationNamed = $"Localisation data (named): {_namedItemDefinitionsLocalisation.Count}"; _eventAggregator.GetEvent().Publish(); - } + }); } } // ItemDefinitionsLocalisation - Itemdefinitions - Cleanup duplicates - _itemDefinitionsLocalisation.Remove("ArrowBT2_MasterName".ToLower()); - _itemDefinitionsLocalisation.Remove("ArrowBT4_MasterName".ToLower()); - _itemDefinitionsLocalisation.Remove("ArrowBT5_MasterName".ToLower()); + _itemDefinitionsLocalisation.Remove("ArrowBT2_MasterName".ToLower(), out string? _); + _itemDefinitionsLocalisation.Remove("ArrowBT4_MasterName".ToLower(), out string? _); + _itemDefinitionsLocalisation.Remove("ArrowBT5_MasterName".ToLower(), out string? _); + // ItemDefinitionsLocalisation - Itemdefinitions - Cleanup resource / item conflicts + // TODO: Remove workaround when named items are separated. + _itemDefinitionsLocalisation.Remove("2hGreatSword_FlintT5_MasterName".ToLower(), out string? _); // ItemDefinitionsLocalisation - HouseItems resourcePath = @".\Data\javelindata_housingitems.loc.xml"; @@ -283,7 +307,9 @@ public void UpdateStoreData() where loc.Name.LocalName == "string" select loc; - foreach (var loc in query) + //foreach (var loc in query) + Parallel.ForEach(query, new ParallelOptions { MaxDegreeOfParallelism = 50 }, + loc => { string key = loc.Attribute("key")?.Value ?? string.Empty; string value = loc.Value; @@ -297,7 +323,7 @@ public void UpdateStoreData() _loadStatusLocalisation = $"Localisation data: {_itemDefinitionsLocalisation.Count}"; _eventAggregator.GetEvent().Publish(); - } + }); } } @@ -417,8 +443,10 @@ public List GetNamedItems() { ItemClass = masterItem.ItemClass, ItemID = masterItem.ItemID, - Localisation = GetItemLocalisation(masterItem.Name), - Tier = masterItem.Tier + Localisation = GetNamedItemLocalisation(masterItem.Name), + Tier = masterItem.Tier, + BindOnEquip = masterItem.BindOnEquip, + BindOnPickup = masterItem.BindOnPickup }); } @@ -441,7 +469,37 @@ public bool IsBindOnPickup(string itemName) return true; } - public string GetItemId(string itemName) + public bool IsNamedItem(string itemName) + { + //var localisationId = _itemDefinitionsLocalisation.FirstOrDefault(x => x.Value.Replace("\\n", " ").Equals(itemName, StringComparison.OrdinalIgnoreCase)).Key; + //var item = _masterItemDefinitionsJson.FirstOrDefault(i => i.Name.Equals($"@{localisationId}", StringComparison.OrdinalIgnoreCase)); + + // TODO: Remove workaround when there is an alternative overlay for named items. + var localisationIds = _itemDefinitionsLocalisation.Where(x => x.Value.Replace("\\n", " ").Equals(itemName, StringComparison.OrdinalIgnoreCase)); + if (localisationIds.Count() == 0) + { + return false; + } + else + { + _logger.LogWarning($"Item: {itemName} is not unique. There are {localisationIds.Count()} entries found"); + } + + // Check all similar named items. If one of them is not "named" return false. + // Workaround need for weapon and resource that both share the name "Flint". + bool uniqueNamedItem = true; + foreach (var localisationId in localisationIds) + { + var item = _masterItemDefinitionsJson.FirstOrDefault(i => i.Name.Equals($"@{localisationId.Key}", StringComparison.OrdinalIgnoreCase)); + if (!item.ItemClass.Contains("Named")) + { + uniqueNamedItem = false; + } + } + return uniqueNamedItem; + } + + public string GetItemId(string itemName)// { var localisationId = _itemDefinitionsLocalisation.FirstOrDefault(x => x.Value.Replace("\\n", " ").Equals(itemName, StringComparison.OrdinalIgnoreCase)).Key; MasterItemDefinitionsJson? item = _masterItemDefinitionsJson.FirstOrDefault(i => i.Name.Equals($"@{localisationId}", StringComparison.OrdinalIgnoreCase)); @@ -491,7 +549,7 @@ public string GetLevenshteinItemName(string itemName) Debug.WriteLine($"Levenshtein. Item: {itemName}, Match: {currentItem}, Distance: {currentDistance}"); //return currentDistance <= Math.Max(3, itemName.Length) ? currentItem : itemName; - return currentDistance <= 3 ? currentItem : itemName; + return currentDistance <= 5 ? currentItem : itemName; } public string GetItemLocalisation(string itemMasterName) @@ -499,6 +557,11 @@ public string GetItemLocalisation(string itemMasterName) return _itemDefinitionsLocalisation.GetValueOrDefault(itemMasterName.Trim(new char[] { '@' }).ToLower()) ?? itemMasterName.Trim(new char[] { '@' }); } + public string GetNamedItemLocalisation(string itemMasterName) + { + return _namedItemDefinitionsLocalisation.GetValueOrDefault(itemMasterName.Trim(new char[] { '@' }).ToLower()) ?? itemMasterName.Trim(new char[] { '@' }); + } + public List GetRelatedRecipes(string itemId) { // Note: The following recipes are ignored: diff --git a/NewWorldCompanion.Services/OverlayHandler.cs b/NewWorldCompanion.Services/OverlayHandler.cs index 815ab9f..f3eee3d 100644 --- a/NewWorldCompanion.Services/OverlayHandler.cs +++ b/NewWorldCompanion.Services/OverlayHandler.cs @@ -23,8 +23,7 @@ public class OverlayHandler : IOverlayHandler private readonly IOcrHandler _ocrHandler; private readonly IPriceManager _priceManager; private readonly IScreenProcessHandler _screenProcessHandler; - - private DispatcherTimer _timer = new(); + private readonly IStorageManager _storageManager; private readonly GraphicsWindow _window; private readonly Dictionary _brushes; @@ -36,13 +35,14 @@ public class OverlayHandler : IOverlayHandler private int _overlayY = 0; private int _overlayWidth = 0; private int _overlayHeigth = 0; + private double _mouseDelta = 0; // Start of Constructor region #region Constructor public OverlayHandler(IEventAggregator eventAggregator, ISettingsManager settingsManager, ICraftingRecipeManager craftingRecipeManager, INewWorldDataStore newWorldDataStore, - IOcrHandler ocrHandler, IPriceManager priceManager, IScreenProcessHandler screenProcessHandler) + IOcrHandler ocrHandler, IPriceManager priceManager, IScreenProcessHandler screenProcessHandler, IStorageManager storageManager) { // Init IEventAggregator _eventAggregator = eventAggregator; @@ -51,6 +51,7 @@ public OverlayHandler(IEventAggregator eventAggregator, ISettingsManager setting _eventAggregator.GetEvent().Subscribe(HandleOverlayShowEvent); _eventAggregator.GetEvent().Subscribe(HandleRoiImageReadyEvent); _eventAggregator.GetEvent().Subscribe(HandleNewWorldDataStoreUpdatedEvent); + _eventAggregator.GetEvent().Subscribe(HandleMouseDeltaUpdatedEvent); // Init services _settingsManager = settingsManager; @@ -59,6 +60,7 @@ public OverlayHandler(IEventAggregator eventAggregator, ISettingsManager setting _ocrHandler = ocrHandler; _priceManager = priceManager; _screenProcessHandler = screenProcessHandler; + _storageManager = storageManager; _brushes = new Dictionary(); _fonts = new Dictionary(); @@ -119,7 +121,20 @@ private void DrawGraphics(object? sender, DrawGraphicsEventArgs e) } else { - DrawGraphicsItem(e, itemName); + if (!_newWorldDataStore.IsNamedItem(itemName)) + { + DrawGraphicsItem(e, itemName); + } + else if (_settingsManager.Settings.NamedItemsTooltipEnabled) + { + DrawGraphicsNamedItem(e, itemName); + } + else + { + // Clear + var gfx = e.Graphics; + gfx.ClearScene(); + } } } @@ -179,6 +194,24 @@ private void DrawGraphicsItem(DrawGraphicsEventArgs e, string itemName) } } + private void DrawGraphicsNamedItem(DrawGraphicsEventArgs e, string itemName) + { + string infoItemName = itemName; + string infoStorage = $"Storage: {_storageManager.GetItemStorageInfo(itemName)}"; + + _window.X = _overlayX; + _window.Y = _overlayY; + _window.Width = _overlayWidth; + _window.Height = _overlayHeigth; + + var gfx = e.Graphics; + gfx.ClearScene(_brushes["background"]); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 20, infoItemName); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 40, "Named"); + gfx.DrawText(_fonts["consolas"], _brushes["text"], 20, 60, infoStorage); + gfx.DrawRectangle(_brushes["border"], 0, 0, _overlayWidth, _overlayHeigth, 1); + } + private void DrawGraphicsRecipe(DrawGraphicsEventArgs e, CraftingRecipe craftingRecipe) { NwmarketpriceJson nwmarketpriceJson = _priceManager.GetPriceData(craftingRecipe.LocalisationUserFriendly); @@ -255,7 +288,7 @@ private void HandleOverlayShowEvent() // - Tradable items string itemName = _itemName; var craftingRecipe = _craftingRecipeManager.CraftingRecipes.FirstOrDefault(r => r.LocalisationUserFriendly.StartsWith(itemName, StringComparison.OrdinalIgnoreCase)); - if (craftingRecipe != null || !_newWorldDataStore.IsBindOnPickup(itemName)) + if (craftingRecipe != null || !_newWorldDataStore.IsBindOnPickup(itemName) || _newWorldDataStore.IsNamedItem(itemName)) { _window.Show(); } @@ -266,8 +299,12 @@ private void HandleOverlayHideEvent() { if (_window.IsInitialized) { - _window.Hide(); - } + if ((_settingsManager.Settings.NamedItemsTooltipEnabled && _mouseDelta != 0) || + !_settingsManager.Settings.NamedItemsTooltipEnabled) + { + _window.Hide(); + } + } } private void HandleRoiImageReadyEvent() @@ -283,6 +320,11 @@ private void HandleNewWorldDataStoreUpdatedEvent() StartOverlay(); } + private void HandleMouseDeltaUpdatedEvent(double delta) + { + _mouseDelta = delta; + } + #endregion // Start of Methods region diff --git a/NewWorldCompanion.Services/ScreenCaptureHandler.cs b/NewWorldCompanion.Services/ScreenCaptureHandler.cs index d9c4b85..53f862d 100644 --- a/NewWorldCompanion.Services/ScreenCaptureHandler.cs +++ b/NewWorldCompanion.Services/ScreenCaptureHandler.cs @@ -27,6 +27,8 @@ public class ScreenCaptureHandler : IScreenCaptureHandler private Bitmap? _currentScreenMouseArea = null; private int _delay = 100; private int _delayCoordinates = 100; + private int _mouseX = 0; + private int _mouseY = 0; private int _offsetX = 0; private int _offsetY = 0; private ScreenCapture _screenCapture = new ScreenCapture(); @@ -77,7 +79,7 @@ public ScreenCaptureHandler(IEventAggregator eventAggregator, ILogger _delayCoordinates; set => _delayCoordinates = value; } public bool IsActive { - get => _settingsManager.Settings.TooltipEnabled; + get => _settingsManager.Settings.TooltipEnabled || _settingsManager.Settings.NamedItemsTooltipEnabled; } public string MouseCoordinates { get; set; } = string.Empty; public string MouseCoordinatesScaled { get; set; } = string.Empty; @@ -191,10 +193,19 @@ private void UpdateCoordinates() var dpiScaling = Math.Round(dpi / (double)96, 2); MouseCoordinatesScaled = $"X: {(int)(cursorInfo.ptScreenPos.x / dpiScaling)}, Y: {(int)(cursorInfo.ptScreenPos.y / dpiScaling)}"; + int x1 = _mouseX; + int y1 = _mouseY; + int x2 = cursorInfo.ptScreenPos.x; + int y2 = cursorInfo.ptScreenPos.y; + double delta = Math.Sqrt(Math.Pow((x2 - x1), 2) + Math.Pow((y2 - y1), 2)); + _mouseX = cursorInfo.ptScreenPos.x; + _mouseY = cursorInfo.ptScreenPos.y; + _coordinatesTimer.Interval = TimeSpan.FromMilliseconds(DelayCoordinates); _coordinatesTimer.IsEnabled = true; _eventAggregator.GetEvent().Publish(); + _eventAggregator.GetEvent().Publish(delta); } public BitmapSource? ImageSourceFromScreenCapture() diff --git a/NewWorldCompanion.Services/ScreenProcessHandler.cs b/NewWorldCompanion.Services/ScreenProcessHandler.cs index b224666..c0efacc 100644 --- a/NewWorldCompanion.Services/ScreenProcessHandler.cs +++ b/NewWorldCompanion.Services/ScreenProcessHandler.cs @@ -9,6 +9,7 @@ using Prism.Events; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; @@ -116,6 +117,160 @@ private void HandleScreenCaptureReadyEvent() private void ProcessImage(Mat img) { + bool result = ProcessImageNormal(img); + if (!result) + { + result = ProcessImageSwirling(img); + } + if (!result) + { + _eventAggregator.GetEvent().Publish(); + } + } + + private bool ProcessImageNormal(Mat img) + { + bool result = true; + + UMat filter = new UMat(); + UMat cannyEdges = new UMat(); + Mat crop = new Mat(img.Size, DepthType.Cv8U, 3); + + //Convert the image to grayscale and filter out the noise + CvInvoke.CvtColor(img, filter, ColorConversion.Bgr2Gray); + //CvInvoke.GaussianBlur(filter, filter, new System.Drawing.Size(3, 3), 1); + + // Canny and edge detection + double cannyThreshold = HysteresisLower; + double cannyThresholdLinking = HysteresisUpper; + CvInvoke.Canny(filter, cannyEdges, cannyThreshold, cannyThresholdLinking); + + // Find rectangles + List rectangleList = new List(); + VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); + + CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple); + int count = contours.Size; + for (int i = 0; i < count; i++) + { + using (VectorOfPoint contour = contours[i]) + using (VectorOfPoint approxContour = new VectorOfPoint()) + { + CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05, true); + // Only consider contours with area between upper and lower values + // e.g. when using 2560x1440 + // Equipment icon size: ca. 5000-6000 + // Schematic icon size: ca. 10000-10800 + if (CvInvoke.ContourArea(approxContour, false) > AreaLower && + CvInvoke.ContourArea(approxContour, false) < AreaUpper) + { + // The contour has 4 vertices. + if (approxContour.Size >= 2) + { + // Determine if all the angles in the contour are within [80, 100] degree + bool isRectangle = true; + System.Drawing.Point[] pts = approxContour.ToArray(); + LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true); + + int angleCounter = 0; + for (int j = 0; j < edges.Length; j++) + { + double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j])); + + if (angle > 80 && angle < 100) + { + angleCounter++; + } + } + isRectangle = angleCounter > 1; + + if (isRectangle) + { + var rotatedRec = CvInvoke.MinAreaRect(approxContour); + if (!rectangleList.Exists(rec => + ((rec.Center.X - rotatedRec.Center.X > -2 && rec.Center.X - rotatedRec.Center.X < 2) || (rotatedRec.Center.X - rec.Center.X > -2 && rotatedRec.Center.X - rec.Center.X < 2)) && + ((rec.Center.Y - rotatedRec.Center.Y > -2 && rec.Center.Y - rotatedRec.Center.Y < 2) || (rotatedRec.Center.Y - rec.Center.Y > -2 && rotatedRec.Center.Y - rec.Center.Y < 2)))) + { + // We only want squares + float ratio = rotatedRec.Size.Width / rotatedRec.Size.Height; + if (ratio > 0.8 && ratio < 1.2) + { + rectangleList.Add(rotatedRec); + } + } + } + } + } + } + } + + // Draw rectangles + foreach (RotatedRect rectangle in rectangleList) + { + CvInvoke.Polylines(img, Array.ConvertAll(rectangle.GetVertices(), System.Drawing.Point.Round), true, + new Bgr(Color.DarkOrange).MCvScalar, 2); + } + ProcessedImage = img.ToBitmap(); + _eventAggregator.GetEvent().Publish(); + + // Create roi + if (rectangleList.Count == 1) + { + var roiRectangle = new Rectangle + ( + rectangleList[0].MinAreaRect().X + rectangleList[0].MinAreaRect().Width + 5, + rectangleList[0].MinAreaRect().Y, + (int)(rectangleList[0].MinAreaRect().Width * 2.8), + (int)(rectangleList[0].MinAreaRect().Height * 0.5) + ); + + // Update overlay position + // Note: Using a constant height of 100. So that all text rows fit the overlay for every resolution. + OverlayX = _screenCaptureHandler.OffsetX + rectangleList[0].MinAreaRect().X; + //OverlayY = _screenCaptureHandler.OffsetY + rectangleList[0].MinAreaRect().Y - (rectangleList[0].MinAreaRect().Height + 12); + OverlayY = _screenCaptureHandler.OffsetY + rectangleList[0].MinAreaRect().Y - (100 + 12); + OverlayWidth = rectangleList[0].MinAreaRect().Width + (int)(rectangleList[0].MinAreaRect().Width * 2.8); + //OverlayHeigth = rectangleList[0].MinAreaRect().Height; + OverlayHeigth = 100; + + try + { + // Validate width + roiRectangle.Width = (roiRectangle.X + roiRectangle.Width) <= img.Width ? roiRectangle.Width : roiRectangle.Width - (roiRectangle.X + roiRectangle.Width - img.Width); + + if (roiRectangle.Width > 0 && roiRectangle.Y > 0) + { + crop = new Mat(img, roiRectangle); + RoiImage = crop.ToBitmap(); + _eventAggregator.GetEvent().Publish(); + ProcessImageOCR(crop); + } + else + { + //_eventAggregator.GetEvent().Publish(); + result = false; + } + } + catch (Exception ex) + { + _logger.LogError(ex, MethodBase.GetCurrentMethod()?.Name); + + //_eventAggregator.GetEvent().Publish(); + result = false; + } + } + else + { + //_eventAggregator.GetEvent().Publish(); + result = false; + } + return result; + } + + private bool ProcessImageSwirling(Mat img) + { + bool result = true; + UMat filter = new UMat(); UMat cannyEdges = new UMat(); Mat crop = new Mat(img.Size, DepthType.Cv8U, 3); @@ -149,22 +304,24 @@ private void ProcessImage(Mat img) CvInvoke.ContourArea(approxContour, false) < AreaUpper) { // The contour has 4 vertices. - if (approxContour.Size == 4) + if (approxContour.Size >= 2) { // Determine if all the angles in the contour are within [80, 100] degree bool isRectangle = true; System.Drawing.Point[] pts = approxContour.ToArray(); LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true); + int angleCounter = 0; for (int j = 0; j < edges.Length; j++) { double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j])); - if (angle < 80 || angle > 100) + + if (angle > 80 && angle < 100) { - isRectangle = false; - break; + angleCounter++; } } + isRectangle = angleCounter > 1; if (isRectangle) { @@ -220,22 +377,33 @@ private void ProcessImage(Mat img) // Validate width roiRectangle.Width = (roiRectangle.X + roiRectangle.Width) <= img.Width ? roiRectangle.Width : roiRectangle.Width - (roiRectangle.X + roiRectangle.Width - img.Width); - crop = new Mat(img, roiRectangle); - RoiImage = crop.ToBitmap(); - _eventAggregator.GetEvent().Publish(); - ProcessImageOCR(crop); + if (roiRectangle.Width > 0 && roiRectangle.Y > 0) + { + crop = new Mat(img, roiRectangle); + RoiImage = crop.ToBitmap(); + _eventAggregator.GetEvent().Publish(); + ProcessImageOCR(crop); + } + else + { + //_eventAggregator.GetEvent().Publish(); + result = false; + } } catch (Exception ex) { _logger.LogError(ex, MethodBase.GetCurrentMethod()?.Name); - _eventAggregator.GetEvent().Publish(); + //_eventAggregator.GetEvent().Publish(); + result = false; } } else { - _eventAggregator.GetEvent().Publish(); + //_eventAggregator.GetEvent().Publish(); + result = false; } + return result; } private void ProcessImageCount(Mat img) diff --git a/NewWorldCompanion.Services/StorageManager.cs b/NewWorldCompanion.Services/StorageManager.cs index 3a0a7f9..f749820 100644 --- a/NewWorldCompanion.Services/StorageManager.cs +++ b/NewWorldCompanion.Services/StorageManager.cs @@ -1,4 +1,5 @@ using NewWorldCompanion.Entities; +using NewWorldCompanion.Events; using NewWorldCompanion.Interfaces; using Prism.Events; using System; @@ -84,6 +85,20 @@ public void SaveStorage() using FileStream stream = File.Create(fileName); var options = new JsonSerializerOptions { WriteIndented = true }; JsonSerializer.Serialize(stream, Items, options); + + _eventAggregator.GetEvent().Publish(); + } + + public string GetItemStorageInfo(string itemName) + { + string storageInfo = string.Empty; + var items = _items.ToList().FindAll(i => i.Localisation.ToLower().Equals(itemName.ToLower())); + foreach ( var item in items) + { + storageInfo = storageInfo.Length > 0 ? $"{storageInfo}, {item.Storage}" : item.Storage; + } + + return storageInfo.Length > 0 ? storageInfo : "Missing"; } #endregion diff --git a/NewWorldCompanion/App.xaml.cs b/NewWorldCompanion/App.xaml.cs index 157ab39..f120f09 100644 --- a/NewWorldCompanion/App.xaml.cs +++ b/NewWorldCompanion/App.xaml.cs @@ -21,7 +21,7 @@ namespace NewWorldCompanion /// public partial class App : PrismApplication { - private static Mutex _mutex = null; + private static Mutex? _mutex = null; protected override void OnStartup(StartupEventArgs e) { diff --git a/NewWorldCompanion/ViewModels/MainWindowViewModel.cs b/NewWorldCompanion/ViewModels/MainWindowViewModel.cs index ff89975..bb8e3bc 100644 --- a/NewWorldCompanion/ViewModels/MainWindowViewModel.cs +++ b/NewWorldCompanion/ViewModels/MainWindowViewModel.cs @@ -72,6 +72,7 @@ public MainWindowViewModel(IEventAggregator eventAggregator, ILogger _newWorldDataStore.LoadStatusCraftingRecipes; public string DataStatusHouseItems => _newWorldDataStore.LoadStatusHouseItems; public string DataStatusLocalisation => _newWorldDataStore.LoadStatusLocalisation; + public string DataStatusLocalisationNamed => _newWorldDataStore.LoadStatusLocalisationNamed; #endregion @@ -124,6 +125,7 @@ private void HandleDataStatusUpdatedEvent() RaisePropertyChanged(nameof(DataStatusCraftingRecipes)); RaisePropertyChanged(nameof(DataStatusHouseItems)); RaisePropertyChanged(nameof(DataStatusLocalisation)); + RaisePropertyChanged(nameof(DataStatusLocalisationNamed)); } #endregion diff --git a/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs b/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs index 2fe68be..2efcaef 100644 --- a/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs +++ b/NewWorldCompanion/ViewModels/Tabs/Config/ConfigOverlayViewModel.cs @@ -29,6 +29,7 @@ public class ConfigOverlayViewModel : BindableBase private OverlayResource _selectedOverlayResource = new OverlayResource(); private bool _toggleTooltip = false; private bool _toggleExtendedTooltip = false; + private bool _toggleNamedItemsTooltip = false; private int _serverIndex = 0; private string _itemNameFilter = string.Empty; @@ -106,6 +107,19 @@ public bool ToggleExtendedTooltip } } + public bool ToggleNamedItemsTooltip + { + get => _toggleNamedItemsTooltip; + set + { + _toggleNamedItemsTooltip = value; + RaisePropertyChanged(); + + _settingsManager.Settings.NamedItemsTooltipEnabled = value; + _settingsManager.SaveSettings(); + } + } + public int ServerIndex { get @@ -215,6 +229,7 @@ private void InitOverlayResources() // Load extended tooltip toggle ToggleTooltip = _settingsManager.Settings.TooltipEnabled; ToggleExtendedTooltip = _settingsManager.Settings.ExtendedTooltipEnabled; + ToggleNamedItemsTooltip = _settingsManager.Settings.NamedItemsTooltipEnabled; // Get interesting resources for extended overlay var overlayResources = _newWorldDataStore.GetOverlayResources(); diff --git a/NewWorldCompanion/ViewModels/Tabs/Items/NamedItemsViewModel.cs b/NewWorldCompanion/ViewModels/Tabs/Items/NamedItemsViewModel.cs new file mode 100644 index 0000000..d186f8f --- /dev/null +++ b/NewWorldCompanion/ViewModels/Tabs/Items/NamedItemsViewModel.cs @@ -0,0 +1,453 @@ +using NewWorldCompanion.Entities; +using NewWorldCompanion.Events; +using NewWorldCompanion.Interfaces; +using Prism.Commands; +using Prism.Events; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace NewWorldCompanion.ViewModels.Tabs.Items +{ + public class NamedItemsViewModel : BindableBase + { + private readonly IEventAggregator _eventAggregator; + private readonly ISettingsManager _settingsManager; + private readonly INewWorldDataStore _newWorldDataStore; + private readonly IStorageManager _storageManager; + + private ObservableCollection _items = new ObservableCollection(); + + private string _itemNameFilter = string.Empty; + private NamedItem _selectedItem = new(); + private bool _toggleFilter = false; + + // Start of Constructor region + + #region Constructor + + public NamedItemsViewModel(IEventAggregator eventAggregator, ISettingsManager settingsManager, INewWorldDataStore newWorldDataStore, IStorageManager storageManager) + { + // Init IEventAggregator + _eventAggregator = eventAggregator; + _eventAggregator.GetEvent().Subscribe(HandleNewWorldDataStoreUpdatedEvent); + _eventAggregator.GetEvent().Subscribe(HandleStorageManagerUpdatedEvent); + + // Init services + _settingsManager = settingsManager; + _newWorldDataStore = newWorldDataStore; + _storageManager = storageManager; + + // Init View commands + VisitNwdbCommand = new DelegateCommand(VisitNwdbExecute); + + // Init filter views + CreateItemsFilteredView(); + } + + #endregion + + // Start of Properties region + + #region Properties + + public DelegateCommand VisitNwdbCommand { get; } + + public ObservableCollection Items { get => _items; set => _items = value; } + public ListCollectionView? ItemsFiltered { get; private set; } + + public bool FilterTier2 + { + get => _settingsManager.Settings.NamedItemsFilterTier2; + set + { + _settingsManager.Settings.NamedItemsFilterTier2 = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterTier2)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterTier3 + { + get => _settingsManager.Settings.NamedItemsFilterTier3; + set + { + _settingsManager.Settings.NamedItemsFilterTier3 = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterTier3)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterTier4 + { + get => _settingsManager.Settings.NamedItemsFilterTier4; + set + { + _settingsManager.Settings.NamedItemsFilterTier4 = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterTier4)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterTier5 + { + get => _settingsManager.Settings.NamedItemsFilterTier5; + set + { + _settingsManager.Settings.NamedItemsFilterTier5 = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterTier5)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterItemClassArmor + { + get => _settingsManager.Settings.NamedItemsFilterItemClassArmor; + set + { + _settingsManager.Settings.NamedItemsFilterItemClassArmor = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterItemClassArmor)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterItemClassJewelry + { + get => _settingsManager.Settings.NamedItemsFilterItemClassJewelry; + set + { + _settingsManager.Settings.NamedItemsFilterItemClassJewelry = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterItemClassJewelry)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterItemClassWeapon + { + get => _settingsManager.Settings.NamedItemsFilterItemClassWeapon; + set + { + _settingsManager.Settings.NamedItemsFilterItemClassWeapon = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterItemClassWeapon)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterStorageCollected + { + get => _settingsManager.Settings.NamedItemsFilterStorageCollected; + set + { + _settingsManager.Settings.NamedItemsFilterStorageCollected = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterStorageCollected)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterStorageMissing + { + get => _settingsManager.Settings.NamedItemsFilterStorageMissing; + set + { + _settingsManager.Settings.NamedItemsFilterStorageMissing = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterStorageMissing)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterStorageDuplicates + { + get => _settingsManager.Settings.NamedItemsFilterStorageDuplicates; + set + { + _settingsManager.Settings.NamedItemsFilterStorageDuplicates = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterStorageDuplicates)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterBindOnEquip + { + get => _settingsManager.Settings.NamedItemsFilterBindOnEquip; + set + { + _settingsManager.Settings.NamedItemsFilterBindOnEquip = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterBindOnEquip)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public bool FilterBindOnPickup + { + get => _settingsManager.Settings.NamedItemsFilterBindOnPickup; + set + { + _settingsManager.Settings.NamedItemsFilterBindOnPickup = value; + _settingsManager.SaveSettings(); + RaisePropertyChanged(nameof(FilterBindOnPickup)); + + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered?.Refresh(); + }); + } + } + + public string ItemNameFilter + { + get => _itemNameFilter; + set + { + _itemNameFilter = value; + RaisePropertyChanged(nameof(ItemNameFilter)); + ItemsFiltered?.Refresh(); + + if (ItemsFiltered?.Count == 1) + { + var selection = ItemsFiltered.GetItemAt(0) as NamedItem; + if (selection != null) + { + SelectedItem = selection; + } + } + } + } + + public NamedItem SelectedItem + { + get => _selectedItem; + set + { + SetProperty(ref _selectedItem, value, () => { RaisePropertyChanged(nameof(SelectedItem)); }); + } + } + + public bool ToggleFilter + { + get => _toggleFilter; + set + { + _toggleFilter = value; + RaisePropertyChanged(nameof(ToggleFilter)); + } + } + + #endregion + + // Start of Events region + + #region Events + + private void HandleNewWorldDataStoreUpdatedEvent() + { + UpdateNamedItems(); + UpdateStorageInfo(); + } + + private void HandleStorageManagerUpdatedEvent() + { + UpdateStorageInfo(); + } + + #endregion + + // Start of Methods region + + #region Methods + + private void CreateItemsFilteredView() + { + // As the view is accessed by the UI it will need to be created on the UI thread + Application.Current?.Dispatcher?.Invoke(() => + { + ItemsFiltered = new ListCollectionView(Items) + { + Filter = FilterItems + }; + // Sorting + ItemsFiltered.SortDescriptions.Add(new SortDescription("Localisation", ListSortDirection.Ascending)); + //ItemsFiltered.SortDescriptions.Add(new SortDescription("Storage", ListSortDirection.Ascending)); + //ItemsFiltered.SortDescriptions.Add(new SortDescription("Tier", ListSortDirection.Ascending)); + + // Live sorting + ItemsFiltered.LiveSortingProperties.Add("Localisation"); + //ItemsFiltered.LiveSortingProperties.Add("Storage"); + //ItemsFiltered.LiveSortingProperties.Add("Tier"); + ItemsFiltered.IsLiveSorting = true; + }); + } + + private bool FilterItems(object itemsObj) + { + var allowed = true; + if (itemsObj == null) return false; + NamedItem item = (NamedItem)itemsObj; + + switch(item.Tier) + { + case 2: + allowed = FilterTier2; + break; + case 3: + allowed = FilterTier3; + break; + case 4: + allowed = FilterTier4; + break; + case 5: + allowed = FilterTier5; + break; + + } + + if (allowed) + { + allowed = item.ItemClass.ToLower().Contains("armor") && !FilterItemClassArmor ? false : true; + } + + if (allowed) + { + allowed = item.ItemClass.ToLower().Contains("jewelry") && !FilterItemClassJewelry ? false : true; + } + + if (allowed) + { + allowed = item.ItemClass.ToLower().Contains("weapon") && !FilterItemClassWeapon ? false : true; + } + + if (allowed) + { + var allowedCollected = item.Storage.Length > 0 && FilterStorageCollected ? true : false; + var allowedMissing = string.IsNullOrWhiteSpace(item.Storage) && FilterStorageMissing ? true : false; + var allowedDuplicates = item.Storage.Contains(",") && FilterStorageDuplicates ? true : false; + + allowed = allowedCollected || allowedMissing || allowedDuplicates; + } + + if (allowed) + { + allowed = item.BindOnEquip && !FilterBindOnEquip ? false : true; + } + + if (allowed) + { + allowed = item.BindOnPickup && !FilterBindOnPickup ? false : true; + } + + if (allowed) + { + allowed = string.IsNullOrWhiteSpace(ItemNameFilter) ? true : item.Localisation.ToLower().Contains(ItemNameFilter.ToLower()); + } + + return allowed; + } + + private void VisitNwdbExecute(object url) + { + Process.Start(new ProcessStartInfo(url as string ?? string.Empty) { UseShellExecute = true }); + } + + private void UpdateNamedItems() + { + if (Application.Current?.Dispatcher != null) + { + Application.Current?.Dispatcher.Invoke(() => + { + Items.Clear(); + Items.AddRange(_newWorldDataStore.GetNamedItems()); + }); + } + } + + private void UpdateStorageInfo() + { + if (Application.Current?.Dispatcher != null) + { + Application.Current?.Dispatcher.Invoke(() => + { + // Reset storage info + foreach (var item in Items) + { + item.Storage = string.Empty; + } + + // Update storage info + foreach (var item in _storageManager.Items) + { + if(Items.Any(i => i.ItemID.Equals(item.ItemID))) + { + var namedItem = Items.First(i => i.ItemID.Equals(item.ItemID)); + namedItem.Storage = (namedItem.Storage.Length > 0) ? $"{namedItem.Storage}, {item.Storage}" : $"{item.Storage}"; + } + } + }); + } + } + + #endregion + + } +} diff --git a/NewWorldCompanion/ViewModels/Tabs/StorageViewModel.cs b/NewWorldCompanion/ViewModels/Tabs/Items/StorageViewModel.cs similarity index 99% rename from NewWorldCompanion/ViewModels/Tabs/StorageViewModel.cs rename to NewWorldCompanion/ViewModels/Tabs/Items/StorageViewModel.cs index a766691..952772d 100644 --- a/NewWorldCompanion/ViewModels/Tabs/StorageViewModel.cs +++ b/NewWorldCompanion/ViewModels/Tabs/Items/StorageViewModel.cs @@ -16,7 +16,7 @@ using System.Windows; using System.Windows.Data; -namespace NewWorldCompanion.ViewModels.Tabs +namespace NewWorldCompanion.ViewModels.Tabs.Items { public class StorageViewModel : BindableBase { diff --git a/NewWorldCompanion/Views/MainWindow.xaml b/NewWorldCompanion/Views/MainWindow.xaml index c41ed50..9a08b75 100644 --- a/NewWorldCompanion/Views/MainWindow.xaml +++ b/NewWorldCompanion/Views/MainWindow.xaml @@ -40,8 +40,8 @@ - - + + @@ -58,6 +58,7 @@ + diff --git a/NewWorldCompanion/Views/MainWindow.xaml.cs b/NewWorldCompanion/Views/MainWindow.xaml.cs index 57e6a1d..cb47e84 100644 --- a/NewWorldCompanion/Views/MainWindow.xaml.cs +++ b/NewWorldCompanion/Views/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using NewWorldCompanion.Views.Tabs; using NewWorldCompanion.Views.Tabs.Config; using NewWorldCompanion.Views.Tabs.Debug; +using NewWorldCompanion.Views.Tabs.Items; using Prism.Regions; using System; @@ -24,7 +25,9 @@ public MainWindow(IRegionManager regionManager) // Regions regionManager.RegisterViewWithRegion("CraftingView", typeof(CraftingView)); regionManager.RegisterViewWithRegion("CooldownView", typeof(CooldownView)); + regionManager.RegisterViewWithRegion("ItemsView", typeof(ItemsView)); regionManager.RegisterViewWithRegion("StorageView", typeof(StorageView)); + regionManager.RegisterViewWithRegion("NamedItemsView", typeof(NamedItemsView)); regionManager.RegisterViewWithRegion("ConfigView", typeof(ConfigView)); regionManager.RegisterViewWithRegion("ConfigOverlayView", typeof(ConfigOverlayView)); diff --git a/NewWorldCompanion/Views/Tabs/Config/ConfigOverlayView.xaml b/NewWorldCompanion/Views/Tabs/Config/ConfigOverlayView.xaml index 8d8780b..5aaa1d3 100644 --- a/NewWorldCompanion/Views/Tabs/Config/ConfigOverlayView.xaml +++ b/NewWorldCompanion/Views/Tabs/Config/ConfigOverlayView.xaml @@ -20,6 +20,7 @@ + @@ -42,13 +43,14 @@ - Price overlay + Price overlay Extended overlay + Named items overlay - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NewWorldCompanion/Views/Tabs/Items/NamedItemsView.xaml.cs b/NewWorldCompanion/Views/Tabs/Items/NamedItemsView.xaml.cs new file mode 100644 index 0000000..b16389d --- /dev/null +++ b/NewWorldCompanion/Views/Tabs/Items/NamedItemsView.xaml.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace NewWorldCompanion.Views.Tabs.Items +{ + /// + /// Interaction logic for NamedItemsView.xaml + /// + public partial class NamedItemsView : UserControl + { + public NamedItemsView() + { + InitializeComponent(); + } + + private void TextBoxFilter_GotFocus(object sender, RoutedEventArgs e) + { + TextBoxFilterWatermark.Visibility = Visibility.Collapsed; + } + + private void TextBoxFilter_LostFocus(object sender, RoutedEventArgs e) + { + if (string.IsNullOrWhiteSpace(TextBoxFilter.Text)) + { + TextBoxFilterWatermark.Visibility = Visibility.Visible; + } + } + } +} diff --git a/NewWorldCompanion/Views/Tabs/StorageView.xaml b/NewWorldCompanion/Views/Tabs/Items/StorageView.xaml similarity index 92% rename from NewWorldCompanion/Views/Tabs/StorageView.xaml rename to NewWorldCompanion/Views/Tabs/Items/StorageView.xaml index 4f1b9ec..6d5df1d 100644 --- a/NewWorldCompanion/Views/Tabs/StorageView.xaml +++ b/NewWorldCompanion/Views/Tabs/Items/StorageView.xaml @@ -1,12 +1,13 @@ - diff --git a/NewWorldCompanion/Views/Tabs/StorageView.xaml.cs b/NewWorldCompanion/Views/Tabs/Items/StorageView.xaml.cs similarity index 95% rename from NewWorldCompanion/Views/Tabs/StorageView.xaml.cs rename to NewWorldCompanion/Views/Tabs/Items/StorageView.xaml.cs index 84888cd..8c213b5 100644 --- a/NewWorldCompanion/Views/Tabs/StorageView.xaml.cs +++ b/NewWorldCompanion/Views/Tabs/Items/StorageView.xaml.cs @@ -13,7 +13,7 @@ using System.Windows.Navigation; using System.Windows.Shapes; -namespace NewWorldCompanion.Views.Tabs +namespace NewWorldCompanion.Views.Tabs.Items { /// /// Interaction logic for StorageView.xaml diff --git a/NewWorldCompanion/Views/Tabs/ItemsView.xaml b/NewWorldCompanion/Views/Tabs/ItemsView.xaml new file mode 100644 index 0000000..ecf8fb9 --- /dev/null +++ b/NewWorldCompanion/Views/Tabs/ItemsView.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/NewWorldCompanion/Views/Tabs/ItemsView.xaml.cs b/NewWorldCompanion/Views/Tabs/ItemsView.xaml.cs new file mode 100644 index 0000000..c6abdbc --- /dev/null +++ b/NewWorldCompanion/Views/Tabs/ItemsView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace NewWorldCompanion.Views.Tabs +{ + /// + /// Interaction logic for ItemsView.xaml + /// + public partial class ItemsView : UserControl + { + public ItemsView() + { + InitializeComponent(); + } + } +} diff --git a/NewWorldCompanion/common.props b/NewWorldCompanion/common.props index f797a17..2ec0e95 100644 --- a/NewWorldCompanion/common.props +++ b/NewWorldCompanion/common.props @@ -1,7 +1,7 @@ - 1.0.11.0 - 1.0.11.0 + 1.0.12.0 + 1.0.12.0 Copyright © 2023 net6.0-windows