mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Work on cleaning up docs
This commit is contained in:
parent
1c5746d59c
commit
40023923e1
17 changed files with 218 additions and 456 deletions
|
|
@ -13,7 +13,9 @@ SPHINXAPIDOC ?= sphinx-apidoc
|
|||
SPHINXAPIDOCOPTS = --tocfile evennia-api --module-first --force -d 6 --separate --templatedir=$(SOURCEDIR)/_templates/
|
||||
SPHINXAPIDOCOPTSQUICK = --tocfile evennia-api --module-first -d 6 --separate --templatedir=$(SOURCEDIR)/_templates/
|
||||
SPHINXAPIDOCENV = members,undoc-members,show-inheritance
|
||||
SPHINXAPIDOCEXCLUDE = ../*/migrations/* ../evennia/game_template/* ../evennia/*/tests/* ../evennia/*/tests.py
|
||||
SPHINXAPIDOCEXCLUDE = ../*/migrations/* ../evennia/game_template/* ../evennia/*/tests.py ../evennia/accounts/tests/* ../evennia/commands/tests/* ../evennia/comms/tests/* ../evennia/help/tests/* ../evennia/locks/tests/* ../evennia/objects/tests/* ../evennia/prototypes/tests/* ../evennia/scripts/tests/* ../evennia/server/tests/* ../evennia/typeclasses/tests/* ../evennia/utils/tests/* ../evennia/web/tests/* # don't exclude contrib tests for tutorial purposes
|
||||
|
||||
# ../evennia/*/tests/* ../evennia/*/tests.py
|
||||
|
||||
EVDIR ?= $(realpath ../evennia)
|
||||
EVGAMEDIR ?= $(realpath ../../gamedir)
|
||||
|
|
|
|||
|
|
@ -1,279 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Copy data from old Evennia github Wiki to static files.
|
||||
|
||||
Prepare files for mkdoc. This assumes evennia.wiki is cloned
|
||||
to a folder at the same level as the evennia repo.
|
||||
|
||||
Just run this to update everything.
|
||||
|
||||
We also need to build the toc-tree and should do so automatically for now.
|
||||
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import re
|
||||
|
||||
_RE_MD_LINK = re.compile(r"\[(?P<txt>[\w -\[\]]+?)\]\((?P<url>.+?)\)", re.I + re.S + re.U)
|
||||
_RE_REF_LINK = re.compile(r"\[[\w -\[\]]*?\]\(.+?\)", re.I + re.S + re.U)
|
||||
|
||||
_RE_CLEAN = re.compile(r"\|-+?|-+\|", re.I + re.S + re.U)
|
||||
|
||||
_IGNORE_FILES = (
|
||||
"_Sidebar.md",
|
||||
# "Wiki-Index.md"
|
||||
)
|
||||
|
||||
_INDEX_PREFIX = f"""
|
||||
|
||||
|
||||
# VERSION WARNING
|
||||
|
||||
> This is the experimental static v0.9 documentation of Evennia, _automatically_ generated from the
|
||||
> [evennia wiki](https://github.com/evennia/evennia/wiki/) at {datetime.datetime.now()}.
|
||||
> There are known conversion issues which will _not_ be addressed in this version - refer to
|
||||
> the original wiki if you have trouble.
|
||||
>
|
||||
> Manual conversion and cleanup will instead happen during development of the upcoming v1.0
|
||||
> version of this static documentation.
|
||||
|
||||
"""
|
||||
|
||||
_WIKI_DIR = "../../../evennia.wiki/"
|
||||
_INFILES = [
|
||||
path
|
||||
for path in sorted(glob.glob(_WIKI_DIR + "/*.md"))
|
||||
if path.rsplit("/", 1)[-1] not in _IGNORE_FILES
|
||||
]
|
||||
_FILENAMES = [path.rsplit("/", 1)[-1] for path in _INFILES]
|
||||
_FILENAMES = [path.split(".", 1)[0] for path in _FILENAMES]
|
||||
_FILENAMESLOW = [path.lower() for path in _FILENAMES]
|
||||
_OUTDIR = "../source/"
|
||||
_OLD_WIKI_URL = "https://github.com/evennia/evennia/wiki/"
|
||||
_OLD_WIKI_URL_LEN = len(_OLD_WIKI_URL)
|
||||
_CODE_PREFIX = "github:"
|
||||
_API_PREFIX = "api:"
|
||||
|
||||
_CUSTOM_LINK_REMAP = {
|
||||
"CmdSets": "Command-Sets",
|
||||
"CmdSet": "Command-Sets",
|
||||
"Cmdsets": "Command-Sets",
|
||||
"CommandSet": "Command-Sets",
|
||||
"batch-code-processor": "Batch-Code-Processor",
|
||||
"Batch-code-processor": "Batch-Code-Processor",
|
||||
"batch-command-processor": "Batch-Command-Processor",
|
||||
"Batch-command-processor": "Batch-Command-Processor",
|
||||
"evennia-API": "Evennia-API",
|
||||
"Channels": "Communications#Channels",
|
||||
"Comms": "Communications",
|
||||
"typeclass": "Typeclasses",
|
||||
"Home": "index",
|
||||
"Help-system": "Help-System",
|
||||
"Using-Mux-as-a-Standard": "Using-MUX-as-a-Standard",
|
||||
"Building-quickstart": "Building-Quickstart",
|
||||
"Adding-Object-Typeclass-tutorial": "Adding-Object-Typeclass-Tutorial",
|
||||
"EvTable": _API_PREFIX + "evennia.utils#module-evennia.utils.evtable",
|
||||
}
|
||||
# complete reference remaps
|
||||
_REF_REMAP = {
|
||||
"[![Getting Started][icon_new]](Getting-Started)": "![Getting Started][icon_new]",
|
||||
"[![Admin Docs][icon_admin]](Administrative-Docs)": "![Admin Docs][icon_admin]",
|
||||
"[![Builder Docs][icon_builder]](Builder-Docs)": "![Builder Docs][icon_builder]",
|
||||
"[![Developer-Central][icon_devel]](Developer-Central)": "![Developer-Central][icon_devel]",
|
||||
"[![tutorial][icon_tutorial]](Tutorials)": "![Tutorials][icon_tutorial]",
|
||||
"[![API][icon_api]](evennia)": "![API][icon_api]",
|
||||
"[](Wiki-front-page.)": "",
|
||||
}
|
||||
|
||||
|
||||
# absolute links (mainly github links) that should not be converted. This
|
||||
# should be given without any #anchor.
|
||||
_ABSOLUTE_LINK_SKIP = (
|
||||
# "https://github.com/evennia/evennia/wiki/feature-request",
|
||||
)
|
||||
|
||||
# specific references tokens that should be ignored. Should be given
|
||||
# without any #anchor.
|
||||
_REF_SKIP = (
|
||||
"[5](Win)",
|
||||
"[6](Win)",
|
||||
"[7](Win)",
|
||||
"[10](Win)",
|
||||
"[11](Mac)",
|
||||
"[13](Win)",
|
||||
"[14](IOS)",
|
||||
"[15](IOS)",
|
||||
"[16](Andr)",
|
||||
"[17](Andr)",
|
||||
"[18](Unix)",
|
||||
"[21](Chrome)",
|
||||
# these should be checked
|
||||
"[EvTable](EvTable)",
|
||||
"[styled](OptionStyles)",
|
||||
"[Inputfunc](Inputfunc)",
|
||||
"[online documentation wiki](index)",
|
||||
"[online documentation](index)",
|
||||
"[Accounts](Account)",
|
||||
"[Session](Session)",
|
||||
"[Inputfuncs](Inputfunc)",
|
||||
)
|
||||
|
||||
|
||||
_CURRENT_TITLE = ""
|
||||
|
||||
|
||||
def _sub_remap(match):
|
||||
"""Total remaps"""
|
||||
ref = match.group(0)
|
||||
if ref in _REF_REMAP:
|
||||
new_ref = _REF_REMAP[ref]
|
||||
print(f" Replacing reference {ref} -> {new_ref}")
|
||||
return new_ref
|
||||
return ref
|
||||
|
||||
|
||||
def _sub_link(match):
|
||||
|
||||
mdict = match.groupdict()
|
||||
txt, url_orig = mdict["txt"], mdict["url"]
|
||||
url = url_orig
|
||||
# if not txt:
|
||||
# # the 'comment' is not supported by Mkdocs
|
||||
# return ""
|
||||
print(f" [{txt}]({url})")
|
||||
|
||||
url = _CUSTOM_LINK_REMAP.get(url, url)
|
||||
|
||||
url, *anchor = url.rsplit("#", 1)
|
||||
|
||||
if url in _ABSOLUTE_LINK_SKIP:
|
||||
url += ("#" + anchor[0]) if anchor else ""
|
||||
return f"[{txt}]({url})"
|
||||
|
||||
if url.startswith("evennia"):
|
||||
print(f" Convert evennia url {url} -> {_CODE_PREFIX + url}")
|
||||
url = _API_PREFIX + url
|
||||
|
||||
if url.startswith(_OLD_WIKI_URL):
|
||||
# old wiki is an url on the form https://<wikiurl>/wiki/TextTags#header
|
||||
# we don't refer to the old wiki but use internal mapping.
|
||||
if len(url) != len(_OLD_WIKI_URL):
|
||||
url_conv = url[_OLD_WIKI_URL_LEN:]
|
||||
url_conv = re.sub(r"%20", "-", url_conv)
|
||||
if url_conv.endswith("/_edit"):
|
||||
# this is actually a bug in the wiki format
|
||||
url_conv = url_conv[:-6]
|
||||
if url_conv.startswith("evennia"):
|
||||
# this is an api link
|
||||
url_conv = _CODE_PREFIX + url_conv
|
||||
|
||||
print(f" Converting wiki-url: {url} -> {url_conv}")
|
||||
url = url_conv
|
||||
|
||||
if not url and anchor:
|
||||
# this happens on same-file #labels in wiki
|
||||
url = _CURRENT_TITLE
|
||||
|
||||
if url not in _FILENAMES and not url.startswith("http") and not url.startswith(_CODE_PREFIX):
|
||||
|
||||
url_cap = url.capitalize()
|
||||
url_plur = url[:-3] + "s" + ".md"
|
||||
url_cap_plur = url_plur.capitalize()
|
||||
|
||||
link = f"[{txt}]({url})"
|
||||
if link in _REF_SKIP:
|
||||
url = link
|
||||
elif url_cap in _FILENAMES:
|
||||
print(f" Replacing (capitalized): {url.capitalize()}")
|
||||
url = url_cap
|
||||
elif url_plur in _FILENAMES:
|
||||
print(f" Replacing (pluralized): {url + 's'}")
|
||||
url = url_plur
|
||||
elif url_cap_plur in _FILENAMES:
|
||||
print(f" Replacing (capitalized, pluralized): {url.capitalize() + 's'}")
|
||||
url = url_cap_plur
|
||||
elif url.lower() in _FILENAMESLOW:
|
||||
ind = _FILENAMESLOW.index(url.lower())
|
||||
alt = _FILENAMES[ind]
|
||||
print(f" Replacing {url} with different cap: {alt}")
|
||||
url = alt
|
||||
|
||||
# print(f"\nlink {link} (orig: [{txt}]({url_orig})) found no file match")
|
||||
# inp = input("Enter alternate url (return to keep old): ")
|
||||
# if inp.strip():
|
||||
# url = inp.strip()
|
||||
|
||||
if anchor:
|
||||
url += "#" + anchor[0]
|
||||
|
||||
return f"[{txt}]({url})"
|
||||
|
||||
|
||||
def create_toctree(files):
|
||||
with open("../source/toc.md", "w") as fil:
|
||||
fil.write("# Toc\n")
|
||||
|
||||
for path in files:
|
||||
filename = path.rsplit("/", 1)[-1]
|
||||
ref = filename.rsplit(".", 1)[0]
|
||||
linkname = ref.replace("-", " ")
|
||||
|
||||
if ref == "Home":
|
||||
ref = "index"
|
||||
|
||||
fil.write(f"\n* [{linkname}]({ref}.md)")
|
||||
|
||||
|
||||
def convert_links(files, outdir):
|
||||
global _CURRENT_TITLE
|
||||
|
||||
for inpath in files:
|
||||
|
||||
is_index = False
|
||||
outfile = inpath.rsplit("/", 1)[-1]
|
||||
if outfile == "Home.md":
|
||||
outfile = "index.md"
|
||||
is_index = True
|
||||
outfile = _OUTDIR + outfile
|
||||
|
||||
title = inpath.rsplit("/", 1)[-1].split(".", 1)[0].replace("-", " ")
|
||||
|
||||
print(f"Converting links in {inpath} -> {outfile} ...")
|
||||
with open(inpath) as fil:
|
||||
text = fil.read()
|
||||
|
||||
if is_index:
|
||||
text = _INDEX_PREFIX + text
|
||||
lines = text.split("\n")
|
||||
lines = (
|
||||
lines[:-11]
|
||||
+ [" - The [TOC](toc) lists all regular documentation pages.\n\n"]
|
||||
+ lines[-11:]
|
||||
)
|
||||
text = "\n".join(lines)
|
||||
|
||||
_CURRENT_TITLE = title.replace(" ", "-")
|
||||
text = _RE_CLEAN.sub("", text)
|
||||
text = _RE_REF_LINK.sub(_sub_remap, text)
|
||||
text = _RE_MD_LINK.sub(_sub_link, text)
|
||||
text = (
|
||||
text.split("\n")[1:]
|
||||
if text.split("\n")[0].strip().startswith("[]")
|
||||
else text.split("\n")
|
||||
)
|
||||
text = "\n".join(text)
|
||||
|
||||
if not is_index:
|
||||
text = f"# {title}\n\n{text}"
|
||||
|
||||
with open(outfile, "w") as fil:
|
||||
fil.write(text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("This should not be run on develop files, it would overwrite changes.")
|
||||
# create_toctree(_INFILES)
|
||||
# convert_links(_INFILES, _OUTDIR)
|
||||
|
|
@ -1,19 +1,18 @@
|
|||
# EvAdventure
|
||||
|
||||
Contrib by Griatch 2022
|
||||
Contrib by Griatch 2023-
|
||||
|
||||
|
||||
```{warning}
|
||||
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
|
||||
releasing Evennia 1.0. You will still learn things from it, but don't expect
|
||||
perfection.
|
||||
NOTE - this tutorial is WIP and NOT complete yet! You will still learn
|
||||
things from it, but don't expect perfection.
|
||||
```
|
||||
|
||||
A complete example MUD using Evennia. This is the final result of what is
|
||||
implemented if you follow the Getting-Started tutorial. It's recommended
|
||||
that you follow the tutorial step by step and write your own code. But if
|
||||
you prefer you can also pick apart or use this as a starting point for your
|
||||
own game.
|
||||
implemented if you follow [Part 3 of the Getting-Started tutorial](../Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Part3-Overview.md).
|
||||
It's recommended that you follow the tutorial step by step and write your own
|
||||
code. But if you prefer you can also pick apart or use this as a starting point
|
||||
for your own game.
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
|||
|
|
@ -674,13 +674,12 @@ character make small verbal observations at irregular intervals.
|
|||
|
||||
### `evadventure`
|
||||
|
||||
_Contrib by Griatch 2022_
|
||||
_Contrib by Griatch 2023-_
|
||||
|
||||
|
||||
```{warning}
|
||||
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
|
||||
releasing Evennia 1.0. You will still learn things from it, but don't expect
|
||||
perfection.
|
||||
NOTE - this tutorial is WIP and NOT complete yet! You will still learn
|
||||
things from it, but don't expect perfection.
|
||||
```
|
||||
|
||||
[Read the documentation](./Contrib-Evadventure.md) - [Browse the Code](evennia.contrib.tutorials.evadventure)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# Combat base framework
|
||||
|
||||
Combat is core to many games. Exactly how it works is very game-dependent. For EvAdventure we will show off two common flavors:
|
||||
Combat is core to many games. Exactly how it works is very game-dependent. In this lesson we will build a framework to implement two common flavors:
|
||||
|
||||
- "Twitch-based" combat means that you perform a combat action by entering a command, and after some delay (which may depend on your skills etc), the action happens. It's called 'twitch' because actions often happen fast enough that changing your strategy may involve some element of quick thinking and a 'twitchy trigger finger'.
|
||||
- "Turn-based" combat means that players input actions in clear turns. Timeout for entering/queuing your actions is often much longer than twitch-based style. Once everyone made their choice (or the timeout is reached), everyone's action happens all at once, after which the next turn starts. This style of combat requires less reflexes.
|
||||
- "Twitch-based" combat ([specific lesson here](./Beginner-Tutorial-Combat-Twitch.md)) means that you perform a combat action by entering a command, and after some delay (which may depend on your skills etc), the action happens. It's called 'twitch' because actions often happen fast enough that changing your strategy may involve some element of quick thinking and a 'twitchy trigger finger'.
|
||||
- "Turn-based" combat ([specific lesson here](./Beginner-Tutorial-Combat-Turnbased.md)) means that players input actions in clear turns. Timeout for entering/queuing your actions is often much longer than twitch-based style. Once everyone made their choice (or the timeout is reached), everyone's action happens all at once, after which the next turn starts. This style of combat requires less player reflexes.
|
||||
|
||||
We will design a common combat system that supports both styles.
|
||||
We will design a base combat system that supports both styles.
|
||||
|
||||
- We need a `CombatHandler` to track the progress of combat. This will be a [Script](../../../Components/Scripts.md). Exactly how this works (and where it is stored) will be a bit different between Twitch- and Turnbased combat. We will create its common framework in this lesson.
|
||||
- Combat are divided into _actions_. We want to be able to easily extend our combat with more possible actions. An action needs Python code to show what actually happens when the action is performed. We will define such code in `Action` classes.
|
||||
|
|
@ -20,9 +20,9 @@ In [evennia/contrib/tutorials/evadventure/combat_base.py](evennia.contrib.tutori
|
|||
```
|
||||
Our "Combat Handler" will handle the administration around combat. It needs to be _persistent_ (even is we reload the server your combat should keep going).
|
||||
|
||||
Creating the CombatHandler is a little of a catch-22 - how it works depends on how actions and action-dicts look. But without having the CombatHandler, it's hard to know how to design Actions and Action-dicts. So we'll start with its general structure and fill out the details later in this lesson.
|
||||
Creating the CombatHandler is a little of a catch-22 - how it works depends on how Actions and Action-dicts look. But without having the CombatHandler, it's hard to know how to design Actions and Action-dicts. So we'll start with its general structure and fill out the details later in this lesson.
|
||||
|
||||
Below, methods with `pass` will be filled out this lesson while those raising `NotImplementedError` will be different for Twitch/Turnbased combat and will be implemented in their respective lessons.
|
||||
Below, methods with `pass` will be filled out this lesson while those raising `NotImplementedError` will be different for Twitch/Turnbased combat and will be implemented in their respective lessons following this one.
|
||||
|
||||
```python
|
||||
# in evadventure/combat_base.py
|
||||
|
|
@ -41,15 +41,15 @@ class EvAdventureCombatBaseHandler(DefaultSCript):
|
|||
and tracks all sides of it.
|
||||
|
||||
"""
|
||||
# common for all types of combat
|
||||
# common for all types of combat
|
||||
|
||||
action_classes = {} # to fill in later
|
||||
fallback_action_dict = {}
|
||||
|
||||
@classmethod
|
||||
def get_or_create_combathandler(cls, obj, **kwargs):
|
||||
""" Get or create combathandler on `obj`."""
|
||||
pass
|
||||
@classmethod
|
||||
def get_or_create_combathandler(cls, obj, **kwargs):
|
||||
""" Get or create combathandler on `obj`."""
|
||||
pass
|
||||
|
||||
def msg(self, message, combatant=None, broadcast=True, location=True):
|
||||
"""
|
||||
|
|
@ -58,13 +58,13 @@ class EvAdventureCombatBaseHandler(DefaultSCript):
|
|||
"""
|
||||
pass # TODO
|
||||
|
||||
def get_combat_summary(self, combatant):
|
||||
"""
|
||||
Get a nicely formatted 'battle report' of combat, from the
|
||||
perspective of the combatant.
|
||||
|
||||
"""
|
||||
pass # TODO
|
||||
def get_combat_summary(self, combatant):
|
||||
"""
|
||||
Get a nicely formatted 'battle report' of combat, from the
|
||||
perspective of the combatant.
|
||||
|
||||
"""
|
||||
pass # TODO
|
||||
|
||||
# implemented differently by Twitch- and Turnbased combat
|
||||
|
||||
|
|
@ -75,28 +75,28 @@ class EvAdventureCombatBaseHandler(DefaultSCript):
|
|||
(who is _not_ included in the `allies` list.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
raise NotImplementedError
|
||||
|
||||
def give_advantage(self, recipient, target):
|
||||
"""
|
||||
Give advantage to recipient against target.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
raise NotImplementedError
|
||||
|
||||
def give_disadvantage(self, recipient, target):
|
||||
"""
|
||||
Give disadvantage to recipient against target.
|
||||
|
||||
"""
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def has_advantage(self, combatant, target):
|
||||
"""
|
||||
Does combatant have advantage against target?
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
def has_advantage(self, combatant, target):
|
||||
"""
|
||||
Does combatant have advantage against target?
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def has_disadvantage(self, combatant, target):
|
||||
"""
|
||||
|
|
@ -105,13 +105,13 @@ class EvAdventureCombatBaseHandler(DefaultSCript):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def queue_action(self, combatant, action_dict):
|
||||
"""
|
||||
Queue an action for the combatant by providing
|
||||
an action dict.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
def queue_action(self, combatant, action_dict):
|
||||
"""
|
||||
Queue an action for the combatant by providing
|
||||
action dict.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def execute_next_action(self, combatant):
|
||||
"""
|
||||
|
|
@ -120,26 +120,26 @@ class EvAdventureCombatBaseHandler(DefaultSCript):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def start_combat(self):
|
||||
"""
|
||||
Start combat.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_stop_combat(self):
|
||||
"""
|
||||
Check if the combat is over and if it should be stopped.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def stop_combat(self):
|
||||
"""
|
||||
Stop combat and do cleanup.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
def start_combat(self):
|
||||
"""
|
||||
Start combat.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_stop_combat(self):
|
||||
"""
|
||||
Check if the combat is over and if it should be stopped.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def stop_combat(self):
|
||||
"""
|
||||
Stop combat and do cleanup.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
```
|
||||
|
|
@ -163,7 +163,7 @@ from evennia import create_script
|
|||
|
||||
class EvAdventureCombatBaseHandler(DefaultScript):
|
||||
|
||||
# ...
|
||||
# ...
|
||||
|
||||
@classmethod
|
||||
def get_or_create_combathandler(cls, obj, **kwargs):
|
||||
|
|
@ -204,9 +204,11 @@ class EvAdventureCombatBaseHandler(DefaultScript):
|
|||
|
||||
This helper method uses `obj.scripts.get()` to find if the combat script already exists 'on' the provided `obj`. If not, it will create it using Evennia's [create_script](evennia.utils.create.create_script) function. For some extra speed we cache the handler as `obj.ndb.combathandler` The `.ndb.` (non-db) means that handler is cached only in memory.
|
||||
|
||||
To know if the cache is out of date, we make sure to also check if the combathandler we got has an `id` that is not `None` . If it's `None`, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it.
|
||||
```{sidebar} Checking .id (or .pk)
|
||||
When getting it from cache, we make sure to also check if the combathandler we got has a database `.id` that is not `None` (we could also check `.pk`, stands for "primary key") . If it's `None`, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it.
|
||||
```
|
||||
|
||||
This is a `classmethod`, meaning it should be used on the handler class directly (rather than on an _instance_ of said class). This makes sense because this method actually should return the new instance.
|
||||
`get_or_create_combathandler` is decorated to be a [classmethod](https://docs.python.org/3/library/functions.html#classmethod), meaning it should be used on the handler class directly (rather than on an _instance_ of said class). This makes sense because this method actually should return the new instance.
|
||||
|
||||
As a class method we'll need to call this directly on the class, like this:
|
||||
|
||||
|
|
@ -392,7 +394,7 @@ In EvAdventure we will only support a few common combat actions, mapping to the
|
|||
|
||||
To pass around the details of an attack (the second point above), we will use a `dict`. A `dict` is simple and also easy to save in an `Attribute`. We'll call this the `action_dict` and here's what we need for each action.
|
||||
|
||||
> You don't need to type these out anywhere, we will use them when calling `combathandler.queue_action(combatant, action_dict)`.
|
||||
> You don't need to type these out anywhere, it's listed here for reference. We will use these dicts when calling `combathandler.queue_action(combatant, action_dict)`.
|
||||
|
||||
```python
|
||||
hold_action_dict = {
|
||||
|
|
@ -408,7 +410,8 @@ stunt_action_dict = {
|
|||
"target": <Character/NPC>, # who the recipient gainst adv/dis against
|
||||
"advantage": bool, # grant advantage or disadvantage?
|
||||
"stunt_type": Ability, # Ability to use for the challenge
|
||||
"defense_type": Ability, # what Ability for recipient to defend against dis
|
||||
"defense_type": Ability, # what Ability for recipient to defend with if we
|
||||
# are trying to give disadvantage
|
||||
}
|
||||
use_item_action_dict = {
|
||||
"key": "use",
|
||||
|
|
@ -419,6 +422,11 @@ wield_action_dict = {
|
|||
"key": "wield",
|
||||
"item": <Object>
|
||||
}
|
||||
|
||||
# used only for the turnbased combat, so its Action will be defined there
|
||||
flee_action_dict = {
|
||||
"key": "flee"
|
||||
}
|
||||
```
|
||||
|
||||
Apart from the `stunt` action, these dicts are all pretty simple. The `key` identifes the action to perform and the other fields identifies the minimum things you need to know in order to resolve each action.
|
||||
|
|
@ -432,7 +440,7 @@ attack_action_dict = {
|
|||
}
|
||||
```
|
||||
|
||||
Let's make the `Stunt` dict a little clearer too. In this example, The `Trickster` is performing a _Stunt_ in order to help his friend `Paladin` to gain an INT- _advantage_ against the `Goblin` (maybe the paladin is preparing to cast a spell of something). Since `Trickster` is doing the action, he's not showing up in the dict:
|
||||
Let's explain the longest action dict, the `Stunt` action dict in more detail as well. In this example, The `Trickster` is performing a _Stunt_ in order to help his friend `Paladin` to gain an INT- _advantage_ against the `Goblin` (maybe the paladin is preparing to cast a spell of something). Since `Trickster` is doing the action, he's not showing up in the dict:
|
||||
|
||||
```python
|
||||
stunt_action_dict - {
|
||||
|
|
@ -440,11 +448,15 @@ stunt_action_dict - {
|
|||
"recipient": Paladin,
|
||||
"target": Goblin,
|
||||
"advantage": True,
|
||||
"stunt_type": Ability.DEX,
|
||||
"stunt_type": Ability.INT,
|
||||
"defense_type": Ability.INT,
|
||||
}
|
||||
```
|
||||
This should result in a DEX vs INT based check between the `Trickster` and the `Goblin` (maybe the trickster is trying to trick the goblin with some sleight-of-hand?). If the `Trickster` wins, the `Paladin` gains advantage against the Goblin on the `Paladin`'s next action.
|
||||
```{sidebar}
|
||||
In EvAdventure, we'll always set `stunt_type == defense_type` for simplicity. But you could also consider mixing things up so you could use DEX to confuse someone and give them INT disadvantage, for example.
|
||||
```
|
||||
This should result in an INT vs INT based check between the `Trickster` and the `Goblin` (maybe the trickster is trying to confuse the goblin with some clever word play). If the `Trickster` wins, the `Paladin` gains advantage against the Goblin on the `Paladin`'s next action .
|
||||
|
||||
|
||||
## Action classes
|
||||
|
||||
|
|
@ -509,6 +521,8 @@ if action.can_use():
|
|||
```python
|
||||
# in evadventure/combat_base.py
|
||||
|
||||
# ...
|
||||
|
||||
class CombatActionHold(CombatAction):
|
||||
"""
|
||||
Action that does nothing
|
||||
|
|
@ -527,6 +541,8 @@ Holding does nothing but it's cleaner to nevertheless have a separate class for
|
|||
```python
|
||||
# in evadventure/combat_base.py
|
||||
|
||||
# ...
|
||||
|
||||
class CombatActionAttack(CombatAction):
|
||||
"""
|
||||
A regular attack, using a wielded weapon.
|
||||
|
|
@ -555,6 +571,10 @@ Refer to how we [designed Evadventure weapons](./Beginner-Tutorial-Objects.md#we
|
|||
### Stunt Action
|
||||
|
||||
```python
|
||||
# in evadventure/combat_base.py
|
||||
|
||||
# ...
|
||||
|
||||
class CombatActionStunt(CombatAction):
|
||||
"""
|
||||
Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a
|
||||
|
|
@ -622,8 +642,6 @@ class CombatActionStunt(CombatAction):
|
|||
"|yHaving succeeded, you hold back to plan your next move.|n [hold]",
|
||||
broadcast=False,
|
||||
)
|
||||
combathandler.queue_action(
|
||||
attacker, combathandler.fallback_action_dict)
|
||||
else:
|
||||
self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.")
|
||||
|
||||
|
|
@ -638,6 +656,10 @@ After we have performed a successful stunt, we queue the `combathandler.fallback
|
|||
### Use Item Action
|
||||
|
||||
```python
|
||||
# in evadventure/combat_base.py
|
||||
|
||||
# ...
|
||||
|
||||
class CombatActionUseItem(CombatAction):
|
||||
"""
|
||||
Use an item in combat. This is meant for one-off or limited-use items (so things like scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune, we refer to the item to determine what to use for attack/defense rolls.
|
||||
|
|
@ -663,11 +685,6 @@ class CombatActionUseItem(CombatAction):
|
|||
disadvantage=self.combathandler.has_disadvantage(user, target),
|
||||
)
|
||||
item.at_post_use(user, target)
|
||||
# to back to idle after this
|
||||
self.combathandler.queue_action(
|
||||
self.combatant, i
|
||||
self.combathandler.fallback_action_dict
|
||||
)
|
||||
```
|
||||
|
||||
See the [Consumable items in the Object lesson](./Beginner-Tutorial-Objects.md) to see how consumables work. Like with weapons, we offload all the logic to the item we use.
|
||||
|
|
@ -675,6 +692,10 @@ See the [Consumable items in the Object lesson](./Beginner-Tutorial-Objects.md)
|
|||
### Wield Action
|
||||
|
||||
```python
|
||||
# in evadventure/combat_base.py
|
||||
|
||||
# ...
|
||||
|
||||
class CombatActionWield(CombatAction):
|
||||
"""
|
||||
Wield a new weapon (or spell) from your inventory. This will
|
||||
|
|
@ -689,9 +710,6 @@ class CombatActionWield(CombatAction):
|
|||
|
||||
def execute(self):
|
||||
self.combatant.equipment.move(self.item)
|
||||
self.combathandler.queue_action(
|
||||
self.combatant, self.combathandler.fallback_action_dict
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1203,9 +1203,12 @@ Our turnbased combat system is complete!
|
|||
|
||||
## Testing
|
||||
|
||||
```{sidebar}
|
||||
See [evennia/contrib/tutorials/evadventure/tests/test_combat.py](evennia.contrib.tutorials.evadventure.tests.test_combat)
|
||||
```
|
||||
Unit testing of the Turnbased combat handler is straight forward, you follow the process of earlier lessons to test that each method on the handler returns what you expect with mocked inputs.
|
||||
|
||||
Unit-testing the menu is more complex. You will find examples of doing this in [evennia.utils.tests.test_evmenu](evennia.utils.tests.test_evmenu).
|
||||
Unit-testing the menu is more complex. You will find examples of doing this in [evennia.utils.tests.test_evmenu](github:main/evennia/utils/testss/test_evmenu.py).
|
||||
|
||||
## A small combat test
|
||||
|
||||
|
|
@ -1219,7 +1222,7 @@ Unit testing the code is not enough to see that combat works. We need to also ma
|
|||
- An item (like a potion) we can `use`.
|
||||
|
||||
```{sidebar}
|
||||
You can find an example batch-code script in [evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.ev](evennia.contrib.tutorials.evadventure.batchscripts)
|
||||
You can find an example batch-code script in [evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py](github:evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py)
|
||||
```
|
||||
|
||||
In [The Twitch combat lesson](./Beginner-Tutorial-Combat-Twitch.md) we used a [batch-command script](../../../Components/Batch-Command-Processor.md) to create the testing environment in game. This runs in-game Evennia commands in sequence. For demonstration purposes we'll instead use a [batch-code script](../../../Components/Batch-Code-Processor.md), which runs raw Python code in a repeatable way. A batch-code script is much more flexible than a batch-command script.
|
||||
|
|
|
|||
|
|
@ -67,14 +67,14 @@ An example of an implemented Twitch combat system can be found in [evennia/contr
|
|||
Here is the general design of the Twitch-based combat handler:
|
||||
|
||||
- The twitch-version of the CombatHandler will be stored on each combatant whenever combat starts. When combat is over, or they leave the room with combat, the handler will be deleted.
|
||||
- The handler will start queue each action independently, starting a timer until they fire.
|
||||
- The handler will queue each action independently, starting a timer until they fire.
|
||||
- All input are handled via Commands.
|
||||
|
||||
## Twitch combat handler
|
||||
|
||||
> Create a new module `evadventure/combat_twitch.py`.
|
||||
|
||||
We will make use of the _Combat Actions_, _Combat dicts_ and the parent `EvAdventureCombatBaseHandler` [we created previously](./Beginner-Tutorial-Combat-Base.md).
|
||||
We will make use of the _Combat Actions_, _Action dicts_ and the parent `EvAdventureCombatBaseHandler` [we created previously](./Beginner-Tutorial-Combat-Base.md).
|
||||
|
||||
```python
|
||||
# in evadventure/combat_twitch.py
|
||||
|
|
@ -941,7 +941,7 @@ This is what we need for a minimal test:
|
|||
- An item (like a potion) we can `use`.
|
||||
|
||||
```{sidebar}
|
||||
You can find an example batch-command script in [evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev](evennia.contrib.tutorials.evadventure.batchscripts)
|
||||
You can find an example batch-command script in [evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev](github:evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.ev)
|
||||
```
|
||||
While you can create these manually in-game, it can be convenient to create a [batch-command script](../../../Components/Batch-Command-Processor.md) to set up your testing environment.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,15 +22,21 @@ The tutorial game is under development and is not yet complete, nor tested. Use
|
|||
In part three of the Evennia Beginner tutorial we will go through the actual creation of
|
||||
our tutorial game _EvAdventure_, based on the [Knave](https://www.drivethrurpg.com/product/250888/Knave) RPG ruleset.
|
||||
|
||||
Even if this is not the game-style you are interested in, following along will give you a lot
|
||||
of experience using Evennia and be really helpful for doing your own thing later!
|
||||
|
||||
This is a big part. You'll be seeing a lot of code and there are plenty of lessons to go through. Take your time!
|
||||
|
||||
If you followed the previous parts of this tutorial series you will have some notions about Python and where to find and make use of things in Evennia. We also have a good idea of the type of game we will create.
|
||||
|
||||
Fully coded examples of all code we make in this part can be found in the
|
||||
[evennia/contrib/tutorials/evadventure](../../../api/evennia.contrib.tutorials.evadventure.md) package.
|
||||
Even if this is not the game-style you are interested in, following along will give you a lot
|
||||
of experience using Evennia and be really helpful for doing your own thing later! The EvAdventure game code is also built to easily be expanded upon.
|
||||
|
||||
Fully coded examples of all code we make in this part can be found in the
|
||||
[evennia/contrib/tutorials/evadventure](../../../api/evennia.contrib.tutorials.evadventure.md) package. There are three common ways to learn from this:
|
||||
|
||||
1. Follow the tutorial lessons in sequence and use it to write your own code, referring to the ready-made code as extra help, context, or as a 'facit' to check yourself.
|
||||
2. Read through the code in the package and refer to the tutorial lesson for each part for more information on what you see.
|
||||
3. Some mix of the two.
|
||||
|
||||
Which approach you choose is individual - we all learn in different ways.
|
||||
|
||||
Either way, this is a big part. You'll be seeing a lot of code and there are plenty of lessons to go through. We are making a whole game from scratch after all. Take your time!
|
||||
|
||||
## Lessons
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
```{eval-rst}
|
||||
evennia.contrib.tutorials.evadventure.batchscripts
|
||||
==========================================================
|
||||
|
||||
.. automodule:: evennia.contrib.tutorials.evadventure.batchscripts
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 6
|
||||
|
||||
evennia.contrib.tutorials.evadventure.batchscripts.turnbased_combat_demo
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
```{eval-rst}
|
||||
evennia.contrib.tutorials.evadventure.batchscripts.turnbased\_combat\_demo
|
||||
=================================================================================
|
||||
|
||||
.. automodule:: evennia.contrib.tutorials.evadventure.batchscripts.turnbased_combat_demo
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
```
|
||||
|
|
@ -36,6 +36,7 @@ evennia.contrib.tutorials.evadventure
|
|||
.. toctree::
|
||||
:maxdepth: 6
|
||||
|
||||
evennia.contrib.tutorials.evadventure.batchscripts
|
||||
evennia.contrib.tutorials.evadventure.tests
|
||||
|
||||
```
|
||||
|
|
@ -179,7 +179,7 @@ def url_resolver(app, docname, source):
|
|||
Convert urls by catching special markers.
|
||||
|
||||
Supported replacements (used e.g. as [txt](github:...)
|
||||
github:master/<url> - add path to Evennia github master branch
|
||||
github:main/<url> - add path to Evennia github master branch
|
||||
github:develop/<url> - add path to Evennia github develop branch
|
||||
github:issue - add link to the Evennia github issue-create page
|
||||
src:foo.bar#Foo - add link to source doc in _modules
|
||||
|
|
@ -189,7 +189,6 @@ def url_resolver(app, docname, source):
|
|||
"""
|
||||
|
||||
def _url_remap(url):
|
||||
|
||||
# determine depth in tree of current document
|
||||
docdepth = docname.count("/") + 1
|
||||
relative_path = "../".join("" for _ in range(docdepth))
|
||||
|
|
@ -294,7 +293,6 @@ def autodoc_post_process_docstring(app, what, name, obj, options, lines):
|
|||
Post-process docstring in various ways. Must modify lines-list in-place.
|
||||
"""
|
||||
try:
|
||||
|
||||
# clean out ANSI colors
|
||||
|
||||
if ansi_clean:
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
# EvAdventure
|
||||
|
||||
Contrib by Griatch 2022
|
||||
Contrib by Griatch 2023-
|
||||
|
||||
|
||||
```{warning}
|
||||
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
|
||||
releasing Evennia 1.0. You will still learn things from it, but don't expect
|
||||
perfection.
|
||||
NOTE - this tutorial is WIP and NOT complete yet! You will still learn
|
||||
things from it, but don't expect perfection.
|
||||
```
|
||||
|
||||
A complete example MUD using Evennia. This is the final result of what is
|
||||
implemented if you follow the Getting-Started tutorial. It's recommended
|
||||
that you follow the tutorial step by step and write your own code. But if
|
||||
you prefer you can also pick apart or use this as a starting point for your
|
||||
own game.
|
||||
implemented if you follow [Part 3 of the Getting-Started tutorial](Beginner-Tutorial-Part3-Overview).
|
||||
It's recommended that you follow the tutorial step by step and write your own
|
||||
code. But if you prefer you can also pick apart or use this as a starting point
|
||||
for your own game.
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# This file needs to be here for autodocs to pick up this folder/package
|
||||
|
|
@ -3,13 +3,15 @@ EvAdventure Base combat utilities.
|
|||
|
||||
This establishes the basic building blocks for combat:
|
||||
|
||||
- `CombatFailure` - exception for combat-specific errors.
|
||||
- `CombatAction` (and subclasses) - classes encompassing all the working around an action.
|
||||
They are initialized from 'action-dicts` - dictionaries with all the relevant data for the
|
||||
particular invocation
|
||||
- `CombatHandler` - base class for running a combat. Exactly how this is used depends on the
|
||||
type of combat intended (twitch- or turn-based) so many details of this will be implemented
|
||||
in child classes.
|
||||
- `CombatFailure` - exception for combat-specific errors.
|
||||
- `CombatAction` (and subclasses) - classes encompassing all the working around an action.
|
||||
They are initialized from 'action-dicts` - dictionaries with all the relevant data for the
|
||||
particular invocation
|
||||
- `CombatHandler` - base class for running a combat. Exactly how this is used depends on the
|
||||
type of combat intended (twitch- or turn-based) so many details of this will be implemented
|
||||
in child classes.
|
||||
|
||||
----
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -23,7 +25,7 @@ from . import rules
|
|||
|
||||
class CombatFailure(RuntimeError):
|
||||
"""
|
||||
Some failure during actions.
|
||||
Some failure during combat actions.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -98,28 +100,21 @@ class CombatAction:
|
|||
class CombatActionHold(CombatAction):
|
||||
"""
|
||||
Action that does nothing.
|
||||
|
||||
Note:
|
||||
Refer to as 'hold'
|
||||
|
||||
action_dict = {
|
||||
"key": "hold"
|
||||
}
|
||||
::
|
||||
action_dict = {
|
||||
"key": "hold"
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class CombatActionAttack(CombatAction):
|
||||
"""
|
||||
A regular attack, using a wielded weapon.
|
||||
|
||||
action-dict = {
|
||||
"key": "attack",
|
||||
"target": Character/Object
|
||||
}
|
||||
|
||||
Note:
|
||||
Refer to as 'attack'
|
||||
|
||||
::
|
||||
action-dict = {
|
||||
"key": "attack",
|
||||
"target": Character/Object
|
||||
}
|
||||
"""
|
||||
|
||||
def execute(self):
|
||||
|
|
@ -140,19 +135,16 @@ class CombatActionStunt(CombatAction):
|
|||
target. Whenever performing a stunt that would affect another negatively (giving them
|
||||
disadvantage against an ally, or granting an advantage against them, we need to make a check
|
||||
first. We don't do a check if giving an advantage to an ally or ourselves.
|
||||
|
||||
action_dict = {
|
||||
"key": "stunt",
|
||||
"recipient": Character/NPC,
|
||||
"target": Character/NPC,
|
||||
"advantage": bool, # if False, it's a disadvantage
|
||||
"stunt_type": Ability, # what ability (like STR, DEX etc) to use to perform this stunt.
|
||||
"defense_type": Ability, # what ability to use to defend against (negative) effects of
|
||||
this stunt.
|
||||
}
|
||||
|
||||
Note:
|
||||
refer to as 'stunt'.
|
||||
::
|
||||
action_dict = {
|
||||
"key": "stunt",
|
||||
"recipient": Character/NPC,
|
||||
"target": Character/NPC,
|
||||
"advantage": bool, # if False, it's a disadvantage
|
||||
"stunt_type": Ability, # what ability (like STR, DEX etc) to use to perform this stunt.
|
||||
"defense_type": Ability, # what ability to use to defend against (negative) effects of
|
||||
this stunt.
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -209,16 +201,12 @@ class CombatActionUseItem(CombatAction):
|
|||
Use an item in combat. This is meant for one-off or limited-use items (so things like
|
||||
scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune,
|
||||
we refer to the item to determine what to use for attack/defense rolls.
|
||||
|
||||
action_dict = {
|
||||
"key": "use",
|
||||
"item": Object
|
||||
"target": Character/NPC/Object/None
|
||||
}
|
||||
|
||||
Note:
|
||||
Refer to as 'use'
|
||||
|
||||
::
|
||||
action_dict = {
|
||||
"key": "use",
|
||||
"item": Object
|
||||
"target": Character/NPC/Object/None
|
||||
}
|
||||
"""
|
||||
|
||||
def execute(self):
|
||||
|
|
@ -234,22 +222,17 @@ class CombatActionUseItem(CombatAction):
|
|||
disadvantage=self.combathandler.has_disadvantage(user, target),
|
||||
)
|
||||
item.at_post_use(user, target)
|
||||
# to back to idle after this
|
||||
|
||||
|
||||
class CombatActionWield(CombatAction):
|
||||
"""
|
||||
Wield a new weapon (or spell) from your inventory. This will swap out the one you are currently
|
||||
wielding, if any.
|
||||
|
||||
action_dict = {
|
||||
"key": "wield",
|
||||
"item": Object
|
||||
}
|
||||
|
||||
Note:
|
||||
Refer to as 'wield'.
|
||||
|
||||
::
|
||||
action_dict = {
|
||||
"key": "wield",
|
||||
"item": Object
|
||||
}
|
||||
"""
|
||||
|
||||
def execute(self):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
"""
|
||||
Unit tests for EvAdventure components.
|
||||
|
||||
"""
|
||||
|
|
@ -159,18 +159,7 @@ class InMemoryAttribute(IAttribute):
|
|||
|
||||
class AttributeProperty:
|
||||
"""
|
||||
Attribute property descriptor. Allows for specifying Attributes as Django-like 'fields'
|
||||
on the class level. Note that while one can set a lock on the Attribute,
|
||||
there is no way to *check* said lock when accessing via the property - use
|
||||
the full `AttributeHandler` if you need to do access checks. Note however that if you use the
|
||||
full `AttributeHandler` to access this Attribute, the `at_get/at_set` methods on this class will
|
||||
_not_ fire (because you are bypassing the `AttributeProperty` entirely in that case).
|
||||
|
||||
Example:
|
||||
::
|
||||
|
||||
class Character(DefaultCharacter):
|
||||
foo = AttributeProperty(default="Bar")
|
||||
AttributeProperty.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -178,6 +167,13 @@ class AttributeProperty:
|
|||
|
||||
def __init__(self, default=None, category=None, strattr=False, lockstring="", autocreate=True):
|
||||
"""
|
||||
Allows for specifying Attributes as Django-like 'fields' on the class level. Note that while
|
||||
one can set a lock on the Attribute, there is no way to *check* said lock when accessing via
|
||||
the property - use the full `AttributeHandler` if you need to do access checks. Note however
|
||||
that if you use the full `AttributeHandler` to access this Attribute, the `at_get/at_set`
|
||||
methods on this class will _not_ fire (because you are bypassing the `AttributeProperty`
|
||||
entirely in that case).
|
||||
|
||||
Initialize an Attribute as a property descriptor.
|
||||
|
||||
Keyword Args:
|
||||
|
|
@ -194,6 +190,11 @@ class AttributeProperty:
|
|||
is explicitly assigned a value. This makes it more efficient while it retains
|
||||
its default (there's no db access), but without an actual Attribute generated,
|
||||
one cannot access it via .db, the AttributeHandler or see it with `examine`.
|
||||
Example:
|
||||
::
|
||||
|
||||
class Character(DefaultCharacter):
|
||||
foo = AttributeProperty(default="Bar")
|
||||
|
||||
"""
|
||||
self._default = default
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue