mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
readme update to support new cache interface (+3 squashed commit)
Squashed commit: [872cb2621] added instance-cache link (thanks aogier!) added tests for cache link fix poison samplebuff fix min tickrate [6bd6ce3e6] fixed infinite-duration ticking buffs [2dbc0594d] tickrate improvements, init hook
This commit is contained in:
parent
d93f4762a0
commit
e77b6d4d74
4 changed files with 63 additions and 36 deletions
|
|
@ -283,20 +283,6 @@ Regardless of any other functionality, all buffs have the following class attrib
|
|||
- They can `refresh` (bool), which resets the duration when stacked or reapplied. (default: True)
|
||||
- They can be `playtime` (bool) buffs, where duration only counts down during active play. (default: False)
|
||||
|
||||
They also always store some useful mutable information about themselves in the cache:
|
||||
|
||||
- `ref` (class): The buff class path we use to construct the buff.
|
||||
- `start` (float): The timestamp of when the buff was applied.
|
||||
- `source` (Object): If specified; this allows you to track who or what applied the buff.
|
||||
- `prevtick` (float): The timestamp of the previous tick.
|
||||
- `duration` (float): The cached duration. This can vary from the class duration, depending on if the duration has been modified (paused, extended, shortened, etc).
|
||||
- `stacks` (int): How many stacks they have.
|
||||
- `paused` (bool): Paused buffs do not clean up, modify values, tick, or fire any hook methods.
|
||||
|
||||
You can always access the raw cache dictionary through the `cache` attribute on an instanced buff. This is grabbed when you get the buff through
|
||||
a handler method, so it may not always reflect recent changes you've made, depending on how you structure your buff calls. All of the above
|
||||
mutable information can be found in this cache, as well as any arbitrary information you pass through the handler `add` method (via `to_cache`).
|
||||
|
||||
Buffs also have a few useful properties:
|
||||
|
||||
- `owner`: The object this buff is attached to
|
||||
|
|
@ -304,6 +290,29 @@ Buffs also have a few useful properties:
|
|||
- `timeleft`: How much time is remaining on the buff
|
||||
- `ticking`/`stacking`: If this buff ticks/stacks (checks `tickrate` and `maxstacks`)
|
||||
|
||||
#### Buff Cache (Advanced)
|
||||
|
||||
Buffs always store some useful mutable information about themselves in the cache (what is stored on the owning object's database attribute). A buff's cache corresponds to `{buffkey: buffcache}`, where `buffcache` is a dictionary containing __at least__ the mutable information below.:
|
||||
|
||||
- `ref` (class): The buff class path we use to construct the buff.
|
||||
- `start` (float): The timestamp of when the buff was applied.
|
||||
- `source` (Object): If specified; this allows you to track who or what applied the buff.
|
||||
- `prevtick` (float): The timestamp of the previous tick.
|
||||
- `duration` (float): The cached duration. This can vary from the class duration, depending on if the duration has been modified (paused, extended, shortened, etc).
|
||||
- `tickrate` (float): The buff's tick rate. Cannot go below 0. Altering the tickrate on an applied buff will not cause it to start ticking if it wasn't ticking before. (pause and unpause to start/stop ticking on existing buffs)
|
||||
- `stacks` (int): How many stacks they have.
|
||||
- `paused` (bool): Paused buffs do not clean up, modify values, tick, or fire any hook methods.
|
||||
|
||||
Sometimes you will want to dynamically update a buff's cache at runtime, such as changing a tickrate in a hook method, or altering a buff's duration.
|
||||
You can do so by using the interface `buff.cachekey`. As long as the attribute name matches a key in the cache dictionary,
|
||||
it will update the stored cache with the new value.
|
||||
|
||||
> **Example**: You want to increase a buff's duration by 30 seconds. You use `buff.duration += 30`. This new duration is now reflected on both the instance and the cache.
|
||||
|
||||
All of the above mutable information can be found in this cache, as well as any arbitrary information you pass through the handler `add` method (via `to_cache`).
|
||||
|
||||
> **Example**: You store `damage` as a value in the buff cache and use it for your poison buff. You want to increase it over time, so you use `buff.damage += 1` in the tick method.
|
||||
|
||||
### Modifiers
|
||||
|
||||
Mods are stored in the `mods` list attribute. Buffs which have one or more Mod objects in them can modify stats. You can use the handler method to check all
|
||||
|
|
@ -418,4 +427,4 @@ and unpause when the object the handler is attached to is puppetted or unpuppett
|
|||
although if you have less than 1 second of tick duration remaining, it will round up to 1s.
|
||||
|
||||
> **Note**: If you want more control over this process, you can comment out the signal subscriptions on the handler and move the autopause logic
|
||||
> to your object's `at_pre/post_puppet/unpuppet` hooks.
|
||||
> to your object's `at_pre/post_puppet/unpuppet` hooks.
|
||||
|
|
@ -116,9 +116,8 @@ class BaseBuff:
|
|||
|
||||
handler = None
|
||||
start = 0
|
||||
# Default buff duration; -1 or lower for permanent, 0 for "instant" (removed immediately)
|
||||
duration = -1
|
||||
|
||||
duration = -1 # Default buff duration; -1 for permanent, 0 for "instant", >0 normal
|
||||
playtime = False # Does this buff autopause when owning object is unpuppeted?
|
||||
|
||||
refresh = True # Does the buff refresh its timer on application?
|
||||
|
|
@ -133,7 +132,7 @@ class BaseBuff:
|
|||
@property
|
||||
def ticknum(self):
|
||||
"""Returns how many ticks this buff has gone through as an integer."""
|
||||
x = (time.time() - self.start) / self.tickrate
|
||||
x = (time.time() - self.start) / max(1, self.tickrate)
|
||||
return int(x)
|
||||
|
||||
@property
|
||||
|
|
@ -145,12 +144,12 @@ class BaseBuff:
|
|||
|
||||
@property
|
||||
def timeleft(self):
|
||||
"""Returns how much time this buff has left"""
|
||||
"""Returns how much time this buff has left. If -1, it is permanent."""
|
||||
_tl = 0
|
||||
if not self.start:
|
||||
_tl = self.duration
|
||||
else:
|
||||
_tl = self.duration - (time.time() - self.start)
|
||||
_tl = max(-1, self.duration - (time.time() - self.start))
|
||||
return _tl
|
||||
|
||||
@property
|
||||
|
|
@ -169,17 +168,18 @@ class BaseBuff:
|
|||
handler: The handler this buff is attached to
|
||||
buffkey: The key this buff uses on the cache
|
||||
cache: The cache dictionary (what you get if you use `handler.buffcache.get(key)`)"""
|
||||
self.handler: BuffHandler = handler
|
||||
self.buffkey = buffkey
|
||||
# Cache assignment
|
||||
self.cache = cache
|
||||
# Default system cache values
|
||||
self.start = self.cache.get("start")
|
||||
self.duration = self.cache.get("duration")
|
||||
self.prevtick = self.cache.get("prevtick")
|
||||
self.paused = self.cache.get("paused")
|
||||
self.stacks = self.cache.get("stacks")
|
||||
self.source = self.cache.get("source")
|
||||
required = {"handler": handler, "buffkey": buffkey, "cache": cache}
|
||||
self.__dict__.update(cache)
|
||||
self.__dict__.update(required)
|
||||
# Init hook
|
||||
self.at_init()
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr in self.cache:
|
||||
if attr == "tickrate":
|
||||
value = max(0, value)
|
||||
self.handler.buffcache[self.buffkey][attr] = value
|
||||
super().__setattr__(attr, value)
|
||||
|
||||
def conditional(self, *args, **kwargs):
|
||||
"""Hook function for conditional evaluation.
|
||||
|
|
@ -249,6 +249,10 @@ class BaseBuff:
|
|||
# endregion
|
||||
|
||||
# region hook methods
|
||||
def at_init(self, *args, **kwargs):
|
||||
"""Hook function called when this buff object is initialized."""
|
||||
pass
|
||||
|
||||
def at_apply(self, *args, **kwargs):
|
||||
"""Hook function to run when this buff is applied to an object."""
|
||||
pass
|
||||
|
|
@ -461,6 +465,7 @@ class BuffHandler:
|
|||
"ref": buff,
|
||||
"start": time.time(),
|
||||
"duration": buff.duration,
|
||||
"tickrate": buff.tickrate,
|
||||
"prevtick": time.time(),
|
||||
"paused": False,
|
||||
"stacks": stacks,
|
||||
|
|
@ -1159,7 +1164,7 @@ def tick_buff(handler: BuffHandler, buffkey: str, context=None, initial=True):
|
|||
|
||||
# Instantiate the buff and tickrate
|
||||
buff: BaseBuff = handler.get(buffkey)
|
||||
tr = buff.tickrate
|
||||
tr = max(1, buff.tickrate)
|
||||
|
||||
# This stops the old ticking process if you refresh/stack the buff
|
||||
if tr > time.time() - buff.prevtick and initial != True:
|
||||
|
|
@ -1172,7 +1177,7 @@ def tick_buff(handler: BuffHandler, buffkey: str, context=None, initial=True):
|
|||
buff.at_tick(initial, **context)
|
||||
|
||||
# Tick this buff one last time, then remove
|
||||
if buff.duration <= time.time() - buff.start:
|
||||
if buff.duration > -1 and buff.duration <= time.time() - buff.start:
|
||||
if tr < time.time() - buff.prevtick:
|
||||
buff.at_tick(initial, **context)
|
||||
buff.remove(expire=True)
|
||||
|
|
@ -1183,6 +1188,7 @@ def tick_buff(handler: BuffHandler, buffkey: str, context=None, initial=True):
|
|||
buff.at_tick(initial, **context)
|
||||
|
||||
handler.buffcache[buffkey]["prevtick"] = time.time()
|
||||
tr = max(1, buff.tickrate)
|
||||
|
||||
# Recur this function at the tickrate interval, if it didn't stop/fail
|
||||
utils.delay(
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ class Poison(BaseBuff):
|
|||
|
||||
def at_pause(self, *args, **kwargs):
|
||||
self.owner.db.prelogout_location.msg_contents(
|
||||
"{actor} stops twitching, their flesh a deathly pallor.".format(actor=self.owner.named)
|
||||
"{actor} stops twitching, their flesh a deathly pallor.".format(actor=self.owner)
|
||||
)
|
||||
|
||||
def at_unpause(self, *args, **kwargs):
|
||||
self.owner.location.msg_contents(
|
||||
"{actor} begins to twitch again, their cheeks flushing red with blood.".format(
|
||||
actor=self.owner.named
|
||||
actor=self.owner
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ class Poison(BaseBuff):
|
|||
if not initial:
|
||||
self.owner.location.msg_contents(
|
||||
"Poison courses through {actor}'s body, dealing {damage} damage.".format(
|
||||
actor=self.owner.named, damage=_dmg
|
||||
actor=self.owner, damage=_dmg
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from evennia.contrib.rpg.buffs import buff
|
|||
|
||||
|
||||
class _EmptyBuff(BaseBuff):
|
||||
key = "empty"
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -372,6 +373,17 @@ class TestBuffsAndHandler(EvenniaTest):
|
|||
handler.cleanup()
|
||||
self.assertFalse(handler.get("ttib"), None)
|
||||
|
||||
@patch("evennia.contrib.rpg.buffs.buff.utils.delay", new=Mock())
|
||||
def test_cacheattrlink(self):
|
||||
"""tests the link between the instance attribute and the cache attribute"""
|
||||
# setup
|
||||
handler: BuffHandler = self.testobj.buffs
|
||||
handler.add(_EmptyBuff)
|
||||
self.assertEqual(handler.buffcache["empty"]["duration"], -1)
|
||||
empty: _EmptyBuff = handler.get("empty")
|
||||
empty.duration = 30
|
||||
self.assertEqual(handler.buffcache["empty"]["duration"], 30)
|
||||
|
||||
@patch("evennia.contrib.rpg.buffs.buff.utils.delay", new=Mock())
|
||||
def test_buffableproperty(self):
|
||||
"""tests buffable properties"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue