2023-01-10 21:20:48 +01:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using UnityEngine ;
using UnityEngine.SceneManagement ;
/// <summary>
/// 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
/// </summary>
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 < CounterRespowner > ( ) . Respown = false ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . 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 < NPC > ( ) . State = NPCStateEnum . None ;
BattleState = WizardBattleStepsEnum . First ;
CurrentPoint = 0 ;
SummonedMinionsCounter = 3 ; // deffault value at start
}
defecnceCalculate ( ) ;
speedCalculate ( ) ;
// control battle proggress
ManageBehaviourScenario ( ) ;
}
private void Update ( )
{
if ( gameObject . GetComponent < NPC > ( ) . State = = NPCStateEnum . Walking )
{
var dir = TargetPosition - transform . position ;
float angle = Mathf . Atan2 ( dir . y , dir . x ) * Mathf . Rad2Deg ;
dir . Normalize ( ) ;
gameObject . GetComponent < Animator > ( ) . 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 < CounterRespowner > ( ) . killedMinions > = SummonedMinionsCounter * 3 / 5f )
{
SummonedMinionsCounter * = 2 ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . 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 < NPC > ( ) . State = NPCStateEnum . Walking ;
SaveProggress ( BattleState ) ;
}
}
}
public void OnTriggerEnter2D ( Collider2D collision )
{
// after reaching teleport position remove component
if ( collision . gameObject . tag = = "SceneTransition" )
{
collision . gameObject . GetComponent < DoorBehaviour > ( ) . 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 < NPC > ( ) . State = = NPCStateEnum . None )
{
Debug . Log ( "First" ) ;
approaching = true ;
gameObject . GetComponent < NPC > ( ) . 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 < NPC > ( ) . 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 < AStarPathfindingAgent > ( ) . 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 < NPC > ( ) . State = NPCStateEnum . Walking ; // go to newxt base point
} else
if ( BattleState = = WizardBattleStepsEnum . Summoning )
{
if ( ! IsAfterSummoning )
{
speedCalculate ( ) ;
SummonManagment ( ) ;
}
}
}
public void HandleState ( )
{
switch ( gameObject . GetComponent < NPC > ( ) . State )
{
case NPCStateEnum . Walking : // to player, to next point, to exit xd
{
WalkingManagment ( ) ;
break ;
}
case NPCStateEnum . Attacking :
{
gameObject . GetComponent < Animator > ( ) . SetBool ( "isRunning" , false ) ;
AttackManagment ( ) ;
break ;
}
case NPCStateEnum . Pending :
{
gameObject . GetComponent < Animator > ( ) . 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 < AStarPathfindingAgent > ( ) . FindPath ( ) ;
StartCoroutine ( gameObject . GetComponent < AStarPathfindingAgent > ( ) . FollowPath ( ) ) ;
}
else
{
timeToWaitBeforeNextAttack = 0 ; // to allowa to first hit withot waiting
// in this script we set attacking mode
gameObject . GetComponent < NPC > ( ) . State = NPCStateEnum . Attacking ;
gameObject . GetComponent < AStarPathfindingAgent > ( ) . 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 < AStarPathfindingAgent > ( ) . point = TargetPosition ;
gameObject . GetComponent < AStarPathfindingAgent > ( ) . FindPoint ( ) ;
StartCoroutine ( gameObject . GetComponent < AStarPathfindingAgent > ( ) . FollowPath ( ) ) ;
}
else
{
// ------ anim.SetBool("isRunning", false);
gameObject . GetComponent < Animator > ( ) . SetBool ( "isRunning" , false ) ;
// set next point for future
gameObject . GetComponent < NPC > ( ) . State = NPCStateEnum . Pending ; // decide what next
}
}
}
public void SummonManagment ( )
{
// 2. Spown minions (depending on iteration increase spowned amount
BlockRespowners ( ) ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . Counter = SummonedMinionsCounter ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . Respown = true ;
IsAfterSummoning = true ;
Debug . Log ( "After summoning" ) ;
}
#region damage managment
/// <summary>
/// Take damage only once in a time and only when IT IS ALLOWED
/// </summary>
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 < Enemy > ( ) . health < = 0 )
{
gameObject . SetActive ( false ) ;
gameObject . GetComponent < Enemy > ( ) . isKilled = 1 ;
GameObject . FindGameObjectWithTag ( "Player" ) . GetComponent < Player > ( ) . 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 < Enemy > ( ) . 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 > ( ) ;
Rigidbody2D player = GameObject . FindGameObjectWithTag ( "Player" ) . GetComponent < Rigidbody2D > ( ) ;
if ( enemy ! = null )
{
enemy . isKinematic = false ;
Vector2 difference = enemy . transform . position - player . transform . position ;
2023-01-14 14:39:29 +01:00
difference = difference . normalized * 10 ; // thrust
2023-01-10 21:20:48 +01:00
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 < NPC > ( ) . State = NPCStateEnum . Walking ;
return ;
}
// Attack logic
if ( timerDmg > = timeToWaitBeforeNextAttack )
{
timerDmg = 0f ;
GameObject . FindGameObjectWithTag ( "Player" ) . GetComponent < Player > ( ) . 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 )
{
2023-01-14 14:39:29 +01:00
gameObject . GetComponent < AStarPathfindingAgent > ( ) . speed = 2f ;
2023-01-10 21:20:48 +01:00
timeToWaitBeforeNextAttack = 1f ;
}
else if ( currentHealth < = maxHealth * 0.75f & & currentHealth > maxHealth * 0.50f )
{
2023-01-14 14:39:29 +01:00
gameObject . GetComponent < AStarPathfindingAgent > ( ) . speed = 2.55f ;
2023-01-10 21:20:48 +01:00
timeToWaitBeforeNextAttack = 0.8f ;
}
else if ( currentHealth < = maxHealth * 0.50f & & currentHealth > maxHealth * 0.25f )
{
2023-01-14 14:39:29 +01:00
gameObject . GetComponent < AStarPathfindingAgent > ( ) . speed = 3 ;
2023-01-10 21:20:48 +01:00
timeToWaitBeforeNextAttack = 0.7f ;
}
else if ( currentHealth < = maxHealth * 0.25f & & currentHealth > maxHealth * 0.10f )
{
2023-01-14 14:39:29 +01:00
gameObject . GetComponent < AStarPathfindingAgent > ( ) . speed = 3.25f ;
2023-01-10 21:20:48 +01:00
timeToWaitBeforeNextAttack = 0.6f ;
}
}
#region Respown minions managments
private void BlockRespowners ( )
{
ResetRespowners ( ) ;
if ( SummonedMinionsCounter = = 3 ) // use only 1
{
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 1 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 2 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 3 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 4 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 5 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
}
if ( SummonedMinionsCounter = = 6 ) // use 2 & 3
{
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 0 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 3 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 4 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 5 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
}
if ( SummonedMinionsCounter = = 12 ) // use 2 & 4 & 5
{
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 0 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 1 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 5 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
}
if ( SummonedMinionsCounter > 12 ) // use 4 & 5 & 6
{
SummonedMinionsCounter = 15 ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 0 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 1 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints . ElementAtOrDefault ( 2 ) . GetComponent < MinionRespowner > ( ) . Blocked = true ;
}
}
private void ResetRespowners ( )
{
foreach ( var respownPointObject in MinionRespowner . GetComponent < CounterRespowner > ( ) . respownPoints )
{
respownPointObject . GetComponent < MinionRespowner > ( ) . 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
}