ludumdare50/Assets/Scripts/AIEntity.cs
2022-04-02 23:57:27 -04:00

247 lines
7.9 KiB
C#

#nullable enable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class AIEntity : Entity
{
[FormerlySerializedAs("stats")] [SerializeField]
public AIStats AIStats = null!;
BaseState currentState = null!;
public string[] 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 (currentState.UpdateState() is {} newState)
SwitchState(newState);
}
override protected void FixedUpdate() {
base.FixedUpdate();
if (currentState.FixedUpdateState() is {} newState)
SwitchState(newState);
FlipAccordingToInput();
}
void OnDrawGizmos() => currentState?.OnDrawGizmos();
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){
foreach (string name in enemies){
if(other.entityName == name && other.IsAlive()){
return true;
}
}
return false;
}
override public bool TakeDamage(float amount, Entity other){
Entity currTargetEntity = GetTarget().GetComponent<Entity>();
if(!(currTargetEntity is null)){
if(currTargetEntity.entityName == "Vampire")
SetTarget(other.transform);
}
return base.TakeDamage(amount, other);
}
#region Flip
public void Flip() {
facingRight = !facingRight;
Vector3 scaler = transform.localScale;
scaler.x *= -1;
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
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.GetTarget().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(){
Transform target = entity.GetTarget();
entity.direction = Vector3.RotateTowards(entity.direction, (target.position - entity.transform.position), entity.rotSpeed*Time.fixedDeltaTime, 0.0f);
if(entity.IsTargetable(entity.GetTarget().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);
}
Entity[] entities = FindObjectsOfType<Entity>();
float lastDist = float.MaxValue;
Entity chosenEntity = null!;
foreach (Entity other in entities){// Find the closest entity
if(entity.IsTargetable(other)){
float distance = Vector2.Distance(other.transform.position, entity.transform.position);
if(distance < lastDist){
lastDist = distance;
chosenEntity = other;
if(lastDist <= entity.AIStats.closeEnough)break;
}
}
}
if(chosenEntity != null){
entity.SetTarget(chosenEntity.transform);
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.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.GetTarget().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{
public DeadState(AIEntity entity) : base(entity){
}
public override void EnterState() {
entity.animator.Play("Death");
}
public override BaseState? UpdateState(){
return null;
}
}
}