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.
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.