mixedcards/Assets/Battle/BattleController.cs
2020-09-20 15:26:18 -04:00

336 lines
11 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class BattleController : MonoBehaviour, IBattleEffectApplier, IBattleEffectResolver, MonsterViewpoint
{
public HeroId[] heroIds;
public MonsterId[] monsterIds;
public CardId[] cardIds;
public MCardId[] mCardIds;
public int[] mCardOwnerIndexes;
public HeroInfoUI heroUIPrefab;
public GameObject uiCanvas;
public BattlePlayerControls battlePlayerControls;
public EnergyUI heroEnergyUI;
public EnergyUI monsterEnergyUI;
private HeroCardDatabase heroCardDatabase;
private MonsterCardDatabase monsterCardDatabase;
private BattleDeck<HeroCard> heroDeck;
private BattleDeck<MonsterCard> monsterDeck;
private PlayArea playArea;
public BattleHero[] heroes;
private GameObject[] heroSprites;
private HeroInfoUI[] heroUIs;
public BattleMonster[] monsters;
private GameObject[] monsterSprites;
private HeroInfoUI[] monsterUIs;
private bool heroTurn;
private bool heroPassedTurn;
private EnergyPool heroEnergy;
private EnergyPool monsterEnergy;
void Awake() {
this.heroCardDatabase = new HeroCardDatabase();
this.monsterCardDatabase = new MonsterCardDatabase();
this.heroEnergy = new EnergyPool();
this.heroEnergyUI.Init(this.heroEnergy);
this.heroEnergy.OnEnergyChanged += this.heroEnergyUI.NotifyEnergyChanged;
this.monsterEnergy = new EnergyPool();
this.monsterEnergyUI.Init(this.monsterEnergy);
this.monsterEnergy.OnEnergyChanged += this.monsterEnergyUI.NotifyEnergyChanged;
heroes = new BattleHero[3];
heroSprites = new GameObject[3];
heroUIs = new HeroInfoUI[3];
for (int i = 0; i < 3; i++) {
HeroId id = heroIds[i];
heroes[i] = new BattleHero(id);
heroes[i].Init();
heroSprites[i] = HeroDatabase.createHeroSprite(id);
heroSprites[i].name = "Hero" + i + "_Sprite";
heroSprites[i].transform.position = new Vector3(-1 + -3 + 3 * i, -1.8f, 0);
BattleParticipantSprite bps = heroSprites[i].AddComponent<BattleParticipantSprite>();
bps.Init(battlePlayerControls, heroes[i]);
bps.OnSpriteClicked += battlePlayerControls.NotifyParticipantSpriteClicked;
heroUIs[i] = (HeroInfoUI) Instantiate(heroUIPrefab.gameObject, uiCanvas.transform).GetComponent(typeof(HeroInfoUI));
heroUIs[i].Init(heroSprites[i], heroes[i]);
}
monsters = new BattleMonster[3];
monsterSprites = new GameObject[3];
monsterUIs = new HeroInfoUI[3];
for (int i = 0; i < 3; i++) {
MonsterId id = monsterIds[i];
monsters[i] = MonsterDatabase.createMonsterScript(id);
monsters[i].Init();
monsterSprites[i] = MonsterDatabase.createMonsterSprite(id);
monsterSprites[i].name = "Monster" + i + "_Sprite";
monsterSprites[i].transform.position = new Vector3(+1 + -3 + 3 * i, 1.2f, 0);
BattleParticipantSprite bps = monsterSprites[i].AddComponent<BattleParticipantSprite>();
bps.Init(battlePlayerControls, monsters[i]);
bps.OnSpriteClicked += battlePlayerControls.NotifyParticipantSpriteClicked;
monsterUIs[i] = (HeroInfoUI) Instantiate(heroUIPrefab.gameObject, uiCanvas.transform).GetComponent(typeof(HeroInfoUI));
monsterUIs[i].Init(monsterSprites[i], monsters[i]);
}
BattleParticipant[] cardOwners = new BattleParticipant[this.cardIds.Length];
for (int i = 0; i < this.cardIds.Length; i++) {
cardOwners[i] = FindHeroByClass(HeroCardDatabase.GetHeroIdOfCard(this.cardIds[i]));
}
this.heroDeck = new BattleDeck<HeroCard>(
this.heroCardDatabase, this.cardIds.Cast<int>().ToArray(), cardOwners);
BattleParticipant[] mCardOwners = new BattleParticipant[this.mCardIds.Length];
if (this.mCardIds.Length != this.mCardOwnerIndexes.Length) {
throw new System.ArgumentException(
"mCardIds and mCardOwnerIndexes need to have the same length");
}
for (int i = 0; i < this.mCardIds.Length; i++) {
mCardOwners[i] = this.monsters[this.mCardOwnerIndexes[i]];
}
this.monsterDeck = new BattleDeck<MonsterCard>(
this.monsterCardDatabase, this.mCardIds.Cast<int>().ToArray(), mCardOwners);
this.playArea = new PlayArea();
this.battlePlayerControls.Init(this.heroDeck, this.monsterDeck, this.playArea);
this.battlePlayerControls.OnCardUsed += this.NotifyHeroCardUsed;
this.battlePlayerControls.OnTurnPassed += this.NotifyHeroTurnPassed;
this.heroDeck.OnHandPileChanged += this.battlePlayerControls.NotifyHeroHandPileChanged;
this.monsterDeck.OnHandPileChanged += this.battlePlayerControls.NotifyMonsterHandPileChanged;
}
private BattleParticipant FindHeroByClass(HeroId heroId) {
foreach (BattleHero battleHero in this.heroes) {
if (battleHero.GetId() == heroId) return battleHero;
}
return null;
}
// Start is called before the first frame update
void Start()
{
StartCoroutine(RunBattleRounds());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator RunBattleRounds() {
while (true) {
yield return RunBattleRound();
}
}
private IEnumerator RunBattleRound() {
this.heroTurn = true;
this.damageTriggers.RemoveAll(t => t.Expiration == TriggerExpiration.StartOfNextHeroTurn);
heroEnergy.Replenish();
monsterEnergy.Replenish();
this.heroDeck.DrawHand();
this.monsterDeck.DrawHand();
yield return RunHeroTurn();
this.heroTurn = false;
yield return RunMonsterTurn();
}
private IEnumerator RunHeroTurn() {
// Start of hero turn
// TODO: Play emotions from hand
if (this.heroEnergy.MaxEnergy < 9) {
this.heroEnergy.DropOneEmotion();
}
foreach (BattleHero hero in this.heroes) {
hero.ResetBlock();
}
// Heroes act until they pass
while (!this.heroPassedTurn) {
yield return null;
}
this.heroPassedTurn = false;
// End of hero turn
this.heroDeck.DiscardHand();
}
private IEnumerator RunMonsterTurn() {
yield return new WaitForSeconds(0.5f);
// Start of monster turn
if (this.monsterEnergy.MaxEnergy < 9) {
this.monsterEnergy.DropOneEmotion();
}
foreach (BattleMonster monster in this.monsters) {
monster.ResetBlock();
}
// Monsters act until they pass
List<MonsterCard> monsterHand = this.monsterDeck.GetHandPile();
for (int i = 0; i < monsterHand.Count; i++) {
MonsterCard monsterCard = monsterHand[i];
if (CanUseMonsterCard(monsterCard)) {
yield return UseMonsterCard(monsterCard);
}
}
// End of monster turn
this.monsterDeck.DiscardHand();
}
public void NotifyHeroCardUsed(HeroCard card) {
if (!CanUseHeroCard(card)) {
throw new System.InvalidOperationException("Cannot use card " + card.GetDisplayName());
}
StartCoroutine(UseHeroCard(card));
}
public IEnumerator UseHeroCard(HeroCard card) {
yield return card.Use(this, battlePlayerControls);
this.heroEnergy.PayForCard(card);
this.heroDeck.RemoveCardFromHand(card);
if (card.IsPermanent()) {
this.playArea.AddPlayedHeroPermanent(card);
} else {
this.heroDeck.MoveCardToDiscard(card);
}
}
public IEnumerator UseMonsterCard(MonsterCard card) {
yield return card.Use(this, this);
this.monsterEnergy.PayForCard(card);
this.monsterDeck.RemoveCardFromHand(card);
if (card.IsPermanent()) {
this.playArea.AddPlayedMonsterPermanent(card);
} else {
this.monsterDeck.MoveCardToDiscard(card);
}
yield return new WaitForSeconds(0.5f);
}
public void NotifyHeroTurnPassed() {
if (this.heroTurn) {
this.heroPassedTurn = true;
}
}
public bool CanUseHeroCard(HeroCard card) {
return this.heroTurn && this.heroEnergy.CanPayForCard(card);
}
public bool CanUseMonsterCard(MonsterCard card) {
return !this.heroTurn && this.monsterEnergy.CanPayForCard(card);
}
public BattleDeck<HeroCard> GetHeroDeck() {
return this.heroDeck;
}
public PlayArea GetPlayArea() {
return this.playArea;
}
private void EnqueueActivatedTrigger(Trigger trigger) {
this.activatedTriggers.Add(trigger);
}
private void RunActivatedTriggers() {
foreach (Trigger t in this.activatedTriggers) {
t.Run(this);
}
this.activatedTriggers.Clear();
// Cleanup expired triggers
this.damageTriggers.RemoveAll(t => t.Expired);
}
public EnergyPool HeroEnergy {get => this.heroEnergy;}
public EnergyPool MonsterEnergy {get => this.monsterEnergy;}
// IBattleEffectApplier
public void Apply(IBattleEffect effect) {
effect.Apply(this);
}
// IBattleEffectResolver
public void IncreaseHeroMaxEnergy(IncreaseMaxEnergyEffect effect) {
this.heroEnergy.MaxEnergy += effect.Amount;
}
public void IncreaseMaxHP(IncreaseMaxHPEffect effect) {
effect.Target.IncreaseMaxHP(effect.Amount);
}
public void AcquireHeroCards(AcquireCardsEffect effect) {
BattleHero hero = (BattleHero) effect.Participant;
foreach (CardId cardId in effect.CardIds) {
HeroCard acquiredCard = this.heroCardDatabase.CreateCard((int) cardId, hero);
switch (effect.Zone) {
case BattleZone.DrawPile:
this.heroDeck.PutCardOnTopOfDrawPile(acquiredCard);
break;
default:
throw new System.NotImplementedException();
}
}
if (effect.Zone == BattleZone.DrawPile) {
this.heroDeck.ShuffleDrawPile();
}
}
private List<Trigger> activatedTriggers = new List<Trigger>();
private List<DamageTrigger> damageTriggers = new List<DamageTrigger>();
public int DealDamage(DealDamageEffect effect) {
int damageDealt = effect.Target.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) {
int healedAmount = effect.Target.Heal(effect.Amount);
return healedAmount;
}
public void AddBlock(BlockEffect effect) {
effect.Target.AddBlock(effect.Amount);
}
public void ArmDamageTrigger(DamageTrigger damageTrigger) {
this.damageTriggers.Add(damageTrigger);
}
// MonsterViewpoint
public BattleHero[] Heroes {get => this.heroes;}
}