In our game we use a system where the "damage" event can return a value to tell the bullet/damaging object weather it actually dealt damage (and that object can then act accordingly, i.e. disappear, continue on or explode or anything like that). All attacks have a unique ID, a team, and potentially an "owner" and so on, and the damage event holds an optional pointer to a such structure.
This allows the enemies and objects to build pretty complex/advanced behaviors while keeping the "bullets"(most of the times its bullets, but we have other things as well) as small and simple as possible (more lean and mean?). In our case it's up to the enemies to remember the last few attack IDs that damaged them in order to stop single attacks from damaging them multiple times. The amount of such enemies that care about this is much fewer and consistent than the amount of bullets and attacks, and to my experience let us manipulate much less data much less frequently.
This might though seem like it could potentially cause a problem where too many attacks damage one enemy at the same time so that it's list over recent attacks get full and attacks start to deal damage again, though that's only theoretical, something like that have so far never happened, and we can easily add a safety system to detect such things during development so we can know if we should make the list larger or not (even detecting if the list get close to being full/start looping, to have a safety margin for release).
Anyway, this allows us to build exactly what we want, and a single bullet can on its own decide if it want to hurt the same target multiple times by changing its attack ID, so a boomerang could potentially change its attack ID when it’s about to turn around and go back, or through some other criteria.
Sure, this could also be achieved with the handmade hero system if you could just tell the attack to clear out its list of damaged entities, which would enable it to damage everyone again.
I like the system we have in our game, as it give us a lot of control and can create really complex behaviors and rules where we need it, i.e. some enemies sometimes reflect bullets, but the bullet never have to care about that. All it knows it that it tried to damage an entity that didn’t take damage, while the entity itself have a lot of checks to make sure that the conditions are correct for a reflection. Same goes for some shields that allow friendly bullets to pass through but blocks enemy bullets and so on. The bullets are very simple and don’t handle that kind of logic that is only relevant for a few entities in the game. Also, most entries in the game don’t care for being damaged, and will then not store any attacks. If the damaged entities were stored in the bullet, the list could potentially get very long if all entities were stored (grass flowers, everything the bullet potential passes through, it can with our system still react to the bullet, but it don’t need to keep track of anything advanced cross frames). Alternatively the bullet would need some kind of logic for what entities to store, but to me that sounds a bit unwieldy, but it’s all down to preference in the end I guess.
I can shine light on one small potential issue we have encountered with this system though (no system is perfect?). In our case bullets have a little more complex system, where they don’t count hits that don’t deal damage to an object if the hit only intersected the target with the bullets radius. It only collide with none damaged objects if it intersects with an inner radius… which makes large bullets much more manageable and more translates to what the player actually would expect. But as that system is entirely up to the bullet to handle and nothing any enemy know anything about, it has a small implication, as the shield I talked about earlier, don’t know anything about this and will potentially reflect bullets if they at intersect at all and send them the damage event. This makes it harder to shoot past a shield that it is to shoot past a normal wall, which can be hard to communicate to players. I have considered adding such intersection info to the attack strut to let the shield take part of that info, but it too late now as the game is already out, and I’m not sure that I’m happy with adding even more complexity to the shields damage handling, and then it’s also the aspect of exposing even more parameters to all other damage events even though most would not care about it. I’m not sure how to solve this with other systems either, but I don’t like that the information that need to be transferred between objects can grow potentially pretty large (though that might always be the case when dealing with complex rules).
Damage systems are complex ^^;… and a lot of the complexity comes from just trying to do what the player expects to happen so they don’t feel "cheated" on. One would think simple rules would be best, but I’ve found that at least with 3D games, that’s sadly not true that often, as the players perception of the world is often very “wrong”/incorrect, or skewed, or what to call it…
I would be very happy to hear anyone else thoughts/insights on these kind of systems :)
*EDIT* Oh, and it should be noted that we built very "gamey" rules here... if we were to actually handle more realistic bullet penetration of objects at the same time, we would probably have to add some other logics instead of just “have taken damage”, but I would still prefer the entity that's getting hit to handle those calculations for the most time, as it could easily determine how advanced that calculation need to be depending on the object in question… I.e. a wall with metal bares and other materials inside it could potentially simulate that, but a simple wooden wall could just do a calculation on wood thickness and impact angle and so on… but then again… if the game only used realistic bullets I would probably do handle those things in some other way… this system I talk about is built to allow advanced rules and interactions…