To simplify the code base and making things more readable, reusable and easier to work with, I added a gameplay abilities and tags frameworks to the code base. The out of the box implementation of UE5 is great but slightly more complex than what I would need in this case and to make it also a good learning experience I decided to make it from scratch.
To add the system to any actor that requires tags and abilities, I made the component that manages it all first. Its called the ActionComponent.
This component manages all tags and abilities present on the parent actor. It will try to start new actions and will check if there are not any blocking actions present that prevent the action from being started.
The Actions (Abilities) themselves are all based of a main class that manages setting tags to the ActionComponent. From that base class individual logic can be added unique to the action that is made. For example, player primary fire logic or sprinting etc. Anything the player can do as an gameplay action should be made into an Action that is managed in the ActionComponent.
Then in the UE5 editor we can use the built in functionality of Tags and add them to the project.
Now we can create a new BP class based on the CPP class for the action we made. In the Editor we can then assign the tags that the action grants and which should block this action from being executed. In this simple example you see the BloodRage tag being granted when the Action is started. It can not be started while its active since the BloodRage tag is also a blocked tag. The player can only start this action when the BloodRage tag is not present in the ActionComponent.
Then the ActionComponent is added to the Actor that can perform actions. Then within the editor we can set all the Actions as default for that Actor. Here you can see all the Actions the player is able to perform. The Act Blocking Actions is empty for the player as this section is only used by the AI.
The AI Actions are slightly more complex as the AI behavior also makes heavy use of this system. In this example below you can see the Magic Projectile being shot by the Cultist enemy. Here we can set the projectile class that the enemy using this action will shoot. How much damage it does etc. You can also see it will grant specific Damage Type tags and it has more blocking tags too. The Damage Type tag is used to add additional effects like damage over time, different hit effects etc.
The AI Actor then gets the ActionComponent and we assign the actions this enemy can perform.
In this case the Act Blocking Actions are also added. These actions are used by the enemy Behavior Tree and will stop all the logic in the BT if any of the assigned actions are found in the ActionComponent. In this case they are things like Stagger, Knockdown and Siphoned.
Only assigning the Actions is not enough, the Actions still need to be started and stopped and thats where the Behavior Tree comes into play.
The BT will first check for the presence of any of the Act Blocking Tags, then it will try to execute an Action based on the success of a check. If it succeeds, it will try and start the Action. If the ActionComponent fails to start it, it will fail the Action and the BT will try the next sequence. It is succeeds it will star the Action, add the Granted Tag to the Action component and execute its code.
That was a high level look into the Actions (Gameplay Abilities) and Tags implementation in Anathema. The system itself has proven to be very valuable and it makes the code base easier to work with and more manageable. There are challenges in the Action code however of which I am not entirely happy with just yet. Especially keeping all the code loosely coupled to a specific actor is challenging but for now it is very much worth the effort and I will refactor things over time when I learn more about C++ and UE5 specifically.