using HPJ.Simulation; using HPJ.Simulation.Enums; using HPJ.Simulation.Map; using System; using System.Collections.Generic; using UnityEngine; namespace HPJ.Presentation.Obstacle { /// /// The Base class for all navigation obstacles /// public abstract class NavigationObstacle : MonoBehaviour { #region Unity // Start is called before the first frame update public virtual void Start() { if (!NavigationManager.Instance.Initialized) { Invoke(nameof(Start), 01f); return; } UpdatePosition(transform.position, true); NavigationManager.Instance?.AddObstacle(this); } public virtual void OnDestroy() { NavigationManager.Instance?.RemoveObstacle(this); } #endregion #region Obstacle /// /// Does the obstacle update automatically through the built in systems /// [SerializeField] protected bool _automaticUpdate = true; public bool AutomaticUpdate { get { return _automaticUpdate; } set { _automaticUpdate = value; } } /// /// The Height difference from the map to count as being on top of the map /// [SerializeField, Tooltip("The Height from the transform y position to the ground that this counts as an obstacle")] protected float _checkMapHeight = 1f; public float CheckMapHeight { get { return _checkMapHeight; } set { _checkMapHeight = value; } } /// /// The tile type the obstacle changes the tiles beneath it too /// [SerializeField] protected TileTypes _obstacleTileType = TileTypes.Obstacle; public TileTypes ObstacleTileType { get { return _obstacleTileType; } } /// /// The Update cycle of the obstacle /// public virtual void UpdateObstacle() { if (_automaticUpdate) { UpdatePosition(transform.position); } } /// /// The tiles and map relationship data with this obstacle /// public Dictionary MapTileRelationships { get; protected set; } /// /// Checks to see if the new position changes and if so it changes the mesh to block out the new corresponding tiles /// /// /// public abstract void UpdatePosition(Vector3 NewPosition, bool ForceCheck = false); /// /// What happens the the obstacle mesh position changes /// /// /// protected abstract bool PositionChanged(Vector3 NewPosition); /// /// Rotates the obstacle based on the desired Rotation. Basic NavigationObstacle will only swap the object size x and y values. Inherit to ovverride this behaviour /// /// public abstract void Rotate(Quaternion Rotation); /// /// Revalidates the tiles of the obstacle in case another obstacle travels over top of this one /// public abstract void RevalidateTiles(); #endregion } [Serializable] public struct PositionSquareRect { public Vector3 Position; public Vector2 Size; public Vector3[] Corners; public PositionSquareRect(Vector3 position, Vector2 size) { Position = position; Size = size; Corners = new Vector3[4]; Corners[0] = Position + new Vector3(Size.x / 2, 0, Size.y / 2); Corners[1] = Position + new Vector3(Size.x / 2, 0, -Size.y / 2); Corners[2] = Position + new Vector3(-Size.x / 2, 0, -Size.y / 2); Corners[3] = Position + new Vector3(-Size.x / 2, 0, Size.y / 2); } public PositionSquareRect(Vector3 position, Vector3 LowerBounds, Vector3 HigherBounds) { Position = position; Size = new Vector2(HigherBounds.x - LowerBounds.x, HigherBounds.z - LowerBounds.z); Corners = new Vector3[4]; LowerBounds.y = 0; HigherBounds.y = 0; Corners[0] = HigherBounds; Corners[1] = new Vector3(HigherBounds.x, 0, LowerBounds.z); Corners[2] = LowerBounds; Corners[3] = new Vector3(LowerBounds.x, 0, HigherBounds.z); } public void UpdatePosition(Vector3 position) { Position = position; Corners[0] = Position + new Vector3(Size.x / 2, 0, Size.y / 2); Corners[1] = Position + new Vector3(Size.x / 2, 0, -Size.y / 2); Corners[2] = Position + new Vector3(-Size.x / 2, 0, -Size.y / 2); Corners[3] = Position + new Vector3(-Size.x / 2, 0, Size.y / 2); } public void UpdateSize(Vector2 newSize) { Size = newSize; Corners[0] = Position + new Vector3(Size.x / 2, 0, Size.y / 2); Corners[1] = Position + new Vector3(Size.x / 2, 0, -Size.y / 2); Corners[2] = Position + new Vector3(-Size.x / 2, 0, -Size.y / 2); Corners[3] = Position + new Vector3(-Size.x / 2, 0, Size.y / 2); } public bool InBounds(Vector3 Point) { if (Point.x > Corners[0].x || Point.x < Corners[2].x || Point.z > Corners[0].z || Point.z < Corners[2].z) { return false; } return true; } } [Serializable] public class MapObstacleRelationshipData { public MapSet Map; public NavigationObstacle Obstacle; public Dictionary RelatedTilesData; public HashSet ValidatedTiles; public HashSet InvalidatedTiles; public HashSet AddedTiles; public TileTypes ObstacleTileType; public MapObstacleRelationshipData(MapSet map, NavigationObstacle obstacle, TileTypes ObstacleType) { ValidatedTiles = new HashSet(); InvalidatedTiles = new HashSet(); AddedTiles = new HashSet(); RelatedTilesData = new Dictionary(); Map = map; Obstacle = obstacle; ObstacleTileType = ObstacleType; } public void UnvalidateTiles() { InvalidatedTiles.Clear(); ValidatedTiles.Clear(); AddedTiles.Clear(); foreach (ObstacleData Data in RelatedTilesData.Values) { InvalidatedTiles.Add(Data.TileIndexKey); Data.UnValidate(); } } public void AddOrValidateTile(IntVector3 tileIndex) { if (RelatedTilesData.ContainsKey(tileIndex)) { RelatedTilesData[tileIndex].Validate(); InvalidatedTiles.Remove(tileIndex); ValidatedTiles.Add(tileIndex); } else { ObstacleData NewTile = new ObstacleData(tileIndex); RelatedTilesData.Add(tileIndex, NewTile); AddedTiles.Add(tileIndex); } } public void SortRelatedTileData() { foreach(IntVector3 InvalidatedTile in InvalidatedTiles) { RelatedTilesData.Remove(InvalidatedTile); } } public ChangeMapJob GenerateMapChangeJob() { if (AddedTiles.Count == 0 && InvalidatedTiles.Count == 0) { //Debug.Log("No Tiles Changed"); return null; } ChangeMapJob MapChange = new ChangeMapJob(); // Set to Obstacle type foreach (IntVector3 Tile in AddedTiles) { MapChange.AddTileChange(Tile.Vector2(), Obstacle.ObstacleTileType); } // Set back to default foreach (IntVector3 Tile in InvalidatedTiles) { MapChange.AddTileChange(Tile.Vector2(), (TileTypes)Map.SetSettings.MapSettings.MapInfo.SavedTiles[Tile.x, Tile.y]); } //Debug.Log($"Tiles To Change {MapChange.TilesToBeChanged.Count} -> Added {AddedTiles.Count} -> Removed {InvalidatedTiles.Count}"); return MapChange; } internal void Revalidate() { ChangeMapJob MapChange = new ChangeMapJob(); bool ChangedTiles = false; foreach (IntVector3 Tile in ValidatedTiles) { if (Map.GetTileType(Tile.Vector2()) != ObstacleTileType) { ChangedTiles = true; MapChange.AddTileChange(Tile.Vector2(), ObstacleTileType); } } if (ChangedTiles) { Map.ChangeMap(MapChange); } } } }