diff --git a/Assets/Scripts/Fighter.cs b/Assets/Scripts/Fighter.cs new file mode 100644 index 0000000000000000000000000000000000000000..078202b480350cf6679206e19f09da3142db045e --- /dev/null +++ b/Assets/Scripts/Fighter.cs @@ -0,0 +1,88 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public interface IFighterCallback +{ + void OnDeath(); +} + +// Generic subclass for any Fighter on the field, whether it's an Enemy or the AntiPlayer. +// Contains stats management such as health, attacks, damage, death, etc. +public abstract class Fighter : MonoBehaviour +{ + + [SerializeField] int baseHealth = 100; + [SerializeField, Tooltip("Attack every x ms")] int baseAttackSpeed = 1000; + [SerializeField] int baseAttackDamage = 10; + [SerializeField] int baseArmor = 1; + + public List<IFighterCallback> callbacks = new List<IFighterCallback>(); + + protected int currentHealth = 100; + protected Animator animator; + protected bool alive { get => currentHealth > 0; } + protected FighterTypes fighterType; + protected abstract void Attack(); + + float timeSinceLastAttack = 0f; + + protected virtual void Awake() + { + animator = GetComponent<Animator>(); + fighterType = FighterTypes.ENEMY; + } + + protected virtual void Start() + { + currentHealth = Mathf.RoundToInt(baseHealth * GetStats().healthMultiplier); + } + + protected virtual void Update() + { + if (alive) + { + timeSinceLastAttack += Time.deltaTime; + var timeUntilNextAttack = baseAttackSpeed - timeSinceLastAttack; + if (timeUntilNextAttack <= 0) + { + Attack(); + // TODO delegate to subclass? Some attack animations take time to charge up. + animator.SetTrigger("Attack"); + timeSinceLastAttack = -timeUntilNextAttack; // To account for overshoot + } + } + } + + public void DealDamage(int dmg) + { + var actualDamage = Mathf.Max(0, Mathf.RoundToInt(dmg - baseArmor * GetStats().armorMultiplier)); + currentHealth = Mathf.Max(currentHealth - actualDamage, 0); + if (currentHealth == 0) + { + animator.SetTrigger("Death"); + callbacks.ForEach(c => c.OnDeath()); + // TODO (Depending on the death animation) only destroy on round end so that corpses stay on the ground + // Destroy(gameObject); + } + else + { + animator.SetTrigger("Hurt"); + } + } + + protected FighterStats GetStats() + { + return StatsManager.instance.fighterStats[fighterType]; + } + + void OnTriggerEnter2D(Collider2D other) + { + if (other.CompareTag("AntiPlayer")) + { + var damageToDeal = Mathf.RoundToInt(baseAttackDamage * GetStats().damageMultiplier); + other.GetComponent<Fighter>().DealDamage(damageToDeal); + } + } + +} diff --git a/Assets/Scripts/Fighter.cs.meta b/Assets/Scripts/Fighter.cs.meta new file mode 100644 index 0000000000000000000000000000000000000000..e6b9012d8615f474908ed57403b7c6f94db0b64f --- /dev/null +++ b/Assets/Scripts/Fighter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b15d34b9baeff19448f00b406b4ff048 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/StatsManager.cs b/Assets/Scripts/StatsManager.cs index ea985e2465f905d35fad488d07d6e669c5b40344..dc23221b67f2d53b24281164e1af48102dc0c183 100644 --- a/Assets/Scripts/StatsManager.cs +++ b/Assets/Scripts/StatsManager.cs @@ -11,14 +11,13 @@ public enum UpgradeTypes MANA_COST, } -public struct EnemyStats +public enum FighterTypes { - public float healthMultiplier; - public float damageMultiplier; - public float armorMultiplier; + ANTI_PLAYER, + ENEMY, } -public struct PlayerStats +public struct FighterStats { public float healthMultiplier; public float damageMultiplier; @@ -53,26 +52,15 @@ public class StatsManager : MonoBehaviour public static StatsManager instance { get; private set; } - EnemyStats currentEnemyStats; - PlayerStats currentPlayerStats; [SerializeField] int currentMana; - [SerializeField] List<Upgrade> availableUpgrades; + // The currently active statistics for each fighter type + public Dictionary<FighterTypes, FighterStats> fighterStats = new Dictionary<FighterTypes, FighterStats>(); + // Information about currently available and purchased upgrades. Should only be read, not modified. public Dictionary<UpgradeTypes, UpgradeData> upgrades = new Dictionary<UpgradeTypes, UpgradeData>(); - public EnemyStats GetEnemyStats() - { - return currentEnemyStats; - } - - public PlayerStats GetPlayerStats(int stage) - { - // TODO use `stage` - return currentPlayerStats; - } - public int GetMana() { return currentMana; @@ -120,9 +108,10 @@ public class StatsManager : MonoBehaviour void RecalculateStatMultipliers() { - currentEnemyStats.healthMultiplier = upgrades[UpgradeTypes.ENEMY_HEALTH].GetTotalEffectMultiplier(); - currentEnemyStats.damageMultiplier = upgrades[UpgradeTypes.ENEMY_DAMAGE].GetTotalEffectMultiplier(); - currentEnemyStats.armorMultiplier = upgrades[UpgradeTypes.ENEMY_ARMOR].GetTotalEffectMultiplier(); + var enemyStats = fighterStats[FighterTypes.ENEMY]; + enemyStats.healthMultiplier = upgrades[UpgradeTypes.ENEMY_HEALTH].GetTotalEffectMultiplier(); + enemyStats.damageMultiplier = upgrades[UpgradeTypes.ENEMY_DAMAGE].GetTotalEffectMultiplier(); + enemyStats.armorMultiplier = upgrades[UpgradeTypes.ENEMY_ARMOR].GetTotalEffectMultiplier(); } void Awake() @@ -143,11 +132,17 @@ public class StatsManager : MonoBehaviour upgrades.Add(upgrade.type, upgradeData); } - currentEnemyStats.healthMultiplier = 1f; - currentEnemyStats.damageMultiplier = 1f; - currentEnemyStats.armorMultiplier = 1f; - currentPlayerStats.healthMultiplier = 1f; - currentPlayerStats.damageMultiplier = 1f; - currentPlayerStats.armorMultiplier = 1f; + fighterStats.Add(FighterTypes.ANTI_PLAYER, new FighterStats() + { + healthMultiplier = 1f, + damageMultiplier = 1f, + armorMultiplier = 1f, + }); + fighterStats.Add(FighterTypes.ENEMY, new FighterStats() + { + healthMultiplier = 1f, + damageMultiplier = 1f, + armorMultiplier = 1f, + }); } }