Source code for pyzork.modifiers
from .enums import StatEnum
from .entities import Entity
[docs]class Modifier:
"""Modifiers define changes to the stats of the entity they are attached to. They can do stuff like
increase the attack of an ally for 3 turns or deal poison damage to an enemy over 5 turns. You can
stack as many different modifiers as you want on a single entity but you can never give an entity
the same modifier twice. This is the default behavior for every entity but it can be overwritten.
Parameters
-----------
duration : int
How many "turns" a buff lasts. A turn is all the players and enemies present in the battle
doing one action. This can be a positive int to demonstrate a temporary modifier which will
expire after a certain number of turns or it can be -1 to represent a permanent buff which
will only expire at the end of the battle.
stat_type : Optional[StatEnum]
An optional StatEnum that defines which stat this modifiers affect, this is only necessary
if you have defined the `buff` method.
name : Optional[str]
An optional string to give a name to the modifier, if this is not provided, the library will
fall back to the docstring of the class and if this isn't provided either it will fallback to
the nameof the class
description : Optional[str]
An optional string to give a short (or long) description about the buff and its effects. If
this is not provided the library will fall back to combining the docstring of the `buff`
and `effect` method.
Attributes
-----------
name : str
Name of the ability
description : Optional[str]
Optional description of the ability
duration : int
How long the ability lasts
stat_type : Optional[StatEnum]
Optional enum that dictates which stat is affected
"""
def __init__(self, **kwargs):
if not hasattr(self, "stat_type"):
self.stat_type = kwargs.pop("stat_type", StatEnum.null)
if not hasattr(self, "duration"):
self.duration = kwargs.pop("duration")
if not hasattr(self, "name"):
if "name" in kwargs:
self.name = kwargs.pop("name")
else:
self.name = self.__doc__ if self.__doc__ else self.__class__.__name__
if not hasattr(self, "description"):
self.description = kwargs.pop("description", f"{self.buff.__doc__} {self.effect.__doc__}")
def __hash__(self):
return hash(self.__class__.__name__)
def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
return self.__class__.__name__ == other.__class__.__name__
def __repr__(self):
return f"<{self.name} duration={self.duration} stat={self.stat_type.name}>"
def __str__(self):
return self.name
[docs] def is_expired(self):
"""Simple method to check if is a buff is expired.
Returns
--------
bool
Whether or not the modifier is expired
"""
return self.duration == 0
def calc(self, entity : Entity):
"""This is normally the method that would have to be implemented by the user but because
buffs are timed we use this to check if they're still valid before calling the logic."""
if not self.is_expired():
return self.buff(entity)
[docs] def buff(self, entity : Entity):
"""Method to implement to provide a stat buff to an entity. This method is called everytime
the library calculates the stat that this modifier changes. There is no guarantee that this
method will only be called once per turn so don't make any permanent changes to the player
such as restoring health or granting experience.
Parameters
-----------
entity : Entity
The entity this modifier is attached to.
"""
pass
[docs] def effect(self, entity : Entity):
"""Method to implement to do a once per turn change to an entity. This method is called
exactly once per turn (given the buff is not expired) during the end turn phase. This is where
you can do stuff like restore or take away health from the entity or make other permanent changes
that the entity will carry on beyond this battle.
Parameters
-----------
entity : Entity
The entity this modifier is attached to.
"""
pass
def end_turn(self, entity : Entity):
"""Method called at the end of the turn for each buff to show time passed and left until the buff expires.
Permanent buffs are set to -1"""
if not self.is_expired():
self.effect(entity)
if self.duration > 0:
self.duration -= 1
[docs] @classmethod
def add_effect(cls, **kwargs):
"""Decorator function to allow the user to define an effect by decorating a function. Takes the
same parameters as the class. """
def decorator(func):
if not cls is Modifier:
cls.effect = func
cls.description = f"{cls.description} {func.__doc__}"
return func
else:
new_class = type(func.__name__, (cls,), {
"duration": kwargs.pop("duration"),
"effect": func,
"name": kwargs.pop("name", func.__name__),
"description": kwargs.pop("description", func.__doc__)
})
return new_class
return decorator
[docs] @classmethod
def add_buff(cls, **kwargs):
"""Decorator function to allow the user to define a buff by decorating a function. Takes
the same parameters as the class. Since this specifically adds a buff, the `stat_type` parameter
is required"""
def decorator(func):
if not cls is Modifier:
cls.buff = func
cls.stat_type = kwargs.pop("stat_type")
cls.description = f"{func.__doc__} {cls.description}"
return func
else:
new_class = type(func.__name__, (cls,), {
"duration": kwargs.pop("duration"),
"stat_type": kwargs.pop("stat_type"),
"buff": func,
"name": kwargs.pop("name", func.__name__),
"description": kwargs.pop("description", func.__doc__)
})
return new_class
return decorator