Am I approaching implementing perks in the right way?

I’m working on a very simple roguelike game, where each character has an arbitrary number of Perks, and each Perk have widely varied effects ranging from linear buffs to character stats to esoterica like “If you hit an enemy while standing in the same room as a rat, the rat explodes”.

Since the logic is potentially so unique, I’ve been approaching this with inheritance: there’s a parent BasePerk class with a single virtual method Initialize(NPC myCharacter). Every unique perk is a child of BasePerk, and it uses Initialize to hook whatever methods it needs to listen to on the NPC (Attacked, Damaged, Moved, Ate, or whatever else) into event delegates. Thus, the pseudocode of our exploding rat perk might look like

//in the NPC class
public delegate void NPCMessage;
public event NPCMessage Attacked;

void AttackEnemy(Enemy badGuy){
//do stuff
Attacked();
}

//in the Rat Perk class
NPC myCharacter=null;

Initialize(NPC newCharacter){
myCharacter=newCharacter;
myCharacter.Attacked += MyAttackLogic;
}

MyAttackLogic(){

if (myCharacter.adjacentEntities.Contains(Rat)){
Rat.explode();
}
}

The advantage of doing it this way is that it’s very hard to break, but I’m a little worried about using delegates to accomplish this- if every character has, say, ten perks, and each of those perks needs to know when an attack occurs, every single time that character swings I’ll have ten delegates firing, and whatever respective logic they contain executing. Am I prematurely optimizing something that’ll never matter, or is the CPU cost of events high enough to make it worth rethinking my approach?

I’d imagine you would fire off all ten perks at once if a player has those 10 perks. A perk is meant to be an addon, and should activate when it is called, regardless of the situation.

If you are talking about not having the player super overpowered with an insane amount of perks, what you should be controlling is how perks are acquired , kept, and lost.

When a player acquires a perk, is there a max number of perks a player can hold? Is an existing perk overwritten?
Are perks held onto forever or do they have a time limit?
Can perks be lost by other means like suffering too much damage?

I’m not worried about being overpowered persay, since that’s something we can handle with playtesting and tuning (I’m speculatively running with the idea that perks are theoretically unlimited, but progression is structured so most characters will only earn a handful), but I was a little worried about the computational cost of having tons and tons of events everywhere. I suspect there’s no way around it (since the whole idea of a perk is predicated on a class somewhere in the world hearing a notification and responding to it), but I thought it was worth checking in case experienced programmers caught wind of the idea and started screaming in horror over concerns I didn’t know to have :slight_smile:

That looks fine, and it’s the way I’d probably do it too. If you didn’t use an event you’d have to do something else like loop through a list of all the perks on the character anyway, which is going to be almost exactly the same performance-wise. Events are pretty fast.

Also, FYI, you can shorten your event declarations; you can use System.Action instead of making a blank delegate type. Instead of:

public delegate void NPCMessage;
public event NPCMessage Attacked;

Just do:

public event Action Attacked;
1 Like

Ooh, thank you for bringing that up! I had no idea that was possible, shortening things is always better.

1 Like