Skip to content

Instantly share code, notes, and snippets.

@markdekuijer
Last active November 26, 2018 22:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markdekuijer/ad0d4afe8a93e81700b7aaa8b0643ca0 to your computer and use it in GitHub Desktop.
Save markdekuijer/ad0d4afe8a93e81700b7aaa8b0643ca0 to your computer and use it in GitHub Desktop.
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
public class HexUnit : MonoBehaviour
{
const float travelSpeed = 4f;
const float rotationSpeed = 180f;
List<HexCell> pathToTravel;
public bool hasMovedThisTurn;
public bool hasAttackThisTurn;
public bool hasTurned;
public bool isEnemy;
[SerializeField] private GameObject enemyIndicator;
public HexUnitAnimator animHandler;
private List<SkinnedMeshRenderer> skinnedRenderers = new List<SkinnedMeshRenderer>();
private List<MeshRenderer> renderers = new List<MeshRenderer>();
public bool IsTraveling
{
get
{
return isTraveling;
}
}
bool isTraveling;
HexCell location, currentTravelLocation;
public HexCell Location
{
get
{
return location;
}
set
{
if (location)
{
if (!isEnemy)
{
Grid.DecreaseVisibility(location, unitType.VisionRange);
}
location.Unit = null;
}
location = value;
value.Unit = this;
if(!isEnemy)
Grid.IncreaseVisibility(value, unitType.VisionRange);
transform.localPosition = value.Position;
}
}
float orientation;
public float Orientation
{
get
{
return orientation;
}
set
{
orientation = value;
transform.localRotation = Quaternion.Euler(0, value, 0);
}
}
public HexGrid Grid { get; set; }
public UnitType unitType;
public int typeID;
int health;
public int Health
{
get
{
return health;
}
private set
{
if (health != value)
health = value;
}
}
public void ValidatePosition()
{
transform.localPosition = location.Position;
}
public bool IsValidDestination(HexCell cell)
{
return cell.IsExplored && !cell.IsUnderWater && !cell.Unit;
}
private void OnEnable()
{
if (location)
{
transform.localPosition = location.Position;
if (currentTravelLocation)
{
if (!isEnemy)
{
Grid.IncreaseVisibility(location, unitType.VisionRange);
Grid.DecreaseVisibility(currentTravelLocation, unitType.VisionRange);
}
currentTravelLocation = null;
}
}
}
//Set id and get associated unit data
//also set some base values
public void Initialize(int id, HexCell spawnCell, bool isEnemy)
{
this.isEnemy = isEnemy;
typeID = id;
health = unitType.Health;
skinnedRenderers = GetComponentsInChildren<SkinnedMeshRenderer>().ToList();
renderers = GetComponentsInChildren<MeshRenderer>().ToList();
if (isEnemy)
if(enemyIndicator != null)
enemyIndicator.SetActive(true);
if (spawnCell.IsExplored || spawnCell.IsVisible)
{
if (isEnemy)
DisplayRenderers(true);
}
else
{
DisplayRenderers(false);
}
}
//This function will be called in combination with the fog of war
//Definitly write a shader for this later on instead of changing the color
public void DisplayRenderers(bool show)
{
for (int i = 0; i < skinnedRenderers.Count; i++)
{
skinnedRenderers[i].material.color = show ? new Color(1, 1, 1, 1) : new Color(0, 0, 0, 0);
}
for (int i = 0; i < renderers.Count; i++)
{
renderers[i].material.color = show ? new Color(1, 1, 1, 1) : new Color(0, 0, 0, 0);
}
if(enemyIndicator != null)
enemyIndicator.SetActive(show);
}
public int GetMoveCost(HexCell fromCell, HexCell toCell, HexDirection direciton)
{
HexEdgeType edgeType = fromCell.GetEdgeType(direciton);
if(edgeType == HexEdgeType.Cliff)
{
return -1;
}
int moveCost;
if (fromCell.HasRoadThroughEdge(direciton))
{
moveCost = 1;
}
else if (fromCell.Walled != toCell.Walled)
{
return -1;
}
else
{
moveCost = edgeType == HexEdgeType.Flat ? 5 : 10;
moveCost += toCell.UrbanLevel + toCell.PlantLevel + toCell.FarmLevel;
}
return moveCost;
}
//Function called by enemys to get the closest enemy and attack that
//While also avoiding getting to close if the enemy has a range greater then 1
public void CalculateNextMove(HexGrid grid, List<HexUnit> unitsToCheck)
{
List<HexUnit> allUnits = new List<HexUnit>(unitsToCheck);
if (allUnits.Count == 0)
{
print("No Units");
hasTurned = true;
return;
}
HexUnit target = TurnbasedManager.Instance.GetClosestAlly(location.coordinates, allUnits);
bool canWalkThisPath = grid.Search(location, target.location, this, true);
if (canWalkThisPath)
{
List<HexCell> path = grid.GetPathWithoutExistCheck(unitType.speed, target.location, location);
int reduceSteps = unitType.attackRange - 1;
if(path.Count > 1)
{
if (path[path.Count - 1].Unit)
{
path.RemoveAt(path.Count - 1);
if (reduceSteps > 0)
{
if(path[path.Count - 1].coordinates.DistanceTo(target.location.coordinates) != 1)
{
for (int i = 0; i < reduceSteps; i++)
{
path.RemoveAt(path.Count - 1);
}
}
}
}
if(path.Count > 1)
{
if(path[path.Count - 1].coordinates.DistanceTo(target.location.coordinates) == 1)
{
for (int i = 0; i < reduceSteps; i++)
{
path.RemoveAt(path.Count - 1);
}
}
}
Travel(path, null, target.location, false);
}
}
else
{
if (!target)
{
Debug.LogError("fcked up no target existing");
}
allUnits.Remove(target);
CalculateNextMove(grid, allUnits);
}
}
//Travel gets the path and makes the units walk over it.
//after this a seperate function for AI or Player units will get called
//It has small differences on how the fog of war and other things get called
public void Travel(List<HexCell> path, HexGameUI gameUI, HexCell c, bool isPlayer)
{
HexCell origin = location;
if(path.Count != 0)
{
location.Unit = null;
location = path[path.Count - 1];
}
location.Unit = this;
pathToTravel = path;
StopAllCoroutines();
if (isPlayer)
StartCoroutine(TravelPathPlayer(gameUI, c));
else
StartCoroutine(TravelPathEnemy(c, origin));
}
IEnumerator TravelPathPlayer(HexGameUI gameUI, HexCell attackCell)
{
isTraveling = true;
Vector3 a, b, c = pathToTravel[0].Position;
yield return LookAt(pathToTravel[1].Position);
animHandler.SetWalking(isTraveling);
Grid.DecreaseVisibility(currentTravelLocation ? currentTravelLocation : pathToTravel[0], unitType.VisionRange);
float t = Time.deltaTime * travelSpeed;
for (int i = 1; i < pathToTravel.Count; i++)
{
currentTravelLocation = pathToTravel[i];
a = c;
b = pathToTravel[i - 1].Position;
c = (b + currentTravelLocation.Position) * 0.5f;
Grid.IncreaseVisibility(pathToTravel[i], unitType.VisionRange);
for (; t < 1; t += Time.deltaTime * travelSpeed)
{
transform.localPosition = Bezier.GetPoint(a, b, c, t);
Vector3 d = Bezier.GetDerivative(a, b, c, t);
d.y = 0f;
transform.localRotation = Quaternion.LookRotation(d);
yield return null;
}
Grid.DecreaseVisibility(pathToTravel[i], unitType.VisionRange);
t -= 1f;
}
currentTravelLocation = null;
a = c;
b = location.Position;
c = b;
Grid.IncreaseVisibility(location, unitType.VisionRange);
for (; t < 1; t += Time.deltaTime * travelSpeed)
{
transform.localPosition = Bezier.GetPoint(a, b, c, t);
Vector3 d = Bezier.GetDerivative(a, b, c, t);
d.y = 0f;
transform.localRotation = Quaternion.LookRotation(d); yield return null;
}
transform.localPosition = location.Position;
orientation = transform.localRotation.eulerAngles.y;
ListPool<HexCell>.Add(pathToTravel);
pathToTravel = null;
isTraveling = false;
hasMovedThisTurn = true;
animHandler.SetWalking(isTraveling);
if(gameUI)
gameUI.AttackAfterCheck(attackCell);
}
IEnumerator TravelPathEnemy(HexCell attackCell, HexCell originalCell)
{
if (originalCell.coordinates.DistanceTo(attackCell.coordinates) <= unitType.attackRange)
{
InitAttack(attackCell, null);
yield break;
}
isTraveling = true;
Vector3 a, b, c = pathToTravel[0].Position;
yield return LookAt(pathToTravel[1].Position);
animHandler.SetWalking(isTraveling);
float t = Time.deltaTime * travelSpeed;
for (int i = 1; i < pathToTravel.Count; i++)
{
currentTravelLocation = pathToTravel[i];
a = c;
b = pathToTravel[i - 1].Position;
c = (b + currentTravelLocation.Position) * 0.5f;
for (; t < 1; t += Time.deltaTime * travelSpeed)
{
transform.localPosition = Bezier.GetPoint(a, b, c, t);
Vector3 d = Bezier.GetDerivative(a, b, c, t);
d.y = 0f;
transform.localRotation = Quaternion.LookRotation(d);
yield return null;
}
t -= 1f;
if (currentTravelLocation.IsExplored && currentTravelLocation.IsVisible)
DisplayRenderers(true);
}
currentTravelLocation = null;
a = c;
b = location.Position;
c = b;
for (; t < 1; t += Time.deltaTime * travelSpeed)
{
transform.localPosition = Bezier.GetPoint(a, b, c, t);
Vector3 d = Bezier.GetDerivative(a, b, c, t);
d.y = 0f;
transform.localRotation = Quaternion.LookRotation(d); yield return null;
}
transform.localPosition = location.Position;
orientation = transform.localRotation.eulerAngles.y;
ListPool<HexCell>.Add(pathToTravel);
pathToTravel = null;
isTraveling = false;
hasMovedThisTurn = true;
animHandler.SetWalking(isTraveling);
yield return null;
if(location.coordinates.DistanceTo(attackCell.coordinates) <= unitType.attackRange)
InitAttack(attackCell, null);
else
hasTurned = true;
}
#region attack
//start a coroutine for the units attack
//in the start of the coroutine it will rotate towards the unit that its attacking
//after that it will set certain variables required by the system and deal damage
public void InitAttack(HexCell cell, HexGameUI gameUI)
{
if(location.IsExplored)
StartCoroutine(Attack(cell, gameUI));
}
IEnumerator Attack(HexCell attackedCell, HexGameUI gameUI)
{
yield return LookAt(attackedCell.Position);
animHandler.InitAttack();
hasMovedThisTurn = true;
hasAttackThisTurn = true;
if(gameUI)
gameUI.CloseSelect();
DoDamage(attackedCell.Unit);
yield return new WaitForSeconds(2f);
hasTurned = true;
}
IEnumerator LookAt(Vector3 point)
{
point.y = transform.localPosition.y;
Quaternion fromRotation = transform.localRotation;
Quaternion toRotation = Quaternion.LookRotation(point - transform.localPosition);
float angle = Quaternion.Angle(fromRotation, toRotation);
if(angle > 0f)
{
float speed = rotationSpeed / angle;
for (float t = Time.deltaTime * speed; t < 1f; t += Time.deltaTime * speed)
{
transform.localRotation = Quaternion.Slerp(fromRotation, toRotation, t);
yield return null;
}
}
transform.LookAt(point);
orientation = transform.localRotation.eulerAngles.y;
}
#endregion
#region dmg
public void DoDamage(HexUnit otherUnit)
{
if (otherUnit == null)
return;
otherUnit.TakeDamage(unitType.damage);
}
public void TakeDamage(int damage)
{
health -= damage;
if(health <= 0)
{
Die();
}
}
public void Die()
{
if(unitType.objectName == "castle")
{
string gameoverString;
if (!isEnemy)
gameoverString = "You Lost!";
else
gameoverString = "You Won!";
HexGameUI.instance.InitGameOver(gameoverString);
return;
}
if (location)
{
if (!isEnemy)
{
Grid.DecreaseVisibility(location, unitType.VisionRange);
}
}
location.Unit = null;
animHandler.Die();
if (isEnemy)
TurnbasedManager.Instance.enemyUnits.Remove(this);
else
TurnbasedManager.Instance.allyUnits.Remove(this);
//Grid.RemoveUnit(this);
}
#endregion
#region savedata
//saving and loading data gets down in bits and bytes
//this same format gets used everywhere in the game
//The reason for doing this is because you can save mid-game and continue playing without any problem later on
public void Save(BinaryWriter writer)
{
location.coordinates.Save(writer);
writer.Write(orientation);
writer.Write(isEnemy);
writer.Write(typeID);
}
public static void Load(BinaryReader reader, HexGrid grid)
{
HexCoordinates coordinates = HexCoordinates.Load(reader);
float orientation = reader.ReadSingle();
bool isEnemy = reader.ReadBoolean();
int typeID = reader.ReadInt32();
HexUnit u = Instantiate(HexGameUI.instance.unitTypes.unitTypeIDs[typeID].GetComponent<HexUnit>());
u.Initialize(typeID, grid.GetCell(coordinates), isEnemy);
grid.AddUnit(u, grid.GetCell(coordinates), orientation);
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment