diff --git a/Assets/Battle/BattleController.cs b/Assets/Battle/BattleController.cs index 13ab0c6..a9b3eac 100644 --- a/Assets/Battle/BattleController.cs +++ b/Assets/Battle/BattleController.cs @@ -175,6 +175,9 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe // End of hero turn this.heroDeck.DiscardHand(); + foreach (BattleHero hero in this.heroes) { + hero.TickStatuses(); + } } private IEnumerator RunMonsterTurn() { @@ -199,6 +202,9 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe // End of monster turn this.monsterDeck.DiscardHand(); + foreach (BattleMonster monster in this.monsters) { + monster.TickStatuses(); + } } public void NotifyHeroCardUsed(HeroCard card) { @@ -243,11 +249,11 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe } public bool CanUseHeroCard(HeroCard card) { - return this.heroTurn && card.Owner.IsAlive() && this.heroEnergy.CanPayForCard(card); + return this.heroTurn && card.Owner.CanUseCard(card) && this.heroEnergy.CanPayForCard(card); } public bool CanUseMonsterCard(MonsterCard card) { - return !this.heroTurn && card.Owner.IsAlive() && this.monsterEnergy.CanPayForCard(card); + return !this.heroTurn && card.Owner.CanUseCard(card) && this.monsterEnergy.CanPayForCard(card); } public BattleDeck GetHeroDeck() { @@ -305,14 +311,29 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe effect.Target.IncreaseMaxHP(effect.Amount); } - public void AcquireHeroCards(AcquireCardsEffect effect) { + public void AcquireCards(AcquireCardsEffect effect) { if (effect.Participant == null) return; - BattleHero hero = (BattleHero) effect.Participant; - foreach (CardId cardId in effect.CardIds) { - HeroCard acquiredCard = this.heroCardDatabase.CreateCard((int) cardId, hero); + + if (effect.Participant.IsHero()) { + this.AcquireCardsTyped(effect, this.heroDeck, this.heroCardDatabase); + } else { + this.AcquireCardsTyped(effect, this.monsterDeck, this.monsterCardDatabase); + } + } + + private void AcquireCardsTyped( + AcquireCardsEffect effect, + BattleDeck deck, + CardDatabase cardDatabase) + where BaseCardT : BaseCard { + foreach (int cardId in effect.CardIds) { + BaseCardT acquiredCard = cardDatabase.CreateCard(cardId, effect.Participant); switch (effect.Zone) { case BattleZone.DrawPile: - this.heroDeck.PutCardOnTopOfDrawPile(acquiredCard); + deck.PutCardOnTopOfDrawPile(acquiredCard); + break; + case BattleZone.DiscardPile: + deck.MoveCardToDiscard(acquiredCard); break; default: throw new System.NotImplementedException(); @@ -320,7 +341,7 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe } if (effect.Zone == BattleZone.DrawPile) { - this.heroDeck.ShuffleDrawPile(); + deck.ShuffleDrawPile(); } } @@ -328,9 +349,11 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe private List damageTriggers = new List(); public int DealDamage(DealDamageEffect effect) { + Debug.Log("DealDamage to " + effect.Target.GetDisplayName() + " " + effect.Amount); if (effect.Target == null) return 0; int damageDealt = effect.Target.DealDamage(effect.Amount); + Debug.Log("damageDealt " + damageDealt); foreach (DamageTrigger dt in this.damageTriggers) { if (dt.IsTriggeredBy(effect)) { EnqueueActivatedTrigger(dt); @@ -355,6 +378,11 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe effect.Target.AddBlock(effect.Amount); } + public void AddStatus(StatusEffect effect) { + if (effect.Target == null) return; + effect.Target.AddStatus(effect.Status, effect.Amount); + } + public void ArmDamageTrigger(DamageTrigger damageTrigger) { this.damageTriggers.Add(damageTrigger); } diff --git a/Assets/Battle/BattleParticipant.cs b/Assets/Battle/BattleParticipant.cs index 42ed922..37dd67f 100644 --- a/Assets/Battle/BattleParticipant.cs +++ b/Assets/Battle/BattleParticipant.cs @@ -14,9 +14,11 @@ public class BattleParticipant protected int maxHP; protected int block; protected bool dead; + protected Dictionary statuses; public BattleParticipant(bool isHero) { this.isHero = isHero; + this.statuses = new Dictionary(); } public void Init() { @@ -95,4 +97,29 @@ public class BattleParticipant this.OnDeath(this); } } + + public int AddStatus(BattleStatus status, int amount) { + int currentAmount; + this.statuses.TryGetValue(status, out currentAmount); + this.statuses[status] = currentAmount + amount; + return amount; + } + + public void TickStatuses() { + Dictionary newStatuses = new Dictionary(); + foreach (KeyValuePair pair in this.statuses) { + if (pair.Value >= 1) { + newStatuses[pair.Key] = pair.Value - 1; + } + } + this.statuses = newStatuses; + } + + public bool CanUseCard(BaseCard card) { + return IsAlive() && !HasStatus(BattleStatus.Stun); + } + + public bool HasStatus(BattleStatus status) { + return this.statuses.ContainsKey(status); + } } diff --git a/Assets/Battle/BattleScene.unity b/Assets/Battle/BattleScene.unity index 4c38d19..905cbb6 100644 --- a/Assets/Battle/BattleScene.unity +++ b/Assets/Battle/BattleScene.unity @@ -304,10 +304,10 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: heroIds: 010000000000000002000000 - monsterIds: 000000000100000000000000 - cardIds: 00000000000000000000000000000000010000000100000001000000030000000300000004000000 - mCardIds: 01000000010000000100000001000000010000000100000005000000050000000500000005000000 - mCardOwnerIndexes: 00000000000000000000000002000000020000000200000001000000010000000100000001000000 + monsterIds: 010000000000000000000000 + cardIds: 00000000000000000000000001000000010000000100000004000000030000000300000000000000 + mCardIds: 0800000008000000080000000800000008000000 + mCardOwnerIndexes: 0000000000000000000000000000000000000000 heroUIPrefab: {fileID: 6641511084601005557, guid: ef1b469b45cedee418e4c4adf577cd00, type: 3} uiCanvas: {fileID: 35522588} diff --git a/Assets/Battle/BattleStatus.cs b/Assets/Battle/BattleStatus.cs new file mode 100644 index 0000000..efeeb66 --- /dev/null +++ b/Assets/Battle/BattleStatus.cs @@ -0,0 +1,3 @@ +public enum BattleStatus { + Stun, +} diff --git a/Assets/Battle/BattleStatus.cs.meta b/Assets/Battle/BattleStatus.cs.meta new file mode 100644 index 0000000..e264453 --- /dev/null +++ b/Assets/Battle/BattleStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6fae0f0d5eccf2e41a949f4ccfca501f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Battle/CardsMonsters/GolemBerserkMCard.cs b/Assets/Battle/CardsMonsters/GolemBerserkMCard.cs new file mode 100644 index 0000000..14c8c13 --- /dev/null +++ b/Assets/Battle/CardsMonsters/GolemBerserkMCard.cs @@ -0,0 +1,27 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class GolemBerserkMCard : MonsterCard +{ + public GolemBerserkMCard(BattleParticipant owner) + : base(owner, MCardId.GolemBerserk, "Golem Berserk", 7, CardType.Attack, + "Deals 8 damage to target and 4 damage to other enemies." + + "Target: nearest hero.") { + } + + public override IEnumerator Use(IBattleEffectApplier effectApplier, MonsterViewpoint monsterViewpoint) { + BattleParticipant target = AIUtils.GetNearestHero(monsterViewpoint, this.owner); + + effectApplier.Apply(new DealDamageEffect(target, 8)); + yield return new WaitForSeconds(1f); + foreach (BattleHero hero in monsterViewpoint.Heroes) { + if (hero != target) { + effectApplier.Apply(new DealDamageEffect(hero, 4)); + } + } + + yield return new WaitForSeconds(1f); + yield break; + } +} diff --git a/Assets/Battle/CardsMonsters/GolemBerserkMCard.cs.meta b/Assets/Battle/CardsMonsters/GolemBerserkMCard.cs.meta new file mode 100644 index 0000000..d272f07 --- /dev/null +++ b/Assets/Battle/CardsMonsters/GolemBerserkMCard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8989d6bcb159b934f903860d3bc33c31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Battle/CardsMonsters/GolemChargeMCard.cs b/Assets/Battle/CardsMonsters/GolemChargeMCard.cs new file mode 100644 index 0000000..74ce15c --- /dev/null +++ b/Assets/Battle/CardsMonsters/GolemChargeMCard.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class GolemChargeMCard : MonsterCard +{ + public GolemChargeMCard(BattleParticipant owner) + : base(owner, MCardId.GolemCharge, "Golem Charge", 5, CardType.Attack, + "Deals 4 damage to target and stuns it. Acquire a Golem Berserk. " + + "Target: farthest hero.") { + } + + public override IEnumerator Use(IBattleEffectApplier effectApplier, MonsterViewpoint monsterViewpoint) { + BattleParticipant target = AIUtils.GetFarthestHero(monsterViewpoint, this.owner); + DealDamageEffect damageEffect = new DealDamageEffect(target, 4); + effectApplier.Apply(damageEffect); + StatusEffect statusEffect = new StatusEffect(target, BattleStatus.Stun); + effectApplier.Apply(statusEffect); + AcquireCardsEffect acquireEffect = new AcquireCardsEffect( + MCardId.GolemBerserk, BattleZone.DiscardPile, this.owner); + effectApplier.Apply(acquireEffect); + yield break; + } +} diff --git a/Assets/Battle/CardsMonsters/GolemChargeMCard.cs.meta b/Assets/Battle/CardsMonsters/GolemChargeMCard.cs.meta new file mode 100644 index 0000000..14197d1 --- /dev/null +++ b/Assets/Battle/CardsMonsters/GolemChargeMCard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 227631868e2bca746a7fa186c249248c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Battle/Effects/AcquireCardsEffect.cs b/Assets/Battle/Effects/AcquireCardsEffect.cs index 9ab0e8a..3f5aa74 100644 --- a/Assets/Battle/Effects/AcquireCardsEffect.cs +++ b/Assets/Battle/Effects/AcquireCardsEffect.cs @@ -1,28 +1,41 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using UnityEngine; public class AcquireCardsEffect : IBattleEffect { - private CardId[] cardIds; + private int[] cardIds; private BattleZone zone; private BattleParticipant participant; + public AcquireCardsEffect(CardId cardId, BattleZone zone, BattleParticipant participant) { + Initialize(new int[1] {(int) cardId}, zone, participant); + } + public AcquireCardsEffect(CardId[] cardIds, BattleZone zone, BattleParticipant participant) { + Initialize(cardIds.Cast().ToArray(), zone, participant); + } + + public AcquireCardsEffect(MCardId mCardId, BattleZone zone, BattleParticipant participant) { + Initialize(new int[1] {(int) mCardId}, zone, participant); + } + + public AcquireCardsEffect(MCardId[] mCardIds, BattleZone zone, BattleParticipant participant) { + Initialize(mCardIds.Cast().ToArray(), zone, participant); + } + + private void Initialize(int[] cardIds, BattleZone zone, BattleParticipant participant) { this.cardIds = cardIds; this.zone = zone; this.participant = participant; } public void Apply(IBattleEffectResolver resolver) { - if (this.participant.IsHero()) { - resolver.AcquireHeroCards(this); - } else { - throw new System.NotImplementedException(); - } + resolver.AcquireCards(this); } - public CardId[] CardIds {get => this.cardIds;} + public int[] CardIds {get => this.cardIds;} public BattleZone Zone {get => this.zone;} public BattleParticipant Participant {get => this.participant;} } diff --git a/Assets/Battle/Effects/StatusEffect.cs b/Assets/Battle/Effects/StatusEffect.cs new file mode 100644 index 0000000..a2e9ba2 --- /dev/null +++ b/Assets/Battle/Effects/StatusEffect.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class StatusEffect : IBattleEffect +{ + private BattleParticipant target; + private BattleStatus status; + private int amount; + + public StatusEffect(BattleParticipant target, BattleStatus status) { + this.target = target; + this.status = status; + this.amount = 1; + } + + public StatusEffect(BattleParticipant target, BattleStatus status, int amount) { + this.target = target; + this.status = status; + this.amount = amount; + } + + public void Apply(IBattleEffectResolver resolver) { + resolver.AddStatus(this); + } + + public BattleParticipant Target {get => this.target;} + public BattleStatus Status {get => this.status;} + public int Amount {get => this.amount;} +} diff --git a/Assets/Battle/Effects/StatusEffect.cs.meta b/Assets/Battle/Effects/StatusEffect.cs.meta new file mode 100644 index 0000000..8c1a781 --- /dev/null +++ b/Assets/Battle/Effects/StatusEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b5c00b59e47f93143aae014a3351c16b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Battle/IBattleEffectResolver.cs b/Assets/Battle/IBattleEffectResolver.cs index 75b1fe3..0284148 100644 --- a/Assets/Battle/IBattleEffectResolver.cs +++ b/Assets/Battle/IBattleEffectResolver.cs @@ -6,9 +6,10 @@ public interface IBattleEffectResolver { int DealDamage(DealDamageEffect effect); int Heal(HealEffect effect); void AddBlock(BlockEffect effect); + void AddStatus(StatusEffect effect); void IncreaseHeroMaxEnergy(IncreaseMaxEnergyEffect delta); void IncreaseMaxHP(IncreaseMaxHPEffect effect); - void AcquireHeroCards(AcquireCardsEffect effect); + void AcquireCards(AcquireCardsEffect effect); void ArmDamageTrigger(DamageTrigger damageTrigger); } diff --git a/Assets/Battle/MonsterAI/AIUtils.cs b/Assets/Battle/MonsterAI/AIUtils.cs index fba5687..4cd3f4f 100644 --- a/Assets/Battle/MonsterAI/AIUtils.cs +++ b/Assets/Battle/MonsterAI/AIUtils.cs @@ -3,43 +3,63 @@ using System.Collections.Generic; using UnityEngine; public class AIUtils { - public static BattleHero GetNearestHero(MonsterViewpoint monsterViewpoint, + public static BattleHero GetNearestHero(MonsterViewpoint viewpoint, BattleParticipant owner) { - int ownerPosition = GetMonsterPosition(monsterViewpoint, owner); + int ownerPosition = GetMonsterPosition(viewpoint, owner); int nearestHeroIndex = -1; int nearestDistance = int.MaxValue; - for (int i = 0; i < monsterViewpoint.Heroes.Length; i++) { - if (!monsterViewpoint.Heroes[i].IsAlive()) continue; + for (int i = 0; i < viewpoint.Heroes.Length; i++) { + if (!viewpoint.Heroes[i].IsAlive()) continue; int distance = System.Math.Abs(i - ownerPosition); if (distance < nearestDistance) { nearestHeroIndex = i; nearestDistance = distance; } } - if (nearestHeroIndex == -1) return null; - return monsterViewpoint.Heroes[nearestHeroIndex]; + return HeroAtIndex(nearestHeroIndex, viewpoint); } - public static int GetMonsterPosition(MonsterViewpoint monsterViewpoint, BattleParticipant monster) { - for (int i = 0; i < monsterViewpoint.Monsters.Length; i++) { - if (monsterViewpoint.Monsters[i] == monster) return i; + public static BattleHero GetFarthestHero(MonsterViewpoint viewpoint, + BattleParticipant owner) { + int ownerPosition = GetMonsterPosition(viewpoint, owner); + + int farthestHeroIndex = -1; + int farthestDistance = -1; + for (int i = 0; i < viewpoint.Heroes.Length; i++) { + if (!viewpoint.Heroes[i].IsAlive()) continue; + int distance = System.Math.Abs(i - ownerPosition); + if (distance > farthestDistance) { + farthestHeroIndex = i; + farthestDistance = distance; + } + } + return HeroAtIndex(farthestHeroIndex, viewpoint); + } + + private static int GetMonsterPosition(MonsterViewpoint viewpoint, BattleParticipant monster) { + for (int i = 0; i < viewpoint.Monsters.Length; i++) { + if (viewpoint.Monsters[i] == monster) return i; } return -1; } - public static BattleHero GetHeroWithHighestHP(MonsterViewpoint monsterViewpoint) { + public static BattleHero GetHeroWithHighestHP(MonsterViewpoint viewpoint) { int highestHPHeroIndex = -1; int highestHP = -1; - for (int i = 0; i < monsterViewpoint.Heroes.Length; i++) { - if (!monsterViewpoint.Heroes[i].IsAlive()) continue; - int heroHP = monsterViewpoint.Heroes[i].GetHP(); + for (int i = 0; i < viewpoint.Heroes.Length; i++) { + if (!viewpoint.Heroes[i].IsAlive()) continue; + int heroHP = viewpoint.Heroes[i].GetHP(); if (heroHP > highestHP) { highestHPHeroIndex = i; highestHP = heroHP; } } - if (highestHPHeroIndex == -1) return null; - return monsterViewpoint.Heroes[highestHPHeroIndex]; + return HeroAtIndex(highestHPHeroIndex, viewpoint); + } + + private static BattleHero HeroAtIndex(int index, MonsterViewpoint viewpoint) { + if (index == -1) return null; + return viewpoint.Heroes[index]; } } diff --git a/Assets/Battle/MonsterCardDatabase.cs b/Assets/Battle/MonsterCardDatabase.cs index 3b39fc5..385635c 100644 --- a/Assets/Battle/MonsterCardDatabase.cs +++ b/Assets/Battle/MonsterCardDatabase.cs @@ -21,12 +21,16 @@ public class MonsterCardDatabase : CardDatabase MCardId monsterCardId = (MCardId) id; switch (monsterCardId) { + case MCardId.QuickSwiftDeath: + return new QuickSwiftDeathMCard(owner); case MCardId.VampireFangs: return new VampireFangsMCard(owner); case MCardId.GolemFist: return new GolemFistMCard(owner); - case MCardId.QuickSwiftDeath: - return new QuickSwiftDeathMCard(owner); + case MCardId.GolemCharge: + return new GolemChargeMCard(owner); + case MCardId.GolemBerserk: + return new GolemBerserkMCard(owner); default: throw new System.ArgumentException("MCardId unknown", "id"); } diff --git a/TODO.txt b/TODO.txt index 761b38b..04fa99d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,2 +1,3 @@ - Rename CardId to HCardId -- Animate rearrange hand after card is used \ No newline at end of file +- Animate rearrange hand after card is used +- Make hero cards be used in sequence Coroutines, not in parallel \ No newline at end of file