Skip to content

Abilities and Triggers

Types of abilities

The most familiar type of ability for mafia players is the "activated" ability, where the player decides when to "activate" the ability. This is represented in the engine by the Ability type. Some examples: voting, night kills, protection, role blocking, double-voting.

"Automatic" or "passive" abilities are Triggers, which perform actions in response to external events. Though the player may percieve these to be different (e. g. having multiple lives seems different than being a Paranoid Gun Owner), the implementation is similar: both handle particular Event types and create Actions in response.

Note that both are derived from ATBase. This class automatically applies Constraints to objects' event handlers.

Abilities

An Ability is probably the easiest case to implement. It handles EActivate events, which signal that we want to activate a particular ability, and responds with a corresponding Action, given the sent parameters.

To create a custom Ability, you can either Ability and override the activate() (and possibly __init__()) method(s):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class MyAbility(Ability):

    def __init__(
        self,
        game: Game,
        /,
        owner: Actor, 
        name: str,
        desc: str = "Does arg2 to arg1.",
        *,
        use_default_constraints: bool = True,
    ):
        super().__init__(
            game, owner, name=name, desc=desc, 
            use_default_constraints=use_default_constraints,
        )

    def activate(self, arg1: Actor, arg2: str) -> Optional[List[Action]]:
        return []

Alternatively, you can generate one from a function or pre-existing Action:

1
2
3
MyAbility = Ability.generate(
    func, params=["arg1", "arg2"], name="MyAbility", desc="Does arg2 to arg1."
)

This does the same as the above, but auto-generates the MyAbility class (note that "name" in the generator is the class name, while "name" in the __init__ function is the display name of the ability.)

Triggers

Abilities that are triggered in other ways (e. g. passive abilities) should subclass Trigger directly. You can add any event handlers you want, and the trigger will automatically apply the correct Constraints.

Constraints

Generally, abilites can only be used under particular conditions. The most typical ones are:

  • The source (Actor, parent of ability) must be alive.
  • The target must be alive (if an Actor).
  • You can use the ability only during the (day) or (night).
  • Only one player from your Faction may use this ability, and once per night.

These conditions are represented by Constraint objects. If a Constraint is violated, the Ability or Trigger doesn't actually return the action(s) back to the action queue that called it, which means the ability or trigger don't actually run. It will currently just send a warning to the logging module... this is not super elegant, but it works well enough for now. 😅