Skip to content

Instantly share code, notes, and snippets.

@LuizMoratelli
Last active December 11, 2023 07:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LuizMoratelli/c1ffae651d2e30e8e950466cf760d70e to your computer and use it in GitHub Desktop.
Save LuizMoratelli/c1ffae651d2e30e8e950466cf760d70e to your computer and use it in GitHub Desktop.
[TCG Engine] Create Start of Game Effects

1. Create a new AbilityTrigger "StartOfGame" in AbilityData.cs

public enum AbilityTrigger {
  //...
  StartOfGame = 19, // On-time
  //...
}

2. Update GameLogic.cs to resolve "StartOfGame" Abilities before the 1st turn begins.

2.1 Create a new method that trigger card abilities of cards in the deck

public virtual void TriggerPlayerDeckCardsAbilityType(Player player, AbilityTrigger type)
{
    if (player.hero != null)
        TriggerCardAbilityType(type, player.hero, player.hero);

    foreach (Card card in player.cards_deck)
    {
        TriggerCardAbilityType(type, card, card);
    }
}

2.2 Call the new method before the starting cards are drawn (avoiding bugs if you draw a card with StartOfGame effect)

public virtual void StartGame()
{
  //...
  //Init each player
  foreach (Player player in game_data.players)
  {
    //...
    TriggerPlayerDeckCardsAbilityType(player, AbilityTrigger.StartOfGame); // <-- NEW LINE CALLING THE NEW METHOD
    
    //Draw starting cards
    //...
  }
}

2.3 Resolve the queue before start the 1st turn

public virtual void StartGame()
{
  //...
  resolve_queue.ResolveAll(0.2f); // <-- NEW LINE RESOLVING THE QUEUE
  //Start state
  onGameStart?.Invoke();
  resolve_queue.AddCallback(StartTurn); // <-- CHANGED TO BE ADD TO QUEUE
  //...
}

3. Create a new AbilityData ScriptableObject

In my case I'll add +1 spell damage for the player, so:

3.1 Trigger

Change the trigger to be Start of Game
image

3.2 Target

Change the target to be Player Self
image

3.3 Effect

Add the effect add_spell_damage
image

4. Add the new Ability to a CardData

I just added it to the Unicorn card.
image

5. (Optional) Show ability trigger at the start of game

5.1 ResolveQueue.cs

First we'll add a new delay to be able to have delay between ability activations

public class ResolveQueue {
    //...
    private float resolve_between_delay = 0f;
    private float resolve_between_delay_max = 0f;
    //...
    
    public virtual voidUpdate(float delta)
    {
        //...
        if (resolve_delay > 0f || resolve_between_delay > 0f) {
            resolve_delay -= delta;
            resolve_between_delay -= delta;
            if (resolve_delay <= 0f && resolve_between_delay <= 0)
                ResolveAll();
        }
        //...
    }
    //...
    
    public virtual void ResolveAll(float delay, float delayBetween)
    {
        SetBetweenDelay(delayBetween);
        ResolveAll(delay);
    }
    
    public virtual void ResolveAll()
    {
        //...
        while (CanResolve())
        {
            Resolve();
            resolve_between_delay = resolve_between_delay_max;
        }
    }
    
    public virtual void SetBetweenDelay(float delay)
    {
        if (!skip_delay)
        {
            resolve_between_delay_max = delay;
        }
    }
    
    public virtual bool CanResolve()
    {
        if (resolve_between_delay > 0f)
            return false; // Is waiting between effects
    }
    
    public virtual bool IsResolving()
    {
        return is_resolving || resolve_delay > 0f || resolve_between_delay > 0f;
    }
}

5.2 GameAction.cs

Create the new Action so server and client should know what expect

public const ushort CardActivated = 2019;

5.3 GameLogic.cs

Add the new Action to GameLogic, and change the ResolveAll to have the new Delay, invoking the event when resolving a card with StartOfGame ability

public class GameLogic {
    //...
    public UnityAction<Card> onCardActivated;
    //...
    
    public virtual void StartGame()
    {
        //Init each players
        foreach (Player player in game_data.players)
        {
            resolve_queue.ResolveAll(1f, 3f);
        }
    }
    
    public virtual void StartTurn()
    {
        //...
        resolve_queue.SetBetweenDelay(0);
        //...
    }
    
    protected virtual void ResolveCardAbility(AbilityData iability, Card caster, Card triggerer)
    {
        //...
        if (iability.trigger == AbilityTrigger.StartOfGame)
            onCardActivated?.Invoke(caster);
    }
}

5.4 GameServer.cs

Setup the server to send a message to the client when onCardActivated is called

public class GameServer
{
    protected virtual void Init(string uid, int players, bool online)
    {
        //...
        gameplay.onCardActivated += OnCardActivated;
        //...
    }
    
    protected virtual void Clear()
    {
        //...
        gameplay.onCardActivated -= OnCardActivated;
        //...
    }
    
    protected virtual void OnCardActivated(Card card)
    {
        MsgCard mdata = new MsgCard();
        mdata.card_uid = card.uid;
        SendToAll(GameAction.CardActivated, mdata, NetworkDelivery.Reliable);
    }
}

5.5 GameClient.cs

Setup the client to listen to CardActivated action and invoking it owns UnityAction to notify listeners, example the board.

public class GameClient : MonoBehaviour
{
    //...
    public UnityAction<Card> onCardActivated;
    //...
    
    protected virtual void Start()
    {
        //...
        RegisterRefresh(GameAction.CardActivated, OnCardActivated);
        //...
    }

    private void OnCardActivated(SerializedData sdata)
    {
        MsgCard msg = sdata.Get<MsgCard>();
        Card card = game_data.GetCard(msg.card_uid);
        onCardActivated?.Invoke(card);
    }
}

5.6 GameBoardFX.cs

And finally listen to the Game Client UnityEvent and show the card prefab to both players.

public class GameBoardFX : MonoBehaviour
{
    void Start()
    {
        //...
        client.onCardActivated += OnActivatedCard;
        //...
    }
    
    void OnActivatedCard(Card card)
    {
        if (card != null)
        {
            int player_id = GameClient.Get().GetPlayerID();
            CardData icard = CardData.Get(card.card_id);
            GameObject prefab = player_id == card.player_id ? AssetData.Get().play_card_fx : AssetData.Get().play_card_other_fx;
            GameObject obj = FXTool.DoFX(prefab, Vector3.zero);
            CardUI ui = obj.GetComponentInChildren<CardUI>();
            ui.SetCard(icard, card.VariantData);
            ui.SetText(ui.EnhancedText(card));
        }
    }
}

Result

https://im.ezgif.com/tmp/ezgif-1-4506387715.gif

Notes:

  • You MUST have TCG Engine to add the changes showned above;
  • This gist was created using 1.09, so is possible that in the future you will need to change minor details to make it work;
  • The CardEditor and Inspector are edited by me, so if you just installed TCG Engine, please consider that are differences in the UI, but are just visual.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment