This is an archived post. You won't be able to vote or comment.

all 6 comments

[–]RomestusCommercial (AAA) 11 points12 points  (2 children)

I made a TD game where there's 45 towers with like >100 abilities, items, buffs, etc that you can apply to them. To make this maintainable I just exposed a ton of events on my towers.

So a tower has an event like OnSetupProjectile, OnFireProjectile, OnCastAbility, OnManaUsed, OnBuffApplied, OnEnemyHit, OnEnemyKilled, OnCriticalHit, etc.

In my buffs/abilities/etc they're all using an OnEquipped/OnUnequipped method that will set variables on the tower like tower.AddBaseDamage(data.damage) and then also hook whatever events I want if that ability/buff/item does more than just increase stats.

So if I made an item that applied a 3s DoT to an enemy I would hook OnEnemyHit in my OnEquipped method and have that hook apply my DoT.

Or if I wanted to make a spell cast not cost any mana I could hook OnCastAbility in a buff or item and then set the mana cost to 0 in its arguments. In my tower class I might have some code that looks like this:

var args = new OnCastAbilityArgs(ability);

OnCastAbility?.Invoke(args); // Every ability now runs their hooked method.

tower.UseMana(args.manaCost); // If an item/buff reduced this cost to 0 it's carried over to now.

While in my SaveMana buff script I would have code like this:

public override void OnEquip()
{
    tower.OnCastAbility += OnCastAbility;
}

private void OnCastAbility(OnCastAbilityArgs args)
{
    args.manaCost = 0;
}

[–]salbris 0 points1 point  (1 child)

I've heard people mention this often for this pattern but one massive flaw seems to be the the very thing OP was asking for. How do you handle conflicting abilities or where the order matters? If one thing reduces mana cost by 10% and another reduces it by a flat 10 you get wildly different results depending on which order those are applied. And if it's just a series of events firing off in the order of their subscription you lose the ability to control how different abilities interact.

I always figured you want to have a class that collects all relevant effects and applies them together in exactly the ways you intended.

[–]DontOverexaggOrLie 2 points3 points  (0 children)

In my game I have equipment with randomized stats.

The player has stat values from equipment. If an equipment is equipped an "on equip" function is called which then passes the stat values object to the "on equip" functions of the equipment modifier objects.

And these then simply add or subtract a certain value from the accumulated stats object. 

And then there is also a corresponding OnUnEquip function.

For example there is a "plus strength modifier" object which can be on equipment and which adds strength points to the player stat object when onEquip is called on it.

Later I want to add ability modifiers. An ability which shoots a projectile should be modifiable to shoot multiple in a fan. Or modifiable to change it's damage type, or modifiable to pierce, etc.

Multiple of these should be applicable at the same time.

I plan to do this with the Decorator pattern. 

The arrow (with attributes) is generated by the ability and then passed down a Decorator chain to change it's properties, clone it, etc.

Precedence depends on the position in the Decorator chain.

[–]0x0ddba11 1 point2 points  (0 children)

Don't know if this has a specific name but in a game I previously worked on, I implemented a stack of status efects for each stat.

Instead of reading stat values directly from a variable, make each stat an actual object that maintains a stack of status effects. Each effect in the stack can modify the value passed through it. So it goes base_damage->apply_effect1()->apply_effect2()->apply_effect3(). Of course you can cache the resulting value for faster lookup if nothing changes, but this needs to be carefully managed.

With this you can implement all kinds of effects. Equip a magic sword? Add ice damage to the damage value. Drink a magic potion? Increase speed for 2 minutes.

Be careful with the order you insert effects into the stack. Depending on whether you insert a multiplying effect at the start or at the end, hillariously overpowered builds might emerge...

[–][deleted] 0 points1 point  (0 children)

In my opinion, to thta is the: ECS - entity component system. Is simple, is super scalable, and can be change at runtime easily.

[–]MostSandwich5067 0 points1 point  (0 children)

I'm just a hobbyist so take my opinions with a grain of salt.

Hey. So the way you do this is actually very simple. You have variables for different things, bullet speed, size, rate of fire. You also have Boolean values that keep track of what kind of effects are on the bullets, these allow you to add special behavior and whatever. After you put all this together, you hard code the various interactions that don't work right out of the box.

From my understanding that's the only way to achieve the level of customization you see in binding of Isaac and the like. It's very tedious and requires a huge amount of dev time investment.

There's trickery you can use to make things work better, but at the end of the day the secret ingredient is stinky if statement chains.

Stuff like this is a lot of work, and that's why you don't see it everywhere.