how to add pointers to functions (that have parameters) without parameters and call them later with custom parameters C++

74 Views Asked by At

I'm gonna try to keep this short. As much as I can.

I have a Spell class which is mostly duration, cooldown, damage, type of damage, etc.. I have SpellManager class which is supposed to instantiate spells, give them to players or enemies and keep track of every (pointer) spell instantiated. Every spell has 3 effects. Like fireball has (damage over time, aoe, and something else) - so different abilities with different prameters.

I have something like

vector<function<void()>> _spell_effects;

in my Spell.h and have a function

void Spell::SetSpellEffects(vector<function<void()>> spell_effects) {
    _spell_effects = spell_effects;
}

Which sets the effects to instance of the spell after being instantiated by Manager which works like this

void SpellManager::CreateSpell(Character* spell_owner, ESpellID spell_id) {

    Spell* spell = Spell::CreateSpell(spell_id);
    string spell_name = GameplayStatics::GetEnumString(spell_id);
    map<string, Spell*> spell_instance;
    spell_instance[spell_name] = spell;

    if (dynamic_cast<PlayerCharacter*>(spell_owner) != nullptr) {
        _instanced_spells.push_back(spell_instance);
    }
    else {
        _instanced_enemy_spells.push_back(spell_instance);
    }

    GameplayStatics::AddSpellToCharacter(spell_owner, spell);
    
    AddSpellEffects(spell);
}

void SpellManager::AddSpellEffects(Spell* spell) {


    CharacterData data(ECharacterClass::BARBARIAN);
    PlayerCharacter* player = new PlayerCharacter(data.GetCharacterData(), data.GetPlayerAttributeData());

    CharacterData enemy_data(ECharacterClass::ENEMY);
    EnemyCharacter* enemy = new EnemyCharacter(enemy_data.GetCharacterData());
    enemy->name = "dzura";

    vector<Character*> enemies;
    enemies.push_back(enemy);

    vector<function<void()>> _effects;
    
    function<void()> Fireball_Default = [this, player, enemies]() {
        this->Fireball_Default(player, enemies);
    };
    _effects.push_back(Fireball_Default);

    function<void()> Fireball_Level2 = [this]() {
        this->Fireball_Level2();
    };
    _effects.push_back(Fireball_Level2);

    function<void()> Fireball_Level3 = [this]() {
        this->Fireball_Level3(5);
    };
    _effects.push_back(Fireball_Level3);

    spell->SetSpellEffects(_effects);

}

so in main i can call them like this

    CharacterData data(ECharacterClass::BARBARIAN);
    PlayerCharacter* player = new PlayerCharacter(data.GetCharacterData(), data.GetPlayerAttributeData());

    SpellManager spell_manager(player);

    spell_manager.CreateSpell(player, SPELL_FIREBALL);

    _enemies.push_back(enemy);

    player->GetActives()[0]->InvokeEffect(1);

The spell manager then keeps track of pointers to Spell and handles everything else. Now with InvokeEffect(1) I can Invoke the effect of the spell 1-3 but they are always called with the same parameters i provided in lambdas. My question is. Could someone refactor the code, tell me how to Instantiate a Spell, then add 3 void functions to it in SpellManager (thats where they're defined), then pass that vector to instance of Spell, and call them at user input with InvokeEffect(1-3) BUT also with custom parameters?

I tried many things I tried different data structures I tried adding the Spells' Effects directly to Spell, I either couldn't get it to work or to compile. This provided code works only with starting data. Should I somehow pass these in the constructor of the spell? How would I do that? How would I call the spells? Some of the data structures I used


using Function = std::function<void(SpellManager&, vector<any>)>;

vector<map<string, vector<void(*)()>>> _effect_map;

and these ones work, where I keep track

vector<map<string, Spell*>> _instanced_spells;

vector<map<string, Spell*>> _instanced_enemy_spells;

vector<pair<int, pair<string, Spell*>>> _active_spells;

I can change whatever it takes in my design, I need this to work.

1

There are 1 best solutions below

0
crvenkapavica On

I Fixed the problem using std::function<void(param1, param2, param3)> instead of void pointer and mainstereaming each function where it passes the instigator, the target(s) and a pointer to a params struct which can hold as many parameters a sneeded. now when they all have the same signature its easy to solve.