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.
 
 
 
 
 
 

777 lines
28 KiB

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using Networking.Server;
public class Character : MonoBehaviour
{
public enum Animation { Walk, Run, Jump, Sit, Attack, Hit }
public string nextScene;
Animator characterAnimator;
public bool isTuteLevel = false;
public bool inWater = false; //Am I in the water?
public bool inPit = false; //Did I fall into a pit?
public bool stuck = false; //Am I still stuck?
public bool justMoved = false; //Was the logic block I just executed a move command?
#region Inspector Fields
[SerializeField]
[Tooltip("Will move to this block at start, else will try and find a block below")]
private Block _currentBlock;
[SerializeField]
[Tooltip("Layers to ignore when checking for blocks")]
public LayerMask Ignore;
[Tooltip("Current Inventory of the player")]
public Inventory Inventory;
public bool CloneInventoryOnStart = false;
[Tooltip("How many lives to start out with")]
public int lives = 3;
[SerializeField]
[Tooltip("Character to display")]
private string CharacterModel = "Bear";
[SerializeField]
public ClientData ClientLink;
[SerializeField]
private TMPro.TextMeshPro BlockTitlePrefab;
#endregion Inspector Fields
#region Read Only
public Block CurrentBlock { get { return _currentBlock; } }
#endregion Read Only
#region Unity Functions
private void Start()
{
if (Inventory != null && CloneInventoryOnStart)
Inventory = Inventory.Clone(Inventory);
//If no starting block find one below it
if (_currentBlock == null)
Block.isBlockAtPosition(transform.position + Vector3.down / 2, 1, ~Ignore, out _currentBlock);
//move to starting block
transform.position = _currentBlock.VisualPosition;
//get character string from player replace from "Bear"
GameObject prefab = Resources.Load(CharacterModel) as GameObject;
GameObject animal = Instantiate(prefab, this.gameObject.transform);
characterAnimator = GetComponentInChildren<Animator>();
}
#endregion Unity Functions
#region Class Implementation
public void Initialise(Block startingBlock, Inventory inventory, string Character)
{
_currentBlock = startingBlock;
Inventory = inventory;
CharacterModel = Character;
}
public void DisplayBlock(LogicBlock block)
{
if (isTuteLevel == false)
{
if (BlockTitlePrefab == null)
return;
TMPro.TextMeshPro temp = Instantiate(BlockTitlePrefab.gameObject).GetComponent<TMPro.TextMeshPro>();
temp.text = block.DisplayName;
temp.color = ClientLink.Color;
temp.transform.position = transform.position + (Vector3.one * 0.25f);
temp.transform.rotation = Quaternion.LookRotation(temp.transform.position - Camera.main.transform.position);
}
}
public IEnumerator MoveToBlock(Block target, Animation animation, float time)
{
Block oldBlock = _currentBlock;
_currentBlock.OnLeftByPlayer(this);
System.Func<float, float> yFunction = null;
if (animation == Animation.Jump)
yFunction = (t) => Mathf.Sin((Mathf.PI * t));
StartAnimation(animation, time);
yield return StartCoroutine(LerpToBlock(target.VisualPosition, time, yFunction));
_currentBlock= target;
_currentBlock.OnWalkedOnByPlayer(this);
yield return StartCoroutine(DoPush(oldBlock,_currentBlock, time));
StopAnimation(animation);
}
public IEnumerator RotateToDirection(Direction direction, Animation animation, float time, bool isLocal = true)
{
System.Func<float, float> yFunction = null;
if (animation == Animation.Jump)
yFunction = (t) => Mathf.Sin((Mathf.PI * t));
Vector3 _endDir = isLocal ? direction.ToVector(transform) : direction.ToVector();
StartAnimation(animation, time);
yield return StartCoroutine(LerpToRotation(_endDir, time, yFunction));
StopAnimation(animation);
}
private IEnumerator LerpToBlock(Vector3 target, float time, System.Func<float, float> heightOffset = null)
{
Vector3 _startPos = transform.position;
Vector3 _endPos = target;
Vector3 _newPos;
float elapsedTime = 0;
while (elapsedTime / time < 1)
{
_newPos = Vector3.Lerp(_startPos, _endPos, (elapsedTime / time));
if (heightOffset != null)
_newPos.y += heightOffset(elapsedTime / time);
transform.position = _newPos;
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
_newPos = _endPos;
if (heightOffset != null)
_newPos.y += heightOffset(1);
transform.position = _newPos;
}
private IEnumerator LerpToRotation(Vector3 target, float time, System.Func<float, float> heightOffset = null)
{
Vector3 _startDir = transform.forward;
Vector3 _endDir = target;
Vector3 _startPos = transform.position;
float elapsedTime = 0;
while (elapsedTime / time < 1)
{
transform.forward = Vector3.Slerp(_startDir, _endDir, (elapsedTime / time));
if (heightOffset != null)
transform.position = _startPos + Vector3.up * heightOffset(elapsedTime / time);
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
transform.forward = target;
if (heightOffset != null)
transform.position = _startPos + Vector3.up * heightOffset(1);
}
public IEnumerator DoPush(Block lastBlock, Block currentBlock, float animationTime)
{
//Direction player moved in ignoring Y value
Vector3 pushDirection = Vector3.ProjectOnPlane(currentBlock.position - lastBlock.position, Vector3.up).normalized;
foreach(Character character in _currentBlock.GetPushablePlayers())
{
if (character == this)
continue;
yield return StartCoroutine(character.GetPushed(pushDirection));
}
}
public IEnumerator GetPushed(Vector3 direction)
{
yield return null;
}
private Block getPushLocation(Vector3 pushDirection)
{
//setting up variables
Vector3 position = _currentBlock.position + pushDirection; // position wanted
Block hit; //output of block detection
//if move is obstucted no where to move
if (Block.isBlockAtPosition(position + Vector3.up, 1, ~Ignore))
return _currentBlock;
//If block at Position is walkable set it to moveTo
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
return hit;
}
//else if block down one is walkable
else if (Block.isBlockAtPosition(position + Vector3.down, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
return hit;
}
return _currentBlock;
}
public void StartAnimation(Animation animation,float speed = 1)
{
characterAnimator.SetFloat("AnimationSpeed", (1 / speed));
switch (animation)
{
case Animation.Walk:
characterAnimator.SetBool("isWalking", true);
break;
case Animation.Run:
characterAnimator.SetBool("isRunning", true);
break;
case Animation.Sit:
characterAnimator.SetBool("isSitting", true);
break;
case Animation.Jump:
characterAnimator.SetTrigger("Jump");
break;
case Animation.Attack:
characterAnimator.SetTrigger("Attack");
break;
case Animation.Hit:
characterAnimator.SetTrigger("Hit");
break;
default:
break;
}
}
public void StopAnimation(Animation animation)
{
characterAnimator.SetFloat("AnimationSpeed", 1f);
switch (animation)
{
case Animation.Walk:
characterAnimator.SetBool("isWalking", false);
break;
case Animation.Run:
characterAnimator.SetBool("isRunning", false);
break;
case Animation.Sit:
characterAnimator.SetBool("isSitting", false);
break;
}
}
public void respawnCharacter()
{
/* Will introduce more complex criteria for choosing where to respawn the player in future
* For now: if the square one back (x =- 1) from the pit is walkable and unoccupied, then put them there
* Otherwise, try the next square back, etc, until we find an available one
*/
Block currentBlock = null;
//We start from the position of our pit, at ground level
Vector3 currentPos = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z);
Debug.Log("Commencing respawn");
//Hardcoding the number of iterations for now for simplicity, should change this later
for (int i = 0; i < 100; i++)
{
//First we check one back
currentPos.x -= 1;
Debug.Log("currentPos = " + currentPos.x + ", " + currentPos.y + ", " + currentPos.z);
if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock)) //Does a block exist here?
{
Debug.Log("Block exists");
if (currentBlock.isWalkable(~Ignore)) //If so, can we walk on it?
{
Debug.Log("Block is walkable");
//Don't yet have a check for whether it's occupied
break; //If it is, we stop here
}
}
//If the block one back isn't an option, we check to the left and right
currentPos.z += 1;
Debug.Log("currentPos = " + currentPos.x + ", " + currentPos.y + ", " + currentPos.z);
if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock)) //Does a block exist here?
{
Debug.Log("Block exists");
if (currentBlock.isWalkable(~Ignore)) //If so, can we walk on it?
{
Debug.Log("Block is walkable");
//Don't yet have a check for whether it's occupied
break; //If it is, we stop here
}
}
currentPos.z -= 2;
Debug.Log("currentPos = " + currentPos.x + ", " + currentPos.y + ", " + currentPos.z);
if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock)) //Does a block exist here?
{
Debug.Log("Block exists");
if (currentBlock.isWalkable(~Ignore)) //If so, can we walk on it?
{
Debug.Log("Block is walkable");
//Don't yet have a check for whether it's occupied
break; //If it is, we stop here
}
}
//If we've gotten this far and haven't found an available spot, we move back a row and try again
currentPos.z += 1;
}
//Having found our target block, we move the character there
if (currentBlock != null)
{
this.transform.position = currentBlock.VisualPosition;
this.inPit = false;
this._currentBlock = currentBlock;
Debug.Log("Moved " + this.name + " to "
+ this.transform.position.x + ", "
+ this.transform.position.y + ", "
+ this.transform.position.z + ", "
+ " inPit = " + inPit);
}
else
{
Debug.Log("Failed to find anywhere to put " + this.name);
}
}
/// <summary>
/// Upon collision with a floating block, collect its
/// Upon collision with the end portal, end of level
/// </summary>
/// <param name="other">name of collided object</param>
void OnTriggerEnter(Collider other)
{
Collectable collectable = other.GetComponentInChildren<Collectable>();
if (collectable != null)
{
collectable.OnCollect(this);
}
if (other.gameObject.name == "collect_sphere")
{
other.gameObject.SetActive(false);
//player.collected +=1;
}
if (other.gameObject.name == "End Portal")
{
other.GetComponent<Collider>().enabled = false;
SceneManager.LoadScene(nextScene);
}
}
/// <summary>
/// Rotates to point in specific direction based on current direction
/// </summary>
/// <param name="direction">Local direction to point</param>
public void Rotate(Direction direction, float speed)
{
StartCoroutine(rotateCoroutine(direction, transform, speed, 0.15f));
//transform.forward = direction.ToVector(transform);
}
public void RotateHalf(Direction direction, float speed)
{
StartCoroutine(rotateHalfCoroutine(direction, transform, speed, 0.15f));
//transform.forward = direction.ToVector(transform);
}
/// <summary>
/// Jumps in specefied direction, picks between Long Jump and Jumping up
/// </summary>
/// <param name="direction">Direction to Jump</param>
public void Jump(Direction direction, float speed)
{
//if there is a block infront JumpUp else LongJump
if (Block.isBlockAtPosition(_currentBlock.position + direction.ToVector(transform) + Vector3.up, 1, ~Ignore))
JumpUp(direction, speed);
else
JumpLong(direction, speed);
}
#endregion Class Implementation
#region Private Functions
/// <summary>
/// Jumps up obstacle of specific height. Jumps as high as possible
/// </summary>
/// <param name="direction">Direction of obstacle</param>
/// <param name="height">max height</param>
private void JumpUp(Direction direction, float speed, int height = 1)
{
//setting up variables
Vector3 position; // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
//Check blocks in going up then move to the heighest walkable one
for (int i = 0; i <= height; i++)
{
//next position up the tower
position = _currentBlock.position + direction.ToVector(transform) + (Vector3.up * i);
//if block is walkable set it to last known position
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
moveTo = hit;
}
//set current block && move
_currentBlock = moveTo;
StartCoroutine(JumpCoroutine(_currentBlock, transform, speed, 0.5f));
//transform.position = CurrentBlock.VisualPosition;
}
/// <summary>
/// Long jumps forward a specified distance. Can Jump gaps. Stops at obstruction
/// </summary>
/// <param name="direction">Direction to Jump</param>
/// <param name="Distance">Max distance</param>
private void JumpLong(Direction direction, float speed, int Distance = 2)
{
//setting up variables
Vector3 position; // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
//Check blocks in front until we hit an obstruction or went the distance
for (int i = 1; i <= Distance; i++)
{
//Next position to MoveTo
position = _currentBlock.position + (direction.ToVector(transform) * i);
//if jump is obstructed, stop and go to last known block
if (Block.isBlockAtPosition(position + Vector3.up, 1, ~Ignore))
break;
//If block at Position is walkable set it to last known position
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
moveTo = hit;
//else if block down one is walkable
else if (Block.isBlockAtPosition(position + Vector3.down, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
moveTo = hit;
}//end for
//Set Current Block and move
_currentBlock = moveTo;
StartCoroutine(JumpCoroutine(_currentBlock, transform, speed, 0.5f));
//transform.position = CurrentBlock.VisualPosition;
}
#endregion Private Functions
#region Depricated
IEnumerator JumpCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
time *= 0.8f;
characterAnimator.Play("Jump Inplace");
yield return new WaitForSeconds(0.15f);
while (elapsedTime < time)
{
transform.position = Vector3.Lerp(startPosition, Target.VisualPosition, (elapsedTime / time));
//transform.position += yValue((elapsedTime / time), heightMax);
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
transform.position = Target.VisualPosition;
}
IEnumerator MoveCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
time *= 0.8f;
characterAnimator.Play("Walk");
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
transform.position = Vector3.Lerp(startPosition, Target.VisualPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
transform.position = Target.VisualPosition;
}
IEnumerator MoveConveyorForwardCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
Vector3 charEndPosition = new Vector3(Current.position.x + 1.0f, Current.position.y, Current.position.z);
time *= 0.8f;
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
Current.position = charEndPosition;
}
IEnumerator MoveConveyorBackwardCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
Vector3 charEndPosition = new Vector3(Current.position.x - 1.0f, Current.position.y, Current.position.z);
time *= 0.8f;
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
Current.position = charEndPosition;
}
IEnumerator MoveConveyorLeftCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
Vector3 charEndPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z + 1.0f);
time *= 0.8f;
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
Current.position = charEndPosition;
}
IEnumerator MoveConveyorRightCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
Vector3 charEndPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z - 1.0f);
time *= 0.8f;
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
Current.position = charEndPosition;
}
IEnumerator PushLeftCoroutine(Transform Current, float time)
{
float elapsedTime = 0;
Vector3 startPosition = Current.transform.position;
Vector3 endPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z + 1.0f);
time *= 0.8f;
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
Current.position = Vector3.Lerp(startPosition, endPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
Current.position = endPosition;
}
IEnumerator PushRightCoroutine(Transform Current, float time)
{
float elapsedTime = 0;
Vector3 startPosition = Current.transform.position;
Vector3 endPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z - 1.0f);
time *= 0.8f;
yield return new WaitForSeconds(0.05f);
while (elapsedTime < time)
{
Current.position = Vector3.Lerp(startPosition, endPosition, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
Current.position = endPosition;
}
IEnumerator MoveDownCoroutine(Block Target, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
Vector3 startPosition = Current.position;
time *= 0.8f;
characterAnimator.Play("Walk");
while (elapsedTime < time)
{
transform.position = Vector3.Lerp(startPosition, Target.VisualPosition, (elapsedTime / time));
//transform.position += yValue((elapsedTime / time), heightMax);
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
transform.position = Target.VisualPosition;
}
IEnumerator rotateCoroutine(Direction direction, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
time *= 0.8f;
Vector3 endDirection = direction.ToVector(Current);
Vector3 startDirection = Current.forward;
while (elapsedTime < time)
{
characterAnimator.Play("Jump");
transform.forward = Vector3.Slerp(startDirection, endDirection, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
transform.forward = endDirection;
}
IEnumerator rotateHalfCoroutine(Direction direction, Transform Current, float time, float heightMax)
{
float elapsedTime = 0;
time *= 0.8f;
Vector3 endDirection = (direction.ToVector(Current) + Current.forward).normalized;
Vector3 startDirection = Current.forward;
while (elapsedTime < time)
{
characterAnimator.Play("Jump");
transform.forward = Vector3.Slerp(startDirection, endDirection, (elapsedTime / time));
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
}
transform.forward = endDirection;
}
/// <summary>
/// Moves one block in specefied direction, Can walk off obstacles
/// </summary>
/// <param name="direction">direction to walk</param>
/// <remarks>Technically is same as JumpLong(1) but kept seperate to avoid confusion</remarks>
public void Move(Direction direction, float speed)
{
//setting up variables
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
//if move is obstucted no where to move
if (Block.isBlockAtPosition(position + Vector3.up, 1, ~Ignore))
return;
//If block at Position is walkable set it to moveTo
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(MoveCoroutine(_currentBlock, transform, speed, 0.3f));
}
//else if block down one is walkable
else if (Block.isBlockAtPosition(position + Vector3.down, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(MoveDownCoroutine(_currentBlock, transform, speed, 0.3f));
}
}
public void conveyorMoveForward(Direction direction, float speed)
{
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(MoveConveyorForwardCoroutine(_currentBlock, transform, speed, 0.3f));
}
}
public void conveyorMoveBackward(Direction direction, float speed)
{
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(MoveConveyorBackwardCoroutine(_currentBlock, transform, speed, 0.3f));
}
}
public void conveyorMoveLeft(Direction direction, float speed)
{
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(MoveConveyorLeftCoroutine(_currentBlock, transform, speed, 0.3f));
}
}
public void conveyorMoveRight(Direction direction, float speed)
{
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(MoveConveyorRightCoroutine(_currentBlock, transform, speed, 0.3f));
}
}
public void CannonRMove(float speed)
{
Direction direction = Direction.Right;
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(PushRightCoroutine(transform, speed));
}
}
public void CannonLMove(float speed)
{
Direction direction = Direction.Left;
Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted
Block hit; //output of block detection
Block moveTo = _currentBlock; //block we'll actually move to
if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore))
{
moveTo = hit;
_currentBlock = moveTo;
StartCoroutine(PushLeftCoroutine(transform, speed));
}
}
#endregion Depricated
}