init
This commit is contained in:
745
Assets/TcgEngine/Scripts/GameLogic/Card.cs
Normal file
745
Assets/TcgEngine/Scripts/GameLogic/Card.cs
Normal file
@@ -0,0 +1,745 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TcgEngine
|
||||
{
|
||||
//Represent the current state of a card during the game (data only)
|
||||
|
||||
[System.Serializable]
|
||||
public class Card
|
||||
{
|
||||
public string card_id;
|
||||
public string uid;
|
||||
public int player_id;
|
||||
public string variant_id;
|
||||
|
||||
public Slot slot;
|
||||
public bool exhausted;
|
||||
public int damage = 0;
|
||||
|
||||
public int mana = 0;
|
||||
public int attack = 0;
|
||||
public int hp = 0;
|
||||
|
||||
public int mana_ongoing = 0;
|
||||
public int attack_ongoing = 0;
|
||||
public int hp_ongoing = 0;
|
||||
|
||||
public string equipped_uid = null;
|
||||
|
||||
public List<CardTrait> traits = new List<CardTrait>();
|
||||
public List<CardTrait> ongoing_traits = new List<CardTrait>();
|
||||
|
||||
public List<CardStatus> status = new List<CardStatus>();
|
||||
public List<CardStatus> ongoing_status = new List<CardStatus>();
|
||||
|
||||
public List<string> abilities = new List<string>();
|
||||
public List<string> abilities_ongoing = new List<string>();
|
||||
|
||||
[System.NonSerialized] private int hash = 0;
|
||||
[System.NonSerialized] private CardData data = null;
|
||||
[System.NonSerialized] private VariantData vdata = null;
|
||||
[System.NonSerialized] private List<AbilityData> abilities_data = null;
|
||||
|
||||
public Card(string card_id, string uid, int player_id) { this.card_id = card_id; this.uid = uid; this.player_id = player_id; }
|
||||
|
||||
public virtual void Refresh() { exhausted = false; }
|
||||
public virtual void ClearOngoing() { ongoing_status.Clear(); ongoing_traits.Clear(); ClearOngoingAbility(); attack_ongoing = 0; hp_ongoing = 0; mana_ongoing = 0; }
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
ClearOngoing(); Refresh(); damage = 0; status.Clear();
|
||||
SetCard(CardData, VariantData); //Reset to initial stats
|
||||
equipped_uid = null;
|
||||
}
|
||||
|
||||
public virtual int GetAttack() { return Mathf.Max(attack + attack_ongoing, 0); }
|
||||
public virtual int GetHP() { return Mathf.Max(hp + hp_ongoing - damage, 0); }
|
||||
public virtual int GetHP(int offset) { return Mathf.Max(hp + hp_ongoing - damage + offset, 0); }
|
||||
public virtual int GetHPMax() { return Mathf.Max(hp + hp_ongoing, 0); }
|
||||
public virtual int GetMana() { return Mathf.Max(mana + mana_ongoing, 0); }
|
||||
|
||||
public virtual void SetCard(CardData icard, VariantData cvariant)
|
||||
{
|
||||
data = icard;
|
||||
card_id = icard.id;
|
||||
variant_id = cvariant.id;
|
||||
attack = icard.attack;
|
||||
hp = icard.hp;
|
||||
mana = icard.mana;
|
||||
SetTraits(icard);
|
||||
SetAbilities(icard);
|
||||
}
|
||||
|
||||
public void SetTraits(CardData icard)
|
||||
{
|
||||
traits.Clear();
|
||||
foreach (TraitData trait in icard.traits)
|
||||
SetTrait(trait.id, 0);
|
||||
if (icard.stats != null)
|
||||
{
|
||||
foreach (TraitStat stat in icard.stats)
|
||||
SetTrait(stat.trait.id, stat.value);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAbilities(CardData icard)
|
||||
{
|
||||
abilities.Clear();
|
||||
abilities_ongoing.Clear();
|
||||
if (abilities_data != null)
|
||||
abilities_data.Clear();
|
||||
foreach (AbilityData ability in icard.abilities)
|
||||
AddAbility(ability);
|
||||
}
|
||||
|
||||
//------ Custom Traits/Stats ---------
|
||||
|
||||
public void SetTrait(string id, int value)
|
||||
{
|
||||
CardTrait trait = GetTrait(id);
|
||||
if (trait != null)
|
||||
{
|
||||
trait.value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
trait = new CardTrait(id, value);
|
||||
traits.Add(trait);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTrait(string id, int value)
|
||||
{
|
||||
CardTrait trait = GetTrait(id);
|
||||
if (trait != null)
|
||||
trait.value += value;
|
||||
else
|
||||
SetTrait(id, value);
|
||||
}
|
||||
|
||||
public void AddOngoingTrait(string id, int value)
|
||||
{
|
||||
CardTrait trait = GetOngoingTrait(id);
|
||||
if (trait != null)
|
||||
{
|
||||
trait.value += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
trait = new CardTrait(id, value);
|
||||
ongoing_traits.Add(trait);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTrait(string id)
|
||||
{
|
||||
for (int i = traits.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (traits[i].id == id)
|
||||
traits.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public CardTrait GetTrait(string id)
|
||||
{
|
||||
foreach (CardTrait trait in traits)
|
||||
{
|
||||
if (trait.id == id)
|
||||
return trait;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CardTrait GetOngoingTrait(string id)
|
||||
{
|
||||
foreach (CardTrait trait in ongoing_traits)
|
||||
{
|
||||
if (trait.id == id)
|
||||
return trait;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int GetTraitValue(TraitData trait)
|
||||
{
|
||||
if (trait != null)
|
||||
return GetTraitValue(trait.id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual int GetTraitValue(string id)
|
||||
{
|
||||
int val = 0;
|
||||
CardTrait stat1 = GetTrait(id);
|
||||
CardTrait stat2 = GetOngoingTrait(id);
|
||||
if (stat1 != null)
|
||||
val += stat1.value;
|
||||
if (stat2 != null)
|
||||
val += stat2.value;
|
||||
return val;
|
||||
}
|
||||
|
||||
public bool HasTrait(TraitData trait)
|
||||
{
|
||||
if (trait != null)
|
||||
return HasTrait(trait.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasTrait(string id)
|
||||
{
|
||||
return GetTrait(id) != null || GetOngoingTrait(id) != null;
|
||||
}
|
||||
|
||||
public List<CardTrait> GetAllTraits()
|
||||
{
|
||||
List<CardTrait> all_traits = new List<CardTrait>();
|
||||
all_traits.AddRange(traits);
|
||||
all_traits.AddRange(ongoing_traits);
|
||||
return all_traits;
|
||||
}
|
||||
|
||||
//Alternate names since traits/stats are stored in same var
|
||||
public void SetStat(string id, int value) => SetTrait(id, value);
|
||||
public void AddStat(string id, int value) => AddTrait(id, value);
|
||||
public void AddOngoingStat(string id, int value) => AddOngoingTrait(id, value);
|
||||
public void RemoveStat(string id) => RemoveTrait(id);
|
||||
public int GetStatValue(TraitData trait) => GetTraitValue(trait);
|
||||
public int GetStatValue(string id) => GetTraitValue(id);
|
||||
public bool HasStat(TraitData trait) => HasTrait(trait);
|
||||
public bool HasStat(string id) => HasTrait(id);
|
||||
public List<CardTrait> GetAllStats() => GetAllTraits();
|
||||
|
||||
//------ Status Effects ---------
|
||||
|
||||
public void AddStatus(StatusData status, int value, int duration)
|
||||
{
|
||||
if (status != null)
|
||||
AddStatus(status.effect, value, duration);
|
||||
}
|
||||
|
||||
public void AddOngoingStatus(StatusData status, int value)
|
||||
{
|
||||
if (status != null)
|
||||
AddOngoingStatus(status.effect, value);
|
||||
}
|
||||
|
||||
public void AddStatus(StatusType type, int value, int duration)
|
||||
{
|
||||
if (type != StatusType.None)
|
||||
{
|
||||
CardStatus status = GetStatus(type);
|
||||
if (status == null)
|
||||
{
|
||||
status = new CardStatus(type, value, duration);
|
||||
this.status.Add(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
status.value += value;
|
||||
status.duration = Mathf.Max(status.duration, duration);
|
||||
status.permanent = status.permanent || duration == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddOngoingStatus(StatusType type, int value)
|
||||
{
|
||||
if (type != StatusType.None)
|
||||
{
|
||||
CardStatus status = GetOngoingStatus(type);
|
||||
if (status == null)
|
||||
{
|
||||
status = new CardStatus(type, value, 0);
|
||||
ongoing_status.Add(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
status.value += value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveStatus(StatusType type)
|
||||
{
|
||||
for (int i = status.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (status[i].type == type)
|
||||
status.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CardStatus> GetAllStatus()
|
||||
{
|
||||
List<CardStatus> all_status = new List<CardStatus>();
|
||||
all_status.AddRange(status);
|
||||
all_status.AddRange(ongoing_status);
|
||||
return all_status;
|
||||
}
|
||||
|
||||
public bool HasStatus(StatusType type)
|
||||
{
|
||||
return GetStatus(type) != null || GetOngoingStatus(type) != null;
|
||||
}
|
||||
|
||||
public CardStatus GetStatus(StatusType type)
|
||||
{
|
||||
foreach (CardStatus status in status)
|
||||
{
|
||||
if (status.type == type)
|
||||
return status;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CardStatus GetOngoingStatus(StatusType type)
|
||||
{
|
||||
foreach (CardStatus status in ongoing_status)
|
||||
{
|
||||
if (status.type == type)
|
||||
return status;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual int GetStatusValue(StatusType type)
|
||||
{
|
||||
CardStatus status1 = GetStatus(type);
|
||||
CardStatus status2 = GetOngoingStatus(type);
|
||||
int v1 = status1 != null ? status1.value : 0;
|
||||
int v2 = status2 != null ? status2.value : 0;
|
||||
return v1 + v2;
|
||||
}
|
||||
|
||||
public virtual void ReduceStatusDurations()
|
||||
{
|
||||
for (int i = status.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!status[i].permanent)
|
||||
{
|
||||
status[i].duration -= 1;
|
||||
if (status[i].duration <= 0)
|
||||
status.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----- Abilities ------------
|
||||
|
||||
public void AddAbility(AbilityData ability)
|
||||
{
|
||||
abilities.Add(ability.id);
|
||||
if (abilities_data != null)
|
||||
abilities_data.Add(ability);
|
||||
}
|
||||
|
||||
public void RemoveAbility(AbilityData ability)
|
||||
{
|
||||
abilities.Remove(ability.id);
|
||||
if (abilities_data != null)
|
||||
abilities_data.Remove(ability);
|
||||
}
|
||||
|
||||
public void AddOngoingAbility(AbilityData ability)
|
||||
{
|
||||
if (!abilities_ongoing.Contains(ability.id) && !abilities.Contains(ability.id))
|
||||
{
|
||||
abilities_ongoing.Add(ability.id);
|
||||
if (abilities_data != null)
|
||||
abilities_data.Add(ability);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearOngoingAbility()
|
||||
{
|
||||
if (abilities_data != null)
|
||||
{
|
||||
for (int i = abilities_data.Count - 1; i >= 0; i--)
|
||||
{
|
||||
AbilityData ability = abilities_data[i];
|
||||
if (abilities_ongoing.Contains(ability.id))
|
||||
abilities_data.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
abilities_ongoing.Clear();
|
||||
}
|
||||
|
||||
public AbilityData GetAbility(AbilityTrigger trigger)
|
||||
{
|
||||
foreach (AbilityData iability in GetAbilities())
|
||||
{
|
||||
if (iability.trigger == trigger)
|
||||
return iability;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool HasAbility(AbilityData ability)
|
||||
{
|
||||
foreach (AbilityData iability in GetAbilities())
|
||||
{
|
||||
if (iability.id == ability.id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAbility(AbilityTrigger trigger)
|
||||
{
|
||||
AbilityData iability = GetAbility(trigger);
|
||||
if (iability != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAbility(AbilityTrigger trigger, AbilityTarget target)
|
||||
{
|
||||
foreach (AbilityData iability in GetAbilities())
|
||||
{
|
||||
if (iability.trigger == trigger && iability.target == target)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasActiveAbility(Game data, AbilityTrigger trigger)
|
||||
{
|
||||
AbilityData iability = GetAbility(trigger);
|
||||
if (iability != null && CanDoAbilities() && iability.AreTriggerConditionsMet(data, this))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AreAbilityConditionsMet(AbilityTrigger ability_trigger, Game data, Card caster, Card triggerer)
|
||||
{
|
||||
foreach (AbilityData ability in GetAbilities())
|
||||
{
|
||||
if (ability && ability.trigger == ability_trigger && ability.AreTriggerConditionsMet(data, caster, triggerer))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<AbilityData> GetAbilities()
|
||||
{
|
||||
//Load abilities data, important to do this here since this array will be null after being sent through networking (cant serialize it)
|
||||
if (abilities_data == null)
|
||||
{
|
||||
abilities_data = new List<AbilityData>(abilities.Count + abilities_ongoing.Count);
|
||||
for (int i = 0; i < abilities.Count; i++)
|
||||
abilities_data.Add(AbilityData.Get(abilities[i]));
|
||||
for (int i = 0; i < abilities_ongoing.Count; i++)
|
||||
abilities_data.Add(AbilityData.Get(abilities_ongoing[i]));
|
||||
}
|
||||
|
||||
//Return
|
||||
return abilities_data;
|
||||
}
|
||||
|
||||
//---- Action Check ---------
|
||||
|
||||
public virtual bool CanAttack(bool skip_cost = false)
|
||||
{
|
||||
if (HasStatus(StatusType.Paralysed))
|
||||
return false;
|
||||
if (!skip_cost && exhausted)
|
||||
return false; //no more action
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanMove(bool skip_cost = false)
|
||||
{
|
||||
//In demo we can move freely, since it has no effect
|
||||
//if (HasStatusEffect(StatusEffect.Paralysed))
|
||||
// return false;
|
||||
//if (!skip_cost && exhausted)
|
||||
// return false; //no more action
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanDoActivatedAbilities()
|
||||
{
|
||||
if (HasStatus(StatusType.Paralysed))
|
||||
return false;
|
||||
if (HasStatus(StatusType.Silenced))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanDoAbilities()
|
||||
{
|
||||
if (HasStatus(StatusType.Silenced))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanDoAnyAction()
|
||||
{
|
||||
return CanAttack() || CanMove() || CanDoActivatedAbilities();
|
||||
}
|
||||
|
||||
//----------------
|
||||
|
||||
public CardData CardData
|
||||
{
|
||||
get {
|
||||
if(data == null || data.id != card_id)
|
||||
data = CardData.Get(card_id); //Optimization, store for future use
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public VariantData VariantData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (vdata == null || vdata.id != variant_id)
|
||||
vdata = VariantData.Get(variant_id); //Optimization, store for future use
|
||||
return vdata;
|
||||
}
|
||||
}
|
||||
|
||||
public CardData Data => CardData; //Alternate name
|
||||
|
||||
public int Hash
|
||||
{
|
||||
get {
|
||||
if (hash == 0)
|
||||
hash = Mathf.Abs(uid.GetHashCode()); //Optimization, store for future use
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public static Card Create(CardData icard, VariantData ivariant, Player player)
|
||||
{
|
||||
return Create(icard, ivariant, player, GameTool.GenerateRandomID(11, 15));
|
||||
}
|
||||
|
||||
public static Card Create(CardData icard, VariantData ivariant, Player player, string uid)
|
||||
{
|
||||
Card card = new Card(icard.id, uid, player.player_id);
|
||||
card.SetCard(icard, ivariant);
|
||||
player.cards_all[card.uid] = card;
|
||||
return card;
|
||||
}
|
||||
|
||||
public static Card CloneNew(Card source)
|
||||
{
|
||||
Card card = new Card(source.card_id, source.uid, source.player_id);
|
||||
Clone(source, card);
|
||||
return card;
|
||||
}
|
||||
|
||||
//Clone all card variables into another var, used mostly by the AI when building a prediction tree
|
||||
public static void Clone(Card source, Card dest)
|
||||
{
|
||||
dest.card_id = source.card_id;
|
||||
dest.uid = source.uid;
|
||||
dest.player_id = source.player_id;
|
||||
|
||||
dest.variant_id = source.variant_id;
|
||||
dest.slot = source.slot;
|
||||
dest.exhausted = source.exhausted;
|
||||
dest.damage = source.damage;
|
||||
|
||||
dest.attack = source.attack;
|
||||
dest.hp = source.hp;
|
||||
dest.mana = source.mana;
|
||||
|
||||
dest.mana_ongoing = source.mana_ongoing;
|
||||
dest.attack_ongoing = source.attack_ongoing;
|
||||
dest.hp_ongoing = source.hp_ongoing;
|
||||
|
||||
dest.equipped_uid = source.equipped_uid;
|
||||
|
||||
CardTrait.CloneList(source.traits, dest.traits);
|
||||
CardTrait.CloneList(source.ongoing_traits, dest.ongoing_traits);
|
||||
CardStatus.CloneList(source.status, dest.status);
|
||||
CardStatus.CloneList(source.ongoing_status, dest.ongoing_status);
|
||||
GameTool.CloneList(source.abilities, dest.abilities);
|
||||
GameTool.CloneList(source.abilities_ongoing, dest.abilities_ongoing);
|
||||
GameTool.CloneListRefNull(source.abilities_data, ref dest.abilities_data); //No need to deep copy since AbilityData doesn't change dynamically, its just a reference
|
||||
}
|
||||
|
||||
//Clone a var that could be null
|
||||
public static void CloneNull(Card source, ref Card dest)
|
||||
{
|
||||
//Source is null
|
||||
if (source == null)
|
||||
{
|
||||
dest = null;
|
||||
return;
|
||||
}
|
||||
|
||||
//Dest is null
|
||||
if (dest == null)
|
||||
{
|
||||
dest = CloneNew(source);
|
||||
return;
|
||||
}
|
||||
|
||||
//Both arent null, just clone
|
||||
Clone(source, dest);
|
||||
}
|
||||
|
||||
//Clone dictionary completely
|
||||
public static void CloneDict(Dictionary<string, Card> source, Dictionary<string, Card> dest)
|
||||
{
|
||||
foreach (KeyValuePair<string, Card> pair in source)
|
||||
{
|
||||
bool valid = dest.TryGetValue(pair.Key, out Card val);
|
||||
if (valid)
|
||||
Clone(pair.Value, val);
|
||||
else
|
||||
dest[pair.Key] = CloneNew(pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
//Clone list by keeping references from ref_dict
|
||||
public static void CloneListRef(Dictionary<string, Card> ref_dict, List<Card> source, List<Card> dest)
|
||||
{
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
Card scard = source[i];
|
||||
bool valid = ref_dict.TryGetValue(scard.uid, out Card rcard);
|
||||
if (valid)
|
||||
{
|
||||
if (i < dest.Count)
|
||||
dest[i] = rcard;
|
||||
else
|
||||
dest.Add(rcard);
|
||||
}
|
||||
}
|
||||
|
||||
if(dest.Count > source.Count)
|
||||
dest.RemoveRange(source.Count, dest.Count - source.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class CardStatus
|
||||
{
|
||||
public StatusType type;
|
||||
public int value;
|
||||
public int duration = 1;
|
||||
public bool permanent = true;
|
||||
|
||||
[System.NonSerialized]
|
||||
private StatusData data = null;
|
||||
|
||||
public CardStatus() { }
|
||||
|
||||
public CardStatus(StatusType type, int value, int duration)
|
||||
{
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.duration = duration;
|
||||
this.permanent = (duration == 0);
|
||||
}
|
||||
|
||||
public StatusData StatusData {
|
||||
get
|
||||
{
|
||||
if (data == null || data.effect != type)
|
||||
data = StatusData.Get(type);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public StatusData Data => StatusData; //Alternate name
|
||||
|
||||
public static CardStatus CloneNew(CardStatus copy)
|
||||
{
|
||||
CardStatus status = new CardStatus(copy.type, copy.value, copy.duration);
|
||||
status.permanent = copy.permanent;
|
||||
return status;
|
||||
}
|
||||
|
||||
public static void Clone(CardStatus source, CardStatus dest)
|
||||
{
|
||||
dest.type = source.type;
|
||||
dest.value = source.value;
|
||||
dest.duration = source.duration;
|
||||
dest.permanent = source.permanent;
|
||||
}
|
||||
|
||||
public static void CloneList(List<CardStatus> source, List<CardStatus> dest)
|
||||
{
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
if (i < dest.Count)
|
||||
Clone(source[i], dest[i]);
|
||||
else
|
||||
dest.Add(CloneNew(source[i]));
|
||||
}
|
||||
|
||||
if (dest.Count > source.Count)
|
||||
dest.RemoveRange(source.Count, dest.Count - source.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class CardTrait
|
||||
{
|
||||
public string id;
|
||||
public int value;
|
||||
|
||||
[System.NonSerialized]
|
||||
private TraitData data = null;
|
||||
|
||||
public CardTrait(string id, int value)
|
||||
{
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public CardTrait(TraitData trait, int value)
|
||||
{
|
||||
this.id = trait.id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public TraitData TraitData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (data == null || data.id != id)
|
||||
data = TraitData.Get(id);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public TraitData Data => TraitData; //Alternate name
|
||||
|
||||
|
||||
public static CardTrait CloneNew(CardTrait copy)
|
||||
{
|
||||
CardTrait status = new CardTrait(copy.id, copy.value);
|
||||
return status;
|
||||
}
|
||||
|
||||
public static void Clone(CardTrait source, CardTrait dest)
|
||||
{
|
||||
dest.id = source.id;
|
||||
dest.value = source.value;
|
||||
}
|
||||
|
||||
public static void CloneList(List<CardTrait> source, List<CardTrait> dest)
|
||||
{
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
if (i < dest.Count)
|
||||
Clone(source[i], dest[i]);
|
||||
else
|
||||
dest.Add(CloneNew(source[i]));
|
||||
}
|
||||
|
||||
if (dest.Count > source.Count)
|
||||
dest.RemoveRange(source.Count, dest.Count - source.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/TcgEngine/Scripts/GameLogic/Card.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/Card.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7b7f2eac4a77ae4c84447453e759223
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
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,
|
||||
}
|
||||
}
|
||||
11
Assets/TcgEngine/Scripts/GameLogic/Game.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/Game.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3d07ed3e0a32b04db41e8d2155c460a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Assets/TcgEngine/Scripts/GameLogic/GameAction.cs
Normal file
106
Assets/TcgEngine/Scripts/GameLogic/GameAction.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
using Unity.Netcode;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace TcgEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// List of game actions and refreshes, that can be performed by the player or received
|
||||
/// </summary>
|
||||
|
||||
public static class GameAction
|
||||
{
|
||||
public const ushort None = 0;
|
||||
|
||||
//Commands (client to server)
|
||||
public const ushort PlayCard = 1000;
|
||||
public const ushort Attack = 1010;
|
||||
public const ushort AttackPlayer = 1012;
|
||||
public const ushort Move = 1015;
|
||||
public const ushort CastAbility = 1020;
|
||||
public const ushort SelectCard = 1030;
|
||||
public const ushort SelectPlayer = 1032;
|
||||
public const ushort SelectSlot = 1034;
|
||||
public const ushort SelectChoice = 1036;
|
||||
public const ushort SelectCost = 1037;
|
||||
public const ushort SelectMulligan = 1038;
|
||||
public const ushort CancelSelect = 1039;
|
||||
public const ushort EndTurn = 1040;
|
||||
public const ushort Resign = 1050;
|
||||
public const ushort ChatMessage = 1090;
|
||||
|
||||
public const ushort PlayerSettings = 1100; //After connect, send player data
|
||||
public const ushort PlayerSettingsAI = 1102; //After connect, send player data
|
||||
public const ushort GameSettings = 1105; //After connect, send gameplay settings
|
||||
|
||||
//Refresh (server to client)
|
||||
public const ushort Connected = 2000;
|
||||
public const ushort PlayerReady = 2001;
|
||||
|
||||
public const ushort GameStart = 2010;
|
||||
public const ushort GameEnd = 2012;
|
||||
public const ushort NewTurn = 2015;
|
||||
|
||||
public const ushort CardPlayed = 2020;
|
||||
public const ushort CardSummoned = 2022;
|
||||
public const ushort CardTransformed = 2023;
|
||||
public const ushort CardDiscarded = 2025;
|
||||
public const ushort CardDrawn = 2026;
|
||||
public const ushort CardMoved = 2027;
|
||||
|
||||
public const ushort AttackStart = 2030;
|
||||
public const ushort AttackEnd = 2031;
|
||||
public const ushort AttackPlayerStart = 2032;
|
||||
public const ushort AttackPlayerEnd = 2033;
|
||||
public const ushort CardDamaged = 2036;
|
||||
public const ushort PlayerDamaged = 2037;
|
||||
public const ushort CardHealed = 2038;
|
||||
public const ushort PlayerHealed = 2039;
|
||||
|
||||
public const ushort AbilityTrigger = 2040;
|
||||
public const ushort AbilityTargetCard = 2042;
|
||||
public const ushort AbilityTargetPlayer = 2043;
|
||||
public const ushort AbilityTargetSlot = 2044;
|
||||
public const ushort AbilityEnd = 2048;
|
||||
|
||||
public const ushort SecretTriggered = 2060;
|
||||
public const ushort SecretResolved = 2061;
|
||||
public const ushort ValueRolled = 2070;
|
||||
|
||||
public const ushort ServerMessage = 2190; //Server warning msg
|
||||
public const ushort RefreshAll = 2100;
|
||||
|
||||
public static string GetString(ushort type)
|
||||
{
|
||||
if (type == GameAction.PlayCard)
|
||||
return "play";
|
||||
if (type == GameAction.Move)
|
||||
return "move";
|
||||
if (type == GameAction.Attack)
|
||||
return "attack";
|
||||
if (type == GameAction.AttackPlayer)
|
||||
return "attack_player";
|
||||
if (type == GameAction.CastAbility)
|
||||
return "cast_ability";
|
||||
if (type == GameAction.EndTurn)
|
||||
return "end_turn";
|
||||
if (type == GameAction.SelectCard)
|
||||
return "select_card";
|
||||
if (type == GameAction.SelectPlayer)
|
||||
return "select_player";
|
||||
if (type == GameAction.SelectChoice)
|
||||
return "select_choice";
|
||||
if (type == GameAction.SelectCost)
|
||||
return "select_cost";
|
||||
if (type == GameAction.SelectSlot)
|
||||
return "select_slot";
|
||||
if (type == GameAction.CancelSelect)
|
||||
return "cancel_select";
|
||||
if (type == GameAction.Resign)
|
||||
return "resign";
|
||||
if (type == GameAction.ChatMessage)
|
||||
return "chat";
|
||||
return type.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/TcgEngine/Scripts/GameLogic/GameAction.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/GameAction.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 892d094edaaf21b4fb54d54fa21e9e15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1915
Assets/TcgEngine/Scripts/GameLogic/GameLogic.cs
Normal file
1915
Assets/TcgEngine/Scripts/GameLogic/GameLogic.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/TcgEngine/Scripts/GameLogic/GameLogic.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/GameLogic.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a6b9e34b292c584fb9a76c0823947fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
204
Assets/TcgEngine/Scripts/GameLogic/GameSettings.cs
Normal file
204
Assets/TcgEngine/Scripts/GameLogic/GameSettings.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace TcgEngine
|
||||
{
|
||||
|
||||
[System.Serializable]
|
||||
public enum GameType
|
||||
{
|
||||
Solo = 0,
|
||||
Adventure = 10,
|
||||
Multiplayer = 20,
|
||||
HostP2P = 30,
|
||||
Observer = 40,
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public enum GameMode
|
||||
{
|
||||
Casual = 0,
|
||||
Ranked = 10,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hold all client's game settings, like game mode, game uid and scene to load
|
||||
/// will be sent to server when a match start
|
||||
/// </summary>
|
||||
|
||||
[System.Serializable]
|
||||
public class GameSettings : INetworkSerializable
|
||||
{
|
||||
public string server_url; //Server to connect to
|
||||
public string game_uid; //Game uid on that server
|
||||
public string scene; //Which scene to load
|
||||
public int nb_players; //How many players, including AI (UI only supports 2)
|
||||
|
||||
public GameType game_type = GameType.Solo; //Multiplayer? Solo? Observer?
|
||||
public GameMode game_mode = GameMode.Casual; //Ranked or not? Other special game mode?
|
||||
public string level; //Adventure level ID
|
||||
|
||||
public virtual bool IsHost()
|
||||
{
|
||||
return game_type == GameType.Solo || game_type == GameType.Adventure || game_type == GameType.HostP2P;
|
||||
}
|
||||
|
||||
public virtual bool IsOffline()
|
||||
{
|
||||
return game_type == GameType.Solo || game_type == GameType.Adventure;
|
||||
}
|
||||
|
||||
public virtual bool IsOnline()
|
||||
{
|
||||
return game_type == GameType.HostP2P || game_type == GameType.Multiplayer || game_type == GameType.Observer;
|
||||
}
|
||||
|
||||
public virtual bool IsOnlinePlayer()
|
||||
{
|
||||
return game_type == GameType.HostP2P || game_type == GameType.Multiplayer;
|
||||
}
|
||||
|
||||
public virtual bool IsRanked()
|
||||
{
|
||||
return game_mode == GameMode.Ranked;
|
||||
}
|
||||
|
||||
public virtual string GetUrl()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(server_url))
|
||||
return server_url;
|
||||
return NetworkData.Get().url;
|
||||
}
|
||||
|
||||
public virtual string GetScene()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(scene))
|
||||
return scene;
|
||||
return GameplayData.Get().GetRandomArena();
|
||||
}
|
||||
|
||||
public virtual string GetGameModeId()
|
||||
{
|
||||
if (game_mode == GameMode.Ranked)
|
||||
return "ranked";
|
||||
if (game_mode == GameMode.Casual)
|
||||
return "casual";
|
||||
return "";
|
||||
}
|
||||
|
||||
public virtual LevelData GetLevel()
|
||||
{
|
||||
if (game_type == GameType.Adventure)
|
||||
{
|
||||
return LevelData.Get(level);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
serializer.SerializeValue(ref server_url);
|
||||
serializer.SerializeValue(ref game_uid);
|
||||
serializer.SerializeValue(ref scene);
|
||||
serializer.SerializeValue(ref game_type);
|
||||
serializer.SerializeValue(ref game_mode);
|
||||
serializer.SerializeValue(ref nb_players);
|
||||
serializer.SerializeValue(ref level);
|
||||
}
|
||||
|
||||
public static string GetRankModeString(GameMode rank_mode)
|
||||
{
|
||||
if (rank_mode == GameMode.Ranked)
|
||||
return "ranked";
|
||||
if (rank_mode == GameMode.Casual)
|
||||
return "casual";
|
||||
return "";
|
||||
}
|
||||
|
||||
public static GameMode GetRankMode(string rank_id)
|
||||
{
|
||||
if (rank_id == "ranked")
|
||||
return GameMode.Ranked;
|
||||
if (rank_id == "casual")
|
||||
return GameMode.Casual;
|
||||
return GameMode.Casual;
|
||||
}
|
||||
|
||||
public static GameSettings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
GameSettings settings = new GameSettings();
|
||||
settings.server_url = "";
|
||||
settings.game_uid = "test";
|
||||
settings.game_type = GameType.Solo;
|
||||
settings.game_mode = GameMode.Casual;
|
||||
settings.nb_players = 2;
|
||||
settings.scene = "Game";
|
||||
settings.level = "";
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hold all client's player settings, like avatar, cardback, and deck being used
|
||||
/// will be sent to server when a match start
|
||||
/// </summary>
|
||||
|
||||
[System.Serializable]
|
||||
public class PlayerSettings : INetworkSerializable
|
||||
{
|
||||
public string username;
|
||||
public string avatar;
|
||||
public string cardback;
|
||||
public int ai_level;
|
||||
public UserDeckData deck = UserDeckData.Default;
|
||||
|
||||
public bool HasDeck()
|
||||
{
|
||||
return deck != null && !string.IsNullOrEmpty(deck.tid);
|
||||
}
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
serializer.SerializeValue(ref username);
|
||||
serializer.SerializeValue(ref avatar);
|
||||
serializer.SerializeValue(ref cardback);
|
||||
serializer.SerializeValue(ref ai_level);
|
||||
serializer.SerializeValue(ref deck);
|
||||
}
|
||||
|
||||
public static PlayerSettings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
PlayerSettings settings = new PlayerSettings();
|
||||
settings.username = "Player";
|
||||
settings.avatar = "";
|
||||
settings.cardback = "";
|
||||
settings.deck = UserDeckData.Default;
|
||||
settings.ai_level = 1;
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
public static PlayerSettings DefaultAI
|
||||
{
|
||||
get
|
||||
{
|
||||
PlayerSettings settings = new PlayerSettings();
|
||||
settings.username = "AI";
|
||||
settings.avatar = "";
|
||||
settings.cardback = "";
|
||||
settings.deck = UserDeckData.Default;
|
||||
settings.ai_level = 10;
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/TcgEngine/Scripts/GameLogic/GameSettings.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/GameSettings.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d13a8ed425b26c4a94c0c63c5d55dbb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
614
Assets/TcgEngine/Scripts/GameLogic/Player.cs
Normal file
614
Assets/TcgEngine/Scripts/GameLogic/Player.cs
Normal file
@@ -0,0 +1,614 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TcgEngine
|
||||
{
|
||||
//Represent the current state of a player during the game (data only)
|
||||
|
||||
[System.Serializable]
|
||||
public class Player
|
||||
{
|
||||
public int player_id;
|
||||
public string username;
|
||||
public string avatar;
|
||||
public string cardback;
|
||||
public string deck;
|
||||
|
||||
public bool is_ai = false;
|
||||
public int ai_level;
|
||||
|
||||
public bool connected = false; //Connected to server and game
|
||||
public bool ready = false; //Sent all player data, ready to play
|
||||
|
||||
public int hp;
|
||||
public int hp_max;
|
||||
public int mana = 0;
|
||||
public int mana_max = 0;
|
||||
public int kill_count = 0;
|
||||
|
||||
public Dictionary<string, Card> cards_all = new Dictionary<string, Card>(); //Dictionnary for quick access to any card by UID
|
||||
public Card hero = null;
|
||||
|
||||
public List<Card> cards_deck = new List<Card>(); //Cards in the player's deck
|
||||
public List<Card> cards_hand = new List<Card>(); //Cards in the player's hand
|
||||
public List<Card> cards_board = new List<Card>(); //Cards on the board
|
||||
public List<Card> cards_equip = new List<Card>(); //Cards equipped by characters
|
||||
public List<Card> cards_discard = new List<Card>(); //Cards in the player's discard
|
||||
public List<Card> cards_secret = new List<Card>(); //Cards in the player's secret area
|
||||
public List<Card> cards_temp = new List<Card>(); //Temporary cards that have just been created, not assigned to any zone yet
|
||||
|
||||
public List<CardTrait> traits = new List<CardTrait>(); //Current persistant traits the cards has
|
||||
public List<CardTrait> ongoing_traits = new List<CardTrait>(); //Current ongoing traits the cards has
|
||||
|
||||
public List<CardStatus> status = new List<CardStatus>(); //Current persistant (or with duration) traits the cards has
|
||||
public List<CardStatus> ongoing_status = new List<CardStatus>(); //Current ongoing traits the cards has
|
||||
|
||||
public List<ActionHistory> history_list = new List<ActionHistory>(); //History of actions performed by the player
|
||||
|
||||
public Player(int id) { this.player_id = id; }
|
||||
|
||||
public bool IsReady() { return ready && cards_all.Count > 0; }
|
||||
public bool IsConnected() { return connected || is_ai; }
|
||||
|
||||
public virtual void ClearOngoing() { ongoing_status.Clear(); ongoing_traits.Clear(); }
|
||||
|
||||
//---- Cards ---------
|
||||
|
||||
public void AddCard(List<Card> card_list, Card card)
|
||||
{
|
||||
card_list.Add(card);
|
||||
}
|
||||
|
||||
public void RemoveCard(List<Card> card_list, Card card)
|
||||
{
|
||||
card_list.Remove(card);
|
||||
}
|
||||
|
||||
public virtual void RemoveCardFromAllGroups(Card card)
|
||||
{
|
||||
cards_deck.Remove(card);
|
||||
cards_hand.Remove(card);
|
||||
cards_board.Remove(card);
|
||||
cards_equip.Remove(card);
|
||||
cards_discard.Remove(card);
|
||||
cards_secret.Remove(card);
|
||||
cards_temp.Remove(card);
|
||||
UnequipFromAllCards(card);
|
||||
}
|
||||
|
||||
public virtual void UnequipFromAllCards(Card equip)
|
||||
{
|
||||
foreach (Card card in cards_board)
|
||||
{
|
||||
if (card.equipped_uid == equip.uid)
|
||||
card.equipped_uid = null;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Card GetRandomCard(List<Card> card_list, System.Random rand)
|
||||
{
|
||||
if (card_list.Count > 0)
|
||||
return card_list[rand.Next(0, card_list.Count)];
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool HasCard(List<Card> card_list, Card card)
|
||||
{
|
||||
return card_list.Contains(card);
|
||||
}
|
||||
|
||||
public Card GetHandCard(string uid)
|
||||
{
|
||||
foreach (Card card in cards_hand)
|
||||
{
|
||||
if (card.uid == uid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetBoardCard(string uid)
|
||||
{
|
||||
foreach (Card card in cards_board)
|
||||
{
|
||||
if (card.uid == uid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetEquipCard(string uid)
|
||||
{
|
||||
foreach (Card card in cards_equip)
|
||||
{
|
||||
if (card.uid == uid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetDeckCard(string uid)
|
||||
{
|
||||
foreach (Card card in cards_deck)
|
||||
{
|
||||
if (card.uid == uid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetDiscardCard(string uid)
|
||||
{
|
||||
foreach (Card card in cards_discard)
|
||||
{
|
||||
if (card.uid == uid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetBearerCard(Card equipment)
|
||||
{
|
||||
foreach (Card card in cards_board)
|
||||
{
|
||||
if (card != null && card.equipped_uid == equipment.uid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetSlotCard(Slot slot)
|
||||
{
|
||||
foreach (Card card in cards_board)
|
||||
{
|
||||
if (card != null && card.slot == slot)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Card GetCard(string uid)
|
||||
{
|
||||
if (uid != null)
|
||||
{
|
||||
bool valid = cards_all.TryGetValue(uid, out Card card);
|
||||
if (valid)
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsOnBoard(Card card)
|
||||
{
|
||||
return card != null && GetBoardCard(card.uid) != null;
|
||||
}
|
||||
|
||||
|
||||
//---- Slots ---------
|
||||
|
||||
public Slot GetRandomSlot(System.Random rand)
|
||||
{
|
||||
return Slot.GetRandom(player_id, rand);
|
||||
}
|
||||
|
||||
public virtual Slot GetRandomEmptySlot(System.Random rand, List<Slot> list_mem = null)
|
||||
{
|
||||
List<Slot> valid = GetEmptySlots(list_mem);
|
||||
if (valid.Count > 0)
|
||||
return valid[rand.Next(0, valid.Count)];
|
||||
return Slot.None;
|
||||
}
|
||||
|
||||
public virtual Slot GetRandomOccupiedSlot(System.Random rand, List<Slot> list_mem = null)
|
||||
{
|
||||
List<Slot> valid = GetOccupiedSlots(list_mem);
|
||||
if (valid.Count > 0)
|
||||
return valid[rand.Next(0, valid.Count)];
|
||||
return Slot.None;
|
||||
}
|
||||
|
||||
public List<Slot> GetEmptySlots(List<Slot> list_mem = null)
|
||||
{
|
||||
List<Slot> valid = list_mem != null ? list_mem : new List<Slot>();
|
||||
foreach (Slot slot in Slot.GetAll(player_id))
|
||||
{
|
||||
Card slot_card = GetSlotCard(slot);
|
||||
if (slot_card == null)
|
||||
valid.Add(slot);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
public List<Slot> GetOccupiedSlots(List<Slot> list_mem = null)
|
||||
{
|
||||
List<Slot> valid = list_mem != null ? list_mem : new List<Slot>();
|
||||
foreach (Slot slot in Slot.GetAll(player_id))
|
||||
{
|
||||
Card slot_card = GetSlotCard(slot);
|
||||
if (slot_card != null)
|
||||
valid.Add(slot);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
//------ Custom Traits/Stats ---------
|
||||
|
||||
public void SetTrait(string id, int value)
|
||||
{
|
||||
CardTrait trait = GetTrait(id);
|
||||
if (trait != null)
|
||||
{
|
||||
trait.value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
trait = new CardTrait(id, value);
|
||||
traits.Add(trait);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTrait(string id, int value)
|
||||
{
|
||||
CardTrait trait = GetTrait(id);
|
||||
if (trait != null)
|
||||
trait.value += value;
|
||||
else
|
||||
SetTrait(id, value);
|
||||
}
|
||||
|
||||
public void AddOngoingTrait(string id, int value)
|
||||
{
|
||||
CardTrait trait = GetOngoingTrait(id);
|
||||
if (trait != null)
|
||||
{
|
||||
trait.value += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
trait = new CardTrait(id, value);
|
||||
ongoing_traits.Add(trait);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTrait(string id)
|
||||
{
|
||||
for (int i = traits.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (traits[i].id == id)
|
||||
traits.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public CardTrait GetTrait(string id)
|
||||
{
|
||||
foreach (CardTrait trait in traits)
|
||||
{
|
||||
if (trait.id == id)
|
||||
return trait;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CardTrait GetOngoingTrait(string id)
|
||||
{
|
||||
foreach (CardTrait trait in ongoing_traits)
|
||||
{
|
||||
if (trait.id == id)
|
||||
return trait;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<CardTrait> GetAllTraits()
|
||||
{
|
||||
List<CardTrait> all_traits = new List<CardTrait>();
|
||||
all_traits.AddRange(traits);
|
||||
all_traits.AddRange(ongoing_traits);
|
||||
return all_traits;
|
||||
}
|
||||
|
||||
public int GetTraitValue(TraitData trait)
|
||||
{
|
||||
if (trait != null)
|
||||
return GetTraitValue(trait.id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual int GetTraitValue(string id)
|
||||
{
|
||||
int val = 0;
|
||||
CardTrait stat1 = GetTrait(id);
|
||||
CardTrait stat2 = GetOngoingTrait(id);
|
||||
if (stat1 != null)
|
||||
val += stat1.value;
|
||||
if (stat2 != null)
|
||||
val += stat2.value;
|
||||
return val;
|
||||
}
|
||||
|
||||
public bool HasTrait(TraitData trait)
|
||||
{
|
||||
if (trait != null)
|
||||
return HasTrait(trait.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasTrait(string id)
|
||||
{
|
||||
foreach (CardTrait trait in traits)
|
||||
{
|
||||
if (trait.id == id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//---- Status ---------
|
||||
|
||||
public void AddStatus(StatusData status, int value, int duration)
|
||||
{
|
||||
if (status != null)
|
||||
AddStatus(status.effect, value, duration);
|
||||
}
|
||||
|
||||
public void AddOngoingStatus(StatusData status, int value)
|
||||
{
|
||||
if (status != null)
|
||||
AddOngoingStatus(status.effect, value);
|
||||
}
|
||||
|
||||
public void AddStatus(StatusType effect, int value, int duration)
|
||||
{
|
||||
if (effect != StatusType.None)
|
||||
{
|
||||
CardStatus status = GetStatus(effect);
|
||||
if (status == null)
|
||||
{
|
||||
status = new CardStatus(effect, value, duration);
|
||||
this.status.Add(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
status.value += value;
|
||||
status.duration = Mathf.Max(status.duration, duration);
|
||||
status.permanent = status.permanent || duration == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddOngoingStatus(StatusType effect, int value)
|
||||
{
|
||||
if (effect != StatusType.None)
|
||||
{
|
||||
CardStatus status = GetOngoingStatus(effect);
|
||||
if (status == null)
|
||||
{
|
||||
status = new CardStatus(effect, value, 0);
|
||||
ongoing_status.Add(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
status.value += value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveStatus(StatusType effect)
|
||||
{
|
||||
for (int i = status.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (status[i].type == effect)
|
||||
status.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public CardStatus GetStatus(StatusType effect)
|
||||
{
|
||||
foreach (CardStatus status in status)
|
||||
{
|
||||
if (status.type == effect)
|
||||
return status;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CardStatus GetOngoingStatus(StatusType effect)
|
||||
{
|
||||
foreach (CardStatus status in ongoing_status)
|
||||
{
|
||||
if (status.type == effect)
|
||||
return status;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<CardStatus> GetAllStatus()
|
||||
{
|
||||
List<CardStatus> all_status = new List<CardStatus>();
|
||||
all_status.AddRange(status);
|
||||
all_status.AddRange(ongoing_status);
|
||||
return all_status;
|
||||
}
|
||||
|
||||
public bool HasStatus(StatusType effect)
|
||||
{
|
||||
return GetStatus(effect) != null || GetOngoingStatus(effect) != null;
|
||||
}
|
||||
|
||||
public virtual int GetStatusValue(StatusType type)
|
||||
{
|
||||
CardStatus status1 = GetStatus(type);
|
||||
CardStatus status2 = GetOngoingStatus(type);
|
||||
int v1 = status1 != null ? status1.value : 0;
|
||||
int v2 = status2 != null ? status2.value : 0;
|
||||
return v1 + v2;
|
||||
}
|
||||
|
||||
public virtual void ReduceStatusDurations()
|
||||
{
|
||||
for (int i = status.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!status[i].permanent)
|
||||
{
|
||||
status[i].duration -= 1;
|
||||
if (status[i].duration <= 0)
|
||||
status.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---- History ---------
|
||||
|
||||
public void AddHistory(ushort type, Card card)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
public void AddHistory(ushort type, Card card, Card target)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
order.target_uid = target.uid;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
public void AddHistory(ushort type, Card card, Player target)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
order.target_id = target.player_id;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
public void AddHistory(ushort type, Card card, AbilityData ability)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
order.ability_id = ability.id;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
public void AddHistory(ushort type, Card card, AbilityData ability, Card target)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
order.ability_id = ability.id;
|
||||
order.target_uid = target.uid;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
public void AddHistory(ushort type, Card card, AbilityData ability, Player target)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
order.ability_id = ability.id;
|
||||
order.target_id = target.player_id;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
public void AddHistory(ushort type, Card card, AbilityData ability, Slot target)
|
||||
{
|
||||
ActionHistory order = new ActionHistory();
|
||||
order.type = type;
|
||||
order.card_id = card.card_id;
|
||||
order.card_uid = card.uid;
|
||||
order.ability_id = ability.id;
|
||||
order.slot = target;
|
||||
history_list.Add(order);
|
||||
}
|
||||
|
||||
|
||||
//---- Action Check ---------
|
||||
|
||||
public virtual bool CanPayMana(Card card)
|
||||
{
|
||||
if (card.CardData.IsDynamicManaCost())
|
||||
return true;
|
||||
return mana >= card.GetMana();
|
||||
}
|
||||
|
||||
public virtual void PayMana(Card card)
|
||||
{
|
||||
if (!card.CardData.IsDynamicManaCost())
|
||||
mana -= card.GetMana();
|
||||
}
|
||||
|
||||
public virtual bool CanPayAbility(Card card, AbilityData ability)
|
||||
{
|
||||
bool exhaust = !card.exhausted || !ability.exhaust;
|
||||
return exhaust && mana >= ability.mana_cost;
|
||||
}
|
||||
|
||||
public virtual bool IsDead()
|
||||
{
|
||||
if (cards_hand.Count == 0 && cards_board.Count == 0 && cards_deck.Count == 0)
|
||||
return true;
|
||||
if (hp <= 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
|
||||
//Clone all player variables into another var, used mostly by the AI when building a prediction tree
|
||||
public static void Clone(Player source, Player dest)
|
||||
{
|
||||
dest.player_id = source.player_id;
|
||||
dest.is_ai = source.is_ai;
|
||||
dest.ai_level = source.ai_level;
|
||||
|
||||
//Commented variables are not needed for ai predictions
|
||||
//dest.username = source.username;
|
||||
//dest.avatar = source.avatar;
|
||||
//dest.deck = source.deck;
|
||||
//dest.connected = source.connected;
|
||||
//dest.ready = source.ready;
|
||||
|
||||
dest.hp = source.hp;
|
||||
dest.hp_max = source.hp_max;
|
||||
dest.mana = source.mana;
|
||||
dest.mana_max = source.mana_max;
|
||||
dest.kill_count = source.kill_count;
|
||||
|
||||
Card.CloneNull(source.hero, ref dest.hero);
|
||||
Card.CloneDict(source.cards_all, dest.cards_all);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_board, dest.cards_board);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_equip, dest.cards_equip);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_hand, dest.cards_hand);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_deck, dest.cards_deck);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_discard, dest.cards_discard);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_secret, dest.cards_secret);
|
||||
Card.CloneListRef(dest.cards_all, source.cards_temp, dest.cards_temp);
|
||||
|
||||
CardStatus.CloneList(source.status, dest.status);
|
||||
CardStatus.CloneList(source.ongoing_status, dest.ongoing_status);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class ActionHistory
|
||||
{
|
||||
public ushort type;
|
||||
public string card_id;
|
||||
public string card_uid;
|
||||
public string target_uid;
|
||||
public string ability_id;
|
||||
public int target_id;
|
||||
public Slot slot;
|
||||
}
|
||||
}
|
||||
11
Assets/TcgEngine/Scripts/GameLogic/Player.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/Player.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6da82cc0b350f5843b6597d730b654ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
210
Assets/TcgEngine/Scripts/GameLogic/Slot.cs
Normal file
210
Assets/TcgEngine/Scripts/GameLogic/Slot.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace TcgEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a slot in gameplay (data only)
|
||||
/// </summary>
|
||||
|
||||
[System.Serializable]
|
||||
public struct Slot : INetworkSerializable
|
||||
{
|
||||
public int x; //From 1 to 5
|
||||
public int y; //Not in use, could be used to add more rows or different locations on the board
|
||||
public int p; //0 or 1, represent player ID
|
||||
|
||||
public static int x_min = 1; //Dont change this, should start at 1 (0,0,0 represent invalid slot)
|
||||
public static int x_max = 5; //Number of slots in a row/zone
|
||||
|
||||
public static int y_min = 1; //Dont change this, should start at 1 (0,0,0 represent invalid slot)
|
||||
public static int y_max = 1; //Set this to the number of rows/locations you want to have
|
||||
|
||||
public static bool ignore_p = false; //Set to true if you dont want to use P value
|
||||
|
||||
private static Dictionary<int, List<Slot>> player_slots = new Dictionary<int, List<Slot>>();
|
||||
private static List<Slot> all_slots = new List<Slot>();
|
||||
|
||||
public Slot(int pid)
|
||||
{
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.p = pid;
|
||||
}
|
||||
|
||||
public Slot(int x, int y, int pid)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.p = pid;
|
||||
}
|
||||
|
||||
public Slot(SlotXY slot, int pid)
|
||||
{
|
||||
this.x = slot.x;
|
||||
this.y = slot.y;
|
||||
this.p = pid;
|
||||
}
|
||||
|
||||
public bool IsInRangeX(Slot slot, int range)
|
||||
{
|
||||
return Mathf.Abs(x - slot.x) <= range;
|
||||
}
|
||||
|
||||
public bool IsInRangeY(Slot slot, int range)
|
||||
{
|
||||
return Mathf.Abs(y - slot.y) <= range;
|
||||
}
|
||||
|
||||
public bool IsInRangeP(Slot slot, int range)
|
||||
{
|
||||
return Mathf.Abs(p - slot.p) <= range;
|
||||
}
|
||||
|
||||
//No Diagonal, Diagonal = 2 dist
|
||||
public bool IsInDistanceStraight(Slot slot, int dist)
|
||||
{
|
||||
int r = Mathf.Abs(x - slot.x) + Mathf.Abs(y - slot.y) + Mathf.Abs(p - slot.p);
|
||||
return r <= dist;
|
||||
}
|
||||
|
||||
//Diagonal = 1 dist
|
||||
public bool IsInDistance(Slot slot, int dist)
|
||||
{
|
||||
int dx = Mathf.Abs(x - slot.x);
|
||||
int dy = Mathf.Abs(y - slot.y);
|
||||
int dp = Mathf.Abs(p - slot.p);
|
||||
return dx <= dist && dy <= dist && dp <= dist;
|
||||
}
|
||||
|
||||
public bool IsPlayerSlot()
|
||||
{
|
||||
return x == 0 && y == 0;
|
||||
}
|
||||
|
||||
//Check if the slot is valid one (or if out of board)
|
||||
public bool IsValid()
|
||||
{
|
||||
return x >= x_min && x <= x_max && y >= y_min && y <= y_max && p >= 0;
|
||||
}
|
||||
|
||||
public static int MaxP
|
||||
{
|
||||
get { return ignore_p ? 0 : 1; }
|
||||
}
|
||||
|
||||
//Return slot P-value of player, usually its same as player_id, unless we ignore P value then its 0 for all
|
||||
public static int GetP(int pid)
|
||||
{
|
||||
return ignore_p ? 0 : pid;
|
||||
}
|
||||
|
||||
//Get a random slot on player side
|
||||
public static Slot GetRandom(int pid, System.Random rand)
|
||||
{
|
||||
int p = GetP(pid);
|
||||
if (y_max > y_min)
|
||||
return new Slot(rand.Next(x_min, x_max + 1), rand.Next(y_min, y_max + 1), p);
|
||||
return new Slot(rand.Next(x_min, x_max + 1), y_min, p);
|
||||
}
|
||||
|
||||
//Get a random slot amongts all valid ones
|
||||
public static Slot GetRandom(System.Random rand)
|
||||
{
|
||||
if (y_max > y_min)
|
||||
return new Slot(rand.Next(x_min, x_max + 1), rand.Next(y_min, y_max + 1), rand.Next(0, 2));
|
||||
return new Slot(rand.Next(x_min, x_max + 1), y_min, rand.Next(0, 2));
|
||||
}
|
||||
|
||||
public static Slot Get(int x, int y, int p)
|
||||
{
|
||||
List<Slot> slots = GetAll();
|
||||
foreach (Slot slot in slots)
|
||||
{
|
||||
if (slot.x == x && slot.y == y && slot.p == p)
|
||||
return slot;
|
||||
}
|
||||
return new Slot(x, y, p);
|
||||
}
|
||||
|
||||
//Get all slots on player side
|
||||
public static List<Slot> GetAll(int pid)
|
||||
{
|
||||
int p = GetP(pid);
|
||||
|
||||
if (player_slots.ContainsKey(p))
|
||||
return player_slots[p]; //Faster access
|
||||
|
||||
List<Slot> list = new List<Slot>();
|
||||
for (int y = y_min; y <= y_max; y++)
|
||||
{
|
||||
for (int x = x_min; x <= x_max; x++)
|
||||
{
|
||||
list.Add(new Slot(x, y, p));
|
||||
}
|
||||
}
|
||||
player_slots[p] = list;
|
||||
return list;
|
||||
}
|
||||
|
||||
//Get all valid slots
|
||||
public static List<Slot> GetAll()
|
||||
{
|
||||
if (all_slots.Count > 0)
|
||||
return all_slots; //Faster access
|
||||
|
||||
for (int p = 0; p <= MaxP; p++)
|
||||
{
|
||||
for (int y = y_min; y <= y_max; y++)
|
||||
{
|
||||
for (int x = x_min; x <= x_max; x++)
|
||||
{
|
||||
all_slots.Add(new Slot(x, y, p));
|
||||
}
|
||||
}
|
||||
}
|
||||
return all_slots;
|
||||
}
|
||||
|
||||
public static bool operator ==(Slot slot1, Slot slot2)
|
||||
{
|
||||
return slot1.x == slot2.x && slot1.y == slot2.y && slot1.p == slot2.p;
|
||||
}
|
||||
|
||||
public static bool operator !=(Slot slot1, Slot slot2)
|
||||
{
|
||||
return slot1.x != slot2.x || slot1.y != slot2.y || slot1.p != slot2.p;
|
||||
}
|
||||
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
return base.Equals(o);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
serializer.SerializeValue(ref x);
|
||||
serializer.SerializeValue(ref y);
|
||||
serializer.SerializeValue(ref p);
|
||||
}
|
||||
|
||||
public static Slot None
|
||||
{
|
||||
get { return new Slot(0, 0, 0); }
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct SlotXY
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
}
|
||||
11
Assets/TcgEngine/Scripts/GameLogic/Slot.cs.meta
Normal file
11
Assets/TcgEngine/Scripts/GameLogic/Slot.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30f3e2f0cce43684085b88d531bdd8cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user