Dynamic function calling

I need a method to dynamically call different functions from a huge pile of functions.

.

A small example part of my problem;
There are passive defensive skills which automatically activate when the player about to takes damage.
Problem is there are hundreds of skills and only a couple of it active at a time.
I don’t want to call every single function and check if they are currently active,

.

For example, let’s say I have Functions as;
SuperBlock, Avoid, ThornMail, CounterAttack, X, Y, Z, SuperArmor … etc

.

And only “SuperBlock” is activated for the player so I only need to call SuperBlock function.

.

So basically I am trying to avoid doing this;

bool SuperBlockIsActive = true;
bool AvoidIsActive = false;
bool ThornMailIsActive = false;
bool CounterAttackIsActive = false;

public void DefensiveSkills(int damage, string attackType)
{                                                                                       
    if (SuperBlockIsActive)
    {
        SuperBlock (damage, attackType);
    }
    if (AvoidIsActive)
    {
        Avoid (attackType);
    }
    if (ThornMailIsActive )
    {
        ThornMail (damage, attackType);
    }
    if (CounterAttackIsActive )
    {
        CounterAttack (damage, attackType);
    }
}

....

I tried to solve this problem with “Invoke” function but because I need to pass parameters I couldn’t achieve what I needed.
What I did was creating a list of active function names and calling them with Invoke.

private List<string> ActiveDefensiveSkills = new List<string>(); 

private void StartTurn()
{
    foreach (string function in ActiveDefensiveSkills)
    {
        Invoke(function, 0f);
    }
}

I need to call functions with parameters like damage, attackType, etc.
I also tried using global variables but because the compiler does not wait for Invoke and continue the code, it doesn’t work either.

.

How can I solve this problem?
Is there even a way?

.

Edit:
I decided to share the whole idea just to be more clear so you don’t need to read the rest if you are not interested in my code, I am doing a turn-based game and some attack directly hurt the player (Like getting attacked with a sword) and some attacks give damage each turn (Like burn damage for 3 turns).

using System.Collections.Generic;
using UnityEngine;


[System.Serializable]
public class Conditions
{
    public string attackName;
    public int attackDamage;
    public string attackType;
    public int attackTurn;
    public int attackPenetration;

    public void NewCondition(string aName, int aDamage, string aType, int aTurn, int aPenetration)
    {
        attackName = aName;
        attackDamage = aDamage;
        attackType = aType;
        attackTurn = aTurn;
        attackPenetration = aPenetration;
    }
}


public class Player : MonoBehaviour
{

    [Header("[Current]")]
    [SerializeField] private int currentHealth = 0;

    [Header("[Basic]")]
    [SerializeField] private int maxHealth = 100;

    [Header("[Defensive]")]
    [SerializeField] private int armor = 0;
    [SerializeField] private int magicResistance = 0;
    [SerializeField] private float blockChance = 0f;

    [Header("[ActiveSkills]")]
    [SerializeField] private List<string> ActiveDefensiveSkills = new List<string>(); //Trigger when get attacked
    [SerializeField] private List<Conditions> ActiveConditions = new List<Conditions>(); //List of active Conditions


    //---MainFunctions----


    private void StartTurn()
    {
        //Activated at the beginning of Player's each turn
        foreach (Conditions condition in ActiveConditions)
        {
            Defense(condition.attackDamage, condition.attackType, condition.attackPenetration);
            condition.attackTurn--;
            if (condition.attackTurn <= 0) ActiveConditions.Remove(condition);
        }
    }

    public void GetAttacked (string attackName, int attackDamage, string attackType, int attackTurn, int attackPenetration)
    {
        //Enemy calls this function and everything starts from here, after applying the first damage if the attack is turn-based then it gets added to conditions.

        Defense(attackDamage, attackType, attackPenetration);

        if (attackTurn > 1)
        {
            attackTurn--;
            Conditions condition = new Conditions();
            condition.NewCondition(attackName, attackDamage, attackType, attackTurn, attackPenetration);
            ActiveConditions.Add(condition);
        }

    }

    private void Defense(int attackDamage, string attackType, int attackPenetration)
    {
        //Function called when the player takes damage


        //This is where I have a problem, I want to call only active defensive skills (their name is stored in "ActiveDefensiveSkills")
        foreach (string function in ActiveDefensiveSkills)
        {
            // attackDamage = function(int attackDamage, string attackType, int attackPenetration); i want to do something like this
        }

        TakeDamage(attackDamage);
    }

    private void TakeDamage(int damage)
    {
        //Implement the damage
        Debug.Log("You took " + damage + " dmg");

        if (currentHealth - damage <= 0)
        {
            currentHealth = 0;
            Death();
        }
        else
        {
            currentHealth -= damage;
        }
    }

    private void Death()
    {
        Debug.Log("You Died");
    }

    //---SkillFunctions---

    private int Block(int attackDamage, string attackType, int attackTurn, int attackPenetration)
    {
        //Player can block the some of the incoming damage
        if (blockChance >= Random.Range(1, 101))
        {
            Debug.Log("Damage blocked");
            return attackDamage / 2;
        }
        else return attackDamage;
    }

    private int Resistance(int attackDamage, string attackType, int attackTurn, int attackPenetration)
    {
        //Decrease the incoming damage     
        int result = armor - attackPenetration;
        if (result < 0) result = 0;

        return attackDamage - result >= 0 ? attackDamage - result : 0;
    }

    private int LastStand(int attackDamage, string attackType, int attackTurn, int attackPenetration)
    {
        //Player health will not drop under 1
        return currentHealth - attackDamage >= 1 ? attackDamage : 0;
    }

    private int Invulnerability(int attackDamage, string attackType, int attackTurn, int attackPenetration)
    {
        //Player become invulnerable to any kind of attacks
        return 0;
    }

}

Fore sure there is a way, many ways!

Given the (few) details you gave about your system, a simple solution would be to make all your functions with the same signature: void FunctionName( int damage, string attackType ), even if all the parameters are not used inside the function.

Then, you would use a delegate to “hold” references to the functions to call.

private System.Action<int,string> activeDefensiveSkills;

private bool SuperBlockIsActive
{
    set
    { 
         activeDefensiveSkills -= SuperBlock;
         if( value ) activeDefensiveSkills += SuperBlock
    }
}

private bool AvoidIsActive
{
    set
    { 
         activeDefensiveSkills -= Avoid ;
         if( value ) activeDefensiveSkills += Avoid 
    }
}
private bool ThornMailIsActive
{
    set
    { 
         activeDefensiveSkills -= ThornMail ;
         if( value ) activeDefensiveSkills += ThornMail 
    }
}
private bool CounterAttackIsActive
{
    set
    { 
         activeDefensiveSkills -= CounterAttack ;
         if( value ) activeDefensiveSkills += CounterAttack 
    }
};

 public void DefensiveSkills(int damage, string attackType)
 {                                                                                       
     if (activeDefensiveSkills != null)
     {
         activeDefensiveSkills(damage, attackType);
     }
 }

But, with more details about your system, a more flexible and less verbose solution may be considered

One way that I would do this is by having kind of a “sack” of skills that can be added or removed from the script that is attached to your player. The actual actions are private and there are basically three public methods: a) one to add a skill, b) one to remove a skill; and c) one to evoke all the skills that are currently enabled. I typically use a Dictionary<> of skills (through an enum) which map to a delegate (all of which have the same signature). Here is an example:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum Skill
{
	SuperBlock,
	Avoid,
	ThornMail,
	CounterAttack
}

public class PlayerActions : MonoBehaviour
{
	private Dictionary<Skill, Action<int, string>> actions = new Dictionary<Skill, System.Action<int, string>>();
	
	// Awake is called when the script instance is being loaded.
	protected void Awake()
	{
		// Add 2 skills here (should be called from the outside)
		this.AddSkill(Skill.SuperBlock);
		this.AddSkill(Skill.Avoid);
		
		// Test the use of skills here, but UseDefensiveSkills() should be called from the outside...
		this.UseDefensiveSkills(3, "simple");
		
		// Remove a skill here (should be called from the outside)
		this.RemoveSkill(Skill.SuperBlock);
		
		// Test the use of skills here, but UseDefensiveSkills() should be called from the outside...
		this.UseDefensiveSkills(10, "advanced");		
	}

	public void UseDefensiveSkills(int damage, string attackType)
	{
		foreach (Action<int, string> action in actions.Values)
		{
			action(damage, attackType);
		}
	}

	public void AddSkill(Skill skillType)
	{
		if (!actions.ContainsKey(skillType))
		{
			switch (skillType)
			{
			case Skill.SuperBlock:
				actions.Add(skillType, this.SuperBlock);
				break;
			case Skill.Avoid:
				actions.Add(skillType, this.Avoid);
				break;					
			case Skill.ThornMail:
				actions.Add(skillType, this.ThornMail);
				break;					
			case Skill.CounterAttack:
				actions.Add(skillType, this.CounterAttack);
				break;
			default:
				break;
			}
		}
	}

	public void RemoveSkill(Skill skillType)
	{
		if (actions.ContainsKey(skillType))
		{
			actions.Remove(skillType);
		}
	}

	private void SuperBlock(int value, string text)
	{
		Debug.Log("SuperBlock!");
	}

	private void Avoid(int damage, string attackType)
	{
		Debug.Log("Avoid!");
	}

	private void ThornMail(int damage, string attackType)
	{
		Debug.Log("ThornMail!");
	}

	private void CounterAttack(int damage, string attackType)
	{
		Debug.Log("CounterAttack!");
	}
}