Implement Whirlwind

This commit is contained in:
henriquenakashima 2020-09-24 00:24:49 -04:00
parent 9413ebee7e
commit 6a6f07689e
18 changed files with 330 additions and 52 deletions

View file

@ -5,31 +5,34 @@ using UnityEngine;
public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffectResolver, MonsterViewpoint public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffectResolver, MonsterViewpoint
{ {
public HeroId[] heroIds; // Inspector public
public MonsterId[] monsterIds;
public CardId[] cardIds;
public MCardId[] mCardIds;
public int[] mCardOwnerIndexes;
public HeroInfoUI heroUIPrefab; public HeroInfoUI heroUIPrefab;
public GameObject uiCanvas; public GameObject uiCanvas;
public BattlePlayerControls battlePlayerControls; public BattlePlayerControls battlePlayerControls;
public EnergyUI heroEnergyUI; public EnergyUI heroEnergyUI;
public EnergyUI monsterEnergyUI; public EnergyUI monsterEnergyUI;
// Input
private HeroId[] heroIds;
private MonsterId[] monsterIds;
private CardId[] cardIds;
private MCardId[] mCardIds;
private int[] mCardOwnerIndexes;
// Private
private HeroCardDatabase heroCardDatabase; private HeroCardDatabase heroCardDatabase;
private MonsterCardDatabase monsterCardDatabase; private MonsterCardDatabase monsterCardDatabase;
private BattleDeck<HeroCard> heroDeck; private BattleDeck<HeroCard> heroDeck;
private BattleDeck<MonsterCard> monsterDeck; private BattleDeck<MonsterCard> monsterDeck;
private PlayArea playArea; private PlayArea playArea;
public BattleHero[] heroes; private BattleHero[] heroes;
private GameObject[] heroSprites; private Dictionary<BattleHero, GameObject> heroSprites;
private HeroInfoUI[] heroUIs; private Dictionary<BattleHero, HeroInfoUI> heroUIs;
public BattleMonster[] monsters; private BattleMonster[] monsters;
private GameObject[] monsterSprites; private Dictionary<BattleMonster, GameObject> monsterSprites;
private HeroInfoUI[] monsterUIs; private Dictionary<BattleMonster, HeroInfoUI> monsterUIs;
private bool heroTurn; private bool heroTurn;
private bool heroPassedTurn; private bool heroPassedTurn;
@ -38,6 +41,62 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
private InsightLevel insightLevel; private InsightLevel insightLevel;
void Awake() { void Awake() {
this.heroIds = new HeroId[3] {
HeroId.Brute,
HeroId.Alchemist,
HeroId.Shaman,
};
this.cardIds = new CardId[10] {
CardId.CrashingPunch,
CardId.CrashingPunch,
CardId.CrashingPunch,
CardId.Cover,
CardId.Cover,
CardId.Cover,
CardId.Thunderstorm,
CardId.Scarring,
CardId.Scarring,
CardId.Meditate,
};
this.monsterIds = new MonsterId[3] {
MonsterId.Golem,
MonsterId.Bat,
MonsterId.Bat,
};
this.mCardIds = new MCardId[10] {
MCardId.GraniteDefense,
MCardId.GraniteDefense,
MCardId.GolemCharge,
MCardId.GolemFist,
MCardId.VampireFangs,
MCardId.VampireFangs,
MCardId.Whirlwind,
MCardId.VampireFangs,
MCardId.VampireFangs,
MCardId.Whirlwind,
};
this.mCardOwnerIndexes = new int[10] {
0, 0, 0, 0,
1, 1, 1,
2, 2, 2,
};
// this.mCardIds = new MCardId[5] {
// MCardId.Whirlwind,
// MCardId.Whirlwind,
// MCardId.Whirlwind,
// MCardId.Whirlwind,
// MCardId.Whirlwind,
// };
// this.mCardOwnerIndexes = new int[5] {
// 1, 1,
// 2, 2, 2,
// };
this.heroCardDatabase = new HeroCardDatabase(); this.heroCardDatabase = new HeroCardDatabase();
this.monsterCardDatabase = new MonsterCardDatabase(); this.monsterCardDatabase = new MonsterCardDatabase();
@ -51,45 +110,51 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
this.insightLevel = new InsightLevel(1); this.insightLevel = new InsightLevel(1);
heroes = new BattleHero[3]; heroes = new BattleHero[3];
heroSprites = new GameObject[3]; heroSprites = new Dictionary<BattleHero, GameObject>();
heroUIs = new HeroInfoUI[3]; heroUIs = new Dictionary<BattleHero, HeroInfoUI>();
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
HeroId id = heroIds[i]; HeroId id = heroIds[i];
heroes[i] = new BattleHero(id); BattleHero hero = new BattleHero(id);
heroes[i].Init(); hero.Init();
heroes[i] = hero;
heroSprites[i] = HeroDatabase.createHeroSprite(id); GameObject sprite = HeroDatabase.createHeroSprite(id);
heroSprites[i].name = "Hero" + i + "_Sprite"; heroSprites[hero] = sprite;
heroSprites[i].transform.position = new Vector3(-1 + -3 + 3 * i, -1.8f, 0); sprite.name = "Hero" + i + "_Sprite";
BattleParticipantSprite bps = heroSprites[i].AddComponent<BattleParticipantSprite>(); PositionSprite(TeamChoice.Heroes, sprite, i);
bps.Init(battlePlayerControls, heroes[i]); BattleParticipantSprite bps = sprite.AddComponent<BattleParticipantSprite>();
bps.Init(battlePlayerControls, hero);
bps.OnSpriteClicked += battlePlayerControls.NotifyParticipantSpriteClicked; bps.OnSpriteClicked += battlePlayerControls.NotifyParticipantSpriteClicked;
heroes[i].OnDeath += bps.NotifyDeath; hero.OnDeath += bps.NotifyDeath;
heroUIs[i] = (HeroInfoUI) Instantiate(heroUIPrefab.gameObject, uiCanvas.transform).GetComponent(typeof(HeroInfoUI)); HeroInfoUI heroUI = (HeroInfoUI) Instantiate(heroUIPrefab.gameObject, uiCanvas.transform).GetComponent(typeof(HeroInfoUI));
heroUIs[i].Init(heroSprites[i], heroes[i]); heroUI.Init(sprite, hero);
heroUIs[hero] = heroUI;
} }
monsters = new BattleMonster[3]; monsters = new BattleMonster[3];
monsterSprites = new GameObject[3]; monsterSprites = new Dictionary<BattleMonster, GameObject>();
monsterUIs = new HeroInfoUI[3]; monsterUIs = new Dictionary<BattleMonster, HeroInfoUI>();
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
MonsterId id = monsterIds[i]; MonsterId id = monsterIds[i];
monsters[i] = MonsterDatabase.createMonsterScript(id); BattleMonster monster = MonsterDatabase.createMonsterScript(id);
monsters[i].Init(); monster.Init();
monsters[i] = monster;
monsterSprites[i] = MonsterDatabase.createMonsterSprite(id); GameObject sprite = MonsterDatabase.createMonsterSprite(id);
monsterSprites[i].name = "Monster" + i + "_Sprite"; monsterSprites[monster] = sprite;
monsterSprites[i].transform.position = new Vector3(+1 + -3 + 3 * i, 1.2f, 0); sprite.name = "Monster" + i + "_Sprite";
BattleParticipantSprite bps = monsterSprites[i].AddComponent<BattleParticipantSprite>(); PositionSprite(TeamChoice.Monsters, sprite, i);
bps.Init(battlePlayerControls, monsters[i]); BattleParticipantSprite bps = sprite.AddComponent<BattleParticipantSprite>();
bps.Init(battlePlayerControls, monster);
bps.OnSpriteClicked += battlePlayerControls.NotifyParticipantSpriteClicked; bps.OnSpriteClicked += battlePlayerControls.NotifyParticipantSpriteClicked;
monsters[i].OnDeath += bps.NotifyDeath; monster.OnDeath += bps.NotifyDeath;
monsterUIs[i] = (HeroInfoUI) Instantiate(heroUIPrefab.gameObject, uiCanvas.transform).GetComponent(typeof(HeroInfoUI)); HeroInfoUI monsterUI = (HeroInfoUI) Instantiate(heroUIPrefab.gameObject, uiCanvas.transform).GetComponent(typeof(HeroInfoUI));
monsterUIs[i].Init(monsterSprites[i], monsters[i]); monsterUI.Init(sprite, monster);
monsterUIs[monster] = monsterUI;
} }
BattleParticipant[] cardOwners = new BattleParticipant[this.cardIds.Length]; BattleParticipant[] cardOwners = new BattleParticipant[this.cardIds.Length];
@ -121,6 +186,16 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
this.monsterDeck.OnHandPileChanged += this.battlePlayerControls.NotifyMonsterHandPileChanged; this.monsterDeck.OnHandPileChanged += this.battlePlayerControls.NotifyMonsterHandPileChanged;
} }
private void PositionSprite(TeamChoice team, GameObject sprite, int i) {
Vector3 pos;
if (team == TeamChoice.Heroes) {
pos = new Vector3(-1 + -3 + 3 * i, -1.8f, 0);
} else {
pos = new Vector3(+1 + -3 + 3 * i, 1.2f, 0);
}
sprite.transform.position = pos;
}
private BattleParticipant FindHeroByClass(HeroId heroId) { private BattleParticipant FindHeroByClass(HeroId heroId) {
foreach (BattleHero battleHero in this.heroes) { foreach (BattleHero battleHero in this.heroes) {
if (battleHero.GetId() == heroId) return battleHero; if (battleHero.GetId() == heroId) return battleHero;
@ -298,6 +373,12 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
} }
} }
private BattleParticipant[] GetTeam(TeamChoice team) {
return (team == TeamChoice.Heroes
? this.heroes.Cast<BattleParticipant>().ToArray()
: this.monsters.Cast<BattleParticipant>().ToArray());
}
public EnergyPool HeroEnergy {get => this.heroEnergy;} public EnergyPool HeroEnergy {get => this.heroEnergy;}
public EnergyPool MonsterEnergy {get => this.monsterEnergy;} public EnergyPool MonsterEnergy {get => this.monsterEnergy;}
@ -335,7 +416,7 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
public void AcquireCards(AcquireCardsEffect effect) { public void AcquireCards(AcquireCardsEffect effect) {
if (effect.Participant == null) return; if (effect.Participant == null) return;
if (effect.Participant.IsHero()) { if (effect.Participant.Team == TeamChoice.Heroes) {
this.AcquireCardsTyped(effect, this.heroDeck, this.heroCardDatabase); this.AcquireCardsTyped(effect, this.heroDeck, this.heroCardDatabase);
} else { } else {
this.AcquireCardsTyped(effect, this.monsterDeck, this.monsterCardDatabase); this.AcquireCardsTyped(effect, this.monsterDeck, this.monsterCardDatabase);
@ -385,6 +466,24 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
return damageDealt; return damageDealt;
} }
public int DealMassDamage(MassDamageEffect effect) {
int damageDealt = 0;
BattleParticipant[] affectedTeam = GetTeam(effect.Team);
foreach (BattleParticipant bp in affectedTeam) {
damageDealt += bp.DealDamage(effect.Amount);
foreach (DamageTrigger dt in this.damageTriggers) {
if (dt.IsTriggeredBy(effect)) {
EnqueueActivatedTrigger(dt);
}
}
}
// Do this after the card resolution is over to avoid reentraint effects.
RunActivatedTriggers();
return damageDealt;
}
public int Heal(HealEffect effect) { public int Heal(HealEffect effect) {
if (effect.Target == null) return 0; if (effect.Target == null) return 0;
@ -402,6 +501,61 @@ public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffe
effect.Target.AddStatus(effect.Status, effect.Amount); effect.Target.AddStatus(effect.Status, effect.Amount);
} }
public void Rotate(RotateEffect effect) {
if (effect.Positions == 0) return;
BattleParticipant[] currentPositions = GetTeam(effect.Team);
int start = int.MaxValue;
int end = int.MinValue;
for (int i = 0; i < currentPositions.Length; i++) {
BattleParticipant bp = currentPositions[i];
if (bp != null) {
start = System.Math.Min(start, i);
end = System.Math.Max(end, i);
}
}
int[] newPositions = new int[currentPositions.Length];
for (int i = start; i <= end; i++) {
if (i < start || i > end) {
newPositions[i] = i;
} else {
int newI = start + TrueMod(
i - start - effect.Positions,
end - start + 1);
newPositions[newI] = i;
}
}
MoveTeamToNewPositions(effect.Team, newPositions);
}
private static int TrueMod(int a, int b) {
return (a % b + b) % b;
}
private void MoveTeamToNewPositions(TeamChoice team, int[] newPositions) {
int n = newPositions.Length;
if (team == TeamChoice.Heroes) {
BattleHero[] newHeroes = new BattleHero[n];
for (int i = 0; i < n; i++) {
BattleHero hero = this.heroes[i];
newHeroes[newPositions[i]] = hero;
PositionSprite(team, heroSprites[hero], newPositions[i]);
}
this.heroes = newHeroes;
} else {
BattleMonster[] newMonsters = new BattleMonster[n];
for (int i = 0; i < n; i++) {
BattleMonster monster = this.monsters[i];
newMonsters[newPositions[i]] = monster;
PositionSprite(team, monsterSprites[monster], newPositions[i]);
}
this.monsters = newMonsters;
}
}
public void ArmDamageTrigger(DamageTrigger damageTrigger) { public void ArmDamageTrigger(DamageTrigger damageTrigger) {
this.damageTriggers.Add(damageTrigger); this.damageTriggers.Add(damageTrigger);
} }

View file

@ -6,7 +6,7 @@ public class BattleHero: BattleParticipant
{ {
HeroId id; HeroId id;
public BattleHero(HeroId id) : base(true) { public BattleHero(HeroId id) : base(TeamChoice.Heroes) {
this.id = id; this.id = id;
switch (id) switch (id)
{ {

View file

@ -4,6 +4,6 @@ using UnityEngine;
public class BattleMonster: BattleParticipant public class BattleMonster: BattleParticipant
{ {
public BattleMonster() : base(false) { public BattleMonster() : base(TeamChoice.Monsters) {
} }
} }

View file

@ -9,7 +9,7 @@ public class BattleParticipant
public event BattleParticipantUpdateAction OnDeath; public event BattleParticipantUpdateAction OnDeath;
public event BattleParticipantUpdateAction OnStatusChanged; public event BattleParticipantUpdateAction OnStatusChanged;
protected bool isHero; protected TeamChoice team;
protected string displayName; protected string displayName;
protected int hp; protected int hp;
protected int maxHP; protected int maxHP;
@ -17,8 +17,8 @@ public class BattleParticipant
protected bool dead; protected bool dead;
protected Dictionary<BattleStatus, int> statuses; protected Dictionary<BattleStatus, int> statuses;
public BattleParticipant(bool isHero) { public BattleParticipant(TeamChoice team) {
this.isHero = isHero; this.team = team;
this.statuses = new Dictionary<BattleStatus, int>(); this.statuses = new Dictionary<BattleStatus, int>();
} }
@ -83,8 +83,8 @@ public class BattleParticipant
return this.block; return this.block;
} }
public bool IsHero() { public TeamChoice Team {
return this.isHero; get => this.team;
} }
public void IncreaseMaxHP(int amount) { public void IncreaseMaxHP(int amount) {

View file

@ -306,8 +306,8 @@ MonoBehaviour:
heroIds: 010000000000000002000000 heroIds: 010000000000000002000000
monsterIds: 010000000000000000000000 monsterIds: 010000000000000000000000
cardIds: 00000000000000000000000001000000010000000100000004000000030000000300000007000000 cardIds: 00000000000000000000000001000000010000000100000004000000030000000300000007000000
mCardIds: 06000000060000000800000001000000010000000100000005000000050000000800000006000000 mCardIds: 06000000060000000800000001000000010000000100000005000000050000000200000002000000
mCardOwnerIndexes: 00000000000000000000000001000000010000000200000000000000000000000000000000000000 mCardOwnerIndexes: 00000000000000000000000001000000010000000200000000000000000000000100000002000000
heroUIPrefab: {fileID: 6641511084601005557, guid: ef1b469b45cedee418e4c4adf577cd00, heroUIPrefab: {fileID: 6641511084601005557, guid: ef1b469b45cedee418e4c4adf577cd00,
type: 3} type: 3}
uiCanvas: {fileID: 35522588} uiCanvas: {fileID: 35522588}

View file

@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WhirlwindMCard : MonsterCard
{
public WhirlwindMCard(BattleParticipant owner)
: base(owner, MCardId.Whirlwind, "Whirlwind", 4, CardType.Attack,
"Rotate enemies right. Deal 3 damage to all enemies.") {
}
public override IEnumerator Use(IBattleEffectApplier effectApplier, MonsterViewpoint monsterViewpoint) {
RotateEffect rotateEffect = new RotateEffect(TeamChoice.Heroes, +1);
effectApplier.Apply(rotateEffect);
MassDamageEffect damageEffect = new MassDamageEffect(TeamChoice.Heroes, 3);
effectApplier.Apply(damageEffect);
yield break;
}
}

View file

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

View file

@ -3,11 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
public enum TeamChoice {
Heroes,
Monsters,
}
public class DrawCardsEffect : IBattleEffect public class DrawCardsEffect : IBattleEffect
{ {
private int amount; private int amount;

View file

@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MassDamageEffect : IBattleEffect
{
private TeamChoice team;
private int amount;
private int amountDealt;
public MassDamageEffect(TeamChoice team, int amount) {
this.team = team;
this.amount = amount;
}
public void Apply(IBattleEffectResolver resolver) {
this.amountDealt = resolver.DealMassDamage(this);
}
public TeamChoice Team {get => this.team;}
public int Amount {get => this.amount;}
public int AmountDealt {get => this.amountDealt;}
}

View file

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

View file

@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateEffect : IBattleEffect
{
private TeamChoice team;
private int positions;
public RotateEffect(TeamChoice team, int positions) {
this.team = team;
this.positions = positions;
}
public void Apply(IBattleEffectResolver resolver) {
resolver.Rotate(this);
}
public TeamChoice Team {get => this.team;}
public int Positions {get => this.positions;}
}

View file

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

View file

@ -4,6 +4,7 @@ using UnityEngine;
public interface IBattleEffectResolver { public interface IBattleEffectResolver {
int DealDamage(DealDamageEffect effect); int DealDamage(DealDamageEffect effect);
int DealMassDamage(MassDamageEffect effect);
int Heal(HealEffect effect); int Heal(HealEffect effect);
void AddBlock(BlockEffect effect); void AddBlock(BlockEffect effect);
void AddStatus(StatusEffect effect); void AddStatus(StatusEffect effect);
@ -12,6 +13,7 @@ public interface IBattleEffectResolver {
void ModifyInsight(ModifyInsightEffect effect); void ModifyInsight(ModifyInsightEffect effect);
void DrawCards(DrawCardsEffect effect); void DrawCards(DrawCardsEffect effect);
void AcquireCards(AcquireCardsEffect effect); void AcquireCards(AcquireCardsEffect effect);
void Rotate(RotateEffect effect);
void ArmDamageTrigger(DamageTrigger damageTrigger); void ArmDamageTrigger(DamageTrigger damageTrigger);
} }

View file

@ -25,6 +25,8 @@ public class MonsterCardDatabase : CardDatabase<MonsterCard>
return new QuickSwiftDeathMCard(owner); return new QuickSwiftDeathMCard(owner);
case MCardId.VampireFangs: case MCardId.VampireFangs:
return new VampireFangsMCard(owner); return new VampireFangsMCard(owner);
case MCardId.Whirlwind:
return new WhirlwindMCard(owner);
case MCardId.GolemFist: case MCardId.GolemFist:
return new GolemFistMCard(owner); return new GolemFistMCard(owner);
case MCardId.GraniteDefense: case MCardId.GraniteDefense:

View file

@ -11,6 +11,6 @@ public class OtherHeroTargetPolicy: TargetingPolicy
} }
public override bool CanTarget(BattleParticipant participant) { public override bool CanTarget(BattleParticipant participant) {
return participant.IsHero() && participant != this.owner; return participant.Team == TeamChoice.Heroes && participant != this.owner;
} }
} }

View file

@ -0,0 +1,4 @@
public enum TeamChoice {
Heroes,
Monsters,
}

View file

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

View file

@ -13,4 +13,18 @@ public class DamageTrigger : Trigger {
public bool IsTriggeredBy(DealDamageEffect effect) { public bool IsTriggeredBy(DealDamageEffect effect) {
return effect.Amount > 0 && this.observedParticipants.Contains(effect.Target); return effect.Amount > 0 && this.observedParticipants.Contains(effect.Target);
} }
public bool IsTriggeredBy(MassDamageEffect effect) {
if (effect.Amount <= 0) {
return false;
}
foreach (BattleParticipant bp in this.observedParticipants) {
if (bp.Team == effect.Team) {
return true;
}
}
return false;
}
} }