using HPJ.Simulation.Enums; using HPJ.Simulation.Pathing; using HPJ.Simulation.Utilities; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace HPJ.Simulation.Map { /// /// The Map that represents a area with square tiles /// public class Map { protected string _name; /// /// The Name of the Map /// public string Name { get { return _name; } } protected byte[,] _tiles; /// /// A 2D Array of the tiles of the Map in bytes /// public byte[,] Tiles { get { return _tiles; } } public MapSettings _settings; /// /// The Map Settings /// public MapSettings Settings { get { return _settings; } } /// /// If the Map has been Initialized /// public bool Initialized { get; protected set; } /// /// If the Map logic is currently running /// public bool Running { get; protected set; } /// /// If the Map uses agent avoidence /// public bool AgentAvoidence { get; protected set; } /// /// If the map is currently completing navigation job requests /// public bool CompletingNavigationJobs { get; protected set; } /// /// If the map is currently completing navigation map change requests /// public bool CompletingMapChanges { get; protected set; } /// /// If the map is currently completing tile ownership claims /// public bool CompletingClaims { get; protected set; } /// /// If the map is currently completing tile disownment claims /// public bool CompletingDisownment { get; protected set; } protected Dictionary _pointMaps; /// /// The Byte point maps used by the JPS pathfinding for each valid tile types /// public Dictionary PointMaps { get { return _pointMaps; } } public List _bridges; /// /// A List of all the bridges connected to this Map /// public List Bridges { get { return _bridges; } } public List _seams; /// /// A List of all the seams connected to this Map /// public List Seams { get { return _seams; } } /// /// The map copy that keeps record of tile ownership by agent /// public AgentMap AgentOwnershipMap { get; protected set; } /// /// The Parrallel options used by the map for the multithreading of each Unit that wants pathfinding /// protected ParallelOptions _parallelOptions; public Map(MapSettings mapSettings, string MapName, int DegreeOfParallelism, bool CreateAgentMap) { _name = MapName; _settings = mapSettings; _bridges = new List(); _requestedJobs = new List(); _requestedMapChanges = new List(); _agentClaimRequests = new List(); _agentDisownmentClaimRequests = new List(); _savedPaths = new Dictionary(); _needToBeUpdatedPaths = new List(); _parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = DegreeOfParallelism }; AgentAvoidence = CreateAgentMap; if (CreateAgentMap) { AgentOwnershipMap = new AgentMap(this); } } /// /// The Debug callback to Unity /// public Action DebugLogCallback; /// /// Initialized the Map /// /// public void Initialize(Action debugLogCallback) { DebugLogCallback = debugLogCallback; Running = true; try { _pointMaps = new Dictionary(); _settings.MapInfo.MakeSureSizeIsRight(); _tiles = new byte[_settings.MapWidth, _settings.MapHeight]; // Create Tiles for (int x = 0; x < _settings.MapWidth; x++) { for (int y = 0; y < _settings.MapHeight; y++) { _tiles[x, y] = _settings.MapInfo.SavedTiles[x, y]; } } Initialized = true; _pointMaps.Add(_settings.DefaultTraversaleTiles.TilesToString(), new BytePointMap(_tiles, _settings.DefaultTraversaleTiles, Settings.MapWidth, Settings.MapHeight, Settings.CornerCutting)); DebugLogCallback?.Invoke($"{_name} Map Initialized", SimulationDebugLog.Log); } catch (Exception ex) { Log($"({_settings.MapWidth},{_settings.MapHeight}) vs ({_settings.MapInfo.Width},{_settings.MapInfo.Height}) vs ({_settings.MapInfo.SavedTiles.GetLength(0)},{_settings.MapInfo.SavedTiles.GetLength(1)}) -> {_settings.MapInfo.SavedTiles.Length}, P{_settings.MapInfo.SavedTiles.LongLength}, {_settings.MapInfo.SavedTiles.Rank}"); DebugLogCallback?.Invoke($"{ex.Message}: {ex.StackTrace}", SimulationDebugLog.Error); } } public void OnDestroy() { Running = false; } /// /// Debug.Log /// /// public void Log(string Log) { DebugLogCallback?.Invoke(Log, SimulationDebugLog.Log); } /// /// Debug.Warning /// /// public void LogWarning(string Log) { DebugLogCallback?.Invoke(Log, SimulationDebugLog.Warning); } /// /// Debug.Error /// /// public void LogError(string Log) { DebugLogCallback?.Invoke(Log, SimulationDebugLog.Error); } /// /// Gets or Generates a Byte point map based on a set of traversable tiles types /// /// /// internal BytePointMap GetBytePointMap(NavigationPath path) { lock (_pointMaps) { if (!_pointMaps.ContainsKey(path.Info.TraversableTilesKey)) { _pointMaps.Add(path.Info.TraversableTilesKey, new BytePointMap(_tiles, path.Info.TraversableTilesKey.StringToTilesArray(), Settings.MapWidth, Settings.MapHeight, Settings.CornerCutting)); } } return _pointMaps[path.Info.TraversableTilesKey]; } /// /// Gets the movement speed for a specific tile type on this Map /// /// /// internal byte GetTileSpeedData(TileTypes TileType) { for (int i = 0; i < Settings.TileData.Count; i++) { TileData Data = Settings.TileData[i]; if (Data.Type == TileType) { return Data.Speed; } } return 0; } /// /// A List of the Requested Navigation Jobs /// protected List _requestedJobs; /// /// A List of the Requested Map Changes /// protected List _requestedMapChanges; /// /// A List of the Requested Tile Ownership claims /// protected List _agentClaimRequests; /// /// A List of the Requested Tile Disownment claims /// protected List _agentDisownmentClaimRequests; /// /// A List of all the saved paths that need to be updated because they were invalidated or are new /// protected List _needToBeUpdatedPaths; /// /// A Dictionary of all the saved paths on this Map /// protected Dictionary _savedPaths; /// /// The Update function of the map each update tick /// public void Update() { try { if (!Initialized) { return; } // Update Claims lock (_agentDisownmentClaimRequests) { if (_agentDisownmentClaimRequests.Count > 0) { CompletingDisownment = true; for (int i = 0; i < _agentDisownmentClaimRequests.Count; i++) { Disown(_agentDisownmentClaimRequests[i]); } CompletingDisownment = false; _agentDisownmentClaimRequests.Clear(); } } lock (_agentClaimRequests) { if (_agentClaimRequests.Count > 0) { CompletingClaims = true; for (int i = 0; i < _agentClaimRequests.Count; i++) { Claim(_agentClaimRequests[i]); } CompletingClaims = false; _agentClaimRequests.Clear(); } } // Update Map lock (_requestedMapChanges) { if (_requestedMapChanges.Count > 0) { CompletingMapChanges = true; for (int i = 0; i < _requestedMapChanges.Count; i++) { ChangeMap(_requestedMapChanges[i]); } CompletingMapChanges = false; _requestedMapChanges.Clear(); } } // Complete Navigation Jobs lock (_requestedJobs) { if (_requestedJobs.Count > 0) { CompletingNavigationJobs = true; lock (_savedPaths) { if (AgentAvoidence) { // Find out the paths that need to be recalculated for (int i = 0; i < _requestedJobs.Count; i++) { NavigationJob Job = _requestedJobs[i]; NavigationPath Path; Path = new NavigationPath(Job.Info, this); _needToBeUpdatedPaths.Add(Path); Path.AgentID = Job.AgentID; Job.SetNewCurrentPath(Path); } } else { // Find out the paths that need to be recalculated for (int i = 0; i < _requestedJobs.Count; i++) { NavigationJob Job = _requestedJobs[i]; NavigationPath Path; if (_savedPaths.ContainsKey(Job.Info)) { Path = _savedPaths[Job.Info]; if (!Path.ValidPath || Job.RepathSavedPath) { Path.Path.Clear(); // In Case the Path is already being pathed if (!_needToBeUpdatedPaths.Contains(Path)) { _needToBeUpdatedPaths.Add(Path); } } } else { Path = new NavigationPath(Job.Info, this); _savedPaths.Add(Job.Info, Path); _needToBeUpdatedPaths.Add(Path); } Job.SetNewCurrentPath(Path); } } } // Recalculate Path Parallel.ForEach(_needToBeUpdatedPaths, _parallelOptions, CompletePathfind); // Complete the pathfinding //Parallel.ForEach(_requestedJobs, _parallelOptions, FinishPathfinding); for(int i = _requestedJobs.Count - 1; i >= 0; i--) { _requestedJobs[i].PathfindingComplete(); } _needToBeUpdatedPaths.Clear(); _requestedJobs.Clear(); CompletingNavigationJobs = false; } } } catch (Exception ex) { LogError($"Map Error {ex.Message}: {ex.StackTrace}"); } } /// /// A Check to see if the navigation job can be run on this map /// /// /// internal bool JobCompatible(NavigationJob job) { if (!job.TraverableTileTypes.Contains(GetTileType(job.Info.PointA)) || !job.TraverableTileTypes.Contains(GetTileType(job.Info.PointB))) { return false; } return PointInMap(job.Info.PointA) && PointInMap(job.Info.PointB); } /// /// Checks to see if this point is valid on this map /// /// /// public bool PointInMap(IntVector3 WorldPosition) { return GetTileType(WorldPosition) != TileTypes.OutOfBounds; } /// /// Checks to see if this point is valid on this map /// /// /// public bool PointInMap(IntVector2 TilePosition) { return GetTileType(TilePosition) != TileTypes.OutOfBounds; } /// /// Adds a Map change Request for this map /// /// public void AddMapChange(ChangeMapJob MapChange) { lock (_requestedMapChanges) { _requestedMapChanges.Add(MapChange); } } /// /// Adds a agent tile request claim for this map /// /// public void AddClaim(TileOwnershipClaim Claim) { lock (_agentClaimRequests) { _agentClaimRequests.Add(Claim); } } /// /// Adds a agent tile disownment claim for this map /// /// public void AddDisownmentClaim(DisownTileOwnership Claim) { lock (_agentDisownmentClaimRequests) { _agentDisownmentClaimRequests.Add(Claim); } } /// /// Changes the map tiles based on the map change requests /// /// protected void ChangeMap(ChangeMapJob MapChange) { for (int i = 0; i < MapChange.TilesToBeChanged.Count; i++) { (IntVector2, TileTypes) TileChange = MapChange.TilesToBeChanged[i]; SetTile(TileChange.Item1, TileChange.Item2); foreach(BytePointMap ByteMap in _pointMaps.Values) { ByteMap.ChangeTile(TileChange.Item1, TileChange.Item2); } } } /// /// Agent claims a tile /// /// protected void Claim(TileOwnershipClaim Claim) { AgentOwnershipMap.ClaimTile(Claim.RequestTile, Claim.OwnerID); } /// /// Agent disowns a claims to a tile /// /// protected void Disown(DisownTileOwnership Claim) { AgentOwnershipMap.DisownTile(Claim.RequestTile, Claim.OwnerID); } /// /// Sets the tile to the new type /// /// /// protected void SetTile(IntVector2 TilePosition, TileTypes TypeChange) { _tiles[TilePosition.x, TilePosition.y] = (byte)TypeChange; } /// /// Gets the current tile type of this tile index /// /// /// public TileTypes GetTileType(IntVector3 WorldPosition) { // Offset the Position WorldPosition.x -= Settings.Offset.x; WorldPosition.z -= Settings.Offset.y; WorldPosition.x = (int)Math.Floor((float)WorldPosition.x / Settings.TileSize); WorldPosition.z = (int)Math.Floor((float)WorldPosition.z / Settings.TileSize); if (WorldPosition.x < 0 || WorldPosition.z < 0 || WorldPosition.x >= Settings.MapWidth || WorldPosition.z >= Settings.MapHeight) { return TileTypes.OutOfBounds; } return (TileTypes)Tiles[WorldPosition.x, WorldPosition.z]; } /// /// Gets the current tile type of this tile index /// /// /// public TileTypes GetTileType(IntVector2 TilePosition) { if (TilePosition.x < 0 || TilePosition.y < 0 || TilePosition.x >= Settings.MapWidth || TilePosition.y >= Settings.MapHeight) { return TileTypes.OutOfBounds; } return (TileTypes)Tiles[TilePosition.x, TilePosition.y]; } /// /// Gets the current tile type of this tile index /// /// /// /// public TileTypes GetTileType(int x, int y) { if (x < 0 || y < 0 || x >= Settings.MapWidth || y >= Settings.MapHeight) { return TileTypes.OutOfBounds; } return (TileTypes)Tiles[x, y]; } /// /// Gets the current tile index at this world position /// /// /// public IntVector2 GetTileIndexPosition(IntVector3 WorldTileIndexPosition) { // Offset the Position IntVector2 TileIndex = new IntVector2(); TileIndex.x = (int)((WorldTileIndexPosition.x - _settings.Offset.x) / (float)_settings.TileSize); TileIndex.y = (int)((WorldTileIndexPosition.z - _settings.Offset.z) / (float)_settings.TileSize); return TileIndex; } /// /// Adds a navigation Request to the Map /// /// public void AddNavigationJob(NavigationJob Job) { lock (_requestedJobs) { Job.CurrentStep = PathfindingCalculationStep.Pathfinding; _requestedJobs.Add(Job); } } /// /// Calculates the Requested Path on the Map /// /// protected void CompletePathfind(NavigationPath Path) { if (Path.Info.NavigationType == NavigationTypes.JumpPointSearch) { JumpPointSearch.Instance.CalculatePath(Path, Path.HostMap, out string Status, out bool Succeeded); Path.PreviousStatus = Status; Path.ValidPath = Succeeded; } else if (Path.Info.NavigationType == NavigationTypes.AStar) { AStar.Instance.CalculatePath(Path, Path.HostMap, out string Status, out bool Succeeded); Path.PreviousStatus = Status; Path.ValidPath = Succeeded; } else if (Path.Info.NavigationType == NavigationTypes.Free) { Path.Path.Clear(); Path.Path.Add(Path.Info.PointA); Path.Path.Add(Path.Info.PointB); Path.PreviousStatus = "Free Pathed"; Path.ValidPath = true; } } /// /// Comples the Navigation Job Request /// /// protected void FinishPathfinding(NavigationJob Job) { Job.PathfindingComplete(); } } /// /// The Map settings used to shape the Map /// [Serializable] public class MapSettings { /// /// The Size of the tile in centimeters not meters /// public int TileSize = 100; // In cm /// /// The Number of tiles in the x direction /// public int MapWidth = 100; /// /// The Number of tiles in the z direction /// public int MapHeight = 100; /// /// The Physical offset of the map in centimeters /// public IntVector3 Offset = new IntVector3(); // in cm /// /// If corner cutting is enabled in the pathfinding on this Map /// public bool CornerCutting = false; /// /// The Data for each tile on this Map /// public List TileData = new List(); /// The Data used to keep track of the map width and height /// public MapData MapInfo = new MapData(); /// /// The Default traversable tiles for this Map /// public TileTypes[] DefaultTraversaleTiles = new TileTypes[2] { TileTypes.Standard, TileTypes.Free }; public MapSettings() { } /// /// Adjusts the Map Size in a specific direction /// /// /// /// /// public void AdjustMap(int TilesChanged, MapExpansionSide ExpansionSide, int NewOffset, int Translation = 0) { if (TilesChanged == 0) { return; } if (ExpansionSide == MapExpansionSide.Left) { if (TilesChanged <= 0) { return; } if (TilesChanged >= 100000) { return; } Translation = MapWidth - TilesChanged; MapWidth = TilesChanged; Offset.x = NewOffset; } else if (ExpansionSide == MapExpansionSide.Right) { if (TilesChanged <= 0) { return; } if (TilesChanged >= 100000) { return; } MapWidth = TilesChanged; Offset.x = NewOffset; } else if (ExpansionSide == MapExpansionSide.Top) { if (TilesChanged <= 0) { return; } if (TilesChanged >= 100000) { return; } MapHeight = TilesChanged; Offset.z = NewOffset; } else if (ExpansionSide == MapExpansionSide.Bottom) { if (TilesChanged <= 0) { return; } if (TilesChanged >= 100000) { return; } Translation = MapHeight - TilesChanged; MapHeight = TilesChanged; Offset.z = NewOffset; } UpdateMapSize(); if (ExpansionSide == MapExpansionSide.Left) { MapInfo.TransalateValues(Translation, 0); } else if (ExpansionSide == MapExpansionSide.Right) { MapInfo.TransalateValues(Translation, 0); } else if (ExpansionSide == MapExpansionSide.Bottom) { MapInfo.TransalateValues(0, Translation); } else if (ExpansionSide == MapExpansionSide.Top) { MapInfo.TransalateValues(0, Translation); } } /// /// Updates the Map Size /// public void UpdateMapSize() { MapInfo.UpdateToMapSize(MapWidth, MapHeight); } } /// /// A Set of points that the navigation can use to transfer a Agent from one map to another /// [Serializable] public class MapBridge { /// /// Map on Side A of the Bridge /// public MapSet MapA; /*{ get { return _mapA; } set { _mapA = value; } }*/ //protected MapSet _mapA; /// /// Map on Side B of the Bridge /// public MapSet MapB; /*{ get { return _mapB; } set { _mapB = value; } }*/ //protected MapSet _mapB; /// /// Which ways the bridge can be used /// public BridgeDirections WalkableDirections; /// /// All the points from Map A to Map B /// public List MidPoints; /// /// If this map is a valid and can be used /// public bool ValidBridge = false; public MapBridge(MapSet A, MapSet B, List BridgePoints) { WalkableDirections = BridgeDirections.Both; MapA = A; MapB = B; MidPoints = BridgePoints; } /// /// Checks to see this bridge connects two maps /// /// /// /// public bool DirectConnects(MapSet startingMap, MapSet endingMap) { if (startingMap.InstanceID == MapA.InstanceID) { if (endingMap.InstanceID == MapB.InstanceID) { return true; } } else if (startingMap.InstanceID == MapB.InstanceID) { if (endingMap.InstanceID == MapA.InstanceID) { return true; } } return false; } /// /// Checks to see if you can walk on the bridge in this direction. Starting Map to Ending Map /// /// /// /// public bool CanWalkInDirection(MapSet startingMap, MapSet endingMap) { // Make sure these maps are even connected if (!DirectConnects(startingMap, endingMap)) { return false; } if (WalkableDirections == BridgeDirections.Both) { return true; } if (startingMap.InstanceID == MapA.InstanceID) { if (endingMap.InstanceID == MapB.InstanceID) { return WalkableDirections == BridgeDirections.A_To_B; } } else if (startingMap.InstanceID == MapB.InstanceID) { if (endingMap.InstanceID == MapA.InstanceID) { return WalkableDirections == BridgeDirections.B_To_A; } } return false; } /// /// Gets the starting point for a Map if that map connects to this Bridge /// /// /// public IntVector3 GetPoint(MapSet startingMap) { if (startingMap.InstanceID == MapA.InstanceID) { return MidPoints[0]; } else if (startingMap.InstanceID == MapB.InstanceID) { return MidPoints[MidPoints.Count - 1]; } return new IntVector3(); } /// /// Gets the ending point for a Map if that map connects to this Bridge /// /// /// public IntVector3 GetOpposingPoint(MapSet startingMap) { if (startingMap.InstanceID == MapA.InstanceID) { return MidPoints[MidPoints.Count - 1]; } else if (startingMap.InstanceID == MapB.InstanceID) { return MidPoints[0]; } return new IntVector3(); } /// /// Checks to see if the given maps is on one side of the Bridge /// /// /// public bool Contains(MapSet Map) { if (Map.InstanceID == MapA.InstanceID) { return true; } else if (Map.InstanceID == MapB.InstanceID) { return true; } return false; } /// /// Gets the other Map if you know one side the bridge connects too /// /// /// public MapSet Other(MapSet Map) { if (MapA.InstanceID == Map.InstanceID) { return MapB; } else if (MapB.InstanceID == Map.InstanceID) { return MapA; } return null; } public void RefreshWithNew(MapSet Map) { if (MapA.InstanceID == Map.InstanceID) { MapA = Map; } else if (MapB.InstanceID == Map.InstanceID) { MapB = Map; } } public void Verify() { ValidBridge = MapA != null && MapB != null; } } /// /// A Set of points that the navigation can use to transfer a Agent from one map to another /// [Serializable] public class MapSeam { /// /// Map on Side A of the Bridge /// public MapSet MapA; /// /// The connection side for Map A /// public SeamConnectionSide MapA_ConectionSide; /// /// The Map A Starting Tile Index /// public IntVector2 MapA_StartingTileIndex; /// /// The Map A Ending Tile Index /// public IntVector2 MapA_EndingTileIndex; /// /// Map on Side B of the Bridge /// public MapSet MapB; /// /// The connection side for Map A /// public SeamConnectionSide MapB_ConectionSide; /// /// The Map B Starting Tile Index /// public IntVector2 MapB_StartingTileIndex; /// /// The Map B Ending Tile Index /// public IntVector2 MapB_EndingTileIndex; /// /// If this map is a valid and can be used /// public bool ValidSeam = false; public MapSeam(MapSet A, MapSet B, SeamConnectionSide mapA_ConectionSide, SeamConnectionSide mapB_ConectionSide) { MapA = A; MapB = B; MapA_ConectionSide = mapA_ConectionSide; MapB_ConectionSide = mapB_ConectionSide; if (mapA_ConectionSide == SeamConnectionSide.South) { MapA_StartingTileIndex = IntVector2.Zero; MapA_EndingTileIndex = new IntVector2(A.SetSettings.MapSettings.MapWidth - 1, 0); } else if (mapA_ConectionSide == SeamConnectionSide.North) { MapA_StartingTileIndex = new IntVector2(0, A.SetSettings.MapSettings.MapHeight - 1); MapA_EndingTileIndex = new IntVector2(A.SetSettings.MapSettings.MapWidth - 1, A.SetSettings.MapSettings.MapHeight - 1); } else if (mapA_ConectionSide == SeamConnectionSide.West) { MapA_StartingTileIndex = IntVector2.Zero; MapA_EndingTileIndex = new IntVector2(0, A.SetSettings.MapSettings.MapHeight - 1); } else if (mapA_ConectionSide == SeamConnectionSide.East) { MapA_StartingTileIndex = new IntVector2(A.SetSettings.MapSettings.MapWidth - 1, 0); MapA_EndingTileIndex = new IntVector2(A.SetSettings.MapSettings.MapWidth - 1, A.SetSettings.MapSettings.MapHeight - 1); } if (mapB_ConectionSide == SeamConnectionSide.South) { MapB_StartingTileIndex = IntVector2.Zero; MapB_EndingTileIndex = new IntVector2(B.SetSettings.MapSettings.MapWidth - 1, 0); } else if (mapB_ConectionSide == SeamConnectionSide.North) { MapB_StartingTileIndex = new IntVector2(0, B.SetSettings.MapSettings.MapHeight - 1); MapB_EndingTileIndex = new IntVector2(B.SetSettings.MapSettings.MapWidth - 1, B.SetSettings.MapSettings.MapHeight - 1); } else if (mapB_ConectionSide == SeamConnectionSide.West) { MapB_StartingTileIndex = IntVector2.Zero; MapB_EndingTileIndex = new IntVector2(0, B.SetSettings.MapSettings.MapHeight - 1); } else if (mapB_ConectionSide == SeamConnectionSide.East) { MapB_StartingTileIndex = new IntVector2(B.SetSettings.MapSettings.MapWidth - 1, 0); MapB_EndingTileIndex = new IntVector2(B.SetSettings.MapSettings.MapWidth - 1, B.SetSettings.MapSettings.MapHeight - 1); } } /// /// Checks to see this bridge connects two maps /// /// /// /// public bool DirectConnects(MapSet startingMap, MapSet endingMap) { if (startingMap.InstanceID == MapA.InstanceID) { if (endingMap.InstanceID == MapB.InstanceID) { return true; } } else if (startingMap.InstanceID == MapB.InstanceID) { if (endingMap.InstanceID == MapA.InstanceID) { return true; } } return false; } /// /// Checks to see if the given maps is on one side of the Bridge /// /// /// public bool Contains(MapSet Map) { if (Map.InstanceID == MapA.InstanceID) { return true; } else if (Map.InstanceID == MapB.InstanceID) { return true; } return false; } /// /// Gets the other Map if you know one side the bridge connects too /// /// /// public MapSet Other(MapSet Map) { if (MapA.InstanceID == Map.InstanceID) { return MapB; } else if (MapB.InstanceID == Map.InstanceID) { return MapA; } return null; } public void RefreshWithNew(MapSet Map) { if (MapA.InstanceID == Map.InstanceID) { MapA = Map; } else if (MapB.InstanceID == Map.InstanceID) { MapB = Map; } } public void Verify() { ValidSeam = MapA != null && MapB != null; } /// /// Gets the closest valid point for the starting map to the seam /// /// /// /// public IntVector3 GetPoint(MapSet startingMap, IntVector3 start) { IntVector2 StartIndex = startingMap.GetClosestMapTileIndex(start); if (startingMap == MapA) { if (MapA_ConectionSide == SeamConnectionSide.North) { StartIndex.y = startingMap.SetSettings.MapSettings.MapHeight - 1; } else if (MapA_ConectionSide == SeamConnectionSide.South) { StartIndex.y = 0; } else if (MapA_ConectionSide == SeamConnectionSide.West) { StartIndex.x = startingMap.SetSettings.MapSettings.MapWidth - 1; } else if (MapA_ConectionSide == SeamConnectionSide.East) { StartIndex.x = 0; } } else { if (MapB_ConectionSide == SeamConnectionSide.North) { StartIndex.y = startingMap.SetSettings.MapSettings.MapHeight - 1; } else if (MapB_ConectionSide == SeamConnectionSide.South) { StartIndex.y = 0; } else if (MapB_ConectionSide == SeamConnectionSide.West) { StartIndex.x = startingMap.SetSettings.MapSettings.MapWidth - 1; } else if (MapB_ConectionSide == SeamConnectionSide.East) { StartIndex.x = 0; } } return startingMap.GetWorldTileIndex(StartIndex); } public IntVector3 GetOppositePoint(MapSet startingMap, IntVector3 start) { IntVector3 StartingPoint = GetPoint(startingMap, start); // Iterative Method - Would like to remove when calculative method is fixed MapSet EndingMap = Other(startingMap); if (EndingMap == MapA) { int ClosestScore = int.MaxValue; IntVector2 ClosestEndTileIndex = new IntVector2(); for (int x = 0; x <= MapA_StartingTileIndex.x; x++ ) { for (int y = 0; y <= MapA_EndingTileIndex.y; y++) { int NewScore = IntVector3.Displacement(StartingPoint, EndingMap.GetWorldTileIndex(x, y)); if (NewScore < ClosestScore) { ClosestScore = NewScore; ClosestEndTileIndex = new IntVector2(x, y); } } } return EndingMap.GetWorldTileIndex(ClosestEndTileIndex); } else if (EndingMap == MapB) { int ClosestScore = int.MaxValue; IntVector2 ClosestEndTileIndex = new IntVector2(); for (int x = 0; x <= MapB_StartingTileIndex.x; x++) { for (int y = 0; y <= MapB_EndingTileIndex.y; y++) { int NewScore = IntVector3.Displacement(StartingPoint, EndingMap.GetWorldTileIndex(x, y)); if (NewScore < ClosestScore) { ClosestScore = NewScore; ClosestEndTileIndex = new IntVector2(x, y); } } } return EndingMap.GetWorldTileIndex(ClosestEndTileIndex); } // Caluclation Method - Needs Work somehwere IntVector2 StartIndex = startingMap.GetClosestMapTileIndex(StartingPoint); int startingMapLength; int endingMapLength; double startingMapRatio; if (startingMap == MapA) { if (MapA_ConectionSide == SeamConnectionSide.South || MapA_ConectionSide == SeamConnectionSide.North) { startingMapLength = MapA.SetSettings.MapSettings.MapWidth; startingMapRatio = (double)StartIndex.x / startingMapLength; } else { startingMapLength = MapA.SetSettings.MapSettings.MapHeight; startingMapRatio = (double)StartIndex.y / startingMapLength; } if (MapB_ConectionSide == SeamConnectionSide.South || MapB_ConectionSide == SeamConnectionSide.North) { endingMapLength = MapB.SetSettings.MapSettings.MapWidth; } else { endingMapLength = MapB.SetSettings.MapSettings.MapHeight; } } else { if (MapB_ConectionSide == SeamConnectionSide.South || MapB_ConectionSide == SeamConnectionSide.North) { startingMapLength = MapB.SetSettings.MapSettings.MapWidth; startingMapRatio = (double)StartIndex.x / startingMapLength; } else { startingMapLength = MapB.SetSettings.MapSettings.MapHeight; startingMapRatio = (double)StartIndex.y / startingMapLength; } if (MapA_ConectionSide == SeamConnectionSide.South || MapA_ConectionSide == SeamConnectionSide.North) { endingMapLength = MapA.SetSettings.MapSettings.MapWidth; } else { endingMapLength = MapA.SetSettings.MapSettings.MapHeight; } } double mapLengthRatio = (double)endingMapLength / startingMapLength; int mapEndFinalPoint = (int)System.Math.Round(mapLengthRatio * startingMapRatio * endingMapLength); IntVector2 EndingPoint; MapSet endingMap; if (startingMap == MapA) { endingMap = MapB; if (MapB_ConectionSide == SeamConnectionSide.South) { EndingPoint = new IntVector2(mapEndFinalPoint, 0); } else if (MapB_ConectionSide == SeamConnectionSide.North) { EndingPoint = new IntVector2(mapEndFinalPoint, MapB.SetSettings.MapSettings.MapHeight - 1); } else if (MapB_ConectionSide == SeamConnectionSide.West) { EndingPoint = new IntVector2(MapB.SetSettings.MapSettings.MapWidth - 1, mapEndFinalPoint); } else { EndingPoint = new IntVector2(0, mapEndFinalPoint); } } else { endingMap = MapA; if (MapA_ConectionSide == SeamConnectionSide.South) { EndingPoint = new IntVector2(mapEndFinalPoint, 0); } else if (MapA_ConectionSide == SeamConnectionSide.North) { EndingPoint = new IntVector2(mapEndFinalPoint, MapA.SetSettings.MapSettings.MapHeight - 1); } else if (MapA_ConectionSide == SeamConnectionSide.West) { EndingPoint = new IntVector2(MapA.SetSettings.MapSettings.MapWidth - 1, mapEndFinalPoint); } else { EndingPoint = new IntVector2(0, mapEndFinalPoint); } } return endingMap.GetWorldTileIndex(EndingPoint); } } /// /// Tile Size Data for the Map, used to save default tiles to the Map /// [System.Serializable] public class MapData { public byte[,] SavedTiles = new byte[100, 100]; public int Width = 100; public int Height = 100; public MapData() { } public bool MakeSureSizeIsRight() { if (Width != SavedTiles.GetLength(0) || Height != SavedTiles.GetLength(1)) { try { byte[,] PreviousTiles = SavedTiles; int PreviousWidth = SavedTiles.GetLength(0); int PreviousHeight = SavedTiles.GetLength(1); SavedTiles = new byte[Width, Height]; for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { if (x < PreviousWidth && y < PreviousHeight) { SavedTiles[x, y] = PreviousTiles[x, y]; } else { SavedTiles[x, y] = (byte)TileTypes.Standard; } } } } catch (Exception e) { throw new Exception($"{e.Message} \n {e.StackTrace} \n ----------------------------------------- \n {Width} vs {SavedTiles.GetLength(0)} & {Height} vs {SavedTiles.GetLength(1)}"); } return true; } return false; } /// /// Updates the size of the Data /// /// /// public void UpdateToMapSize(int mapWidth, int mapHeight) { if (mapWidth == Width && mapHeight == Height) { return; } try { byte[,] PreviousTiles = SavedTiles; SavedTiles = new byte[mapWidth, mapHeight]; int PreviousWidth = Width; int PreviousHeight = Height; Width = mapWidth; Height = mapHeight; for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { if (x < PreviousWidth && y < PreviousHeight) { SavedTiles[x, y] = PreviousTiles[x, y]; } else { SavedTiles[x, y] = (byte)TileTypes.Standard; } } } } catch { } } /// /// Moves the values of the tiles /// /// /// public void TransalateValues(int HorizontalTranslation, int VerticalTranslation) { if (HorizontalTranslation == 0 && VerticalTranslation == 0) { return; } byte[,] NewTiles = new byte[Width, Height]; for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { int TranslatedX = x + HorizontalTranslation; int TranslatedY = y + VerticalTranslation; if (TranslatedX < 0 || TranslatedY < 0 || TranslatedX >= Width || TranslatedY >= Height) { NewTiles[x, y] = (byte)TileTypes.Standard; } else { NewTiles[x, y] = SavedTiles[TranslatedX, TranslatedY]; } } } SavedTiles = NewTiles; } /// /// Clears the map of all tile types and turns them into standard tiles /// public void CleanMap() { for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { SavedTiles[x, y] = (byte)TileTypes.Standard; } } } } /// /// The Data that tells the speed multiplier for a given tile type /// [System.Serializable] public class TileData { public TileTypes Type; public byte Speed; public TileData() { Type = TileTypes.Standard; Speed = 100; // 100 = 100% Speed } public TileData(TileTypes tileType, byte speedMult) { Type = tileType; Speed = speedMult; } } /// /// Used to keep track of a obstacles tiles and its state in the obstacle /// [Serializable] public struct ObstacleData { public IntVector3 TileIndexKey; public ObstacleValidationStates ValidationState; public ObstacleData(IntVector3 Index) { TileIndexKey = Index; ValidationState = ObstacleValidationStates.Added; } /// /// Means this tile is no longer valid and will need to be checked if it still belongs on this obstacle /// public void UnValidate() { ValidationState = ObstacleValidationStates.Invalidated; } /// /// Validates the tile, it belongs on this obstacle /// public void Validate() { ValidationState = ObstacleValidationStates.Validated; } } }