init
This commit is contained in:
610
Assets/TcgEngine/Scripts/GameLogic/Game.cs
Normal file
610
Assets/TcgEngine/Scripts/GameLogic/Game.cs
Normal file
@@ -0,0 +1,610 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TcgEngine
|
||||
{
|
||||
//Contains all gameplay state data that is sync across network
|
||||
|
||||
[System.Serializable]
|
||||
public class Game
|
||||
{
|
||||
public string game_uid;
|
||||
public GameSettings settings;
|
||||
|
||||
//Game state
|
||||
public int first_player = 0;
|
||||
public int current_player = 0;
|
||||
public int turn_count = 0;
|
||||
public float turn_timer = 0f;
|
||||
|
||||
public GameState state = GameState.Connecting;
|
||||
public GamePhase phase = GamePhase.None;
|
||||
|
||||
//Players
|
||||
public Player[] players;
|
||||
|
||||
//Selector
|
||||
public SelectorType selector = SelectorType.None;
|
||||
public int selector_player_id = 0;
|
||||
public string selector_ability_id;
|
||||
public string selector_caster_uid;
|
||||
|
||||
//Other reference values
|
||||
public string last_played;
|
||||
public string last_target;
|
||||
public string last_destroyed;
|
||||
public string last_summoned;
|
||||
public string ability_triggerer;
|
||||
public int rolled_value;
|
||||
public int selected_value;
|
||||
|
||||
//Other reference arrays
|
||||
public HashSet<string> ability_played = new HashSet<string>();
|
||||
public HashSet<string> cards_attacked = new HashSet<string>();
|
||||
|
||||
public Game() { }
|
||||
|
||||
public Game(string uid, int nb_players)
|
||||
{
|
||||
this.game_uid = uid;
|
||||
players = new Player[nb_players];
|
||||
for (int i = 0; i < nb_players; i++)
|
||||
players[i] = new Player(i);
|
||||
settings = GameSettings.Default;
|
||||
}
|
||||
|
||||
public virtual bool AreAllPlayersReady()
|
||||
{
|
||||
int ready = 0;
|
||||
foreach (Player player in players)
|
||||
{
|
||||
if (player.IsReady())
|
||||
ready++;
|
||||
}
|
||||
return ready >= settings.nb_players;
|
||||
}
|
||||
|
||||
public virtual bool AreAllPlayersConnected()
|
||||
{
|
||||
int ready = 0;
|
||||
foreach (Player player in players)
|
||||
{
|
||||
if (player.IsConnected())
|
||||
ready++;
|
||||
}
|
||||
return ready >= settings.nb_players;
|
||||
}
|
||||
|
||||
//Check if its player's turn
|
||||
public virtual bool IsPlayerTurn(Player player)
|
||||
{
|
||||
return IsPlayerActionTurn(player) || IsPlayerSelectorTurn(player);
|
||||
}
|
||||
|
||||
public virtual bool IsPlayerActionTurn(Player player)
|
||||
{
|
||||
return player != null && current_player == player.player_id
|
||||
&& state == GameState.Play && phase == GamePhase.Main && selector == SelectorType.None;
|
||||
}
|
||||
|
||||
public virtual bool IsPlayerSelectorTurn(Player player)
|
||||
{
|
||||
return player != null && selector_player_id == player.player_id
|
||||
&& state == GameState.Play && phase == GamePhase.Main && selector != SelectorType.None;
|
||||
}
|
||||
|
||||
public virtual bool IsPlayerMulliganTurn(Player player)
|
||||
{
|
||||
return phase == GamePhase.Mulligan && !player.ready;
|
||||
}
|
||||
|
||||
//Check if a card is allowed to be played on slot
|
||||
public virtual bool CanPlayCard(Card card, Slot slot, bool skip_cost = false)
|
||||
{
|
||||
if (card == null)
|
||||
return false;
|
||||
|
||||
Player player = GetPlayer(card.player_id);
|
||||
if (!skip_cost && !player.CanPayMana(card))
|
||||
return false; //Cant pay mana
|
||||
if (!player.HasCard(player.cards_hand, card))
|
||||
return false; // Card not in hand
|
||||
if (player.is_ai && card.CardData.IsDynamicManaCost() && player.mana == 0)
|
||||
return false; // AI cant play X-cost card at 0 cost
|
||||
|
||||
if (card.CardData.IsBoardCard())
|
||||
{
|
||||
if (!slot.IsValid() || IsCardOnSlot(slot))
|
||||
return false; //Slot already occupied
|
||||
if (Slot.GetP(card.player_id) != slot.p)
|
||||
return false; //Cant play on opponent side
|
||||
return true;
|
||||
}
|
||||
if (card.CardData.IsEquipment())
|
||||
{
|
||||
if (!slot.IsValid())
|
||||
return false;
|
||||
|
||||
Card target = GetSlotCard(slot);
|
||||
if (target == null || target.CardData.type != CardType.Character || target.player_id != card.player_id)
|
||||
return false; //Target must be an allied character
|
||||
|
||||
return true;
|
||||
}
|
||||
if (card.CardData.IsRequireTargetSpell())
|
||||
{
|
||||
return IsPlayTargetValid(card, slot); //Check play target on slot
|
||||
}
|
||||
if (card.CardData.type == CardType.Spell)
|
||||
{
|
||||
return CanAnyPlayAbilityTrigger(card); //Check if spell will have abilities
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Check if a card is allowed to move to slot
|
||||
public virtual bool CanMoveCard(Card card, Slot slot, bool skip_cost = false)
|
||||
{
|
||||
if (card == null || !slot.IsValid())
|
||||
return false;
|
||||
|
||||
if (!IsOnBoard(card))
|
||||
return false; //Only cards in play can move
|
||||
|
||||
if (!card.CanMove(skip_cost))
|
||||
return false; //Card cant move
|
||||
|
||||
if (Slot.GetP(card.player_id) != slot.p)
|
||||
return false; //Card played wrong side
|
||||
|
||||
if (card.slot == slot)
|
||||
return false; //Cant move to same slot
|
||||
|
||||
Card slot_card = GetSlotCard(slot);
|
||||
if (slot_card != null)
|
||||
return false; //Already a card there
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Check if a card is allowed to attack a player
|
||||
public virtual bool CanAttackTarget(Card attacker, Player target, bool skip_cost = false)
|
||||
{
|
||||
if(attacker == null || target == null)
|
||||
return false;
|
||||
|
||||
if (!attacker.CanAttack(skip_cost))
|
||||
return false; //Card cant attack
|
||||
|
||||
if (attacker.player_id == target.player_id)
|
||||
return false; //Cant attack same player
|
||||
|
||||
if (!IsOnBoard(attacker) || !attacker.CardData.IsCharacter())
|
||||
return false; //Cards not on board
|
||||
|
||||
if (target.HasStatus(StatusType.Protected) && !attacker.HasStatus(StatusType.Flying))
|
||||
return false; //Protected by taunt
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Check if a card is allowed to attack another one
|
||||
public virtual bool CanAttackTarget(Card attacker, Card target, bool skip_cost = false)
|
||||
{
|
||||
if (attacker == null || target == null)
|
||||
return false;
|
||||
|
||||
if (!attacker.CanAttack(skip_cost))
|
||||
return false; //Card cant attack
|
||||
|
||||
if (attacker.player_id == target.player_id)
|
||||
return false; //Cant attack same player
|
||||
|
||||
if (!IsOnBoard(attacker) || !IsOnBoard(target))
|
||||
return false; //Cards not on board
|
||||
|
||||
if (!attacker.CardData.IsCharacter() || !target.CardData.IsBoardCard())
|
||||
return false; //Only character can attack
|
||||
|
||||
if (target.HasStatus(StatusType.Stealth))
|
||||
return false; //Stealth cant be attacked
|
||||
|
||||
if (target.HasStatus(StatusType.Protected) && !attacker.HasStatus(StatusType.Flying))
|
||||
return false; //Protected by adjacent card
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanCastAbility(Card card, AbilityData ability)
|
||||
{
|
||||
if (ability == null || card == null || !card.CanDoActivatedAbilities())
|
||||
return false; //This card cant cast
|
||||
|
||||
if (ability.trigger != AbilityTrigger.Activate)
|
||||
return false; //Not an activated ability
|
||||
|
||||
Player player = GetPlayer(card.player_id);
|
||||
if (!player.CanPayAbility(card, ability))
|
||||
return false; //Cant pay for ability
|
||||
|
||||
if (!ability.AreTriggerConditionsMet(this, card))
|
||||
return false; //Conditions not met
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//For choice selector
|
||||
public virtual bool CanSelectAbility(Card card, AbilityData ability)
|
||||
{
|
||||
if (ability == null || card == null || !card.CanDoAbilities())
|
||||
return false; //This card cant cast
|
||||
|
||||
Player player = GetPlayer(card.player_id);
|
||||
if (!player.CanPayAbility(card, ability))
|
||||
return false; //Cant pay for ability
|
||||
|
||||
if (!ability.AreTriggerConditionsMet(this, card))
|
||||
return false; //Conditions not met
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanAnyPlayAbilityTrigger(Card card)
|
||||
{
|
||||
if (card == null)
|
||||
return false;
|
||||
if (card.CardData.IsDynamicManaCost())
|
||||
return true; //Cost not decided so condition could be false
|
||||
|
||||
foreach (AbilityData ability in card.GetAbilities())
|
||||
{
|
||||
if (ability.trigger == AbilityTrigger.OnPlay && ability.AreTriggerConditionsMet(this, card))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check if Player play target is valid, play target is the target when a spell requires to drag directly onto another card
|
||||
public virtual bool IsPlayTargetValid(Card caster, Player target)
|
||||
{
|
||||
if (caster == null || target == null)
|
||||
return false;
|
||||
|
||||
foreach (AbilityData ability in caster.GetAbilities())
|
||||
{
|
||||
if (ability && ability.trigger == AbilityTrigger.OnPlay && ability.target == AbilityTarget.PlayTarget)
|
||||
{
|
||||
if (!ability.CanTarget(this, caster, target))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Check if Card play target is valid, play target is the target when a spell requires to drag directly onto another card
|
||||
public virtual bool IsPlayTargetValid(Card caster, Card target)
|
||||
{
|
||||
if (caster == null || target == null)
|
||||
return false;
|
||||
|
||||
foreach (AbilityData ability in caster.GetAbilities())
|
||||
{
|
||||
if (ability && ability.trigger == AbilityTrigger.OnPlay && ability.target == AbilityTarget.PlayTarget)
|
||||
{
|
||||
if (!ability.CanTarget(this, caster, target))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Check if Slot play target is valid, play target is the target when a spell requires to drag directly onto another card
|
||||
public virtual bool IsPlayTargetValid(Card caster, Slot target)
|
||||
{
|
||||
if (caster == null)
|
||||
return false;
|
||||
|
||||
if (target.IsPlayerSlot())
|
||||
return IsPlayTargetValid(caster, GetPlayer(target.p)); //Slot 0,0, means we are targeting a player
|
||||
|
||||
Card slot_card = GetSlotCard(target);
|
||||
if (slot_card != null)
|
||||
return IsPlayTargetValid(caster, slot_card); //Slot has card, check play target on that card
|
||||
|
||||
foreach (AbilityData ability in caster.GetAbilities())
|
||||
{
|
||||
if (ability && ability.trigger == AbilityTrigger.OnPlay && ability.target == AbilityTarget.PlayTarget)
|
||||
{
|
||||
if (!ability.CanTarget(this, caster, target))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Player GetPlayer(int id)
|
||||
{
|
||||
if (id >= 0 && id < players.Length)
|
||||
return players[id];
|
||||
return null;
|
||||
}
|
||||
|
||||
public Player GetActivePlayer()
|
||||
{
|
||||
return GetPlayer(current_player);
|
||||
}
|
||||
|
||||
public Player GetOpponentPlayer(int id)
|
||||
{
|
||||
int oid = id == 0 ? 1 : 0;
|
||||
return GetPlayer(oid);
|
||||
}
|
||||
|
||||
public Card GetCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
Card acard = player.GetCard(card_uid);
|
||||
if (acard != null)
|
||||
return acard;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetBoardCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_board)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetEquipCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_equip)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetHandCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_hand)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetDeckCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_deck)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetDiscardCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_discard)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetSecretCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_secret)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetTempCard(string card_uid)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_temp)
|
||||
{
|
||||
if (card != null && card.uid == card_uid)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetSlotCard(Slot slot)
|
||||
{
|
||||
foreach (Player player in players)
|
||||
{
|
||||
foreach (Card card in player.cards_board)
|
||||
{
|
||||
if (card != null && card.slot == slot)
|
||||
return card;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual Player GetRandomPlayer(System.Random rand)
|
||||
{
|
||||
Player player = GetPlayer(rand.NextDouble() < 0.5 ? 1 : 0);
|
||||
return player;
|
||||
}
|
||||
|
||||
public virtual Card GetRandomBoardCard(System.Random rand)
|
||||
{
|
||||
Player player = GetRandomPlayer(rand);
|
||||
return player.GetRandomCard(player.cards_board, rand);
|
||||
}
|
||||
|
||||
public virtual Slot GetRandomSlot(System.Random rand)
|
||||
{
|
||||
Player player = GetRandomPlayer(rand);
|
||||
return player.GetRandomSlot(rand);
|
||||
}
|
||||
|
||||
public bool IsInHand(Card card)
|
||||
{
|
||||
return card != null && GetHandCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsOnBoard(Card card)
|
||||
{
|
||||
return card != null && GetBoardCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsEquipped(Card card)
|
||||
{
|
||||
return card != null && GetEquipCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsInDeck(Card card)
|
||||
{
|
||||
return card != null && GetDeckCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsInDiscard(Card card)
|
||||
{
|
||||
return card != null && GetDiscardCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsInSecret(Card card)
|
||||
{
|
||||
return card != null && GetSecretCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsInTemp(Card card)
|
||||
{
|
||||
return card != null && GetTempCard(card.uid) != null;
|
||||
}
|
||||
|
||||
public bool IsCardOnSlot(Slot slot)
|
||||
{
|
||||
return GetSlotCard(slot) != null;
|
||||
}
|
||||
|
||||
public bool HasStarted()
|
||||
{
|
||||
return state != GameState.Connecting;
|
||||
}
|
||||
|
||||
public bool HasEnded()
|
||||
{
|
||||
return state == GameState.GameEnded;
|
||||
}
|
||||
|
||||
//Same as clone, but also instantiates the variable (much slower)
|
||||
public static Game CloneNew(Game source)
|
||||
{
|
||||
Game game = new Game();
|
||||
Clone(source, game);
|
||||
return game;
|
||||
}
|
||||
|
||||
//Clone all variables into another var, used mostly by the AI when building a prediction tree
|
||||
public static void Clone(Game source, Game dest)
|
||||
{
|
||||
dest.game_uid = source.game_uid;
|
||||
dest.settings = source.settings;
|
||||
|
||||
dest.first_player = source.first_player;
|
||||
dest.current_player = source.current_player;
|
||||
dest.turn_count = source.turn_count;
|
||||
dest.turn_timer = source.turn_timer;
|
||||
dest.state = source.state;
|
||||
dest.phase = source.phase;
|
||||
|
||||
if (dest.players == null)
|
||||
{
|
||||
dest.players = new Player[source.players.Length];
|
||||
for(int i=0; i< source.players.Length; i++)
|
||||
dest.players[i] = new Player(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < source.players.Length; i++)
|
||||
Player.Clone(source.players[i], dest.players[i]);
|
||||
|
||||
dest.selector = source.selector;
|
||||
dest.selector_player_id = source.selector_player_id;
|
||||
dest.selector_caster_uid = source.selector_caster_uid;
|
||||
dest.selector_ability_id = source.selector_ability_id;
|
||||
|
||||
dest.last_destroyed = source.last_destroyed;
|
||||
dest.last_played = source.last_played;
|
||||
dest.last_target = source.last_target;
|
||||
dest.last_summoned = source.last_summoned;
|
||||
dest.ability_triggerer = source.ability_triggerer;
|
||||
dest.rolled_value = source.rolled_value;
|
||||
dest.selected_value = source.selected_value;
|
||||
|
||||
CloneHash(source.ability_played, dest.ability_played);
|
||||
CloneHash(source.cards_attacked, dest.cards_attacked);
|
||||
}
|
||||
|
||||
public static void CloneHash(HashSet<string> source, HashSet<string> dest)
|
||||
{
|
||||
dest.Clear();
|
||||
foreach (string str in source)
|
||||
dest.Add(str);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public enum GameState
|
||||
{
|
||||
Connecting = 0, //Players are not connected
|
||||
Play = 20, //Game is being played
|
||||
GameEnded = 99,
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public enum GamePhase
|
||||
{
|
||||
None = 0,
|
||||
Mulligan = 5,
|
||||
StartTurn = 10, //Start of turn resolution
|
||||
Main = 20, //Main play phase
|
||||
EndTurn = 30, //End of turn resolutions
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public enum SelectorType
|
||||
{
|
||||
None = 0,
|
||||
SelectTarget = 10,
|
||||
SelectorCard = 20,
|
||||
SelectorChoice = 30,
|
||||
SelectorCost = 40,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user