Make Trait unit tests pass for base trait types

This commit is contained in:
Griatch 2020-04-19 17:42:11 +02:00
parent d720acef1d
commit cf1671b49c
2 changed files with 126 additions and 7 deletions

View file

@ -399,7 +399,13 @@ class TestTraitCounter(_TraitHandlerBase):
min=0,
max=10,
extra_val1="xvalue1",
extra_val2="xvalue2"
extra_val2="xvalue2",
descs={
0: "range0",
2: "range1",
5: "range3",
7: "range4",
}
)
self.trait = self.traithandler.get("test1")
@ -419,6 +425,12 @@ class TestTraitCounter(_TraitHandlerBase):
"max": 10,
"extra_val1": "xvalue1",
"extra_val2": "xvalue2"
},
"descs": {
0: "range0",
2: "range1",
5: "range3",
7: "range4",
}
)
@ -535,6 +547,22 @@ class TestTraitCounter(_TraitHandlerBase):
del self.trait.min
self.assertEqual(self.trait.percent(), "100.0%")
def test_descs(self):
"""Test descriptions"""
self.trait.mod = 0
self.current = 0
self.assertEqual(self.trait.desc(), "range0")
self.current = 1
self.assertEqual(self.trait.desc(), "range0")
self.current = 3
self.assertEqual(self.trait.desc(), "range1")
self.current = 5
self.assertEqual(self.trait.desc(), "range3")
self.current = 9
self.assertEqual(self.trait.desc(), "range4")
self.current = 100
self.assertEqual(self.trait.desc(), "range4")
class TestTraitGauge(_TraitHandlerBase):
@ -565,7 +593,8 @@ class TestTraitGauge(_TraitHandlerBase):
"mod": 2,
"min": 0,
"extra_val1": "xvalue1",
"extra_val2": "xvalue2"
"extra_val2": "xvalue2",
"descs": None
}
)
def test_actual(self):

View file

@ -5,17 +5,16 @@ Whitenoise 2014, Ainneve contributors,
Griatch 2020
A `Trait` represents a modifiable property of (usually) a Character. They can
A `Trait` represents a modifiable property on (usually) a Character. They can
be used to represent everything from attributes (str, agi etc) to skills
(hunting, swords etc) or effects (poisoned, rested etc) and has extra
functionality beyond using plain Attributes for this.
(hunting 10, swords 14 etc) and dynamically changing things like HP, XP etc.
Traits use Evennia Attributes under the hood, making them persistent (they survive
a server reload/reboot).
### Adding Traits to a typeclass
To access and manipulate tragts on an object, its Typeclass needs to have a
To access and manipulate traits on an object, its Typeclass needs to have a
`TraitHandler` assigned it. Usually, the handler is made available as `.traits`
(in the same way as `.tags` or `.attributes`).
@ -39,6 +38,27 @@ Here's an example for adding the TraitHandler to the base Object class:
```
After a reload you can now try adding some example traits:
```python
>>> obj.traits.add("hunting", "Hunting Skill", trait_type="static", value=4)
>>> obj.traits.hunting.value
4
>>> obj.traits.hunting.value += 5
>>> obj.traits.hunting.value
9
>>> obj.traits.add("hp", "Health", trait_type="gauge", min=0, max=100)
>>> obj.traits.hp.current
100
>>> obj.traits.hp -= 200
>>> obj.traits.hp.current
0
>>> obj.traits.hp.reset()
>>> obj.traits.hp.current
100
```
### Trait Configuration
A single Trait can be one of three basic types:
@ -738,7 +758,6 @@ class NumericTrait(Trait):
actual = base
"""
trait_type = "numeric"
@ -916,6 +935,17 @@ class CounterTrait(NumericTrait):
- actual = current + mod, starts at base + mod
- if min or max is None, there is no upper/lower bound (default)
- if max is set to "base", max will be equal ot base+mod
- descs are used to optionally describe each value interval.
The desc of the current `actual` value can then be retrieved
with .desc(). The property is set as {lower_bound_inclusive:desc}
and should be given smallest-to-biggest. For example, for
a skill rating between 0 and 10:
{0: "unskilled",
1: "neophyte",
5: "traited",
7: "expert",
9: "master"}
"""
@ -927,8 +957,20 @@ class CounterTrait(NumericTrait):
"mod": 0,
"min": None,
"max": None,
"descs": None:
}
@classmethod
def validate(cls, trait_data):
"""Add extra validation for descs"""
trait_data = Trait.validate_input(trait_data)
descs = trait_data['descs']
if isinstance(descs, dict):
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}")
return trait_data
# Helpers
def _enforce_boundaries(self, value):
"""Ensures that incoming value falls within trait's range."""
@ -1040,6 +1082,28 @@ class CounterTrait(NumericTrait):
"""Resets `current` property equal to `base` value."""
del self.current
def desc(self):
"""
Retrieve descriptions of the current value, if available.
This must be a mapping {upper_bound_inclusive: text},
ordered from small to big.
rely on Python3.7+ dicts retaining ordering to let this
describe the interval.
Returns:
str: The description describing the `actual` value.
If not found, returns the empty string.
"""
descs = self._data["descs"]
if descs is None:
return ""
value = self.actual
# we rely on Python3.7+ dicts retaining ordering
for bound, txt in descs.items():
if bound >= value:
return txt
class GaugeTrait(CounterTrait):
"""
@ -1056,6 +1120,17 @@ class GaugeTrait(CounterTrait):
- max value is always base + mad
- .max is an alias of .base
- actual = current and varies from min to max.
- descs is a mapping {upper_bound_inclusive: desc}. These
are checked with .desc() and can be retrieve a text
description for a given current value.
For example, this could be used to describe health
values between 0 and 100:
{0: "Dead"
10: "Badly hurt",
30: "Bleeding",
50: "Hurting",
90: "Healthy"}
"""
@ -1067,6 +1142,7 @@ class GaugeTrait(CounterTrait):
"base": 0,
"mod": 0,
"min": 0,
"descs": None
}
def _enforce_boundaries(self, value):
@ -1171,3 +1247,17 @@ class GaugeTrait(CounterTrait):
Fills the gauge to its maximum allowed by base + mod
"""
del self.current
class SequenceTrait(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, "
"""