using System.Collections.Generic; using UnityEngine; // The main opponent of yours. Defeat him and you win. public class AntiPlayer : Fighter, IFighterCallback { [SerializeField, Tooltip("How close the AntiPlayer has to be to start attacking, or how far away it has to be to start running towards the target")] float maxDistanceToEnemy = 1f; [SerializeField] float movementSpeed = 4f; [SerializeField, Tooltip("After killing an Enemy, how long to stay idle before targeting the next enemy")] float delayUntilNextTarget = 0.3f; string[] attackAnimationTriggers = new string[] { "Attack1", "Attack2", "Attack3" }; List<Fighter> enemies = new List<Fighter>(); Fighter currentTargetEnemy; protected override void Awake() { base.Awake(); fighterType = FighterTypes.ANTI_PLAYER; opponentTag = "Enemy"; animator.SetBool("Grounded", true); } protected override void Update() { base.Update(); if (!alive || currentTargetEnemy == null) { return; } var vectorToNextEnemy = currentTargetEnemy.transform.position - transform.position; var scale = transform.localScale; // Always face the target Enemy. Flip the scale to also flip the position of the attack hitbox. scale.x = vectorToNextEnemy.x < 0 ? -initalScale.x : initalScale.x; transform.localScale = scale; // Run towards target enemy if too far away if (Mathf.Abs(vectorToNextEnemy.x) > maxDistanceToEnemy) { animator.SetInteger("AnimState", 1); // Run Animation rigidbody.velocity = new Vector2(Mathf.Sign(vectorToNextEnemy.x) * movementSpeed, rigidbody.velocity.y); } else { // No horizontal movement animator.SetInteger("AnimState", 0); rigidbody.velocity = new Vector2(0, rigidbody.velocity.y); } } protected override bool CanAttack() { if (currentTargetEnemy == null) { return false; } // Can only attack when close enough to the target Enemy var vectorToNextEnemy = currentTargetEnemy.transform.position - transform.position; return Mathf.Abs(vectorToNextEnemy.x) <= maxDistanceToEnemy; } protected override void Attack() { animator.SetTrigger(attackAnimationTriggers[Random.Range(0, attackAnimationTriggers.Length)]); } void CalculateClosestEnemy() { // Sort by closest distance enemies.Sort((a, b) => (int)Mathf.Sign(Vector3.Distance(transform.position, a.transform.position) - Vector3.Distance(transform.position, b.transform.position))); currentTargetEnemy = enemies.Count > 0 ? enemies[0] : null; } public override int DealDamage(int dmg) { var actualDamage = base.DealDamage(dmg); StatsManager.instance.AddDamageBasedMana(actualDamage, !alive); return actualDamage; } public void OnFighterDeath(Fighter fighter) { enemies.Remove(fighter); // Wait a short time before targeting next enemy. This is to avoid interrupting the current // attack animation, which would happen if we immediately ran to the next target. currentTargetEnemy = null; Invoke(nameof(CalculateClosestEnemy), delayUntilNextTarget); } public override void OnRoundStart() { base.OnRoundStart(); // Listen for Enemy Death events, used for targeting foreach (var enemy in GameObject.FindGameObjectsWithTag("Enemy")) { var fighter = enemy.GetComponent<Fighter>(); fighter.callbacks.Add(this); enemies.Add(fighter); } CalculateClosestEnemy(); } public override void OnRoundEnd(bool won) { if (!alive) // When the player successfully killed us { damageIndicator.FlashText("Upgrade!", true); StatsManager.instance.UpgradeAntiPlayer(); } base.OnRoundEnd(won); enemies.Clear(); currentTargetEnemy = null; } }