Start to add timer component

This commit is contained in:
Griatch 2020-04-19 19:30:40 +02:00
parent 5e2bbedd52
commit 175fcce405
2 changed files with 53 additions and 427 deletions

View file

@ -430,7 +430,9 @@ class TestTraitCounter(_TraitHandlerBase):
2: "range1",
5: "range2",
7: "range3",
}
},
"rate": 0,
"ratetarget": None,
}
)
@ -609,7 +611,9 @@ class TestTraitGauge(_TraitHandlerBase):
2: "range1",
5: "range2",
7: "range3",
}
},
"rate": 0,
"ratetarget": None,
}
)
def test_actual(self):
@ -834,410 +838,3 @@ class TestNumericTraitOperators(TestCase):
self.assertGreaterEqual(8, self.st)
self.assertGreaterEqual(self.st, 0)
self.assertGreaterEqual(10, self.st)
#
#
# class TraitTestCase(TestCase):
# """Test case for basic Trait functionality."""
# def setUp(self):
# # direct instantiation for testing only; use TraitHandler in production
# self.trait = TraitStatic({
# 'trait_type': 'static',
# 'name': 'Strength',
# 'base': 8,
# 'mod': 0,
# })
#
# def tearDown(self):
# self.trait = None
#
# def check_trait(self, base=None, mod=None, actual=None):
# """helper; allows one-line checking of `Trait` properties"""
# if base is not None: self.assertEqual(self.trait.base, base)
# if mod is not None: self.assertEqual(self.trait.mod, mod)
# if actual is not None: self.assertEqual(self.trait.actual, actual)
#
# def test_initial_state(self):
# """`Trait` fixture object properties are as expected"""
# self.assertEqual(self.trait.name, 'Strength')
# self.check_trait(8, 0, 8)
#
# def test_change_base(self):
# """changes to `base` are reflected in `actual`"""
# self.trait.base += 1
# self.check_trait(9, 0, 9)
#
# def test_change_mod(self):
# """changes to `mod` are reflected in `actual`"""
# self.trait.mod = 1
# self.check_trait(8, 1, 9)
#
# def test_reset_mod(self):
# """`reset_mod()` function zeroes out `mod`"""
# self.mod = 1
# self.trait.reset_mod()
# self.check_trait(8, 0, 8)
#
#
# class TraitExtraPropsTestCase(TestCase):
# """Test case for Trait extra properties functionality."""
# def setUp(self):
# # direct instantiation for testing only; use TraitHandler in production
# self.trait = Trait({
# 'type': 'static',
# 'name': 'Strength',
# 'base': 8,
# 'mod': 0,
# 'extra': {'preloaded': True},
# })
#
# def tearDown(self):
# self.trait = None
#
# def test_extra_props_get(self):
# """test that extra properties are gettable"""
# # access via getattr
# self.assertTrue(self.trait.preloaded)
# # access via getitem
# self.assertTrue(self.trait['preloaded'])
#
# def test_extra_props_set(self):
# """test that extra properties are settable"""
# # set via setattr
# self.trait.skill_points = 2
# self.assertIn('skill_points', self.trait.extra)
# # set via setitem
# self.trait['skill_points'] = 2
# self.assertIn('skill_points', self.trait.extra)
#
# def test_extra_props_del(self):
# """test that extra properties are deletable"""
# # delete via delattr
# del self.trait.preloaded
# self.assertNotIn('preloaded', self.trait.extra)
# with self.assertRaises(AttributeError):
# x = self.trait.preloaded
# # delete via delitem
# del self.trait['preloaded']
# self.assertNotIn('preloaded', self.trait.extra)
# with self.assertRaises(KeyError):
# x = self.trait['preloaded']
#
#
#
# class CounterTraitTestCase(TestCase):
# """Test case for counter trait functionality."""
# def setUp(self):
# # direct instantiation for testing only; use TraitHandler in production
# self.trait = Trait({
# 'type': 'counter',
# 'name': 'Bonus/Penalty',
# 'base': 0,
# 'mod': 0,
# 'min': -3,
# 'max': 3,
# })
#
# def tearDown(self):
# self.trait = None
#
# def check_trait(self, base=None, mod=None, current=None,
# actual=None, min=None, max=None,):
# """helper; allows one-line checking of Trait properties"""
# if base is not None: self.assertEqual(self.trait.base, base)
# if mod is not None: self.assertEqual(self.trait.mod, mod)
# if current is not None: self.assertEqual(self.trait.current, current)
# if actual is not None: self.assertEqual(self.trait.actual, actual)
# if min is not None: self.assertEqual(self.trait.min, min)
# if max is not None: self.assertEqual(self.trait.max, max)
#
# def test_initial_state(self):
# """fixture object properties are as expected"""
# self.assertEqual(self.trait.name, 'Bonus/Penalty')
# self.check_trait(0, 0, 0, 0, -3, 3)
#
# def test_upper_bound(self):
# """`current` may not be set above `max` when `max` is numeric"""
# self.trait.current = 5
# self.check_trait(0, 0, 3, 3, -3, 3)
#
# def test_upper_unbound(self):
# """`current` may be set very high when `max` is None"""
# self.trait.max = None
# self.trait.current = 10000000
# self.check_trait(0, 0, 10000000, 10000000, -3, None)
#
# def test_lower_bound(self):
# """`current` may not be set below `min` when `min` is numeric"""
# self.trait.current = -5
# self.check_trait(0, 0, -3, -3, -3, 3)
#
# def test_lower_unbound(self):
# """`current` may be set very low when `min` is None"""
# self.trait.min = None
# self.trait.current = -10000000
# self.check_trait(0, 0, -10000000, -10000000, None, 3)
#
# def test_mod(self):
# """confirm `mod` functionality mid-range"""
# self.trait.mod = 2
# self.check_trait(0, 2, 0, 2, -3, 3)
#
# def test_mod_upper_bound(self):
# """`actual` is constrained even if `current`+`mod` exceed `max`"""
# self.trait.current = 2
# self.trait.mod = 4
# self.check_trait(0, 4, 2, 3, -3, 3)
#
# def test_mod_lower_bound(self):
# """`actual` is constrained even if `current`+`mod` exceeds `min`"""
# self.trait.current = -2
# self.trait.mod = -4
# self.check_trait(0, -4, -2, -3, -3, 3)
#
# def test_no_max_below_base(self):
# """`max` may not be set below `base`"""
# self.trait.max = -1
# self.check_trait(0, 0, 0, 0, -3, 0)
#
# def test_no_base_above_max(self):
# """`base` may not be set above `max`"""
# self.trait.base = 5
# self.check_trait(3, 0, 3, 3, -3, 3)
#
# def test_no_min_above_base(self):
# """`min` may not be set above `base`"""
# self.trait.min = 1
# self.check_trait(0, 0, 0, 0, 0, 3)
#
# def test_no_base_below_min(self):
# """`base` may not be set below `min`"""
# self.trait.base = -5
# self.check_trait(-3, 0, -3, -3, -3, 3)
#
# def test_reset_counter(self):
# """`reset_to_base()` method sets `current` = `base`"""
# self.trait.current = 2
# self.check_trait(0, 0, 2, 2, -3, 3)
# self.trait.reset_counter()
# self.check_trait(0, 0, 0, 0, -3, 3)
#
# def test_percent_divzero(self):
# """confirm `percent()`functionality when unbounded and `base`=0"""
# self.trait.min = self.trait.max = None
# self.check_trait(0, 0, 0, 0, None, None)
# self.assertEqual(self.trait.percent(), '100.0%')
# self.trait.current = 20
# self.check_trait(0, 0, 20, 20, None, None)
# self.assertEqual(self.trait.percent(), '100.0%')
#
#
# class GaugeTraitTestCase(TestCase):
# """Test case for `GaugeTrait` functionality"""
# def setUp(self):
# # direct instantiation for testing only; use TraitHandler in production
# self.trait = Trait({
# 'name': 'HP',
# 'type': 'gauge',
# 'base': 10,
# 'mod': 0,
# 'min': 0,
# 'max': 'base'
# })
#
# def tearDown(self):
# self.trait = None
#
# def check_trait(self, base=None, mod=None, current=None,
# actual=None, min=None, max=None,):
# """helper; allows one-line checking of Trait properties"""
# if base is not None: self.assertEqual(self.trait.base, base)
# if mod is not None: self.assertEqual(self.trait.mod, mod)
# if current is not None: self.assertEqual(self.trait.current, current)
# if actual is not None: self.assertEqual(self.trait.actual, actual)
# if min is not None: self.assertEqual(self.trait.min, min)
# if max is not None: self.assertEqual(self.trait.max, max)
#
# def test_initial_state(self):
# """fixture object properties are as expected"""
# self.assertEqual(self.trait.name, 'HP')
# self.check_trait(10, 0, 10, 10, 0, 10)
#
# def test_change_base(self):
# """test that changing base property is not constrained when `max`='base'"""
# self.trait.base = 20
# self.check_trait(20, 0, 20, 20, 0, 20)
#
# def test_buff_full(self):
# """increasing `mod` increases `current`"""
# self.trait.mod = 2
# self.check_trait(10, 2, 12, 12, 0, 12)
# self.trait.reset_mod()
# self.check_trait(10, 0, 10, 10, 0, 10)
# self.trait.current = 5
# self.trait.mod = 2
# self.check_trait(10, 2, 7, 7, 0, 12)
#
# def test_debuff_full(self):
# """decreasing `mod` decreases `current` if it would be above `max`"""
# self.trait.mod = -2
# self.check_trait(10, -2, 8, 8, 0, 8)
# self.trait.reset_mod()
# self.trait.current = 5
# self.trait.mod = -2
# self.check_trait(10, -2, 5, 5, 0, 8)
#
# def test_fill_max_base(self):
# """`overfill()` method functionality with default `max`=`base` config"""
# self.trait.current = 5
# self.check_trait(10, 0, 5, 5, 0, 10)
# self.trait.fill_gauge()
# self.check_trait(10, 0, 10, 10, 0, 10)
#
# def test_fill_max_static(self):
# """`overfill()` method functionality with static number `max` config"""
# self.trait.max = 20
# self.trait.current = 5
# self.check_trait(10, 0, 5, 5, 0, 20)
# self.trait.fill_gauge()
# self.check_trait(10, 0, 15, 15, 0, 20) # can exceed `mod`+`base`
# self.trait.fill_gauge()
# self.check_trait(10, 0, 20, 20, 0, 20) # but not `max`
#
# def test_fill_max_unbounded(self):
# """`overfill()` method functionality with unbounded `max` config"""
# self.trait.max = None
# self.trait.current = 5
# self.trait.mod = 2
# self.check_trait(10, 2, 7, 7, 0, None)
# self.trait.fill_gauge()
# self.check_trait(10, 2, 19, 19, 0, None)
#
# def test_percent_base(self):
# """`percent()` method functionality with `max`=`base`"""
# self.trait.base = 100
# self.trait.current = 69
# self.check_trait(100, 0, 69, 69, 0, 100)
# self.assertEqual(self.trait.percent(), '69.0%')
#
# def test_percent_static(self):
# """`percent()` method functionality with static number `max` config."""
# self.trait.base = 30
# self.trait.mod += 3
# self.trait.max = 99
# self.check_trait(30, 3, 33, 33, 0, 99)
# self.assertEqual(self.trait.percent(), '33.3%')
#
# def test_percent_unbounded(self):
# """`percent()` method functionality with unbounded `max` config."""
# self.trait.base = 50
# self.trait.max = None
# self.trait.current = 75
# self.check_trait(50, 0, 75, 75, 0, None)
# self.assertEqual(self.trait.percent(), '150.0%')
#
#
# class TraitFactoryTestCase(EvenniaTest):
# """Test case for the TraitHandler class."""
# def setUp(self):
# super(TraitFactoryTestCase, self).setUp()
# self.traits = TraitHandler(self.char1)
#
# def test_add_get(self):
# """test adding and getting Traits via TraitHandler"""
# self.traits.add(
# key='str', name='Strength', type='static')
#
# t = self.traits.get('str')
# self.assertIsInstance(t, Trait)
# self.assertEqual(t.name, "Strength")
# self.assertEqual(t._type, "static")
#
# def test_len_remove(self):
# """test response to len() and removing Traits from TraitHandler"""
# self.traits.add(
# key='str', name='Strength', type='static')
#
# self.assertEqual(len(self.traits), 1)
# self.traits.remove('str')
# self.assertEqual(len(self.traits), 0)
#
# def test_all_clear(self):
# """test `all` property and clear function on TraitHandler"""
# self.traits.add(
# key='str', name='Strength', type='static')
# self.traits.add(
# key='bonus', name='Bonus', type='counter')
# self.traits.add(
# key='hp', name='HP', type='gauge')
#
# self.assertEqual(frozenset(self.traits.all),
# frozenset(['str', 'bonus', 'hp']))
# self.assertEqual(len(self.traits), 3)
# self.traits.clear()
# self.assertEqual(len(self.traits), 0)
#
# def test_alternate_access(self):
# """test `Trait` access by attribute and dict item syntax"""
# self.traits.add(
# key='str', name='Strength', type='static')
#
# # as attribute
# self.assertIsInstance(self.traits.str, Trait)
# self.assertEqual(self.traits.str.name, 'Strength')
# # as dict key
# self.assertIsInstance(self.traits['str'], Trait)
# self.assertEqual(self.traits['str'].name, 'Strength')
#
# def test_assignment_error(self):
# """ensure attempting to assign to a Trait key on a TraitHandler fails."""
# with self.assertRaises(TraitException):
# self.traits.str = 5
# with self.assertRaises(TraitException):
# self.traits['str'] = 5
#
# def test_defaults_static(self):
# """check defaults for static trait optional parameters in config"""
# self.traits.add(
# key='str', name='Strength', type='static')
#
# st = self.traits.str
# self.assertEqual(st._type, 'static')
# self.assertEqual(st.base, 0)
# self.assertEqual(st.mod, 0)
# self.assertEqual(st.current, 0)
# self.assertEqual(st.actual, 0)
# with self.assertRaises(AttributeError):
# x = st.min
# with self.assertRaises(AttributeError):
# x = st.max
#
# def test_defaults_counter(self):
# """check defaults for counter trait optional parameters in config"""
# self.traits.add(
# key='bonus', name='Bonus', type='counter')
#
# bo = self.traits.bonus
# self.assertEqual(bo._type, 'counter')
# self.assertEqual(bo.base, 0)
# self.assertEqual(bo.mod, 0)
# self.assertIs(bo.min, None)
# self.assertIs(bo.max, None)
# self.assertEqual(bo.extra, [])
#
# def test_defaults_gauge(self):
# """check defaults for gauge trait optional parameters in config"""
# self.traits.add(
# key='hp', name='HP', type='gauge')
#
# hp = self.traits.hp
# self.assertEqual(hp._type, 'gauge')
# self.assertEqual(hp.name, 'HP')
# self.assertEqual(hp.base, 0)
# self.assertEqual(hp.mod, 0)
# self.assertEqual(hp.current, 0)
# self.assertEqual(hp.min, 0)
# self.assertEqual(hp.max, 0)
# self.assertEqual(hp.extra, [])
#

View file

@ -257,6 +257,7 @@ Examples:
```
"""
from time import time
from django.conf import settings
from functools import total_ordering
from evennia.utils.dbserialize import _SaverDict
@ -945,7 +946,11 @@ class CounterTrait(NumericTrait):
5: "traited",
7: "expert",
9: "master"}
- rate/ratetarget are optional settings to include a rate-of-change
of the current value. This is calculated on-demand and allows for
describing a value that is gradually growing smaller/bigger. The
increase will stop when either reaching a boundary (if set) or
ratetarget. Setting the rate to 0 (default) stops any change.
"""
@ -958,6 +963,8 @@ class CounterTrait(NumericTrait):
"min": None,
"max": None,
"descs": None,
"rate": 0,
"ratetarget": None
}
@classmethod
@ -969,17 +976,45 @@ class CounterTrait(NumericTrait):
if any(not (isinstance(key, (int, float)) and isinstance(value, str))
for key in descs.items()):
raise TraitException("Trait descs must be defined on the form {number:str}")
if trait_data['rate'] != 0:
trait_data['last_update'] = time()
return trait_data
# Helpers
# timer component
def _timer_running(self):
"""Check if timer mechanism is running"""
return self.rate != 0 and self._data['last_update'] is not None
def _stop_timer(self):
if self._timer_running():
self._data['last_update'] = None
def _check_ratetarget(self):
"""Check if we passed ratetarget."""
ratetarget = self._data['ratetarget']
return (ratetarget is not None and
((self.rate < 0 and new_curr <= ratetarget) or
(self.rate > 0 and new_curr >= ratetarget)))
def _update_current(self, current):
"""Update current value, including any rate change"""
if self.rate != 0 and self._data['last_update'] is not None:
tdiff = now - self._data['last_update']
current += self.rate * tdiff
return current
def _enforce_boundaries(self, value):
"""Ensures that incoming value falls within trait's range."""
if self.min is not None and value <= self.min:
self._stop_timer()
return self.min
if self._data["max"] == "base" and value >= self.mod + self.base:
return self.base + self.mod
if self.max is not None and value >= self.max:
self._stop_timer()
return self.max
if self._timer_running() and self._check_ratetarget():
_stop_timer()
return self._data['ratetarget']
return value
# properties
@ -1046,7 +1081,7 @@ class CounterTrait(NumericTrait):
@property
def current(self):
"""The `current` value of the `Trait`. This does not have .mod added."""
return self._data.get("current", self.base)
return self._update_current(self._data.get("current", self.base))
@current.setter
def current(self, value):
@ -1148,7 +1183,9 @@ class GaugeTrait(CounterTrait):
"base": 0,
"mod": 0,
"min": 0,
"descs": None
"descs": None,
"rate": 0,
"ratetarget": None,
}
def _enforce_boundaries(self, value):
@ -1215,8 +1252,8 @@ class GaugeTrait(CounterTrait):
@property
def current(self):
"""The `current` value of the gauge."""
return self._enforce_boundaries(
self._data.get("current", self.base + self.mod))
return self._update_current(self._enforce_boundaries(
self._data.get("current", self.base + self.mod)))
@current.setter
def current(self, value):
@ -1255,15 +1292,7 @@ class GaugeTrait(CounterTrait):
del self.current
class SequenceTrait(CounterTrait):
class UpdatingTrait(CounterTrait):
"""
A trait that stores an indexed array of strings to
represent distinct values in a sequence. Adding to the trait will
step back and forth in the sequence.
This is useful for systems which don't use numbers as much
as discrete states to represent things, such as
"Weak, "
This is a trait that
"""