Not sure I understand exactly what you mean by “intervene”. I will elaborate little more about how things work in a couple of my projects:
To be honest, maybe this is a brute force way, I don’t really know what that means exactly. But when it comes to elegant code, I don’t care about that. What I care about is standardization. Standardized code is easy to maintain code. So this is the basic pattern I use concerning events:
For any important thing that happens in the game, I have a Pre event, an actual event, and a post event.
So for example, we interact with some item.
Interaction Pre
Interaction Actual
Interaction Post
Often times I do not use all of these events, but I just set them up by default because sometimes the event sequence order does matter.
So an inventory system might listen for Interaction post, and at that time you know that the interaction system will have processed whatever was picked up appropriately. Whereas if there is only a single “interaction” event, you might be doing the processing at the same time the inventory system is asking what the item is.
I’ve found that this staged sequence of events makes pretty consistent and maintainable code for most situations.
example pseudo code:
- Interaction item becomes valid (this is polled for by a collider on the character)
- player presses interaction input
- Preinteraction event dispatched
- interaction system checks if interaction item is valid
- isValid → interaction event dispatched
- valid interaction item is queried to determine what it is
- interaction items identifier is passed along with Post Interaction Event dispatch
- Inventory system binds to the Post Interaction Event and sees that ammo type item was picked up, and handles what to do with it
Some of those events might be redundant in some situations. Like you might not care about pre or actual event with some systems, they could bind to either event. That is just nature of standardization, there is some inefficiency. But overall you gain maintainability.
I would not want for abilities/skills to have to know about other ones. Events can live in the game state, game mode, a subsystem, game instance, or a component which attaches to one of those. This way there is a globally accessible class for calling the events and binding to them. Like a central relay tower. Actors in the level all know about it, but not each other (or skill objects associated with any actor). It facilitates communication between them but breaks any hard links between them.
As for data, I would not want to keep that within skills/abilities themselves. Rather in some other global access storage. Can be game mode or similar, or component which attaches.
So then all important data that any skill wants to query is in a single place. Definitely don’t want for X skill to have to query Y skill.
This might be too high level to be useful, but big ideas behind this architecture have more written about them here: