diff --git a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.md b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.md index 6c12c2f770..559a0b058f 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.md @@ -439,6 +439,7 @@ It's convenient to have the `EquipmentHandler` easily tell you what weapon is cu # mygame/evadventure/equipment.py from .enums import WieldLocation, Ability +from .objects import get_empty_hand # ... @@ -469,16 +470,15 @@ class EquipmentHandler: if not weapon: weapon = slots[WieldLocation.WEAPON_HAND] # if we still don't have a weapon, we return None here + if not weapon: + ~ weapon = get_bare_hands() return weapon ``` In the `.armor()` method we get the item (if any) out of each relevant wield-slot (body, shield, head), and grab their `armor` Attribute. We then `sum()` them all up. -In `.weapon()`, we simply check which of the possible weapon slots (weapon-hand or two-hands) have -something in them. If not we fall back to the 'fake' weapon `WeaponEmptyHand` which is just a 'dummy' -object that represents your bare hands with damage and all. -(created in [The Object tutorial](./Beginner-Tutorial-Objects.md#your-bare-hands) earlier). +In `.weapon()`, we simply check which of the possible weapon slots (weapon-hand or two-hands) have something in them. If not we fall back to the 'Bare Hands' object we created in the [Object tutorial lesson](./Beginner-Tutorial-Objects.md#your-bare-hands) earlier. ## Extra credits diff --git a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.md b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.md index cf8610dc46..4585b90042 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.md @@ -448,6 +448,8 @@ We will use this in the upcoming [Equipment tutorial lesson](./Beginner-Tutoria from evennia import search_object, create_object +_BARE_HANDS = None + # ... class WeaponBareHands(EvAdventureWeapon) @@ -459,15 +461,24 @@ class WeaponBareHands(EvAdventureWeapon) quality = None # let's assume fists are indestructible ... -# common to put this at the bottom of module -BARE_HANDS = search_object("Bare hands", typeclass=WeaponBareHands).first() -if not BARE_HANDS: - BARE_HANDS = create_object(WeaponBareHands, key="Bare hands") - +def get_bare_hands(): + """Get the bare hands""" + global _BARE_HANDS + if not _BARE_HANDS: + _BARE_HANDS = search_object("Bare hands", typeclass=WeaponBareHands).first() + if not _BARE_HANDS: + _BARE_HANDS = create_object(WeaponBareHands, key="Bare hands") + return _BARE_HANDS ``` -Since everyone's empty hands are the same (in our game), we create _one_ `Bare hands` weapon object that everyone shares. We do this by searching for the object with `search_object` (the `.first()` means we grab the first one even if we should by accident have created multiple hands, see [The Django querying tutorial](../Part1/Beginner-Tutorial-Django-queries.md) for more info). If we find none, we create it. This way the `BARE_HANDS` object can be used by everyone (we just need to import `objects.BARE_HANDS`). +```{sidebar} +Creating a single instance of something that is used everywhere is called to create a _Singleton_. +``` +Since everyone's empty hands are the same (in our game), we create _one_ `Bare hands` weapon object that everyone shares. We do this by searching for the object with `search_object` (the `.first()` means we grab the first one even if we should by accident have created multiple hands, see [The Django querying tutorial](../Part1/Beginner-Tutorial-Django-queries.md) for more info). If we find none, we create it. +By use of the `global` Python keyword, we cache the bare hands object get/create in a module level property `_BARE_HANDS`. So this acts as a cache to not have to search the database more than necessary. + +From now on, other modules can just import and run this function to get the bare hands. ## Testing and Extra credits diff --git a/docs/source/Setup/Installation-Troubleshooting.md b/docs/source/Setup/Installation-Troubleshooting.md index 9d8164896b..7eb9d77a31 100644 --- a/docs/source/Setup/Installation-Troubleshooting.md +++ b/docs/source/Setup/Installation-Troubleshooting.md @@ -78,7 +78,7 @@ If `localhost` doesn't work when trying to connect to your local game, try `127. ## Mac Troubleshooting - Some Mac users have reported not being able to connect to `localhost` (i.e. your own computer). If so, try to connect to `127.0.0.1` instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual. -- If you get a `MemoryError` when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. [A user in our forums](https://github.com/evennia/evennia/discussions/2638#discussioncomment-3630761) found a working solution for this. [Here](https://github.com/evennia/evennia/issues/3120#issuecomment-1442540538) is another variation to solve it. +- If you get a `MemoryError` when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. [A user in our forums](https://github.com/evennia/evennia/discussions/2637) found a working solution for this. [Here](https://github.com/evennia/evennia/issues/2854) is another variation to solve it. ## Windows Troubleshooting diff --git a/evennia/contrib/tutorials/evadventure/characters.py b/evennia/contrib/tutorials/evadventure/characters.py index 51464002b6..588e352150 100644 --- a/evennia/contrib/tutorials/evadventure/characters.py +++ b/evennia/contrib/tutorials/evadventure/characters.py @@ -6,7 +6,6 @@ Character class. from evennia.objects.objects import DefaultCharacter from evennia.typeclasses.attributes import AttributeProperty from evennia.utils.evform import EvForm -from evennia.utils.evmenu import EvMenu, ask_yes_no from evennia.utils.evtable import EvTable from evennia.utils.logger import log_trace from evennia.utils.utils import lazy_property @@ -14,7 +13,6 @@ from evennia.utils.utils import lazy_property from . import rules from .equipment import EquipmentError, EquipmentHandler from .quests import EvAdventureQuestHandler -from .utils import get_obj_stats class LivingMixin: diff --git a/evennia/contrib/tutorials/evadventure/chargen.py b/evennia/contrib/tutorials/evadventure/chargen.py index aba9c08122..26f3df6464 100644 --- a/evennia/contrib/tutorials/evadventure/chargen.py +++ b/evennia/contrib/tutorials/evadventure/chargen.py @@ -3,10 +3,9 @@ EvAdventure character generation. """ from django.conf import settings - -from evennia import create_object from evennia.objects.models import ObjectDB from evennia.prototypes.spawner import spawn +from evennia.utils.create import create_object from evennia.utils.evmenu import EvMenu from .characters import EvAdventureCharacter diff --git a/evennia/contrib/tutorials/evadventure/combat_base.py b/evennia/contrib/tutorials/evadventure/combat_base.py index 79f7451d4a..1c26013237 100644 --- a/evennia/contrib/tutorials/evadventure/combat_base.py +++ b/evennia/contrib/tutorials/evadventure/combat_base.py @@ -15,10 +15,10 @@ This establishes the basic building blocks for combat: """ -from evennia import Command, create_script from evennia.scripts.scripts import DefaultScript from evennia.typeclasses.attributes import AttributeProperty from evennia.utils import evtable +from evennia.utils.create import create_script from . import rules diff --git a/evennia/contrib/tutorials/evadventure/combat_turnbased.py b/evennia/contrib/tutorials/evadventure/combat_turnbased.py index ea801a8d3c..e3c646a728 100644 --- a/evennia/contrib/tutorials/evadventure/combat_turnbased.py +++ b/evennia/contrib/tutorials/evadventure/combat_turnbased.py @@ -14,6 +14,8 @@ able to kill and be killed in the same turn). Unlike in twitch-like combat, there is no movement while in turn-based combat. Fleeing is a select action that takes several vulnerable turns to complete. +---- + """ diff --git a/evennia/contrib/tutorials/evadventure/combat_twitch.py b/evennia/contrib/tutorials/evadventure/combat_twitch.py index a53cccfbda..a23fa0871e 100644 --- a/evennia/contrib/tutorials/evadventure/combat_twitch.py +++ b/evennia/contrib/tutorials/evadventure/combat_twitch.py @@ -3,6 +3,8 @@ EvAdventure Twitch-based combat This implements a 'twitch' (aka DIKU or other traditional muds) style of MUD combat. +---- + """ from evennia import AttributeProperty, CmdSet, default_cmds from evennia.commands.command import Command, InterruptCommand diff --git a/evennia/contrib/tutorials/evadventure/enums.py b/evennia/contrib/tutorials/evadventure/enums.py index a47ad5e87a..6ba77d82f9 100644 --- a/evennia/contrib/tutorials/evadventure/enums.py +++ b/evennia/contrib/tutorials/evadventure/enums.py @@ -4,6 +4,7 @@ of using an Enum over, say, a string is that if you make a typo using an unknown enum, Python will give you an error while a typo in a string may go through silently. It's used as a direct reference: +:: from enums import Ability @@ -13,6 +14,8 @@ It's used as a direct reference: To get the `value` of an enum (must always be hashable, useful for Attribute lookups), use `Ability.STR.value` (which would return 'strength' in our case). +---- + """ from enum import Enum diff --git a/evennia/contrib/tutorials/evadventure/equipment.py b/evennia/contrib/tutorials/evadventure/equipment.py index c7dcb8e1fb..f8dc99e344 100644 --- a/evennia/contrib/tutorials/evadventure/equipment.py +++ b/evennia/contrib/tutorials/evadventure/equipment.py @@ -6,7 +6,7 @@ Knave has a system of Slots for its inventory. from evennia.utils.utils import inherits_from from .enums import Ability, WieldLocation -from .objects import BARE_HANDS, EvAdventureObject +from .objects import EvAdventureObject, get_bare_hands class EquipmentError(TypeError): @@ -170,7 +170,7 @@ class EquipmentHandler: if not weapon: weapon = slots[WieldLocation.WEAPON_HAND] if not weapon: - weapon = BARE_HANDS + weapon = get_bare_hands() return weapon def display_loadout(self): diff --git a/evennia/contrib/tutorials/evadventure/npcs.py b/evennia/contrib/tutorials/evadventure/npcs.py index 46cd621ec1..ddb484e121 100644 --- a/evennia/contrib/tutorials/evadventure/npcs.py +++ b/evennia/contrib/tutorials/evadventure/npcs.py @@ -12,7 +12,7 @@ from evennia.utils.utils import make_iter from .characters import LivingMixin from .enums import Ability, WieldLocation -from .objects import BARE_HANDS +from .objects import get_bare_hands from .rules import dice @@ -51,7 +51,7 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter): is_idle = AttributeProperty(default=False, autocreate=False) - weapon = AttributeProperty(default=BARE_HANDS, autocreate=False) # instead of inventory + weapon = AttributeProperty(default=get_bare_hands, autocreate=False) # instead of inventory coins = AttributeProperty(default=1, autocreate=False) # coin loot # if this npc is attacked, everyone with the same tag in the current location will also be diff --git a/evennia/contrib/tutorials/evadventure/objects.py b/evennia/contrib/tutorials/evadventure/objects.py index 78d84467ba..9626af1e5a 100644 --- a/evennia/contrib/tutorials/evadventure/objects.py +++ b/evennia/contrib/tutorials/evadventure/objects.py @@ -26,6 +26,8 @@ from . import rules from .enums import Ability, ObjType, WieldLocation from .utils import get_obj_stats +_BARE_HANDS = None + class EvAdventureObject(DefaultObject): """ @@ -352,9 +354,20 @@ class WeaponBareHands(EvAdventureWeapon): attack_type = Ability.STR defense_type = Ability.ARMOR damage_roll = "1d4" - quality = 100000 # let's assume fists are always available ... + quality = None # let's assume fists are always available ... -BARE_HANDS = search_object("Bare hands", typeclass=WeaponBareHands).first() -if not BARE_HANDS: - BARE_HANDS = create_object(WeaponBareHands, key="Bare hands") +def get_bare_hands(): + """ + Get the bare-hands singleton object. + + Returns: + WeaponBareHands + """ + global _BARE_HANDS + + if not _BARE_HANDS: + _BARE_HANDS = search_object("Bare hands", typeclass=WeaponBareHands).first() + if not _BARE_HANDS: + _BARE_HANDS = create_object(WeaponBareHands, key="Bare hands") + return _BARE_HANDS diff --git a/evennia/contrib/tutorials/evadventure/rules.py b/evennia/contrib/tutorials/evadventure/rules.py index edac0df76b..a714907bed 100644 --- a/evennia/contrib/tutorials/evadventure/rules.py +++ b/evennia/contrib/tutorials/evadventure/rules.py @@ -1,25 +1,10 @@ """ MUD ruleset based on the _Knave_ OSR tabletop RPG by Ben Milton (modified for MUD use). -The rules are divided into a set of classes. While each class (except chargen) could -also have been stand-alone functions, having them as classes makes it a little easier -to use them as the base for your own variation (tweaking values etc). +The center of the rule system is the "RollEngine", which handles all rolling of dice +and determining what the outcome is. -- Roll-engine: Class with methods for making all dice rolls needed by the rules. Knave only - has a few resolution rolls, but we define helper methods for different actions the - character will be able to do in-code. -- Character generation - this is a container used for holding, tweaking and setting - all character data during character generation. At the end it will save itself - onto the Character for permanent storage. -- Improvement - this container holds rules used with experience to improve the - character over time. -- Charsheet - a container with tools for visually displaying the character sheet in-game. - -This module presents several singletons to import - -- `dice` - the `EvAdventureRollEngine` for all random resolution and table-rolling. -- `character_sheet` - the `EvAdventureCharacterSheet` visualizer. -- `improvement` - the EvAdventureImprovement` class for handling char xp and leveling. +---- """ from random import randint @@ -245,8 +230,7 @@ class EvAdventureRollEngine: # tuple with range conditional, like ('1-5', "Blue") or ('10', "Purple") max_range = -1 min_range = 10**6 - for (valrange, choice) in table_choices: - + for valrange, choice in table_choices: minval, *maxval = valrange.split("-", 1) minval = abs(int(minval)) maxval = abs(int(maxval[0]) if maxval else minval) diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index de824d63d5..ca49680f20 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -177,7 +177,8 @@ class AttributeProperty: Initialize an Attribute as a property descriptor. Keyword Args: - default (any): A default value if the attr is not set. + default (any): A default value if the attr is not set. If a callable, this will be + run without any arguments and is expected to return the default value. category (str): The attribute's category. If unset, use class default. strattr (bool): If set, this Attribute *must* be a simple string, and will be stored more efficiently.