using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;
///
/// Załozenia na przyszłość
/// - player atakując powinien wysyłać informacje o akcji ataku z wartością obrażeń
/// enemy przymując ją powinien kalkulować szanse uniku / bloku (i odskakiwać w bokj lub do tyłu) albo przyjmować obrazenia
/// WSZĘDZIE ANIMACJA blokuąca inne akcje na czas odtwarzania efektu
///
public enum WizardBattleStepsEnum
{
First, // have 100% hp, lasts until the value will be reduced to 7%%
Second, // have 75%% hp, lasts until the value will be reduced to 50%
Third, // have 50%% hp, lasts until the value will be reduced to 25%
Last,
Summoning, // after reducing hp to 75, 50, 25 per cent wizard start to summon minions
Escaping // when player have 25% he starts escape - go to teleport
}
[RequireComponent(typeof(NPC))]
[RequireComponent(typeof(AStarPathfindingAgent))]
class BattleWizard : MonoBehaviour
{
public const string BATTLE_STATE = "BattleState";
public WizardBattleStepsEnum BattleState = WizardBattleStepsEnum.First;
[Header("Health")]
public float defence = 0;
public int maxHealth = 100;
public float currentHealth;
public bool canTakeDamage = true;
public float attackValue = 5;
public float attackingRadius = 6;
[Header("Summoned Minions")]
public GameObject MinionRespowner;
public int SummonedMinionsCounter = 3;
public int KilledMinionsCounter = 0;
public bool IsAfterSummoning = false;
[Header("Step points")]
public bool approaching = false; // mean - we can go closer to Player
[Space]
public int CurrentPoint = 0;
public Transform[] Points;
public Vector3 TargetPosition;
[Space]
[Header("Attacking Logic")]
public bool hit = false;
public bool firstAttack = false;
public float timerDmg = 0f;
public float timeToWaitBeforeNextAttack = 1.0f; // time which npc must wait before he can atack player again
public float timerHit = 0f;
public float timeToWaitBeforeNextHitFromPlayer = 0.55f; // should be moved to player script !!!!
public bool isPanelEnabled = true; // flag about some panel status.... (probably youDied / respown)
private void Start()
{
MinionRespowner.GetComponent().Respown = false;
MinionRespowner.GetComponent().Counter = 0;
currentHealth = maxHealth;
if (HasProggress())
{
BattleState = GetProggress();
switch(BattleState)
{
case WizardBattleStepsEnum.First:
{
// to do nothing to do here
break;
}
case WizardBattleStepsEnum.Second:
{
BattleState = WizardBattleStepsEnum.First; // go one stop earlier to propertly go throught Escaping and Summoning
gameObject.transform.position = Points[0].position;
currentHealth = maxHealth * 0.75f;
SummonedMinionsCounter = 3;
CurrentPoint = 1;
break;
}
case WizardBattleStepsEnum.Third:
{
BattleState = WizardBattleStepsEnum.Second;
gameObject.transform.position = Points[1].position;
currentHealth = maxHealth * 0.50f;
SummonedMinionsCounter = 6;
CurrentPoint = 2;
break;
}
case WizardBattleStepsEnum.Last:
{
BattleState = WizardBattleStepsEnum.Third;
gameObject.transform.position = Points[2].position;
currentHealth = maxHealth * 0.25f;
SummonedMinionsCounter = 12;
CurrentPoint = 3;
break;
}
}
}
else
{
gameObject.GetComponent().State = NPCStateEnum.None;
BattleState = WizardBattleStepsEnum.First;
CurrentPoint = 0;
SummonedMinionsCounter = 3; // deffault value at start
}
defecnceCalculate();
speedCalculate();
// control battle proggress
ManageBehaviourScenario();
}
private void Update()
{
PlayerPrefs.SetFloat("WizardMaxHealth", maxHealth);
PlayerPrefs.SetFloat("WizardCurrentHealth", currentHealth);
if (gameObject.GetComponent().State == NPCStateEnum.Walking)
{
var dir = TargetPosition - transform.position;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
dir.Normalize();
gameObject.GetComponent().SetBool("isRunning", new Vector2( dir.x, dir.y) != Vector2.zero);
}
// taking demage by time logic
TakingDamageManagment();
// take action based on npc state
HandleState();
// config battle depending on state
if (IsAfterSummoning)
{
//wait for killing minions by player
if (MinionRespowner.GetComponent().killedMinions >= SummonedMinionsCounter * 3/5f)
{
SummonedMinionsCounter *= 2;
MinionRespowner.GetComponent().killedMinions = 0;
IsAfterSummoning = false;
if ((WizardBattleStepsEnum)GetProggress() == WizardBattleStepsEnum.First)
BattleState = WizardBattleStepsEnum.Second;
else if ((WizardBattleStepsEnum)GetProggress() == WizardBattleStepsEnum.Second)
BattleState = WizardBattleStepsEnum.Third;
else if ((WizardBattleStepsEnum)GetProggress() == WizardBattleStepsEnum.Third)
BattleState = WizardBattleStepsEnum.Last;
approaching = true; // flag whivh tell to go in player direction
canTakeDamage = true;
gameObject.GetComponent().State = NPCStateEnum.Walking;
SaveProggress(BattleState);
}
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
// after reaching teleport position remove component
if (collision.gameObject.tag == "SceneTransition")
{
collision.gameObject.GetComponent().isEnabled = true;
SaveProggress(WizardBattleStepsEnum.First);
Destroy(gameObject);
}
// Hit logic
if (collision.gameObject.tag == "AttackHitbox" || collision.gameObject.tag == "PickaxeHitbox")
{
hit = true;
}
}
public void OnTriggerExit2D(Collider2D collision)
{
// Hit logic
if (collision.gameObject.tag == "AttackHitbox" || collision.gameObject.tag == "PickaxeHitbox")
{
timerDmg = 0f;
hit = false;
}
}
// script controlling battle scenario by listening on change, depending on:
// - Wizard life
// - Wizard state
// - battle state
public void ManageBehaviourScenario()
{
// at first
if (BattleState == WizardBattleStepsEnum.First &&
gameObject.GetComponent().State == NPCStateEnum.None)
{
Debug.Log("First");
approaching = true;
gameObject.GetComponent().State = NPCStateEnum.Walking; // HandleState make rest - makes sure the wizard walks up to the player and cxhange state to attacking
} else
// detect health status after each taked damage from player (invoked in TakeDamage)
if ((BattleState == WizardBattleStepsEnum.First || BattleState == WizardBattleStepsEnum.Second || BattleState == WizardBattleStepsEnum.Third) &&
(gameObject.GetComponent().State & NPCStateEnum.Attacking) > 0)
{
if (IsAfterSummoning == true)
return;
} else
// 1 Change state to pending & block damage taking & go to safe position
if (BattleState == WizardBattleStepsEnum.Escaping)
{
gameObject.GetComponent().speed = 5f;
canTakeDamage = false;
approaching = false;
if (currentHealth <= maxHealth * 0.10f) // summon before
{
Debug.Log("Wizard HP critical");
TargetPosition = Points[Points.Count() - 1].position;
SummonManagment();
}
else
{
TargetPosition = Points[CurrentPoint].position;
CurrentPoint++;
}
gameObject.GetComponent().State = NPCStateEnum.Walking; // go to newxt base point
} else
if (BattleState == WizardBattleStepsEnum.Summoning)
{
if(!IsAfterSummoning)
{
speedCalculate();
SummonManagment();
}
}
}
public void HandleState()
{
switch (gameObject.GetComponent().State)
{
case NPCStateEnum.Walking: // to player, to next point, to exit xd
{
WalkingManagment();
break;
}
case NPCStateEnum.Attacking:
{
gameObject.GetComponent().SetBool("isRunning", false);
AttackManagment();
break;
}
case NPCStateEnum.Pending:
{
gameObject.GetComponent().SetBool("isRunning", false);
BattleState = WizardBattleStepsEnum.Summoning;
ManageBehaviourScenario();
break;
}
default:
{
break;
}
}
}
public void WalkingManagment()
{
StopAllCoroutines();
if(approaching)
{
// when we can go closer to player - we go closer and when we are close enought we start attacking
if (!IsInAttackRadious())
{
gameObject.GetComponent().FindPath();
StartCoroutine(gameObject.GetComponent().FollowPath());
}
else
{
timeToWaitBeforeNextAttack = 0; // to allowa to first hit withot waiting
// in this script we set attacking mode
gameObject.GetComponent().State = NPCStateEnum.Attacking;
gameObject.GetComponent().path.Clear(); // if we are able to talgk we dont want go go further player
}
}
else
{
// we summon minions and go to next save point, else we escape
// IMPOSRANT make summoning and walking independent and unbinded in code
// first focus on targetting next step from list
//Debug.Log(Vector2.Distance(transform.position, TargetPosition));
if (Vector2.Distance(transform.position, TargetPosition) > 0.95f) // count value - path finding stop moving them before reachin position well..
{
gameObject.GetComponent().point = TargetPosition;
gameObject.GetComponent().FindPoint();
StartCoroutine(gameObject.GetComponent().FollowPath());
}
else
{
// ------ anim.SetBool("isRunning", false);
gameObject.GetComponent().SetBool("isRunning", false);
// set next point for future
gameObject.GetComponent().State = NPCStateEnum.Pending; // decide what next
}
}
}
public void SummonManagment()
{
// 2. Spown minions (depending on iteration increase spowned amount
BlockRespowners();
MinionRespowner.GetComponent().Counter = SummonedMinionsCounter;
MinionRespowner.GetComponent().Respown = true;
IsAfterSummoning = true;
Debug.Log("After summoning");
}
#region damage managment
///
/// Take damage only once in a time and only when IT IS ALLOWED
///
public void TakingDamageManagment()
{
// COPIED FROM Following Enemy scripts
// Taking hit logic
timerHit += Time.deltaTime;
if (hit == true && canTakeDamage)
{
if (timerHit >= timeToWaitBeforeNextHitFromPlayer)
{
TakeDamage(PlayerPrefs.GetFloat("attackValue"));
hit = false;
timerHit = 0f;
TakeKnockback();
}
}
}
private void TakeDamage(float damage)
{
var healthBeforeDamage = currentHealth;
damage = damage - defence;
damage = damage < 0 ? 0 : damage;
Debug.Log("Gamage from player: " + damage + "(defence: " + defence + ")");
currentHealth -= damage;
/* if (gameObject.GetComponent().health <= 0)
{
gameObject.SetActive(false);
gameObject.GetComponent().isKilled = 1;
GameObject.FindGameObjectWithTag("Player").GetComponent().GetExp(30);
// pass info about killing assigned enemy to mission manager listener
// pass enemy name from script NOT object name (thats allow to have many different objects variantsa with this same aggregate key (private name - not preffab name) )
ConditionManager.Instance.UpdateKillCondition(gameObject.GetComponent().MinionName);
}*/
if (
(healthBeforeDamage > maxHealth * 0.75f && currentHealth <= maxHealth * 0.75f) ||
(healthBeforeDamage > maxHealth * 0.50f && currentHealth <= maxHealth * 0.50f) ||
(healthBeforeDamage > maxHealth * 0.25f && currentHealth <= maxHealth * 0.25f) ||
(healthBeforeDamage > maxHealth * 0.10f && currentHealth <= maxHealth * 0.10f)
){
Debug.Log("escaping");
BattleState = WizardBattleStepsEnum.Escaping;
Debug.Log(BattleState);
// re-calculate after each damage
defecnceCalculate();
speedCalculate();
ManageBehaviourScenario();
}
}
private void TakeKnockback()
{
Rigidbody2D enemy = gameObject.GetComponent();
Rigidbody2D player = GameObject.FindGameObjectWithTag("Player").GetComponent();
if (enemy != null)
{
enemy.isKinematic = false;
Vector2 difference = enemy.transform.position - player.transform.position;
difference = difference.normalized * 10; // thrust
enemy.AddForce(difference, ForceMode2D.Impulse);
//StartCoroutine(KnockCo(enemy));
}
}
#endregion
#region attack managment
public void AttackManagment()
{
// if during Attacking mode player GO OUT of the attacking radious
if (!IsInAttackRadious())
{
gameObject.GetComponent().State = NPCStateEnum.Walking;
return;
}
// Attack logic
if (timerDmg >= timeToWaitBeforeNextAttack)
{
timerDmg = 0f;
GameObject.FindGameObjectWithTag("Player").GetComponent().TakeDamage(
attackValue,
isPanelEnabled
);
}
speedCalculate(); // to restore property timeToWaitBeforeNextAttack walue
timerDmg += Time.deltaTime;
}
public bool IsInAttackRadious()
{
if (Vector2.Distance(GameObject.FindGameObjectWithTag("Player").transform.position, transform.position) >= attackingRadius)
return false;
return true;
}
#endregion
// jesli gracz ma "x" pkt siły
// Ze wzgledu na wyniki EXCEL'a zakładajac że
// - gracz ma 4/5 pkt sily (skile + bonus naszyjnika)
// obrona nie moze przekroczyc 3 - 3.2f
public void defecnceCalculate()
{
if (currentHealth > maxHealth * 0.75f)
{
defence = 1.5f;
}else if(currentHealth <= maxHealth * 0.75f && currentHealth > maxHealth * 0.50f)
{
defence = 2.5f;
}
else if (currentHealth <= maxHealth * 0.50f && currentHealth > maxHealth * 0.25f)
{
defence = 3.15f;
} else if (currentHealth <= maxHealth * 0.25f && currentHealth > maxHealth * 0.10f)
{
defence = 4f; // or higher ???
}
}
public void speedCalculate()
{
if (currentHealth > maxHealth * 0.75f)
{
gameObject.GetComponent().speed = 2f;
timeToWaitBeforeNextAttack = 1f;
}
else if (currentHealth <= maxHealth * 0.75f && currentHealth > maxHealth * 0.50f)
{
gameObject.GetComponent().speed = 2.55f;
timeToWaitBeforeNextAttack = 0.8f;
}
else if (currentHealth <= maxHealth * 0.50f && currentHealth > maxHealth * 0.25f)
{
gameObject.GetComponent().speed = 3;
timeToWaitBeforeNextAttack = 0.7f;
}
else if (currentHealth <= maxHealth * 0.25f && currentHealth > maxHealth * 0.10f)
{
gameObject.GetComponent().speed = 3.25f;
timeToWaitBeforeNextAttack = 0.6f;
}
}
#region Respown minions managments
private void BlockRespowners()
{
ResetRespowners();
if (SummonedMinionsCounter == 3) // use only 1
{
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(1).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(2).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(3).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(4).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(5).GetComponent().Blocked = true;
}
if (SummonedMinionsCounter == 6) // use 2 & 3
{
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(0).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(3).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(4).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(5).GetComponent().Blocked = true;
}
if (SummonedMinionsCounter == 12) // use 2 & 4 & 5
{
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(0).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(1).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(5).GetComponent().Blocked = true;
}
if (SummonedMinionsCounter > 12) // use 4 & 5 & 6
{
SummonedMinionsCounter = 15;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(0).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(1).GetComponent().Blocked = true;
MinionRespowner.GetComponent().respownPoints.ElementAtOrDefault(2).GetComponent().Blocked = true;
}
}
private void ResetRespowners()
{
foreach(var respownPointObject in MinionRespowner.GetComponent().respownPoints)
{
respownPointObject.GetComponent().Blocked = false;
}
}
#endregion
#region proggress API
public void SaveProggress(WizardBattleStepsEnum status)
{
Debug.Log("Save: " + status);
PlayerPrefs.SetInt(SceneManager.GetActiveScene().name + "." + BATTLE_STATE, (int)status);
}
public WizardBattleStepsEnum GetProggress()
{
return (WizardBattleStepsEnum)PlayerPrefs.GetInt(SceneManager.GetActiveScene().name + "." + BATTLE_STATE);
}
public bool HasProggress()
{
return PlayerPrefs.HasKey(SceneManager.GetActiveScene().name + "." + BATTLE_STATE);
}
#endregion
}