// { "customLocation": [{x, y}] } where x, y are relative to map (for a custom location ex. a new house) // { "customRegion": [{x1, y1}, {x2, y2}] where x, y are relative to map (for a custom region ex. a new farm) // Any custom locations with given location on the map public Dictionary <string, MapVector[]> GetCustomMapLocations() { var customMapVectors = new Dictionary <string, MapVector[]>(); foreach (KeyValuePair <string, JObject[]> mapVectors in Config.CustomMapLocations) { var mapVectorArr = new MapVector[mapVectors.Value.Length]; for (int i = 0; i < mapVectors.Value.Length; i++) { var mapVector = mapVectors.Value[i]; // Marker doesn't need to specify corresponding Tile position if (mapVector.GetValue("TileX") == null || mapVector.GetValue("TileY") == null) { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY") ); } // Region must specify corresponding Tile positions for // Calculations on movement within location else { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY"), (int)mapVector.GetValue("TileX"), (int)mapVector.GetValue("TileY") ); } } customMapVectors.Add(mapVectors.Key, mapVectorArr); } return(customMapVectors); }
// { "customLocation": [{x, y}] } where x, y are relative to map (for a custom location ex. a new house) // { "customRegion": [{x1, y1}, {x2, y2}] where x, y are relative to map (for a custom region ex. a new farm) // Any custom locations with given location on the map public Dictionary <string, MapVector[]> GetCustomMapLocations() { var customMapVectors = new Dictionary <string, MapVector[]>(); var moddedLocations = new List <string>(); foreach (KeyValuePair <string, JObject[]> mapVectors in Config.CustomMapLocations) { var mapVectorArr = new MapVector[mapVectors.Value.Length]; for (int i = 0; i < mapVectors.Value.Length; i++) { var mapVector = mapVectors.Value[i]; // Marker doesn't need to specify corresponding Tile position if (mapVector.GetValue("TileX") == null || mapVector.GetValue("TileY") == null) { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY") ); } // Region must specify corresponding Tile positions for // Calculations on movement within location else { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY"), (int)mapVector.GetValue("TileX"), (int)mapVector.GetValue("TileY") ); } } moddedLocations.Add(mapVectors.Key); customMapVectors.Add(mapVectors.Key, mapVectorArr); } // Automatically adjust tracking for modded maps that are sized differently from vanilla map foreach (var location in Game1.locations) { if (!location.IsOutdoors || location.Name == "Summit" || customMapVectors.ContainsKey(location.Name) || !ModConstants.MapVectors.TryGetValue(location.Name, out var mapVector)) { continue; } if (mapVector.LastOrDefault().TileX != location.Map.DisplayWidth / Game1.tileSize || mapVector.LastOrDefault().TileY != location.Map.DisplayHeight / Game1.tileSize) { moddedLocations.Add(location.Name); customMapVectors.Add(location.Name, new MapVector[] { mapVector.FirstOrDefault(), new MapVector( mapVector.LastOrDefault().MapX, mapVector.LastOrDefault().MapY, location.Map.DisplayWidth / Game1.tileSize, location.Map.DisplayHeight / Game1.tileSize ) }); } } if (moddedLocations.Count > 0) { if (moddedLocations.Count == 1) { Monitor.Log($"Detected modded location {moddedLocations[0]}. Adjusting map tracking to scale.", LogLevel.Debug); } else { var locationList = ""; for (var i = 0; i < moddedLocations.Count; i++) { locationList += moddedLocations[i] + (i + 1 == moddedLocations.Count ? ", " : ""); } Monitor.Log($"Detected modded locations {locationList}. Adjusting map tracking to scale.", LogLevel.Debug); } } return(customMapVectors); }
// Any custom locations with given location on the map private void LoadCustomMapLocations() { foreach (var mapVectors in ModMain.CustomData.CustomMapLocations) { var mapVectorArr = new MapVector[mapVectors.Value.Length]; for (var i = 0; i < mapVectors.Value.Length; i++) { var mapVector = mapVectors.Value[i]; // Marker doesn't need to specify corresponding Tile position if (mapVector.GetValue("TileX") == null || mapVector.GetValue("TileY") == null) { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY") ); } // Region must specify corresponding Tile positions for // Calculations on movement within location else { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY"), (int)mapVector.GetValue("TileX"), (int)mapVector.GetValue("TileY") ); } } MapVectors.Add(mapVectors.Key, mapVectorArr); } // Automatically adjust tracking for modded maps that are sized differently from vanilla map foreach (var location in Game1.locations) { var locationName = location.uniqueName.Value ?? location.Name; if (!location.IsOutdoors || locationName == "Summit" || MapVectors.ContainsKey(locationName) || !ModConstants.MapVectors.TryGetValue(locationName, out var mapVector)) { continue; } if (mapVector.LastOrDefault().TileX != location.Map.DisplayWidth / Game1.tileSize || mapVector.LastOrDefault().TileY != location.Map.DisplayHeight / Game1.tileSize) { MapVectors.Add(locationName, new[] { mapVector.FirstOrDefault(), new MapVector( mapVector.LastOrDefault().MapX, mapVector.LastOrDefault().MapY, location.Map.DisplayWidth / Game1.tileSize, location.Map.DisplayHeight / Game1.tileSize ) }); } } var customLocations = MapVectors.Keys.ToArray(); if (MapVectors.Keys.Count > 0) { if (customLocations.Length == 1) { ModMain.IMonitor.Log($"Handled tracking for custom location: {customLocations[0]}.", LogLevel.Debug); } else { var locationList = ""; for (var i = 0; i < customLocations.Length; i++) { locationList += customLocations[i] + (i + 1 == customLocations.Length ? "" : ", "); } ModMain.IMonitor.Log($"Handled tracking for custom locations: {locationList}.", LogLevel.Debug); } } }
// MAIN METHOD FOR PINPOINTING CHARACTERS ON THE MAP // Calculated from mapping of game tile positions to pixel coordinates of the map in MapModConstants. // Requires MapModConstants and modified map page in ./assets public static Vector2 LocationToMap(string locationName, int tileX = -1, int tileY = -1, Dictionary <string, MapVector[]> CustomMapLocations = null, bool isPlayer = false) { if (FarmBuildings.TryGetValue(locationName, out var mapLoc)) { return(mapLoc.Value); } if (locationName.StartsWith("UndergroundMine")) { var mine = locationName.Substring("UndergroundMine".Length, locationName.Length - "UndergroundMine".Length); if (int.TryParse(mine, out var mineLevel)) { // Skull cave if (mineLevel > 120) { locationName = "SkullCave"; } // Mines else { locationName = "Mine"; } } } if (CustomMapLocations != null && !CustomMapLocations.ContainsKey(locationName) && !ModConstants.MapVectors.ContainsKey(locationName)) { return(new Vector2(-1000, -1000)); } if (!ModConstants.MapVectors.ContainsKey(locationName)) { return(new Vector2(-1000, -1000)); } MapVector[] locVectors = (CustomMapLocations != null && CustomMapLocations.ContainsKey(locationName)) ? CustomMapLocations[locationName] : ModConstants.MapVectors[locationName]; ; int x; int y; // Precise (static) regions and indoor locations if (locVectors.Count() == 1 || tileX == -1 || tileY == -1) { x = locVectors.FirstOrDefault().MapX; y = locVectors.FirstOrDefault().MapY; } else { // Sort map vectors by distance to point var vectors = locVectors.OrderBy(vector => Math.Sqrt(Math.Pow(vector.TileX - tileX, 2) + Math.Pow(vector.TileY - tileY, 2))); MapVector lower = null; MapVector upper = null; var hasEqualTile = false; // Create rectangle bound from two pre-defined points (lower & upper bound) and calculate map scale for that area foreach (var vector in vectors) { if (lower != null && upper != null) { if (lower.TileX == upper.TileX || lower.TileY == upper.TileY) { hasEqualTile = true; } else { break; } } if ((lower == null || hasEqualTile) && tileX >= vector.TileX && tileY >= vector.TileY) { lower = vector; continue; } if ((upper == null || hasEqualTile) && tileX <= vector.TileX && tileY <= vector.TileY) { upper = vector; } } // Handle null cases - not enough vectors to calculate using lower/upper bound strategy // Uses fallback strategy - get closest points such that lower != upper var tilePos = "(" + tileX + ", " + tileY + ")"; if (lower == null) { lower = upper == vectors.First() ? vectors.Skip(1).First() : vectors.First(); } if (upper == null) { upper = lower == vectors.First() ? vectors.Skip(1).First() : vectors.First(); } x = (int)(lower.MapX + (tileX - lower.TileX) / (double)(upper.TileX - lower.TileX) * (upper.MapX - lower.MapX)); y = (int)(lower.MapY + (tileY - lower.TileY) / (double)(upper.TileY - lower.TileY) * (upper.MapY - lower.MapY)); if (DEBUG_MODE && isPlayer) { _tileUpper = new Vector2(upper.TileX, upper.TileY); _tileLower = new Vector2(lower.TileX, lower.TileY); } } return(new Vector2(x, y)); }
// MAIN METHOD FOR PINPOINTING CHARACTERS ON THE MAP // Calculated from mapping of game tile positions to pixel coordinates of the map in MapModConstants. // Requires MapModConstants and modified map page in ./assets public static Vector2 LocationToMap(string locationName, int tileX = -1, int tileY = -1, Dictionary <string, MapVector[]> CustomMapVectors = null, bool isPlayer = false) { if (FarmBuildings.TryGetValue(locationName, out var mapLoc)) { return(mapLoc.Value); } if (locationName.StartsWith("UndergroundMine")) { var mine = locationName.Substring("UndergroundMine".Length, locationName.Length - "UndergroundMine".Length); if (int.TryParse(mine, out var mineLevel)) { // Skull cave if (mineLevel > 120) { locationName = "SkullCave"; } // Mines else { locationName = "Mine"; } } } MapVector[] locVectors; if (CustomMapVectors != null && (locationName == "Farm" || locationName == "FarmHouse" || locationName == "Cellar")) { // Handle different farm types for custom vectors var farms = new string[5] { "Farm", "Farm_Riverland", "Farm_Forest", "Farm_Hills", "Farm_Wilderness" }; if (CustomMapVectors.Keys.Any(locName => locName == farms.ElementAtOrDefault(Game1.whichFarm))) { if (!CustomMapVectors.TryGetValue(locationName, out locVectors)) { if (!ModConstants.MapVectors.TryGetValue(locationName, out locVectors)) { return(Vector2.Zero); } } } else { if (!ModConstants.MapVectors.TryGetValue(locationName, out locVectors)) { return(Vector2.Zero); } } } // If not in custom vectors, use default else if (!(CustomMapVectors != null && CustomMapVectors.TryGetValue(locationName, out locVectors))) { if (!ModConstants.MapVectors.TryGetValue(locationName, out locVectors)) { return(Vector2.Zero); } } if (locVectors == null) { return(Vector2.Zero); } int x; int y; // Precise (static) regions and indoor locations if (locVectors.Count() == 1 || tileX == -1 || tileY == -1) { x = locVectors.FirstOrDefault().MapX; y = locVectors.FirstOrDefault().MapY; } else { // Sort map vectors by distance to point var vectors = locVectors.OrderBy(vector => Math.Sqrt(Math.Pow(vector.TileX - tileX, 2) + Math.Pow(vector.TileY - tileY, 2))); MapVector lower = null; MapVector upper = null; var isSameAxis = false; // Create rectangle bound from two pre-defined points (lower & upper bound) and calculate map scale for that area foreach (var vector in vectors) { if (lower != null && upper != null) { if (lower.TileX == upper.TileX || lower.TileY == upper.TileY) { isSameAxis = true; } else { break; } } if ((lower == null || isSameAxis) && tileX >= vector.TileX && tileY >= vector.TileY) { lower = vector; continue; } if ((upper == null || isSameAxis) && tileX <= vector.TileX && tileY <= vector.TileY) { upper = vector; } } // Handle null cases - not enough vectors to calculate using lower/upper bound strategy // Uses fallback strategy - get closest points such that lower != upper var tilePos = "(" + tileX + ", " + tileY + ")"; if (lower == null) { lower = upper == vectors.First() ? vectors.Skip(1).First() : vectors.First(); } if (upper == null) { upper = lower == vectors.First() ? vectors.Skip(1).First() : vectors.First(); } x = (int)MathHelper.Clamp((int)(lower.MapX + (tileX - lower.TileX) / (double)(upper.TileX - lower.TileX) * (upper.MapX - lower.MapX)), 0, 1200); y = (int)MathHelper.Clamp((int)(lower.MapY + (tileY - lower.TileY) / (double)(upper.TileY - lower.TileY) * (upper.MapY - lower.MapY)), 0, 720); if (DEBUG_MODE && isPlayer) { _tileUpper = new Vector2(upper.TileX, upper.TileY); _tileLower = new Vector2(lower.TileX, lower.TileY); } } return(new Vector2(x, y)); }
// { "customLocation": [{x, y}] } where x, y are relative to map (for a custom location ex. a new house) // { "customRegion": [{x1, y1}, {x2, y2}] where x, y are relative to map (for a custom region ex. a new farm) // Any custom locations with given location on the map private void LoadCustomMapLocations() { // Merge SVE Config with main config var CustomMapLocations = SVEConfig != null ? ModMain.Config.CustomMapLocations.Concat(SVEConfig.CustomMapLocations).ToLookup(x => x.Key, x => x.Value) .ToDictionary(x => x.Key, g => g.First()) : ModMain.Config.CustomMapLocations; foreach (var mapVectors in CustomMapLocations) { var mapVectorArr = new MapVector[mapVectors.Value.Length]; for (var i = 0; i < mapVectors.Value.Length; i++) { // Don't use IF2R config for greenhouse if not default farm (hard-coded location) if (ModMain.IsSVE && mapVectors.Key == "Greenhouse" && Game1.whichFarm != 0) { mapVectorArr[i] = ModConstants.MapVectors["Greenhouse"].FirstOrDefault(); } else { var mapVector = mapVectors.Value[i]; // Marker doesn't need to specify corresponding Tile position if (mapVector.GetValue("TileX") == null || mapVector.GetValue("TileY") == null) { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY") ); } // Region must specify corresponding Tile positions for // Calculations on movement within location else { mapVectorArr[i] = new MapVector( (int)mapVector.GetValue("MapX"), (int)mapVector.GetValue("MapY"), (int)mapVector.GetValue("TileX"), (int)mapVector.GetValue("TileY") ); } } } MapVectors.Add(mapVectors.Key, mapVectorArr); } foreach (var location in ModMain.Config.CustomMapTextures) { Locations.Add(location.Key, new CustomLocation(location.Value)); } // Automatically adjust tracking for modded maps that are sized differently from vanilla map foreach (var location in Game1.locations) { var locationName = location.uniqueName.Value ?? location.Name; if (!location.IsOutdoors || locationName == "Summit" || MapVectors.ContainsKey(locationName) || !ModConstants.MapVectors.TryGetValue(locationName, out var mapVector)) { continue; } if (mapVector.LastOrDefault().TileX != location.Map.DisplayWidth / Game1.tileSize || mapVector.LastOrDefault().TileY != location.Map.DisplayHeight / Game1.tileSize) { MapVectors.Add(locationName, new[] { mapVector.FirstOrDefault(), new MapVector( mapVector.LastOrDefault().MapX, mapVector.LastOrDefault().MapY, location.Map.DisplayWidth / Game1.tileSize, location.Map.DisplayHeight / Game1.tileSize ) }); } } var customLocations = MapVectors.Keys.ToArray(); if (MapVectors.Keys.Count > 0) { if (customLocations.Length == 1) { Monitor.Log($"Handled tracking for custom location: {customLocations[0]}.", LogLevel.Debug); } else { var locationList = ""; for (var i = 0; i < customLocations.Length; i++) { locationList += customLocations[i] + (i + 1 == customLocations.Length ? "" : ", "); } Monitor.Log($"Handled tracking for custom locations: {locationList}.", LogLevel.Debug); } } }