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 onCrystal = false;
|
|
public bool underRock = false;
|
|
public bool stuck = false; //Am I still stuck?
|
|
public bool justMoved = false; //Was the logic block I just executed a move command?
|
|
Vector3 death = new Vector3(-50, 0, 0);
|
|
#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; } }
|
|
public float lastRotation { get; private set;}
|
|
#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>();
|
|
}
|
|
private void Update()
|
|
{
|
|
if(lives < 1)
|
|
{
|
|
this.transform.position = death;
|
|
//this.enabled = false;
|
|
//gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
|
|
#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)
|
|
{
|
|
float startTime = Time.time;
|
|
Vector3 moveDirection = Vector3.ProjectOnPlane(target.position - _currentBlock.position, Vector3.up).normalized;
|
|
|
|
_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 * 0.8f, yFunction));
|
|
|
|
_currentBlock= target;
|
|
yield return StartCoroutine (_currentBlock.OnWalkedOnByPlayer(this,moveDirection));
|
|
|
|
yield return new WaitForSeconds(time - (Time.time - startTime));
|
|
|
|
StopAnimation(animation);
|
|
}
|
|
|
|
|
|
|
|
public IEnumerator RotateInDirection(Direction direction,float angles, Animation animation, float time)
|
|
{
|
|
System.Func<float, float> yFunction = null;
|
|
if (animation == Animation.Jump)
|
|
yFunction = (t) => Mathf.Sin((Mathf.PI * t));
|
|
|
|
StartAnimation(animation, time);
|
|
|
|
//Debug.Log("Rotating by: " + angles);
|
|
yield return StartCoroutine(Rotate(direction,angles, time * 0.8f, yFunction));
|
|
|
|
StopAnimation(animation);
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerator AnimateToPosition(Vector3 position, Animation animation, float time)
|
|
{
|
|
System.Func<float, float> yFunction = null;
|
|
if (animation == Animation.Jump)
|
|
yFunction = (t) => Mathf.Sin((Mathf.PI * t));
|
|
|
|
StartAnimation(animation, time);
|
|
|
|
yield return StartCoroutine(LerpToBlock(position, time * 0.8f, yFunction));
|
|
|
|
StopAnimation(animation);
|
|
}
|
|
|
|
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()
|
|
{
|
|
respawnCharacter(CurrentBlock.position);
|
|
}
|
|
|
|
public void respawnCharacter(Vector3 respawnPosition)
|
|
{
|
|
/* 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 = respawnPosition;
|
|
|
|
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>();
|
|
blockSpawn spawn = GetComponent<blockSpawn>();
|
|
|
|
if (collectable != null)
|
|
{
|
|
//get position from average;
|
|
ClientList list = spawn.clientDataList;
|
|
float average = 0;
|
|
int livePlayerCount = 0;
|
|
foreach (ClientData data in list.ConnectedClients)
|
|
{
|
|
if (data.Lives > 0)
|
|
{
|
|
average += data.playerCharacter.transform.position.x;
|
|
livePlayerCount++;
|
|
}
|
|
}
|
|
average /= livePlayerCount;
|
|
float tosend = lives + (transform.position.x - average);
|
|
|
|
spawn.assignLogicBlock(collectable.gameObject, tosend);
|
|
|
|
collectable.OnCollect(this);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endregion Class Implementation
|
|
|
|
#region Private Functions
|
|
|
|
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 Rotate(Direction direction, float angles, float time, System.Func<float, float> heightOffset = null)
|
|
{
|
|
|
|
int RotationDir = 0;
|
|
switch (direction)
|
|
{
|
|
case Direction.Forward:
|
|
RotationDir = 0;
|
|
break;
|
|
case Direction.Left:
|
|
RotationDir = -1;
|
|
break;
|
|
case Direction.Right:
|
|
RotationDir = 1;
|
|
break;
|
|
case Direction.Back:
|
|
RotationDir = 2;
|
|
break;
|
|
}
|
|
|
|
lastRotation = angles * RotationDir;
|
|
|
|
|
|
float elapsedTime = 0;
|
|
float anglePerSecond = (angles * RotationDir) / time;
|
|
Vector3 _startPos = transform.position;
|
|
Vector3 startDirection = transform.forward;
|
|
|
|
while (elapsedTime < time)
|
|
{
|
|
transform.Rotate(Vector3.up, anglePerSecond * Time.deltaTime);
|
|
|
|
if (heightOffset != null)
|
|
transform.position = _startPos + Vector3.up * heightOffset(elapsedTime / time);
|
|
|
|
yield return new WaitForEndOfFrame();
|
|
elapsedTime += Time.deltaTime;
|
|
}
|
|
|
|
transform.forward = Quaternion.AngleAxis(angles * RotationDir, Vector3.up) * startDirection;
|
|
if (heightOffset != null)
|
|
transform.position = _startPos + Vector3.up * heightOffset(1);
|
|
}
|
|
|
|
|
|
|
|
#endregion Private Functions
|
|
|
|
|
|
}
|