You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
7.9 KiB

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.Linq;
  5. /// <summary>
  6. /// Class which defines blocks around the level
  7. /// </summary>
  8. [RequireComponent(typeof(BoxCollider))]
  9. public class Block : MonoBehaviour
  10. {
  11. #region Inspector Fields
  12. [SerializeField]
  13. [Tooltip("Offset from the top of the block from which a character should stand")]
  14. private Vector3 VisualOffset = Vector3.zero;
  15. [SerializeField]
  16. [Tooltip("Can this type of block be walked on")]
  17. public bool is_Walkable = true;
  18. [Tooltip("Is this block underwater?")]
  19. public bool isWater = false;
  20. [Tooltip("Is this block at the bottom of a pit?")]
  21. public bool isPit = false;
  22. [Header("Spawn Settings")]
  23. [Tooltip("Can this block be spawned on")]
  24. public bool isSpawnable = false;
  25. [Tooltip("Direction Player is poting at when spawned")]
  26. public Direction SpawnDirection = Direction.Forward;
  27. #endregion InspectorFields
  28. #region Private Functions
  29. /// <summary>
  30. /// List of current players on this block
  31. /// </summary>
  32. public Character CurrentPlayer { get; protected set; }
  33. #endregion Private Functions
  34. #region ReadOnly Properties
  35. /// <summary>
  36. /// Blocks position in global space
  37. /// </summary>
  38. public Vector3 position { get { return transform.position; } }
  39. /// <summary>
  40. /// Position character should stand in global space
  41. /// </summary>
  42. public Vector3 VisualPosition { get { return position + VisualOffset + Vector3.up * 0.5f; } }
  43. #endregion ReadOnly Properties
  44. #region Public Functions
  45. /// <summary>
  46. /// Is a block valid to walk on
  47. /// </summary>
  48. /// <param name="layerMask">Layers to check for when checking for blocks above</param>
  49. /// <returns></returns>
  50. public bool isWalkable(LayerMask layerMask)
  51. {
  52. //checks if there is no block above this one and that this is tagged as walkable
  53. return (is_Walkable && !isBlockAtPosition(position + Vector3.up, 1, layerMask));
  54. }
  55. /// <summary>
  56. /// Is called when a player moves onto this block
  57. ///
  58. /// Should be implemented by a derived class
  59. /// </summary>
  60. /// <param name="player">Player which moved on to block</param>
  61. ///<param name="moveDirection">The direction the player moved to get to this block</param>
  62. public virtual IEnumerator OnWalkedOnByPlayer(Character player, Vector3 moveDirection)
  63. {
  64. yield return StartCoroutine(DoPush(player, moveDirection));
  65. }
  66. /// <summary>
  67. /// Is called when a player moves off of block
  68. ///
  69. /// Should be implemented by a derived class
  70. /// </summary>
  71. /// <param name="player">Player which moved on to block</param>
  72. public virtual void OnLeftByPlayer(Character player)
  73. {
  74. CurrentPlayer = null;
  75. }
  76. /// <summary>
  77. /// Called to deal with players colliding on this block
  78. /// </summary>
  79. /// <param name="newPlayer">Player which is moving into this block</param>
  80. /// <param name="moveDirection">The direction the player moved to get to this block</param>
  81. public virtual IEnumerator DoPush(Character newPlayer, Vector3 moveDirection)
  82. {
  83. if (CurrentPlayer == null)
  84. {
  85. CurrentPlayer = newPlayer;
  86. yield break;
  87. }
  88. Block pushBlock = GetPushLocation(moveDirection, ~CurrentPlayer.Ignore);
  89. if (pushBlock != this)
  90. {
  91. Character oldPlayer = CurrentPlayer;
  92. CurrentPlayer = newPlayer;
  93. yield return StartCoroutine(oldPlayer.MoveToBlock(pushBlock, Character.Animation.Hit, 1));
  94. }
  95. else
  96. {
  97. Block returnBlock = GetPushLocation(-moveDirection, ~newPlayer.Ignore);
  98. if (returnBlock != this)
  99. yield return StartCoroutine(newPlayer.MoveToBlock(returnBlock, Character.Animation.Hit, 1));
  100. }
  101. }
  102. #endregion Public Functions
  103. #region Protected Functions
  104. protected Block GetPushLocation(Vector3 pushDirection, LayerMask ignoreMask)
  105. {
  106. //setting up variables
  107. Vector3 newPosition = position + pushDirection; // position wanted
  108. Block hit; //output of block detection
  109. //if move is obstucted no where to move
  110. if (Block.isBlockAtPosition(newPosition + Vector3.up, 1, ignoreMask))
  111. return this;
  112. //If block at Position is walkable set it to moveTo
  113. if (Block.isBlockAtPosition(newPosition, 1, ignoreMask, out hit) && hit.isWalkable(ignoreMask))
  114. {
  115. return hit;
  116. }
  117. //else if block down one is walkable
  118. else if (Block.isBlockAtPosition(newPosition + Vector3.down, 1, ignoreMask, out hit) && hit.isWalkable(ignoreMask))
  119. {
  120. return hit;
  121. }
  122. return this;
  123. }
  124. #endregion Protected Functions
  125. #region Editor Functions
  126. private void OnDrawGizmos()
  127. {
  128. if (!isSpawnable)
  129. return;
  130. Vector3 DrawPosition = VisualPosition + Vector3.up * 0.4f;
  131. Vector3 Perp = Quaternion.Euler(0, 90, 0) * SpawnDirection.ToVector();
  132. DebugExtensions.DrawCube(DrawPosition, 0.4f, Color.magenta, 0);
  133. //Eyes
  134. Vector3 eyePosition = DrawPosition + SpawnDirection.ToVector() * 0.4f;
  135. DebugExtensions.DrawCube(eyePosition + Perp * 0.2f, 0.1f, Color.magenta, 0);
  136. DebugExtensions.DrawCube(eyePosition - Perp * 0.2f, 0.1f, Color.magenta, 0);
  137. //ears
  138. Vector3 earPosition = DrawPosition + SpawnDirection.ToVector() * 0.2f + Vector3.up * 0.4f;
  139. Vector3 earScale = Quaternion.LookRotation(SpawnDirection.ToVector()) * new Vector3(0.1f, 0.1f, 0.05f);
  140. DebugExtensions.DrawCube(earPosition + Perp * 0.3f, earScale, Color.magenta, 0);
  141. DebugExtensions.DrawCube(earPosition - Perp * 0.3f, earScale, Color.magenta, 0);
  142. }
  143. #endregion Editor Functions
  144. #region Static Functions
  145. /// <summary>
  146. /// Checks if there is a block at supplied position
  147. /// </summary>
  148. /// <param name="position">position to check at</param>
  149. /// <param name="Scale">Scale of block. (should be 1)</param>
  150. /// <param name="layerMask">Layers to check on</param>
  151. /// <param name="hit">Block hit</param>
  152. /// <returns>if a block is at position</returns>
  153. public static bool isBlockAtPosition(Vector3 position, float Scale, LayerMask layerMask, out Block hit)
  154. {
  155. //Turn scale into halfextent and shrink a bit so it doesn't hit bordering blocks
  156. Vector3 halfExtent = Vector3.one * ((Scale - 0.1f) / 2);
  157. //Get every collider which is at position
  158. Collider[] cols = Physics.OverlapBox(position, halfExtent, Quaternion.identity, layerMask);
  159. //Filter colliders for only GameObjects with an Block component
  160. Block[] blocks = cols.Where(p => p.GetComponent<Block>() != null).Select(p => p.GetComponent<Block>()).ToArray();
  161. //Draw cube, for visuals
  162. DebugExtensions.DrawCube(position, halfExtent, Color.cyan, 1, false);
  163. //if didn't hit anyblocks return false
  164. if (blocks.Length == 0)
  165. {
  166. hit = null;
  167. return false;
  168. }
  169. else
  170. {
  171. //else get the closest block to disered position, (in case we hit mulitple blocks)
  172. hit = Utility.minBy(blocks, p => Vector3.Distance(p.transform.position, position));
  173. return true;
  174. }
  175. }
  176. /// <summary>
  177. /// Checks if there is a block at supplied position
  178. /// </summary>
  179. /// <param name="position">position to check at</param>
  180. /// <param name="Scale">Scale of block. (should be 1)</param>
  181. /// <param name="layerMask">Layers to check on</param>
  182. /// <returns>if a block is at position</returns>
  183. public static bool isBlockAtPosition(Vector3 position, float scale, LayerMask layerMask)
  184. {
  185. //Return Overloaded function above
  186. Block hit;
  187. return (isBlockAtPosition(position, scale, layerMask, out hit));
  188. }
  189. #endregion
  190. }