Fixed all links

This commit is contained in:
Griatch 2020-10-11 19:31:05 +02:00
parent d4f1733bc7
commit 26f8ba3f71
175 changed files with 11972 additions and 4443 deletions

View file

@ -36,10 +36,25 @@ We strongly recommend that you code your rule system as stand-alone as possible.
spread your skill check code, race bonus calculation, die modifiers or what have you all over your
game.
- Put everything you would need to look up in a rule book into a module in `mygame/world`. Hide away as much as you can. Think of it as a black box (or maybe the code representation of an all-knowing game master). The rest of your game will ask this black box questions and get answers back. Exactly how it arrives at those results should not need to be known outside the box. Doing it this way makes it easier to change and update things in one place later.
- Store only the minimum stuff you need with each game object. That is, if your Characters need values for Health, a list of skills etc, store those things on the Character - don't store how to roll or change them.
- Next is to determine just how you want to store things on your Objects and Characters. You can choose to either store things as individual [Attributes](./Attributes), like `character.db.STR=34` and `character.db.Hunting_skill=20`. But you could also use some custom storage method, like a dictionary `character.db.skills = {"Hunting":34, "Fishing":20, ...}`. A much more fancy solution is to look at the Ainneve [Trait handler](https://github.com/evennia/ainneve/blob/master/world/traits.py). Finally you could even go with a [custom django model](./New-Models). Which is the better depends on your game and the complexity of your system.
- Make a clear [API](http://en.wikipedia.org/wiki/Application_programming_interface) into your rules. That is, make methods/functions that you feed with, say, your Character and which skill you want to check. That is, you want something similar to this:
- Put everything you would need to look up in a rule book into a module in `mygame/world`. Hide away
as much as you can. Think of it as a black box (or maybe the code representation of an all-knowing
game master). The rest of your game will ask this black box questions and get answers back. Exactly
how it arrives at those results should not need to be known outside the box. Doing it this way
makes it easier to change and update things in one place later.
- Store only the minimum stuff you need with each game object. That is, if your Characters need
values for Health, a list of skills etc, store those things on the Character - don't store how to
roll or change them.
- Next is to determine just how you want to store things on your Objects and Characters. You can
choose to either store things as individual [Attributes](./Attributes), like `character.db.STR=34` and
`character.db.Hunting_skill=20`. But you could also use some custom storage method, like a
dictionary `character.db.skills = {"Hunting":34, "Fishing":20, ...}`. A much more fancy solution is
to look at the Ainneve [Trait
handler](https://github.com/evennia/ainneve/blob/master/world/traits.py). Finally you could even go
with a [custom django model](./New-Models). Which is the better depends on your game and the
complexity of your system.
- Make a clear [API](http://en.wikipedia.org/wiki/Application_programming_interface) into your
rules. That is, make methods/functions that you feed with, say, your Character and which skill you
want to check. That is, you want something similar to this:
```python
from world import rules
@ -47,19 +62,33 @@ game.
result = rules.roll_challenge(character1, character2, "swords")
```
You might need to make these functions more or less complex depending on your game. For example the properties of the room might matter to the outcome of a roll (if the room is dark, burning etc). Establishing just what you need to send into your game mechanic module is a great way to also get a feel for what you need to add to your engine.
You might need to make these functions more or less complex depending on your game. For example the
properties of the room might matter to the outcome of a roll (if the room is dark, burning etc).
Establishing just what you need to send into your game mechanic module is a great way to also get a
feel for what you need to add to your engine.
## Coded systems
Inspired by tabletop role playing games, most game systems mimic some sort of die mechanic. To this end Evennia offers a full [dice roller](https://github.com/evennia/evennia/blob/master/evennia/contrib/dice.py) in its `contrib` folder. For custom implementations, Python offers many ways to randomize a result using its in-built `random` module. No matter how it's implemented, we will in this text refer to the action of determining an outcome as a "roll".
Inspired by tabletop role playing games, most game systems mimic some sort of die mechanic. To this
end Evennia offers a full [dice
roller](https://github.com/evennia/evennia/blob/master/evennia/contrib/dice.py) in its `contrib`
folder. For custom implementations, Python offers many ways to randomize a result using its in-built
`random` module. No matter how it's implemented, we will in this text refer to the action of
determining an outcome as a "roll".
In a freeform system, the result of the roll is just compared with values and people (or the game master) just agree on what it means. In a coded system the result now needs to be processed somehow. There are many things that may happen as a result of rule enforcement:
In a freeform system, the result of the roll is just compared with values and people (or the game
master) just agree on what it means. In a coded system the result now needs to be processed somehow.
There are many things that may happen as a result of rule enforcement:
- Health may be added or deducted. This can effect the character in various ways.
- Experience may need to be added, and if a level-based system is used, the player might need to be informed they have increased a level.
- Experience may need to be added, and if a level-based system is used, the player might need to be
informed they have increased a level.
- Room-wide effects need to be reported to the room, possibly affecting everyone in the room.
There are also a slew of other things that fall under "Coded systems", including things like weather, NPC artificial intelligence and game economy. Basically everything about the world that a Game master would control in a tabletop role playing game can be mimicked to some level by coded systems.
There are also a slew of other things that fall under "Coded systems", including things like
weather, NPC artificial intelligence and game economy. Basically everything about the world that a
Game master would control in a tabletop role playing game can be mimicked to some level by coded
systems.
## Example of Rule module
@ -67,17 +96,25 @@ There are also a slew of other things that fall under "Coded systems", including
Here is a simple example of a rule module. This is what we assume about our simple example game:
- Characters have only four numerical values:
- Their `level`, which starts at 1.
- A skill `combat`, which determines how good they are at hitting things. Starts between 5 and 10.
- A skill `combat`, which determines how good they are at hitting things. Starts between 5 and
10.
- Their Strength, `STR`, which determine how much damage they do. Starts between 1 and 10.
- Their Health points, `HP`, which starts at 100.
- When a Character reaches `HP = 0`, they are presumed "defeated". Their HP is reset and they get a failure message (as a stand-in for death code).
- When a Character reaches `HP = 0`, they are presumed "defeated". Their HP is reset and they get a
failure message (as a stand-in for death code).
- Abilities are stored as simple Attributes on the Character.
- "Rolls" are done by rolling a 100-sided die. If the result is below the `combat` value, it's a success and damage is rolled. Damage is rolled as a six-sided die + the value of `STR` (for this example we ignore weapons and assume `STR` is all that matters).
- Every successful `attack` roll gives 1-3 experience points (`XP`). Every time the number of `XP` reaches `(level + 1) ** 2`, the Character levels up. When leveling up, the Character's `combat` value goes up by 2 points and `STR` by one (this is a stand-in for a real progression system).
- "Rolls" are done by rolling a 100-sided die. If the result plus the `combat`value is greater than
the other character, it's a success and damage is rolled. Damage is rolled as a six-sided die + the
value of `STR` (for this example we ignore weapons and assume `STR` is all that matters).
- Every successful `attack` roll gives 1-3 experience points (`XP`). Every time the number of `XP`
reaches `(level + 1) ** 2`, the Character levels up. When leveling up, the Character's `combat`
value goes up by 2 points and `STR` by one (this is a stand-in for a real progression system).
- Characters with the name `dummy` will gain no XP. Allowing us to make a dummy to train with.
### Character
The Character typeclass is simple. It goes in `mygame/typeclasses/characters.py`. There is already an empty `Character` class there that Evennia will look to and use.
The Character typeclass is simple. It goes in `mygame/typeclasses/characters.py`. There is already
an empty `Character` class there that Evennia will look to and use.
```python
from random import randint
@ -97,7 +134,10 @@ class Character(DefaultCharacter):
self.db.combat = randint(5, 10)
```
`@reload` the server to load up the new code. Doing `examine self` will however *not* show the new Attributes on yourself. This is because the `at_object_creation` hook is only called on *new* Characters. Your Character was already created and will thus not have them. To force a reload, use the following command:
`@reload` the server to load up the new code. Doing `examine self` will however *not* show the new
Attributes on yourself. This is because the `at_object_creation` hook is only called on *new*
Characters. Your Character was already created and will thus not have them. To force a reload, use
the following command:
```
@typeclass/force/reset self
@ -110,30 +150,42 @@ The `examine self` command will now show the new Attributes.
This is a module `mygame/world/rules.py`.
```python
"""
mygame/world/rules.py
"""
from random import randint
def roll_hit():
"Roll 1d100"
return randint(1, 100)
def roll_dmg():
"Roll 1d6"
return randint(1, 6)
def check_defeat(character):
"Checks if a character is 'defeated'."
if character.db.HP <= 0:
character.msg("You fall down, defeated!")
character.db.HP = 100 # reset
character.msg("You fall down, defeated!")
character.db.HP = 100 # reset
def add_XP(character, amount):
"Add XP to character, tracking level increases."
character.db.XP += amount
if character.db.XP >= (character.db.level + 1) ** 2:
character.db.level += 1
character.db.STR += 1
character.db.combat += 2
character.msg("You are now level %i!" % character.db.level)
if "training_dummy" in character.tags.all(): # don't allow the training dummy to level
character.location.msg_contents("Training Dummies can not gain XP.")
return
else:
character.db.XP += amount
if character.db.XP >= (character.db.level + 1) ** 2:
character.db.level += 1
character.db.STR += 1
character.db.combat += 2
character.msg("You are now level %i!" % character.db.level)
def skill_combat(*args):
"""
@ -146,7 +198,17 @@ def skill_combat(*args):
failtext = "You are hit by %s for %i damage!"
wintext = "You hit %s for %i damage!"
xp_gain = randint(1, 3)
if char1.db.combat >= roll1 > roll2:
# display messages showing attack numbers
attack_message = f"{char1.name} rolls {roll1} + combat {char1.db.combat} " \
f"= {char1.db.combat+roll1} | {char2.name} rolls {roll2} + combat " \
f"{char2.db.combat} = {char2.db.combat+roll2}"
char1.location.msg_contents(attack_message)
attack_summary = f"{char1.name} {char1.db.combat+roll1} " \
f"vs {char2.name} {char2.db.combat+roll2}"
char1.location.msg_contents(attack_summary)
if char1.db.combat+roll1 > char2.db.combat+roll2:
# char 1 hits
dmg = roll_dmg() + char1.db.STR
char1.msg(wintext % (char2, dmg))
@ -154,7 +216,7 @@ def skill_combat(*args):
char2.msg(failtext % (char1, dmg))
char2.db.HP -= dmg
check_defeat(char2)
elif char2.db.combat >= roll2 > roll1:
elif char2.db.combat+roll2 > char1.db.combat+roll1:
# char 2 hits
dmg = roll_dmg() + char2.db.STR
char1.msg(failtext % (char2, dmg))
@ -168,8 +230,10 @@ def skill_combat(*args):
char1.msg(drawtext)
char2.msg(drawtext)
SKILLS = {"combat": skill_combat}
def roll_challenge(character1, character2, skillname):
"""
Determine the outcome of a skill challenge between
@ -181,9 +245,16 @@ def roll_challenge(character1, character2, skillname):
raise RunTimeError("Skillname %s not found." % skillname)
```
These few functions implement the entirety of our simple rule system. We have a function to check the "defeat" condition and reset the `HP` back to 100 again. We define a generic "skill" function. Multiple skills could all be added with the same signature; our `SKILLS` dictionary makes it easy to look up the skills regardless of what their actual functions are called. Finally, the access function `roll_challenge` just picks the skill and gets the result.
These few functions implement the entirety of our simple rule system. We have a function to check
the "defeat" condition and reset the `HP` back to 100 again. We define a generic "skill" function.
Multiple skills could all be added with the same signature; our `SKILLS` dictionary makes it easy to
look up the skills regardless of what their actual functions are called. Finally, the access
function `roll_challenge` just picks the skill and gets the result.
In this example, the skill function actually does a lot - it not only rolls results, it also informs everyone of their results via `character.msg()` calls.
In this example, the skill function actually does a lot - it not only rolls results, it also informs
everyone of their results via `character.msg()` calls.
### Attack Command
Here is an example of usage in a game command:
@ -219,3 +290,13 @@ number of Combat commands by just extending this functionality - you can easily
pick different skills to check. And if you ever decided to, say, change how to determine hit chance,
you don't have to change every command, but need only change the single `roll_hit` function inside
your `rules` module.
### Training dummy
Create a dummy to test the attack command. Give it a `training_dummy` tag anything with the tag
`training_dummy` will not gain xp.<br>
> create/drop dummy:characters.Character<br>
tag dummy=training_dummy
Attack it with your new command
> attack dummy