Setting a general code to access inherited variables for Buffs/Debuffs system

Hello everyone!

I’m struggling to create a TCG (Trading Card Game) battle system with a Buffs/Debuffs system.
My main problem now is: I have different kinds of Buffs (class buffs, elemental buffs, turn-based buffs, formation buffs, etc.) and I want to create some generic methods that can be used with all of them (i.e.: add stacks, add/remove buff values, etc…).
There are two important examples I can quote here that I think if I can solve them, I’ll probably be able to solve any other related to this topic. They are:

  1. Turn based Add/Remove buffs
    I found a LOT of examples of buffs systems in the internet, but all of them are based in time activation/deactivation and I need to make it work with turns, not time.deltaTime. I tried that with these codes below, but it is not working well (sometimes I keep adding buffs ignoring my limits, or remove my buffs sooner because of extra turns that shouldn’t be counted, my attribute values don’t get back to the original values after buff removal, etc…).
//Infos Battle management variables
    public BattleState currentState;
    public int battleTurn;
    public int battleRound;
    public bool isPlayerTurn = false;
    public bool enemyPlayed = false;
    public DoDamage doDamage;

    //Infos de batalha/dano
    public int damage;
    public int currentHP;

void Start()
    {
        SetupBattle();
    }

    void SetupBattle()
    {
        currentState = BattleState.SETUP;
        battleRound = 1;
        battleTurn = 0;
        playerBehave = playerUnit.GetComponent<CardBehavior>();
        enemyBehave = enemyUnit.GetComponent<CardBehavior>();
        playerBuffs = playerUnit.GetComponentInChildren<CardBuffsController>();
        enemyBuffs = enemyUnit.GetComponentInChildren<CardBuffsController>();
        currentState = BattleState.START;
      
        dialogueText.text = "A new battle starts with ";
      
//Decides which player will be the first to play
        StartJokempo();
    }

public void EndTurn()
    {
        battleTurn++;
        Debug.Log("Turn: " + battleTurn);
        Debug.Log("Round " + battleRound);
      
        if (playerBehave.turnsToPlay == 0 && enemyBehave.turnsToPlay <= 0)
        {
          
            NextRound();
            CheckTurnSort();
            Debug.Log(currentState);
        }
        else if (playerBehave.turnsToPlay <= 0 && enemyBehave.turnsToPlay != 0)
        {
            NextTurn(BattleState.ENEMYTURN);
            Debug.Log(currentState);
        }
        else if (playerBehave.turnsToPlay != 0 && enemyBehave.turnsToPlay == 0)
        {
            NextTurn(BattleState.PLAYERTURN);
            Debug.Log(currentState);
        }

public void NextTurn(BattleState _battleState)
    {
        if (playerBehave.turnsToPlay <= 0 && enemyBehave.turnsToPlay > 0)
        {
            EnemyTurn();
        }
        else if(playerBehave.turnsToPlay > 0 && enemyBehave.turnsToPlay <= 0)
        {
            PlayerTurn();
        }
    }

public void NextRound()
    {
        battleRound++;
        playerBehave.turnsToPlay = 1;
        playerBuffs.gotExtraTurn = false;
        enemyBehave.turnsToPlay = 1;
        enemyBuffs.gotExtraTurn = false;
        }
  1. Access to the General Buffs Variables
    I know that all bufs will have some basic information, like “buff name”, “buff description”, “buff stacks”, and I’ve made the specific buffs (like Class Buffs) inherit this variables. The problem is: when I try to access (and change) this variables in other scripts, I can’t reach them without using the specifications (example: add stacks could be a generic method, but instead, I’m having to create a different method per kind of buff, like “Add Class Bonus Stacks”, “Add Attributes Buffs Stacks”, etc…). It doesn’t make any sense because I’m mostly copying and pasting the same method for each kind of buff and just changing the same variables in the inherited script (such as: rather than just asking my script to get GeneralBuff maxBuffStacks variable, I have to create two methods, one to use maxBuffStacks of Class Bonus and one another to use maxBuffStacks of a Attribute Bonus (but both actually do the same thing: check if there is “room” for new stacks or not. I wanted to make it happen independent of the kind of Buff/Bonus).
public abstract class BuffsGeneral : MonoBehaviour
{
    //Duration = time (in turns) that buff will be activated
    public int duration;
  
    public string buffName;

    //Buff Desc = Buff's description
    public string buffDesc;

    //Buff Alive = active buff turn count
    public int buffAlive;

    //Buff Stacks = cumulative buff effect
    public int buffStacks;

    //Max Buff Stacks = cumulative buff effect limit
    public int bonusMaxStacks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.UI;

public class CardBuffsController : MonoBehaviour
{
    //Buffs Spawner object
    public Transform controllerTransform;

    //Battle Script to control turns/rounds
    public BattleSystem battleSystemScript;

    //Controladores de Buffs
    public List<GameObject> listBuffs = new List<GameObject>();
    public bool gotExtraTurn = false;
    public bool gotClassBonus = false;

    public void AddClassBonus(GameObject _classB, GameObject _tgt)
    {
        //Instantiating the Buff Game Object
        GameObject _bonusController = _tgt.GetComponentInChildren<CardBuffsController>().gameObject as GameObject;
        Vector3 tgtPos = _bonusController.transform.position;
        GameObject _bonus = Instantiate<GameObject>(_classB, tgtPos, Quaternion.identity, _tgt.transform) as GameObject;

        //Add this Buff GameObject to the Buffs list of the character
       _bonus.transform.SetParent(_bonusController.transform);
        _bonusController.GetComponent<CardBuffsController>().listBuffs.Add(_bonus);
      
        //Adding 1 stack to the bonus;
        _bonus.GetComponent<ClassBonus>().buffStacks += 1;
        _bonus.GetComponentInChildren<Text>().text = _bonus.GetComponent<BuffsGeneral>().buffStacks.ToString();
    }

    public void RemoveBonus(GameObject _bonus)
    {
        Destroy(_bonus);
    }   //Remove the _bonus

    public void AddStacks(GameObject _stacksBuff, int _qttStacks)
    {
        //Defining the variables which will be used to check if there are "room" for new stacks
        bool canReceiveStacks;
      
        int cardBuffStacks = _stacksBuff.GetComponent<ClassBonus>().buffStacks;
        int buffMaxStacks = _stacksBuff.GetComponent<ClassBonus>().bonusMaxStacks;

        //UI variables
        Text stacksTxt = _stacksBuff.GetComponentInChildren<Text>();
        stacksTxt.text = cardBuffStacks.ToString();

        //Checking if the list contains the buff
        if (listBuffs.Contains(_stacksBuff))
        {
            if (cardBuffStacks < buffMaxStacks)
            {
                canReceiveStacks = true;
            }
            else
            {
                canReceiveStacks = false;
            }

            //Add stacks and adjust it to the limit (if necessary)
            if(canReceiveStacks == true)
            {
                cardBuffStacks += _qttStacks;
                if(cardBuffStacks >= buffMaxStacks)
                {
                    cardBuffStacks = buffMaxStacks;
                }
            }
          
        }
        else { Debug.Log("Nothing find"); }


    }

NOTE: I’m calling those methods in a “Do Damage” script. If necessary, I can send it too.
EDIT: I kinda solved some issues with the “timing” of a buff add/removal using bool variables, but I still want to try to find a way to do these checks “automatically”, since It’ll probably be complicated to control after adding the other types of buffs and battle features.

Please, can you help me with these 2 struggles? :frowning:

I would guess a combination of interfaces coupled with ScriptableObjects would get you a large amount of what you want above. You would have various types of buffing interfaces (or perhaps just one to start with) and the implement as many different ScriptableObjects instances as you like, and slot them into cards.

Here’s my blurb on interfaces:

Using Interfaces in Unity3D:

Check Youtube for other tutorials about interfaces and working in Unity3D. It’s a pretty powerful combination.