ludumdare50/Assets/Scripts/AIEntity.cs
Jason Durand 01 4d59cedef3 Merge remote-tracking branch 'origin/dev' into jason
# Conflicts:
#	Assets/Scripts/AIEntity.cs
#	Assets/Scripts/GameFlowManager.cs
2022-04-03 13:25:16 -04:00

271 lines
8.7 KiB
C#

#nullable enable
using UnityEngine;
using UnityEngine.Serialization;
public class AIEntity : Entity {
[FormerlySerializedAs("stats")]
[SerializeField]
public AIStats AIStats = null!;
BaseState currentState = null!;
public EntityFlag enemies { get; protected set; }
public bool facingRight { get; private set; } = true;
override protected void Start() {
base.Start();
currentState = CreateInitialState();
currentState.EnterState();
}
override protected void Update() {
base.Update();
if (gameFlowManager.pauseLevel >= GameFlowManager.PauseLevel.TimeStop)
return;
if (currentState.UpdateState() is { } newState)
SwitchState(newState);
}
override protected void FixedUpdate() {
base.FixedUpdate();
if (gameFlowManager.pauseLevel >= GameFlowManager.PauseLevel.TimeStop)
return;
if (currentState.FixedUpdateState() is { } newState)
SwitchState(newState);
FlipAccordingToInput();
}
void OnDrawGizmos() => currentState?.OnDrawGizmos();
protected override void OnDied() {
base.OnDied();
transform.SetParent(arena.graveyard);
}
void SwitchState(BaseState newState) {
currentState.LeaveState();
currentState = newState;
newState.EnterState();
}
protected virtual BaseState CreateInitialState() => new FindTargetState(this);
//Looks into enemy name list to see if the other is targetable
virtual protected bool IsTargetable(Entity other) {
return enemies.HasFlag(other.entityType) && other.IsAlive();
}
override public bool TakeDamage(float amount, Entity other) {
//TODO Should we warn if target is null here?
if (target != null && target.GetComponent<VampireEntity>() is { })
target = other.transform;
return base.TakeDamage(amount, other);
}
#region Flip
public void Flip() {
facingRight = !facingRight;
Vector3 scaler = transform.localScale;
scaler.x *= -1;
transform.localScale = scaler;
healthBar.gameObject.transform.localScale = scaler;
}
public void FlipAccordingToInput() {
Vector3 direction = rb.velocity;
if (target != null) {
direction = target.position - transform.position;
}
if ((!facingRight && direction.x > 0) || (facingRight && direction.x < 0)) {
Flip();
}
}
#endregion
//When ratio is zero, decay is complete. Blood token set to zero
public void Decay(float ratio){
if(ratio <= 0){
bloodTokens = 0;
OnEmpty();
}else{
Color diffColor = deadColor * ratio;
renderer.color = new Color(diffColor.r, diffColor.g, diffColor.b, renderer.color.a);
}
}
protected abstract class BaseStateAI : BaseState{
protected AIEntity entity;
public BaseStateAI(AIEntity entity){
this.entity = entity;
}
}
protected class SeekState : BaseStateAI {
public SeekState(AIEntity entity) : base(entity) {
}
public override void EnterState() {
if (!entity.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack")) {
entity.animator.Play("Running");
}
}
public override BaseState? UpdateState() {
if (!entity.IsAlive()) {
return new DeadState(entity);
}
Entity targetEntity = entity.target.GetComponent<Entity>();
if (targetEntity != null) {
if (targetEntity.IsAlive()) {//target is alive, keep chasing it
return null;
} else {//target is dead, go to findTargetState
return new FindTargetState(entity); ;
}
}
return null;
}
public override BaseState? FixedUpdateState() {
entity.direction = Vector3.RotateTowards(entity.direction, (entity.target.position - entity.transform.position), entity.rotSpeed * Time.fixedDeltaTime, 0.0f);
if (entity.IsTargetable(entity.target.GetComponent<Entity>())) {
if (!entity.IsInAttackRange()) {
entity.rb.MovePosition(entity.transform.position + entity.direction * entity.movementSpeed * Time.fixedDeltaTime);
// entity.animator.Play("Running");
} else {
return new AttackState(entity);
}
} else {
return new FindTargetState(entity);
}
// entity.animator.Play("Idle");
return null;
}
}
protected class FindTargetState : BaseStateAI {
float closeEnough;
Vector3 roamPosition;
public FindTargetState(AIEntity entity) : base(entity) {
}
public override void EnterState() {
if (!entity.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack")) {
entity.animator.Play("Running");
}
}
public override BaseState? UpdateState() {
if (!entity.IsAlive()) {
return new DeadState(entity);
}
Transform entityParent = entity.entityType == EntityFlag.Gladiator ? entity.arena.minionParent : entity.arena.gladiatorParent;
float lastDist = float.MaxValue;
Transform chosenEntity = null!;
foreach (Transform other in entityParent) {// Find the closest entity
float distance = Vector2.Distance(other.position, entity.transform.position);
if (distance < lastDist) {
lastDist = distance;
chosenEntity = other;
if (lastDist <= entity.AIStats.closeEnough) break;
}
}
if (chosenEntity != null) {
entity.target = chosenEntity;
return new SeekState(entity);
} else {
if (roamPosition == new Vector3()) roamPosition = entity.AIStats.getRandomRoamPositon();
return null;
}
}
public override BaseState? FixedUpdateState() {
if (roamPosition == new Vector3()) {
// entity.animator.Play("Idle");
return null;
}
entity.direction = Vector3.RotateTowards(entity.direction, (roamPosition - entity.transform.position), entity.rotSpeed * Time.fixedDeltaTime, 0.0f);
if (Vector2.Distance(entity.transform.position, roamPosition) >= entity.attackRange) {
entity.rb.MovePosition(entity.transform.position + entity.direction * entity.movementSpeed * Time.fixedDeltaTime);
} else {
roamPosition = entity.AIStats.getRandomRoamPositon();
}
// entity.animator.Play("Running");
return null;
}
}
protected class AttackState : BaseStateAI {
public AttackState(AIEntity entity) : base(entity) {
}
public override void EnterState() {
entity.animator.Play("Attack");
}
public override BaseState? UpdateState() {
if (!entity.IsAlive()) {
return new DeadState(entity);
}
if (entity.gameFlowManager.CanDoAction) {
if (entity.IsInAttackRange()) {
if (entity.attackTimer >= entity.attackCooldown) {
entity.attackTimer = 0;
return Attack();
} else {
entity.attackTimer += Time.deltaTime;
}
} else
return new SeekState(entity);
}
return null;
}
private BaseState? Attack() {
Entity targetEntity = entity.target.GetComponent<Entity>();
if (targetEntity != null) {
targetEntity.TakeDamage(entity.attackDmg, entity);
bool isTargetAlive = targetEntity.IsAlive();
if (!isTargetAlive) {
return new FindTargetState(entity);
}
}
return null;
}
}
protected class DeadState : BaseStateAI{
private float decaytimer;
public DeadState(AIEntity entity) : base(entity){
}
public override void EnterState() {
entity.animator.Play("Death");
decaytimer = 0f;
}
public override BaseState? UpdateState(){
if(entity.bloodTokens > 0 && !entity.IsBeingSucked()){
decaytimer += Time.deltaTime;
entity.Decay(1 - (decaytimer/entity.AIStats.decayTime));
}
return null;
}
}
}