Multiple instances of the same EntityComponent

As EntityComponents are Singletons I can’t add multiple instances of the same component to an entity. I don’t feel comfortable when abusing a component holding a list/array and re-copying everything when a single effect changes.

I know the whole API isn’t meant for having multiple instances, so is there some other clever way to do this?
Why: I have an InfluenceStatsComponent which is like additive, intelligence, 10, so int + 10. But what if the same buff also increases your strength by 30%? multiplicative, strength, 1.3?

The only thing I could imagine is using some kind of collection or array.

If I read this right you have a “buff” - so say health gets + 20% because you ate an apple or whatever. That would be an influence of the influencer.

So if you eat 3 of the same influence one after another it would just reset the influence, not add more (Singleton), but if you added a different influence it would accumulate.

In short, construct influences.

Well but what if the apple gives me health +20% and mana +20%? And when the buff expires I would have to keep track of both the health and mana influence to remove it accordingly, that’s why I wanted to combine them somehow

The buff in this instance is an entity, it’ll have a target component with the target id, and a separate component for each buff

Yes and there is the problem, because I don’t want an “AddHealthPercentageComponent” etc just to be able to add multiple components.

But an even simpler example: We have a chest armor, just an entity. It has +10 int and +30 strength. I could create dummy entities which reference this armor as “Influences”, but then when the armor would be removed (unequipped), I manually have to “garbage collect” these entities. Even worse when taking about decaying things.

I think you may be viewing the buffs in a non es fashion, your int systems don’t care about your hp systems, why are they viewing the same component? You’ll either have all the stats in 1 component and have most of them set to 0 most of the time, or have components for each system that cares about them.

If you want to have items that transfer their buffs to another item then that can be a part of the buffing system, too.

As far as cleaning up children when the parent dies, that’s a separate issue and can pretty easily be covered… either generally or specifically as part of the buff system. For example, if you are using Decay(0) to remove your entities then the buff system could use this to add Decay(0) to anything its tracking for buffs for that entity.

In the end, these are all better than trying to have a list of components which would impose completely different sets of unbreakable limitations.

Each influence should be individual. There’s no need to create an abstraction that fits everything, just make a “magicians potion” buff that does +20 armor and +10 mana. And another that does other buffs.

i were always wonder how they exactly solve this in Path of Exile game of GGG.

for example they have

  • “increased physical damage + 30%”
  • “increased one hand damage + 20%”
  • “increaded damage + 50%”
  • “more damage + 10%”

for them word “more” multiply previous calculation from “increases”

assuming player use one handed physical sword.
it will be BASE_DAMAGE * (1+(30+20+50)/100) * (1+(10/100))

IMO their system is Best in this kind of games.

i can only assume they have BuffComponent for this all entites, and System that “summ everything up” updating damage/hp/etc components to not recalculate same things multiple times.

As Paul said, you will have a StatSystem which keeps track of all stat entities are linked to a target entity. For example in a map:

// Keep track of stats by the target id
Map<EntityId, List<EntityId> statsIndex = new HashMap(); 

when the target entity removed you can also remove all stats it has.

Also if you do not want to query EntityData (specially in case of SqlEntityData which makes a call to DB) every time you need to find a specific stat type on a target, you can instead get it from StatSystem as it already keeps track of them.

And for modifying stat, you can use a StatChange(ModdifyType: Additive, StatType: Intellect, Value: 10) entity buffed to target entity, then stat system is going to listen for the StatChange EntitySet and if it finds a stat with specified type (Intellect in this example) on the target entity it’s going to modify stat value else will ignore it and will remove the StatChange entity.