Should the shield be its own entity
that tracks the location of the
player? That might make it hard to
implement the damage filtering. It
also kinda blurs the lines between
attached components and entities.
Edit: I think there's not enough "autonomous behaviour" for a separated entity. In this specific case, a shield follows the target, works for the target and does not outlive the target. While I tend to agree that there's nothing wrong with the concept of a "shield object", in this case we're dealing with behaviour, which fits just fine in a component. But I'm also an advocate of purely logical entities (as opposed to full-blown entity systems in which you can find Transform and Rendering components).
Should the shield be a component that
houses other components? I've never
seen or heard of anything like this,
but maybe it's common and I'm just not
deep enough yet.
See it in a different perspective; adding a component adds other components as well, and upon removal, the additional components are gone too.
Should the shield just be a set of
components that get added to the
player? Possibly with an extra
component to manage the others, e.g.
so they can all be removed as a group.
(accidentally leave behind the damage
reduction component, now that would be
fun).
This could be a solution, it would promote reuse, however it is also more error prone (for the issue you mentioned, for instance). It's not necessarily bad. You might find out new spell combinations with trial and error :)
Something else that's obvious to
someone with more component
experience?
I'm going to elaborate a bit.
I believe you noticed how some components should take priority no matter when they've been added to an entity (this would answer your other question as well).
I'm also going to assume we're using message-based communication (for the sake of discussion, it's just an abstraction over a method call for the moment).
Whenever a shield component is "installed", shield component message handlers are chained with a specific (higher) order.
Handler Stage Handler Level Handler Priority
In Pre System High
Out Invariant High
Post AboveNormal
Normal
BelowNormal
Low
System Low
In - incoming messages
Out - outgoing messages
Index = ((int)Level | (int)Priority)
The "stats" component installs a "damage" message handler at the In/Invariant/Normal index.
Every time a "damage" message is received, decrease the HP by its "value" amount.
Fairly standard behaviour (put in some natural damage resistance and / or racial traits, whatever).
The shield component installs a "damage" message handler at the In/Pre/High index.
Every time a "damage" message is received, deplete the shield energy and substract
the shield energy from the damage value, so that the damage down the message
handler pipeline is reduced.
damage -> stats
stats
stats.hp -= damage.value
damage -> shield -> stats
shield
if(shield.energy) {
remove_me();
return;
}
damage.value -= shield.energyquantum
shield.energy -= shield.energyquantum;
stats
stats.hp -= damage.value
You can see this is pretty flexible, albeit it would require careful planning when designing component interaction, as you're going to have to determine in which part of the message handling pipeline component message event handlers are installed.
Makes sense? Let me know if I can add more detail.
Edit: regarding multiple component instances (two armour components). You can either keep track of the total instance count in just one entity instance (this kills per-component state, however) and just keep adding message event handlers, or make sure your component containers allow for duplicate component types in advance.