Implement statuses, GolemCharge, GolemBerserk, stun

This commit is contained in:
henriquenakashima 2020-09-20 22:02:34 -04:00
parent caf782c884
commit ef9ec43c8a
16 changed files with 260 additions and 38 deletions

View file

@ -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<HeroCard> 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<BaseCardT>(
AcquireCardsEffect effect,
BattleDeck<BaseCardT> deck,
CardDatabase<BaseCardT> 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<DamageTrigger> damageTriggers = new List<DamageTrigger>();
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);
}

View file

@ -14,9 +14,11 @@ public class BattleParticipant
protected int maxHP;
protected int block;
protected bool dead;
protected Dictionary<BattleStatus, int> statuses;
public BattleParticipant(bool isHero) {
this.isHero = isHero;
this.statuses = new Dictionary<BattleStatus, int>();
}
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<BattleStatus, int> newStatuses = new Dictionary<BattleStatus, int>();
foreach (KeyValuePair<BattleStatus, int> 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);
}
}

View file

@ -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}

View file

@ -0,0 +1,3 @@
public enum BattleStatus {
Stun,
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fae0f0d5eccf2e41a949f4ccfca501f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8989d6bcb159b934f903860d3bc33c31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 227631868e2bca746a7fa186c249248c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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<int>().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<int>().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;}
}

View file

@ -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;}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5c00b59e47f93143aae014a3351c16b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}

View file

@ -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];
}
}

View file

@ -21,12 +21,16 @@ public class MonsterCardDatabase : CardDatabase<MonsterCard>
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");
}

View file

@ -1,2 +1,3 @@
- Rename CardId to HCardId
- Animate rearrange hand after card is used
- Make hero cards be used in sequence Coroutines, not in parallel