The system of character abilities is perhaps the most demanding of flexibility in the game. It is not possible at the design stage to predict which spells will appear in the final version or subsequent updates. This post will be about how I have abstracted the process of performing abilities.
Ability itself is nothing but a set of actions. The minimum interface of ability consists of one method: “apply”, but not everything is so simple and about the complexities under the cat.

Every ability begins with a series of checks whether it is possible to apply it. Among them, the usual such as checking recharge, the presence of mana, checking the distance and others. Already here it is clear that not all checks are necessary for all abilities. For example, there are abilities applied at any distance. That is, different abilities require different sets of checks before execution. However, it is clear that many of the checks will be repeated, and often for many abilities the same set of checks is required.
')
The total part of the checks will be logically repeated, and therefore must be consistently changed, that is, in all places at once. In this case, the sets of parts of checks in the general case will be different.
If the parts of the checks are selected into separate objects that implement one interface and are built into a singly-connected list, then the pattern of a chain of duties will be obtained.

In case of a successful check in the link, the check in the next link will start, if there is no next link, then the entire check can be considered successful. In addition to the check itself, the link may also contain an error handler. For example, if, when checking for the presence of mana, it turned out that it is not enough, then the link may notify the player of this.
Using the chain of responsibilities for the [Power Shot] ability, we can easily insert an additional link, which checks whether the character is wearing a bow or a link, which checks that the character has a health level below 30%, for the [Second breath] ability.
Roll back and remember that there are chains of checks that are the same for many abilities. Let's select the essence of the request for executing an ability and describe each type of chain of checks by its class.
The request is only required to make a chain of duties, run it and cancel when the player gives the appropriate command.

We will make chains in query implementations.
At the moment we have already learned how to make flexible checks for the ability to perform abilities. Now, in the case of a successful test, it is necessary to fulfill the ability.
I preferred to do this without changing interfaces, adding the last always successful link that the ability performs as a side effect. Here is its approximate implementation:
public class TerminalChecker: ICastChecker { CastChecker next { get; set; } ISkill skill; public TerminalChecker(ISkill skill) { this.skill = skill; } public bool check() { skill.cast(); return true; } }
This implementation allows us to make requests asynchronous. This is useful when we need additional information from the user. For example, the ability must be applied to a certain area that the player selects with the mouse. The game at this time to stop, of course, impossible.
Now we need to match requests with abilities. We do this, of course, with the help of polymorphism, adding a property to the interface ability. At this stage, we have expanded the ability to this interface:

After all the work done, let's think about what ability is. In the current implementation, this is a set of actions preceded by a series of checks. Note that at a high level, we are not dependent on the specific game logic. With the initial idea to describe the system of abilities in relation to spells, we got a system that, according to certain rules, gives or does not allow us to perform arbitrary actions.
Thanks to this property, this system can describe any modification of the game world. For example, a bargain or building construction team.
Let's take another look at everything.

In this example, Sprint's ability is a conventional no-purpose ability, the class that implements the request for such capabilities is NontargetCastRequest, which in turn makes up a chain of checks from ManaChecker, CooldownChecker and TerminalChecker.
The calling code does not depend on the details of the implementation of this system, that is, we will not break the game logic by adding or changing the ability.
This is the system of character abilities in the minimum form. In this model, there are not enough tools for alerting the calling code, transferring abilities to the user interface and other little things in life. You can think about them yourself.