parent
387533d1f0
commit
c8d75665d2
3 changed files with 24 additions and 51 deletions
|
|
@ -20,6 +20,7 @@
|
||||||
a reload (regression).
|
a reload (regression).
|
||||||
- [Docs] Added new [Server-Lifecycle][doc-server-lifecycle] page to describe
|
- [Docs] Added new [Server-Lifecycle][doc-server-lifecycle] page to describe
|
||||||
the hooks called on server start/stop/reload (Griatch)
|
the hooks called on server start/stop/reload (Griatch)
|
||||||
|
- [Docs] Doc typo fixes (Griatch, chiizujin)
|
||||||
|
|
||||||
[pull3438]: https://github.com/evennia/evennia/pull/3446
|
[pull3438]: https://github.com/evennia/evennia/pull/3446
|
||||||
[pull3485]: https://github.com/evennia/evennia/pull/3485
|
[pull3485]: https://github.com/evennia/evennia/pull/3485
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
# Player Characters
|
# Player Characters
|
||||||
|
|
||||||
In the [previous lesson about rules and dice rolling](./Beginner-Tutorial-Rules.md) we made some
|
In the [previous lesson about rules and dice rolling](./Beginner-Tutorial-Rules.md) we made some assumptions about the "Player Character" entity:
|
||||||
assumptions about the "Player Character" entity:
|
|
||||||
|
|
||||||
- It should store Abilities on itself as `character.strength`, `character.constitution` etc.
|
- It should store Abilities on itself as `character.strength`, `character.constitution` etc.
|
||||||
- It should have a `.heal(amount)` method.
|
- It should have a `.heal(amount)` method.
|
||||||
|
|
||||||
So we have some guidelines of how it should look! A Character is a database entity with values that
|
So we have some guidelines of how it should look! A Character is a database entity with values that should be able to be changed over time. It makes sense to base it off Evennia's
|
||||||
should be able to be changed over time. It makes sense to base it off Evennia's
|
|
||||||
[DefaultCharacter Typeclass](../../../Components/Typeclasses.md). The Character class is like a 'character sheet' in a tabletop
|
[DefaultCharacter Typeclass](../../../Components/Typeclasses.md). The Character class is like a 'character sheet' in a tabletop
|
||||||
RPG, it will hold everything relevant to that PC.
|
RPG, it will hold everything relevant to that PC.
|
||||||
|
|
||||||
|
|
@ -16,8 +14,7 @@ RPG, it will hold everything relevant to that PC.
|
||||||
Player Characters (PCs) are not the only "living" things in our world. We also have _NPCs_
|
Player Characters (PCs) are not the only "living" things in our world. We also have _NPCs_
|
||||||
(like shopkeepers and other friendlies) as well as _monsters_ (mobs) that can attack us.
|
(like shopkeepers and other friendlies) as well as _monsters_ (mobs) that can attack us.
|
||||||
|
|
||||||
In code, there are a few ways we could structure this. If NPCs/monsters were just special cases of PCs,
|
In code, there are a few ways we could structure this. If NPCs/monsters were just special cases of PCs, we could use a class inheritance like this:
|
||||||
we could use a class inheritance like this:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia import DefaultCharacter
|
from evennia import DefaultCharacter
|
||||||
|
|
@ -34,9 +31,7 @@ class EvAdventureMob(EvAdventureNPC):
|
||||||
|
|
||||||
All code we put on the `Character` class would now be inherited to `NPC` and `Mob` automatically.
|
All code we put on the `Character` class would now be inherited to `NPC` and `Mob` automatically.
|
||||||
|
|
||||||
However, in _Knave_, NPCs and particularly monsters are _not_ using the same rules as PCs - they are
|
However, in _Knave_, NPCs and particularly monsters are _not_ using the same rules as PCs - they are simplified to use a Hit-Die (HD) concept. So while still character-like, NPCs should be separate from PCs like this:
|
||||||
simplified to use a Hit-Die (HD) concept. So while still character-like, NPCs should be separate from
|
|
||||||
PCs like this:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia import DefaultCharacter
|
from evennia import DefaultCharacter
|
||||||
|
|
@ -60,8 +55,7 @@ Nevertheless, there are some things that _should_ be common for all 'living thin
|
||||||
- All can loot their fallen foes.
|
- All can loot their fallen foes.
|
||||||
- All can get looted when defeated.
|
- All can get looted when defeated.
|
||||||
|
|
||||||
We don't want to code this separately for every class but we no longer have a common parent
|
We don't want to code this separately for every class but we no longer have a common parent class to put it on. So instead we'll use the concept of a _mixin_ class:
|
||||||
class to put it on. So instead we'll use the concept of a _mixin_ class:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia import DefaultCharacter
|
from evennia import DefaultCharacter
|
||||||
|
|
@ -83,10 +77,7 @@ class EvAdventureMob(LivingMixin, EvadventureNPC):
|
||||||
In [evennia/contrib/tutorials/evadventure/characters.py](../../../api/evennia.contrib.tutorials.evadventure.characters.md)
|
In [evennia/contrib/tutorials/evadventure/characters.py](../../../api/evennia.contrib.tutorials.evadventure.characters.md)
|
||||||
is an example of a character class structure.
|
is an example of a character class structure.
|
||||||
```
|
```
|
||||||
Above, the `LivingMixin` class cannot work on its own - it just 'patches' the other classes with some
|
Above, the `LivingMixin` class cannot work on its own - it just 'patches' the other classes with some extra functionality all living things should be able to do. This is an example of _multiple inheritance_. It's useful to know about, but one should not over-do multiple inheritance since it can also get confusing to follow the code.
|
||||||
extra functionality all living things should be able to do. This is an example of
|
|
||||||
_multiple inheritance_. It's useful to know about, but one should not over-do multiple inheritance
|
|
||||||
since it can also get confusing to follow the code.
|
|
||||||
|
|
||||||
## Living mixin class
|
## Living mixin class
|
||||||
|
|
||||||
|
|
@ -178,7 +169,6 @@ Most of these are empty since they will behave differently for characters and np
|
||||||
|
|
||||||
Once we create more of our game, we will need to remember to actually call these hook methods so they serve a purpose. For example, once we implement combat, we must remember to call `at_attacked` as well as the other methods involving taking damage, getting defeated or dying.
|
Once we create more of our game, we will need to remember to actually call these hook methods so they serve a purpose. For example, once we implement combat, we must remember to call `at_attacked` as well as the other methods involving taking damage, getting defeated or dying.
|
||||||
|
|
||||||
|
|
||||||
## Character class
|
## Character class
|
||||||
|
|
||||||
We will now start making the basic Character class, based on what we need from _Knave_.
|
We will now start making the basic Character class, based on what we need from _Knave_.
|
||||||
|
|
@ -234,8 +224,7 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
||||||
|
|
||||||
We make an assumption about our rooms here - that they have a property `.allow_death`. We need to make a note to actually add such a property to rooms later!
|
We make an assumption about our rooms here - that they have a property `.allow_death`. We need to make a note to actually add such a property to rooms later!
|
||||||
|
|
||||||
In our `Character` class we implement all attributes we want to simulate from the _Knave_ ruleset.
|
In our `Character` class we implement all attributes we want to simulate from the _Knave_ ruleset. The `AttributeProperty` is one way to add an Attribute in a field-like way; these will be accessible on every character in several ways:
|
||||||
The `AttributeProperty` is one way to add an Attribute in a field-like way; these will be accessible on every character in several ways:
|
|
||||||
|
|
||||||
- As `character.strength`
|
- As `character.strength`
|
||||||
- As `character.db.strength`
|
- As `character.db.strength`
|
||||||
|
|
@ -249,7 +238,7 @@ We implement the Player Character versions of `at_defeat` and `at_death`. We als
|
||||||
|
|
||||||
### Funcparser inlines
|
### Funcparser inlines
|
||||||
|
|
||||||
This piece of code is worth some more explanation:
|
This piece of code in the `at_defeat` method above is worth some more extra explanation:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
self.location.msg_contents(
|
self.location.msg_contents(
|
||||||
|
|
@ -259,8 +248,7 @@ self.location.msg_contents(
|
||||||
|
|
||||||
Remember that `self` is the Character instance here. So `self.location.msg_contents` means "send a message to everything inside my current location". In other words, send a message to everyone in the same place as the character.
|
Remember that `self` is the Character instance here. So `self.location.msg_contents` means "send a message to everything inside my current location". In other words, send a message to everyone in the same place as the character.
|
||||||
|
|
||||||
The `$You() $conj(collapse)` are [FuncParser inlines](../../../Components/FuncParser.md). These are functions that
|
The `$You() $conj(collapse)` are [FuncParser inlines](../../../Components/FuncParser.md). These are functions that execute in the string. The resulting string may look different for different audiences. The `$You()` inline function will use `from_obj` to figure out who 'you' are and either show your name or 'You'. The `$conj()` (verb conjugator) will tweak the (English) verb to match.
|
||||||
execute in the string. The resulting string may look different for different audiences. The `$You()` inline function will use `from_obj` to figure out who 'you' are and either show your name or 'You'. The `$conj()` (verb conjugator) will tweak the (English) verb to match.
|
|
||||||
|
|
||||||
- You will see: `"You collapse in a heap, alive but beaten."`
|
- You will see: `"You collapse in a heap, alive but beaten."`
|
||||||
- Others in the room will see: `"Thomas collapses in a heap, alive but beaten."`
|
- Others in the room will see: `"Thomas collapses in a heap, alive but beaten."`
|
||||||
|
|
@ -303,10 +291,7 @@ You can easily make yourself an `EvAdventureCharacter` in-game by using the
|
||||||
|
|
||||||
You can now do `examine self` to check your type updated.
|
You can now do `examine self` to check your type updated.
|
||||||
|
|
||||||
If you want _all_ new Characters to be of this type you need to tell Evennia about it. Evennia
|
If you want _all_ new Characters to be of this type you need to tell Evennia about it. Evennia uses a global setting `BASE_CHARACTER_TYPECLASS` to know which typeclass to use when creating Characters (when logging in, for example). This defaults to `typeclasses.characters.Character` (that is, the `Character` class in `mygame/typeclasses/characters.py`).
|
||||||
uses a global setting `BASE_CHARACTER_TYPECLASS` to know which typeclass to use when creating
|
|
||||||
Characters (when logging in, for example). This defaults to `typeclasses.characters.Character` (that is,
|
|
||||||
the `Character` class in `mygame/typeclasses/characters.py`).
|
|
||||||
|
|
||||||
There are thus two ways to weave your new Character class into Evennia:
|
There are thus two ways to weave your new Character class into Evennia:
|
||||||
|
|
||||||
|
|
@ -327,8 +312,7 @@ instead.
|
||||||
|
|
||||||
> Create a new module `mygame/evadventure/tests/test_characters.py`
|
> Create a new module `mygame/evadventure/tests/test_characters.py`
|
||||||
|
|
||||||
For testing, we just need to create a new EvAdventure character and check
|
For testing, we just need to create a new EvAdventure character and check that calling the methods on it doesn't error out.
|
||||||
that calling the methods on it doesn't error out.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# mygame/evadventure/tests/test_characters.py
|
# mygame/evadventure/tests/test_characters.py
|
||||||
|
|
@ -368,22 +352,18 @@ class TestCharacters(BaseEvenniaTest):
|
||||||
# tests for other methods ...
|
# tests for other methods ...
|
||||||
|
|
||||||
```
|
```
|
||||||
If you followed the previous lessons, these tests should look familiar. Consider adding
|
If you followed the previous lessons, these tests should look familiar. Consider adding tests for other methods as practice. Refer to previous lessons for details.
|
||||||
tests for other methods as practice. Refer to previous lessons for details.
|
|
||||||
|
|
||||||
For running the tests you do:
|
For running the tests you do:
|
||||||
|
|
||||||
evennia test --settings settings.py .evadventure.tests.test_character
|
evennia test --settings settings.py .evadventure.tests.test_characters
|
||||||
|
|
||||||
|
|
||||||
## About races and classes
|
## About races and classes
|
||||||
|
|
||||||
_Knave_ doesn't have any D&D-style _classes_ (like Thief, Fighter etc). It also does not bother with
|
_Knave_ doesn't have any D&D-style _classes_ (like Thief, Fighter etc). It also does not bother with _races_ (like dwarves, elves etc). This makes the tutorial shorter, but you may ask yourself how you'd add these functions.
|
||||||
_races_ (like dwarves, elves etc). This makes the tutorial shorter, but you may ask yourself how you'd
|
|
||||||
add these functions.
|
|
||||||
|
|
||||||
In the framework we have sketched out for _Knave_, it would be simple - you'd add your race/class as
|
In the framework we have sketched out for _Knave_, it would be simple - you'd add your race/class as an Attribute on your Character:
|
||||||
an Attribute on your Character:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# mygame/evadventure/characters.py
|
# mygame/evadventure/characters.py
|
||||||
|
|
@ -399,8 +379,7 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
||||||
charrace = AttributeProperty("Human")
|
charrace = AttributeProperty("Human")
|
||||||
|
|
||||||
```
|
```
|
||||||
We use `charclass` rather than `class` here, because `class` is a reserved Python keyword. Naming
|
We use `charclass` rather than `class` here, because `class` is a reserved Python keyword. Naming `race` as `charrace` thus matches in style.
|
||||||
`race` as `charrace` thus matches in style.
|
|
||||||
|
|
||||||
We'd then need to expand our [rules module](./Beginner-Tutorial-Rules.md) (and later
|
We'd then need to expand our [rules module](./Beginner-Tutorial-Rules.md) (and later
|
||||||
[character generation](./Beginner-Tutorial-Chargen.md) to check and include what these classes mean.
|
[character generation](./Beginner-Tutorial-Chargen.md) to check and include what these classes mean.
|
||||||
|
|
@ -409,23 +388,16 @@ We'd then need to expand our [rules module](./Beginner-Tutorial-Rules.md) (and l
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
|
||||||
With the `EvAdventureCharacter` class in place, we have a better understanding of how our PCs will look
|
With the `EvAdventureCharacter` class in place, we have a better understanding of how our PCs will look like under _Knave_.
|
||||||
like under _Knave_.
|
|
||||||
|
|
||||||
For now, we only have bits and pieces and haven't been testing this code in-game. But if you want
|
For now, we only have bits and pieces and haven't been testing this code in-game. But if you want you can swap yourself into `EvAdventureCharacter` right now. Log into your game and run the command
|
||||||
you can swap yourself into `EvAdventureCharacter` right now. Log into your game and run
|
|
||||||
the command
|
|
||||||
|
|
||||||
type self = evadventure.characters.EvAdventureCharacter
|
type self = evadventure.characters.EvAdventureCharacter
|
||||||
|
|
||||||
If all went well, `ex self` will now show your typeclass as being `EvAdventureCharacter`.
|
If all went well, `ex self` will now show your typeclass as being `EvAdventureCharacter`. Check out your strength with
|
||||||
Check out your strength with
|
|
||||||
|
|
||||||
py self.strength = 3
|
py self.strength = 3
|
||||||
|
|
||||||
```{important}
|
```{important}
|
||||||
When doing `ex self` you will _not_ see all your Abilities listed yet. That's because
|
When doing `ex self` you will _not_ see all your Abilities listed yet. That's because Attributes added with `AttributeProperty` are not available until they have been accessed at least once. So once you set (or look at) `.strength` above, `strength` will show in `examine` from then on.
|
||||||
Attributes added with `AttributeProperty` are not available until they have been accessed at
|
|
||||||
least once. So once you set (or look at) `.strength` above, `strength` will show in `examine` from
|
|
||||||
then on.
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class EvAdventureObject(DefaultObject):
|
||||||
"""The top of the description"""
|
"""The top of the description"""
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def get_display_desc(self, looker, **kwargs)
|
def get_display_desc(self, looker, **kwargs):
|
||||||
"""The main display - show object stats"""
|
"""The main display - show object stats"""
|
||||||
return get_obj_stats(self, owner=looker)
|
return get_obj_stats(self, owner=looker)
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ class EvAdventureConsumable(EvAdventureObject):
|
||||||
"""Called when using the item"""
|
"""Called when using the item"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_post_use(self. user, *args, **kwargs):
|
def at_post_use(self, user, *args, **kwargs):
|
||||||
"""Called after using the item"""
|
"""Called after using the item"""
|
||||||
# detract a usage, deleting the item if used up.
|
# detract a usage, deleting the item if used up.
|
||||||
self.uses -= 1
|
self.uses -= 1
|
||||||
|
|
@ -452,7 +452,7 @@ _BARE_HANDS = None
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
class WeaponBareHands(EvAdventureWeapon)
|
class WeaponBareHands(EvAdventureWeapon):
|
||||||
obj_type = ObjType.WEAPON
|
obj_type = ObjType.WEAPON
|
||||||
inventory_use_slot = WieldLocation.WEAPON_HAND
|
inventory_use_slot = WieldLocation.WEAPON_HAND
|
||||||
attack_type = Ability.STR
|
attack_type = Ability.STR
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue