diff --git a/docs/latest/.buildinfo b/docs/latest/.buildinfo index 9508576a45..2c7bbd3a0a 100644 --- a/docs/latest/.buildinfo +++ b/docs/latest/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: b7f3bde8bd7f2104b246ef81cda88ee7 +config: 3d218971e6732f6d07286e28b58c83c5 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/latest/Coding/Changelog.html b/docs/latest/Coding/Changelog.html index 79b81a6b19..b3ec2881c8 100644 --- a/docs/latest/Coding/Changelog.html +++ b/docs/latest/Coding/Changelog.html @@ -216,6 +216,12 @@ settings (count-infinity)

finds big sword even if another type of sword is around (InspectorCaracal)

  • Fix: In searches, allow special ‘here’ and ‘me’ keywords only be valid queries unless current location and/or caller is in valid search candidates respectively (InspectorCaracal)

  • +
  • [Fix][pull3694]: Funcparser swallowing rest of line after a \-escape (count-infinity)

  • +
  • Fix: Properly serialize IntFlag enum types (0xDEADFED5)

  • +
  • Fix: Correct links in about command (0xDEADFED5)

  • +
  • Fix: Clean reduntant session clearin in at_server_cold_start (InspectorCaracal)

  • +
  • Fix: Usability improvements in the Discord integration (InspectorCaracal)

  • +
  • Fix: Make \\ properly preserve one backlash in funcparser (Griatch)

  • [Docs]: Fixes from InspectorCaracal, Griatch

  • diff --git a/docs/latest/Coding/Debugging.html b/docs/latest/Coding/Debugging.html index ebbbd9ced4..7b8330702c 100644 --- a/docs/latest/Coding/Debugging.html +++ b/docs/latest/Coding/Debugging.html @@ -143,7 +143,7 @@ available out-of-the-box.

    1. Find the point in the code where you want to have more insight. Add the following line at that point.

      -
      from evennia import set_trace;set_trace()
      +
      from evennia import set_trace;set_trace()
       
    2. @@ -164,7 +164,7 @@ default cmdset. Then restart Evennia in interactive mode with
      # In file commands/command.py
       
       
      -class CmdTest(Command):
      +class CmdTest(Command):
       
           """
           A test command just to test pdb.
      @@ -176,8 +176,8 @@ default cmdset. Then restart Evennia in interactive mode with key = "test"
       
      -    def func(self):
      -        from evennia import set_trace; set_trace()   # <--- start of debugger
      +    def func(self):
      +        from evennia import set_trace; set_trace()   # <--- start of debugger
               obj = self.search(self.args)
               self.msg("You've found {}.".format(obj.get_display_name()))
       
      @@ -199,8 +199,8 @@ default cmdset. Then restart Evennia in interactive mode with 43
        44         key = "test"
        45
      - 46         def func(self):
      - 47             from evennia import set_trace; set_trace()   # <--- start of debugger
      + 46         def func(self):
      + 47             from evennia import set_trace; set_trace()   # <--- start of debugger
        48  ->         obj = self.search(self.args)
        49             self.msg("You've found {}.".format(obj.get_display_name()))
        50
      diff --git a/docs/latest/Coding/Evennia-Code-Style.html b/docs/latest/Coding/Evennia-Code-Style.html
      index 3c5031710a..21fc8766c9 100644
      --- a/docs/latest/Coding/Evennia-Code-Style.html
      +++ b/docs/latest/Coding/Evennia-Code-Style.html
      @@ -203,7 +203,7 @@ class. It should usually not describe the exact call sequence nor list
       important methods, this tends to be hard to keep updated as the API
       develops. Don’t use section markers (#, ## etc).

      Example of class docstring:

      -
      class MyClass(object):
      +
      class MyClass(object):
           """
           This class describes the creation of `Objects`. It is useful
           in many situations, such as ...
      @@ -216,7 +216,7 @@ develops. Don’t use section markers (

      Example of function or method docstring:

      
      -def funcname(a, b, c, d=False, **kwargs):
      +def funcname(a, b, c, d=False, **kwargs):
           """
           This is a brief introduction to the function/class/method
       
      diff --git a/docs/latest/Coding/Profiling.html b/docs/latest/Coding/Profiling.html
      index a0a07bef12..de42b370d3 100644
      --- a/docs/latest/Coding/Profiling.html
      +++ b/docs/latest/Coding/Profiling.html
      @@ -143,7 +143,7 @@
       

      Python’s timeit module is very good for testing small things. For example, in order to test if it is faster to use a for loop or a list comprehension you could use the following code:

      -
          import timeit
      +
          import timeit
           # Time to do 1000000 for loops
           timeit.timeit("for i in range(100):\n    a.append(i)", setup="a = []")
          <<< 10.70982813835144
      @@ -174,8 +174,8 @@ could use the following code:

      Then in the shell

      -
      import pstats
      -from pstats import SortKey
      +
      import pstats
      +from pstats import SortKey
       
       p = pstats.Stats('server/log/server.prof')
       p.strip_dirs().sort_stats(-1).print_stats()
      @@ -260,7 +260,7 @@ be able to connect with an existing user since the password hasher chan
       
       # actions
       
      -def c_login(client):
      +def c_login(client):
           name = f"Character-{client.gid}"
           pwd = f"23fwsf23sdfw23wef23"
           return (
      @@ -268,10 +268,10 @@ be able to connect with an existing user since the password hasher chan
               f"connect {name} {pwd}"
           )
       
      -def c_logout(client):
      +def c_logout(client):
           return ("quit", )
       
      -def c_look(client):
      +def c_look(client):
           return ("look here", "look me")
       
       # this is read by dummyrunner.
      diff --git a/docs/latest/Coding/Unit-Testing.html b/docs/latest/Coding/Unit-Testing.html
      index 68180a3d35..cc60a770a5 100644
      --- a/docs/latest/Coding/Unit-Testing.html
      +++ b/docs/latest/Coding/Unit-Testing.html
      @@ -188,31 +188,31 @@ are two special, optional methods Here’s an example of the principle. Let’s assume you put this in mygame/world/tests.py
       and want to test a function in mygame/world/myfunctions.py

          # in a module tests.py somewhere i your game dir
      -    import unittest
      +    import unittest
       
      -    from evennia import create_object
      +    from evennia import create_object
           # the function we want to test
      -    from .myfunctions import myfunc
      +    from .myfunctions import myfunc
       
       
      -    class TestObj(unittest.TestCase):
      +    class TestObj(unittest.TestCase):
              "This tests a function myfunc."
       
      -       def setUp(self):
      +       def setUp(self):
                  """done before every of the test_ * methods below"""
                  self.obj = create_object("mytestobject")
       
      -       def tearDown(self):
      +       def tearDown(self):
                  """done after every test_* method below """
                  self.obj.delete()
       
      -       def test_return_value(self):
      +       def test_return_value(self):
                  """test method. Makes sure return value is as expected."""
                  actual_return = myfunc(self.obj)
                  expected_return = "This is the good object 'mytestobject'."
                  # test
                  self.assertEqual(expected_return, actual_return)
      -       def test_alternative_call(self):
      +       def test_alternative_call(self):
                  """test method. Calls with a keyword argument."""
                  actual_return = myfunc(self.obj, bad=True)
                  expected_return = "This is the baaad object 'mytestobject'."
      @@ -278,32 +278,32 @@ just there for naming symmetry with <
       

      Here’s an example of using EvenniaTest

      # in a test module
       
      -from evennia.utils.test_resources import EvenniaTest
      +from evennia.utils.test_resources import EvenniaTest
       
      -class TestObject(EvenniaTest):
      +class TestObject(EvenniaTest):
           """Remember that the testing class creates char1 and char2 inside room1 ..."""
      -    def test_object_search_character(self):
      +    def test_object_search_character(self):
               """Check that char1 can search for char2 by name"""
               self.assertEqual(self.char1.search(self.char2.key), self.char2)
       
      -    def test_location_search(self):
      +    def test_location_search(self):
               """Check so that char1 can find the current location by name"""
               self.assertEqual(self.char1.search(self.char1.location.key), self.char1.location)
               # ...
       

      This example tests a custom command.

      -
          from evennia.commands.default.tests import EvenniaCommandTest
      -from commands import command as mycommand
      +
          from evennia.commands.default.tests import EvenniaCommandTest
      +from commands import command as mycommand
       
       
      -class TestSet(EvenniaCommandTest):
      +class TestSet(EvenniaCommandTest):
           "tests the look command by simple call, using Char2 as a target"
       
      -    def test_mycmd_char(self):
      +    def test_mycmd_char(self):
               self.call(mycommand.CmdMyLook(), "Char2", "Char2(#7)")
       
      -    def test_mycmd_room(self):
      +    def test_mycmd_room(self):
               "tests the look command by simple call, with target as room"
               self.call(mycommand.CmdMyLook(), "Room",
                         "Room(#1)\nroom_desc\nExits: out(#3)\n"
      @@ -353,9 +353,9 @@ of the Evennia distribution and its unit tests should be run with all other Even
       
      # a file contrib/mycontrib/tests.py
       
      -from django.conf import settings
      -import django
      -from evennia.utils.test_resources import BaseEvenniaTest
      +from django.conf import settings
      +import django
      +from evennia.utils.test_resources import BaseEvenniaTest
       
       OLD_DEFAULT_SETTINGS = settings.INSTALLED_APPS
       DEFAULT_SETTINGS = dict(
      @@ -371,29 +371,29 @@ of the Evennia distribution and its unit tests should be run with all other Even
       )
       
       
      -class TestMyModel(BaseEvenniaTest):
      -    def setUp(self):
      +class TestMyModel(BaseEvenniaTest):
      +    def setUp(self):
               if not settings.configured:
                   settings.configure(**DEFAULT_SETTINGS)
               django.setup()
       
      -        from django.core.management import call_command
      -        from django.db.models import loading
      +        from django.core.management import call_command
      +        from django.db.models import loading
               loading.cache.loaded = False
               call_command('syncdb', verbosity=0)
       
      -    def tearDown(self):
      +    def tearDown(self):
               settings.configure(**OLD_DEFAULT_SETTINGS)
               django.setup()
       
      -        from django.core.management import call_command
      -        from django.db.models import loading
      +        from django.core.management import call_command
      +        from django.db.models import loading
               loading.cache.loaded = False
               call_command('syncdb', verbosity=0)
       
           # test cases below ...
       
      -    def test_case(self):
      +    def test_case(self):
       # test case here
       
      diff --git a/docs/latest/Components/Accounts.html b/docs/latest/Components/Accounts.html index 3fcdc1724f..2a9472e57a 100644 --- a/docs/latest/Components/Accounts.html +++ b/docs/latest/Components/Accounts.html @@ -151,11 +151,11 @@ Evennia’s MULTISESSION_MODE

      Here’s an example of modifying the default Account class in code:

          # in mygame/typeclasses/accounts.py
       
      -    from evennia import DefaultAccount
      +    from evennia import DefaultAccount
       
      -    class Account(DefaultAccount): 
      +    class Account(DefaultAccount): 
               # [...]
      -    	def at_account_creation(self): 
      +    	def at_account_creation(self): 
               	"this is called only once, when account is first created"
           	    self.db.real_name = None      # this is set later 
           	    self.db.real_address = None   #       "
      diff --git a/docs/latest/Components/Attributes.html b/docs/latest/Components/Attributes.html
      index 7b85b01ee7..0820dd6c6d 100644
      --- a/docs/latest/Components/Attributes.html
      +++ b/docs/latest/Components/Attributes.html
      @@ -161,10 +161,10 @@
       
      In-code, using AttributeProperty at class level
      -
      from evennia import DefaultObject
      -from evennia import AttributeProperty
      +
      from evennia import DefaultObject
      +from evennia import AttributeProperty
       
      -class MyObject(DefaultObject):
      +class MyObject(DefaultObject):
           foo = AttributeProperty(default=[1, 2, 3, "bar"])
           myattr = AttributeProperty(100, category='bar')
       
      @@ -177,7 +177,7 @@

      Using .db

      The simplest way to get/set Attributes is to use the .db shortcut. This allows for setting and getting Attributes that lack a category (having category None)

      -
      import evennia
      +
      import evennia
       
       obj = evennia.create_object(key="Foo")
       
      @@ -265,10 +265,10 @@
       

      The third way to set up an Attribute is to use an AttributeProperty. This is done on the class level of your typeclass and allows you to treat Attributes a bit like Django database Fields. Unlike using .db and .attributes, an AttributeProperty can’t be created on the fly, you must assign it in the class code.

      # mygame/typeclasses/characters.py
       
      -from evennia import DefaultCharacter
      -from evennia.typeclasses.attributes import AttributeProperty
      +from evennia import DefaultCharacter
      +from evennia.typeclasses.attributes import AttributeProperty
       
      -class Character(DefaultCharacter):
      +class Character(DefaultCharacter):
       
           strength = AttributeProperty(10, category='stat')
           constitution = AttributeProperty(11, category='stat')
      @@ -278,7 +278,7 @@
           sleepy = AttributeProperty(False, autocreate=False)
           poisoned = AttributeProperty(False, autocreate=False)
       
      -    def at_object_creation(self):
      +    def at_object_creation(self):
             # ...
       
      @@ -449,8 +449,8 @@ set mypobj/mystring = [1, 2, foo] # foo is invalid Python (no quotes)
      Invalid, ‘hidden’ dbobject
      # example of storing an invalid, "hidden" dbobject in Attribute
      -class Container:
      -    def __init__(self, mydbobj):
      +class Container:
      +    def __init__(self, mydbobj):
               # no way for Evennia to know this is a database object!
               self.mydbobj = mydbobj
       
      @@ -463,19 +463,19 @@ set mypobj/mystring = [1, 2, foo]   # foo is invalid Python (no quotes)
       

      By adding two methods __serialize_dbobjs__ and __deserialize_dbobjs__ to the object you want to save, you can pre-serialize and post-deserialize all ‘hidden’ objects before Evennia’s main serializer gets to work. Inside these methods, use Evennia’s evennia.utils.dbserialize.dbserialize and dbunserialize functions to safely serialize the db-objects you want to store.

      Fixing an invalid ‘hidden’ dbobj for storing in Attribute
      -
      from evennia.utils import dbserialize  # important
      +
      from evennia.utils import dbserialize  # important
       
      -class Container:
      -    def __init__(self, mydbobj):
      +class Container:
      +    def __init__(self, mydbobj):
               # A 'hidden' db-object
               self.mydbobj = mydbobj
       
      -    def __serialize_dbobjs__(self):
      +    def __serialize_dbobjs__(self):
               """This is called before serialization and allows
               us to custom-handle those 'hidden' dbobjs"""
               self.mydbobj = dbserialize.dbserialize(self.mydbobj
       
      -    def __deserialize_dbobjs__(self):
      +    def __deserialize_dbobjs__(self):
               """This is called after deserialization and allows you to
               restore the 'hidden' dbobjs you serialized before"""
               if isinstance(self.mydbobj, bytes):
      diff --git a/docs/latest/Components/Batch-Code-Processor.html b/docs/latest/Components/Batch-Code-Processor.html
      index 527d18406e..4c864d34c0 100644
      --- a/docs/latest/Components/Batch-Code-Processor.html
      +++ b/docs/latest/Components/Batch-Code-Processor.html
      @@ -163,9 +163,9 @@
           
           # This will be included in all other #CODE blocks
           
      -    from evennia import create_object, search_object
      -    from evennia.contrib.tutorial_examples import red_button
      -    from typeclasses.objects import Object
      +    from evennia import create_object, search_object
      +    from evennia.contrib.tutorial_examples import red_button
      +    from typeclasses.objects import Object
           
           limbo = search_object('Limbo')[0]
           
      @@ -217,9 +217,9 @@
       

      This shows that you are on the first #CODE block, the first of only two commands in this batch file. Observe that the block has not actually been executed at this point!

      To take a look at the full code snippet you are about to run, use ll (a batch-processor version of look).

      -
          from evennia.utils import create, search
      -    from evennia.contrib.tutorial_examples import red_button
      -    from typeclasses.objects import Object
      +
          from evennia.utils import create, search
      +    from evennia.contrib.tutorial_examples import red_button
      +    from typeclasses.objects import Object
           
           limbo = search.objects(caller, 'Limbo', global_search=True)[0]
       
      diff --git a/docs/latest/Components/Channels.html b/docs/latest/Components/Channels.html
      index f56feb1e02..eeee9188f3 100644
      --- a/docs/latest/Components/Channels.html
      +++ b/docs/latest/Components/Channels.html
      @@ -338,9 +338,9 @@ will still only be able to admin or destroy channels they actually control!

      channels you could override the help command and change the lockstring to:

        # in for example mygame/commands/commands.py
       
      -  from evennia import default_cmds
      +  from evennia import default_cmds
       
      -  class MyCustomChannelCmd(default_cmds.CmdChannel):
      +  class MyCustomChannelCmd(default_cmds.CmdChannel):
             locks = "cmd: not pperm(channel_banned);admin:perm(Builder);manage:perm(Builder);changelocks:perm(Admin)"
       
       
      @@ -396,8 +396,8 @@ Default channels all use To change which channel typeclass Evennia uses for default commands, change settings.BASE_CHANNEL_TYPECLASS. The base command class is evennia.comms.comms.DefaultChannel. There is an empty child class in mygame/typeclasses/channels.py, same as for other typelass-bases.

      In code you create a new channel with evennia.create_channel or Channel.create:

      -
        from evennia import create_channel, search_object
      -  from typeclasses.channels import Channel
      +
        from evennia import create_channel, search_object
      +  from typeclasses.channels import Channel
       
         channel = create_channel("my channel", aliases=["mychan"], locks=..., typeclass=...)
         # alternative
      diff --git a/docs/latest/Components/Coding-Utils.html b/docs/latest/Components/Coding-Utils.html
      index 3cb3172c46..bf6422e556 100644
      --- a/docs/latest/Components/Coding-Utils.html
      +++ b/docs/latest/Components/Coding-Utils.html
      @@ -155,7 +155,7 @@ on all objects. This will search for objects in the same location and inside the
       

      Give the keyword global_search=True to extend search to encompass entire database. Aliases will also be matched by this search. You will find multiple examples of this functionality in the default command set.

      If you need to search for objects in a code module you can use the functions in evennia.utils.search. You can access these as shortcuts evennia.search_*.

      -
           from evennia import search_object
      +
           from evennia import search_object
            obj = search_object(objname)
       
      @@ -173,7 +173,7 @@ on all objects. This will search for objects in the same location and inside the

      Create

      Apart from the in-game build commands (@create etc), you can also build all of Evennia’s game entities directly in code (for example when defining new create commands).

      -
         import evennia
      +
         import evennia
       
          myobj = evennia.create_objects("game.gamesrc.objects.myobj.MyObj", key="MyObj")
       
      @@ -192,7 +192,7 @@ on all objects. This will search for objects in the same location and inside the

      Logging

      Normally you can use Python print statements to see output to the terminal/log. The print statement should only be used for debugging though. For producion output, use the logger which will create proper logs either to terminal or to file.

      -
           from evennia import logger
      +
           from evennia import logger
            #
            logger.log_err("This is an Error!")
            logger.log_warn("This is a Warning!")
      @@ -220,7 +220,7 @@ kill the server.

      Game time

      Evennia tracks the current server time. You can access this time via the evennia.gametime shortcut:

      -
      from evennia import gametime
      +
      from evennia import gametime
       
       # all the functions below return times in seconds).
       
      @@ -246,9 +246,9 @@ kill the server.

      The setting TIME_FACTOR determines how fast/slow in-game time runs compared to the real world. The setting TIME_GAME_EPOCH sets the starting game epoch (in seconds). The functions from the gametime module all return their times in seconds. You can convert this to whatever units of time you desire for your game. You can use the @time command to view the server time info. You can also schedule things to happen at specific in-game times using the gametime.schedule function:

      -
      import evennia
      +
      import evennia
       
      -def church_clock:
      +def church_clock:
           limbo = evennia.search_object(key="Limbo")
           limbo.msg_contents("The church clock chimes two.")
       
      @@ -269,9 +269,9 @@ You can also schedule things to happen at specific in-game times using
       

      utils.delay()

      This allows for making a delayed call.

      -
      from evennia import utils
      +
      from evennia import utils
       
      -def _callback(obj, text):
      +def _callback(obj, text):
           obj.msg(text)
       
       # wait 10 seconds before sending "Echo!" to obj (which we assume is defined)
      @@ -292,7 +292,7 @@ You can also schedule things to happen at specific in-game times using
       will only catch immediate dependence). This function also accepts as input any combination of
       classes, instances or python-paths-to-classes.

      Note that Python code should usually work with duck typing. But in Evennia’s case it can sometimes be useful to check if an object inherits from a given Typeclass as a way of identification. Say for example that we have a typeclass Animal. This has a subclass Felines which in turn has a subclass HouseCat. Maybe there are a bunch of other animal types too, like horses and dogs. Using inherits_from will allow you to check for all animals in one go:

      -
           from evennia import utils
      +
           from evennia import utils
            if (utils.inherits_from(obj, "typeclasses.objects.animals.Animal"):
               obj.msg("The bouncer stops you in the door. He says: 'No talking animals allowed.'")
       
      diff --git a/docs/latest/Components/Command-Sets.html b/docs/latest/Components/Command-Sets.html index 495824dda4..86556307cd 100644 --- a/docs/latest/Components/Command-Sets.html +++ b/docs/latest/Components/Command-Sets.html @@ -159,14 +159,14 @@ but are used for more advanced set manipulation and coding (see the [merge rules Sets#merge-rules) section).

      # file mygame/commands/mycmdset.py
       
      -from evennia import CmdSet
      +from evennia import CmdSet
       
       # this is a theoretical custom module with commands we
       # created previously: mygame/commands/mycommands.py
      -from commands import mycommands
      +from commands import mycommands
       
      -class MyCmdSet(CmdSet):
      -    def at_cmdset_creation(self):
      +class MyCmdSet(CmdSet):
      +    def at_cmdset_creation(self):
               """
               The only thing this method should need
               to do is to add commands to the set.
      @@ -178,7 +178,7 @@ Sets#merge-rules) section).

      The CmdSet’s add() method can also take another CmdSet as input. In this case all the commands from that CmdSet will be appended to this one as if you added them line by line:

      -
          def at_cmdset_creation():
      +
          def at_cmdset_creation():
               ...
               self.add(AdditionalCmdSet) # adds all command from this set
               ...
      @@ -455,16 +455,16 @@ sure we merge onto B.  Setting E’s priority to, say, -4 will make sure to merg
       it appropriately.

      More advanced cmdset example:

      -
      from commands import mycommands
      +
      from commands import mycommands
       
      -class MyCmdSet(CmdSet):
      +class MyCmdSet(CmdSet):
       
           key = "MyCmdSet"
           priority = 4
           mergetype = "Replace"
           key_mergetypes = {'MyOtherCmdSet':'Union'}
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               """
               The only thing this method should need
               to do is to add commands to the set.
      diff --git a/docs/latest/Components/Commands.html b/docs/latest/Components/Commands.html
      index 8f61c6fe96..0841b06a1c 100644
      --- a/docs/latest/Components/Commands.html
      +++ b/docs/latest/Components/Commands.html
      @@ -160,26 +160,26 @@ Evennia actually inherit from a child of , splitting by “=” etc.  Below we’ll avoid mux-
       specifics and use the base Command class directly.

          # basic Command definition
      -    from evennia import Command
      +    from evennia import Command
       
      -    class MyCmd(Command):
      +    class MyCmd(Command):
              """
              This is the help-text for the command
              """
              key = "mycommand"
      -       def parse(self):
      +       def parse(self):
                  # parsing the command line here
      -       def func(self):
      +       def func(self):
                  # executing the command here
       

      Here is a minimalistic command with no custom parsing:

      -
          from evennia import Command
      +
          from evennia import Command
       
      -    class CmdEcho(Command):
      +    class CmdEcho(Command):
               key = "echo"
       
      -        def func(self):
      +        def func(self):
                   # echo the caller's input back to the caller
                   self.caller.msg(f"Echo: {self.args}")
       
      @@ -282,9 +282,9 @@ truthfully report this value - that case the doc string (__doc__) at the top of your class. This string is dynamically read by the Help System to create the help entry for this command. You should decide on a way to format your help and stick to that.

      Below is how you define a simple alternative “smile” command:

      -
      from evennia import Command
      +
      from evennia import Command
       
      -class CmdSmile(Command):
      +class CmdSmile(Command):
           """
           A smile command
       
      @@ -305,11 +305,11 @@ truthfully report this value - that case the locks = "cmd:all()"
           help_category = "General"
       
      -    def parse(self):
      +    def parse(self):
               "Very trivial parser"
               self.target = self.args.strip()
       
      -    def func(self):
      +    def func(self):
               "This actually does things"
               caller = self.caller
       
      @@ -357,13 +357,13 @@ possible. Only if there is a collision, will the prefix be shown in the help sys
       

      Exiting a command

      Normally you just use return in one of your Command class’ hook methods to exit that method. That will however still fire the other hook methods of the Command in sequence. That’s usually what you want but sometimes it may be useful to just abort the command, for example if you find some unacceptable input in your parse method. To exit the command this way you can raise evennia.InterruptCommand:

      -
      from evennia import InterruptCommand
      +
      from evennia import InterruptCommand
       
      -class MyCommand(Command):
      +class MyCommand(Command):
       
          # ...
       
      -   def parse(self):
      +   def parse(self):
              # ...
              # if this fires, `func()` and `at_post_cmd` will not
              # be called at all
      @@ -383,9 +383,9 @@ the current execution of your command and wait for more before processing.

      Note that you cannot just drop yield into any code and expect it to pause. Evennia will only pause for you if you yield inside the Command’s func() method. Don’t expect it to work anywhere else.

      Here’s an example of a command using a small pause of five seconds between messages:

      -
      from evennia import Command
      +
      from evennia import Command
       
      -class CmdWait(Command):
      +class CmdWait(Command):
           """
           A dummy command to show how to wait
       
      @@ -398,7 +398,7 @@ the current execution of your command and wait for more before processing.

      locks = "cmd:all()" help_category = "General" - def func(self): + def func(self): """Command execution.""" self.msg("Beginner-Tutorial to wait ...") yield 5 @@ -421,7 +421,7 @@ the current execution of your command and wait for more before processing.

      Here’s a very simple example:

      -
      class CmdConfirm(Command):
      +
      class CmdConfirm(Command):
       
           """
           A dummy command to show confirmation.
      @@ -433,7 +433,7 @@ the current execution of your command and wait for more before processing.

      key = "confirm" - def func(self): + def func(self): answer = yield("Are you sure you want to go on?") if answer.strip().lower() in ("yes", "y"): self.msg("Yes!") @@ -461,12 +461,12 @@ the current execution of your command and wait for more before processing.

    3. New session connection (syscmdkeys.CMD_LOGINSTART). This command name should be put in the settings.CMDSET_UNLOGGEDIN. Whenever a new connection is established, this command is always called on the server (default is to show the login screen).

    4. Below is an example of redefining what happens when the account doesn’t provide any input (e.g. just presses return). Of course the new system command must be added to a cmdset as well before it will work.

      -
          from evennia import syscmdkeys, Command
      +
          from evennia import syscmdkeys, Command
       
      -    class MyNoInputCommand(Command):
      +    class MyNoInputCommand(Command):
               "Usage: Just press return, I dare you"
               key = syscmdkeys.CMD_NOINPUT
      -        def func(self):
      +        def func(self):
                   self.caller.msg("Don't just press return like that, talk to me!")
       
      @@ -499,10 +499,10 @@ the current execution of your command and wait for more before processing.

      Note: On a server reload, all Commands are rebuilt and memory is flushed.

      To show this in practice, consider this command:

      -
      class CmdTestID(Command):
      +
      class CmdTestID(Command):
           key = "testid"
       
      -    def func(self):
      +    def func(self):
       
               if not hasattr(self, "xval"):
                   self.xval = 0
      @@ -527,9 +527,9 @@ the current execution of your command and wait for more before processing.

      Create a command on the fly

      This is also an advanced topic.

      Commands can also be created and added to a cmdset on the fly. Creating a class instance with a keyword argument, will assign that keyword argument as a property on this paricular command:

      -
      class MyCmdSet(CmdSet):
      +
      class MyCmdSet(CmdSet):
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               self.add(MyCommand(myvar=1, foo="test")
       
      @@ -582,7 +582,7 @@ corresponds to a known command. This is how the command handler sequence looks f
       Evennia does not use this return value at all by default. If you do, you must
       thus do so asynchronously, using callbacks.

           # in command class func()
      -     def callback(ret, caller):
      +     def callback(ret, caller):
               caller.msg(f"Returned is {ret}")
            deferred = self.execute_command("longrunning")
            deferred.addCallback(callback, self.caller)
      diff --git a/docs/latest/Components/EvEditor.html b/docs/latest/Components/EvEditor.html
      index 7172e4f310..e2295b62ca 100644
      --- a/docs/latest/Components/EvEditor.html
      +++ b/docs/latest/Components/EvEditor.html
      @@ -132,7 +132,7 @@ search/replace, fill, dedent and more.

      Launching the editor

      The editor is created as follows:

      -
      from evennia.utils.eveditor import EvEditor
      +
      from evennia.utils.eveditor import EvEditor
       
       EvEditor(caller,
                loadfunc=None, savefunc=None, quitfunc=None,
      @@ -156,10 +156,10 @@ It has no other mechanical function.

      Working with EvEditor

      This is an example command for setting a specific Attribute using the editor.

      -
      from evennia import Command
      -from evennia.utils import eveditor
      +
      from evennia import Command
      +from evennia.utils import eveditor
       
      -class CmdSetTestAttr(Command):
      +class CmdSetTestAttr(Command):
           """
           Set the "test" Attribute using
           the line editor.
      @@ -169,15 +169,15 @@ It has no other mechanical function.

      """ key = "settestattr" - def func(self): + def func(self): "Set up the callbacks and launch the editor" - def load(caller): + def load(caller): "get the current value" return caller.attributes.get("test") - def save(caller, buffer): + def save(caller, buffer): "save the buffer" caller.attributes.add("test", buffer) - def quit(caller): + def quit(caller): "Since we define it, we must handle messages" caller.msg("Editor exited") key = f"{self.caller}/test" @@ -193,22 +193,22 @@ It has no other mechanical function.

      when reloading the game. In order to be persistent, an editor needs to have its callback functions (loadfunc, savefunc and quitfunc) as top-level functions defined in the module. Since these functions will be stored, Python will need to find them.

      -
      from evennia import Command
      -from evennia.utils import eveditor
      +
      from evennia import Command
      +from evennia.utils import eveditor
       
      -def load(caller):
      +def load(caller):
           "get the current value"
           return caller.attributes.get("test")
       
      -def save(caller, buffer):
      +def save(caller, buffer):
           "save the buffer"
           caller.attributes.add("test", buffer)
       
      -def quit(caller):
      +def quit(caller):
           "Since we define it, we must handle messages"
           caller.msg("Editor exited")
       
      -class CmdSetTestAttr(Command):
      +class CmdSetTestAttr(Command):
           """
           Set the "test" Attribute using
           the line editor.
      @@ -218,7 +218,7 @@ functions will be stored, Python will need to find them.

      """ key = "settestattr" - def func(self): + def func(self): "Set up the callbacks and launch the editor" key = f"{self.caller}/test" # launch the editor diff --git a/docs/latest/Components/EvMenu.html b/docs/latest/Components/EvMenu.html index f0a464ee2a..05cb8dc378 100644 --- a/docs/latest/Components/EvMenu.html +++ b/docs/latest/Components/EvMenu.html @@ -178,14 +178,14 @@ chooses, they are forwarded to different nodes in the menu.

      The EvMenu utility class is located in evennia/utils/evmenu.py. It allows for easily adding interactive menus to the game; for example to implement Character creation, building commands or similar. Below is an example of offering NPC conversation choices:

      This is how the example menu at the top of this page will look in code:

      -
      from evennia.utils import evmenu
      +
      from evennia.utils import evmenu
       
      -def _handle_answer(caller, raw_input, **kwargs):
      +def _handle_answer(caller, raw_input, **kwargs):
           answer = kwargs.get("answer")
           caller.msg(f"You chose {answer}!")
           return "end"  # name of next node
       
      -def node_question(caller, raw_input, **kwargs):
      +def node_question(caller, raw_input, **kwargs):
           text = "Is your answer yes or no?"
           options = (
               {"key": ("[Y]es!", "yes", "y"),
      @@ -200,7 +200,7 @@ It allows for easily adding interactive menus to the game; for example to implem
           )
           return text, options
       
      -def node_end(caller, raw_input, **kwargs):
      +def node_end(caller, raw_input, **kwargs):
           text "Thanks for your answer. Goodbye!"
           return text, None  # empty options ends the menu
       
      @@ -228,9 +228,9 @@ the menu (since it doesn’t return any options).

      You can also write menus using the EvMenu templating language. This allows you to use a text string to generate simpler menus with less boiler plate. Let’s create exactly the same menu using the templating language:

      -
      from evennia.utils import evmenu
      +
      from evennia.utils import evmenu
       
      -def _handle_answer(caller, raw_input, **kwargs):
      +def _handle_answer(caller, raw_input, **kwargs):
           answer = kwargs.get("answer")
           caller.msg(f"You chose {answer}!")
           return "end"  # name of next node
      @@ -274,7 +274,7 @@ uses the template-string and a mapping of callables (we must add
       
      
      -def _skill_check(caller, raw_string, **kwargs):
      +def _skill_check(caller, raw_string, **kwargs):
           skills = kwargs.get("skills", [])
           gold = kwargs.get("gold", 0)
       
      @@ -284,7 +284,7 @@ uses the template-string and a mapping of callables (we must add
       
           return next_node_name
       
      -def node_guard(caller, raw_string, **kwarg):
      +def node_guard(caller, raw_string, **kwarg):
           text = (
               'The guard looks at you suspiciously.\n'
               '"No one is supposed to be in here ..."\n'
      @@ -318,13 +318,13 @@ into combat!

      Initializing the menu is done using a call to the evennia.utils.evmenu.EvMenu class. This is the most common way to do so - from inside a Command:

      # in, for example gamedir/commands/command.py
       
      -from evennia.utils.evmenu import EvMenu
      +from evennia.utils.evmenu import EvMenu
       
      -class CmdTestMenu(Command):
      +class CmdTestMenu(Command):
       
           key = "testcommand"
       
      -    def func(self):
      +    def func(self):
       
       	EvMenu(self.caller, "world.mymenu")
       
      @@ -376,15 +376,15 @@ menu. Temporary variables you store on a persistent 
       

      The Menu nodes

      The EvMenu nodes consist of functions on one of these forms.

      -
      def menunodename1(caller):
      +
      def menunodename1(caller):
           # code
           return text, options
       
      -def menunodename2(caller, raw_string):
      +def menunodename2(caller, raw_string):
           # code
           return text, options
       
      -def menunodename3(caller, raw_string, **kwargs):
      +def menunodename3(caller, raw_string, **kwargs):
           # code
           return text, options
       
      @@ -432,7 +432,7 @@ menu. Temporary variables you store on a persistent 

      The options list describe all the choices available to the user when viewing this node. If options is returned as None, it means that this node is an Exit node - any text is displayed and then the menu immediately exits, running the exit_cmd if given.

      Otherwise, options should be a list (or tuple) of dictionaries, one for each option. If only one option is available, a single dictionary can also be returned. This is how it could look:

      -
      def node_test(caller, raw_string, **kwargs):
      +
      def node_test(caller, raw_string, **kwargs):
       
           text = "A goblin attacks you!"
       
      @@ -473,7 +473,7 @@ ________________________________
       

      Whether you want to use a key or rely on numbers is mostly a matter of style and the type of menu.

      EvMenu accepts one important special key given only as "_default". This key is used when a user enters something that does not match any other fixed keys. It is particularly useful for getting user input:

      -
      def node_readuser(caller, raw_string, **kwargs):
      +
      def node_readuser(caller, raw_string, **kwargs):
           text = "Please enter your name"
       
           options = {"key": "_default",
      @@ -495,15 +495,15 @@ ________________________________
       

      option-key ‘goto’

      This is the operational part of the option and fires only when the user chooses said option. Here are three ways to write it

      
      -def _action_two(caller, raw_string, **kwargs):
      +def _action_two(caller, raw_string, **kwargs):
           # do things ...
           return "calculated_node_to_go_to"
       
      -def _action_three(caller, raw_string, **kwargs):
      +def _action_three(caller, raw_string, **kwargs):
           # do things ...
           return "node_four", {"mode": 4}
       
      -def node_select(caller, raw_string, **kwargs):
      +def node_select(caller, raw_string, **kwargs):
       
           text = ("select one",
                   "help - they all do different things ...")
      @@ -539,11 +539,11 @@ ________________________________
       

      Customizing Menu formatting

      The EvMenu display of nodes, options etc are controlled by a series of formatting methods on the EvMenu class. To customize these, simply create a new child class of EvMenu and override as needed. Here is an example:

      -
      from evennia.utils.evmenu import EvMenu
      +
      from evennia.utils.evmenu import EvMenu
       
      -class MyEvMenu(EvMenu):
      +class MyEvMenu(EvMenu):
       
      -    def nodetext_formatter(self, nodetext):
      +    def nodetext_formatter(self, nodetext):
               """
               Format the node text itself.
       
      @@ -555,7 +555,7 @@ ________________________________
       
               """
       
      -    def helptext_formatter(self, helptext):
      +    def helptext_formatter(self, helptext):
               """
               Format the node's help text
       
      @@ -567,7 +567,7 @@ ________________________________
       
               """
       
      -    def options_formatter(self, optionlist):
      +    def options_formatter(self, optionlist):
               """
               Formats the option block.
       
      @@ -581,7 +581,7 @@ ________________________________
       
               """
       
      -    def node_formatter(self, nodetext, optionstext):
      +    def node_formatter(self, nodetext, optionstext):
               """
               Formats the entirety of the node.
       
      @@ -688,10 +688,10 @@ myfunc(foo)      # error!
       

      Templating example

      -
      from random import random
      -from evennia.utils import evmenu
      +
      from random import random
      +from evennia.utils import evmenu
       
      -def _gamble(caller, raw_string, **kwargs):
      +def _gamble(caller, raw_string, **kwargs):
       
           caller.msg("You roll the dice ...")
           if random() < 0.5:
      @@ -762,10 +762,10 @@ request input in a similar way to This will send “Please enter your answer” to the Command’s self.caller and then pause at that
       point. All other players at the server will be unaffected. Once caller enteres a reply, the code
       execution will continue and you can do stuff with the result. Here is an example:

      -
      from evennia import Command
      -class CmdTestInput(Command):
      +
      from evennia import Command
      +class CmdTestInput(Command):
           key = "test"
      -    def func(self):
      +    def func(self):
               result = yield("Please enter something:")
               self.caller.msg(f"You entered {result}.")
               result2 = yield("Now enter something else:")
      @@ -798,10 +798,10 @@ launching the full power of a menu to do so. To use, call prompt appear on their screens and any text they
       enter will be sent into the callback for whatever processing you want.

      Below is a fully explained callback and example call:

      -
      from evennia import Command
      -from evennia.utils.evmenu import get_input
      +
      from evennia import Command
      +from evennia.utils.evmenu import get_input
       
      -def callback(caller, prompt, user_input):
      +def callback(caller, prompt, user_input):
           """
           This is a callback you define yourself.
       
      @@ -842,7 +842,7 @@ list.

      Example: Yes/No prompt

      Below is an example of a Yes/No prompt using the get_input function:

      -
      def yesno(caller, prompt, result):
      +
      def yesno(caller, prompt, result):
           if result.lower() in ("y", "yes", "n", "no"):
               # do stuff to handle the yes/no answer
               # ...
      @@ -879,7 +879,7 @@ It is used to quickly create menus for manipulating large numbers of items.

      The menu will automatically create an multi-page option listing that one can flip through. One can inpect each entry and then select them with prev/next. This is how it is used:

      -
      from evennia.utils.evmenu import list_node
      +
      from evennia.utils.evmenu import list_node
       
       
       ...
      @@ -892,7 +892,7 @@ inpect each entry and then select them with prev/next. This is how it is used:return "next_node"
       
       @list_node(options, select=_select, pagesize=10)
      -def node_mylist(caller, raw_string, **kwargs):
      +def node_mylist(caller, raw_string, **kwargs):
           ...
       
           return text, options
      @@ -986,7 +986,7 @@ helper function accessed as Below is an example of a simple branching menu node leading to different other nodes depending on choice:

      # in mygame/world/mychargen.py
       
      -def define_character(caller):
      +def define_character(caller):
           text = \
           """
           What aspect of your character do you want
      @@ -1022,13 +1022,13 @@ like this instead in the example:

      Example: Dynamic goto

      
      -def _is_in_mage_guild(caller, raw_string, **kwargs):
      +def _is_in_mage_guild(caller, raw_string, **kwargs):
           if caller.tags.get('mage', category="guild_member"):
               return "mage_guild_welcome"
           else:
               return "mage_guild_blocked"
       
      -def enter_guild:
      +def enter_guild:
           text = 'You say to the mage guard:'
           options ({'desc': 'I need to get in there.',
                     'goto': _is_in_mage_guild},
      @@ -1048,7 +1048,7 @@ get to pick between numbers.

      Here is an example of passing arguments into the goto callable and use that to influence which node it should go to next:

      
      -def _set_attribute(caller, raw_string, **kwargs):
      +def _set_attribute(caller, raw_string, **kwargs):
           "Get which attribute to modify and set it"
       
           attrname, value = kwargs.get("attr", (None, None))
      @@ -1059,7 +1059,7 @@ which node it should go to next:

      return next_node -def node_background(caller): +def node_background(caller): text = \ f""" {caller.key} experienced a traumatic event @@ -1095,7 +1095,7 @@ We could also imagine the helper function analyzing what other choices

      Example: Get arbitrary input

      An example of the menu asking the user for input - any input.

      
      -def _set_name(caller, raw_string, **kwargs):
      +def _set_name(caller, raw_string, **kwargs):
       
           inp = raw_string.strip()
       
      @@ -1115,7 +1115,7 @@ We could also imagine the helper function analyzing what other choices

      return None, {"prev_entry": inp} -def enter_name(caller, raw_string, **kwargs): +def enter_name(caller, raw_string, **kwargs): # check if we already entered a name before prev_entry = kwargs.get("prev_entry") @@ -1159,14 +1159,14 @@ previous node, but updating its ingoing kwargs to tell it to display a different every node. The advantage of doing this is that the _evmenu NAttribute will be deleted automatically when you exit the menu.

      
      -def _set_name(caller, raw_string, **kwargs):
      +def _set_name(caller, raw_string, **kwargs):
       
           caller.ndb._evmenu.charactersheet = {}
           caller.ndb._evmenu.charactersheet['name'] = raw_string
           caller.msg(f"You set your name to {raw_string}")
           return "background"
       
      -def node_set_name(caller):
      +def node_set_name(caller):
           text = 'Enter your name:'
           options = {'key': '_default',
                      'goto': _set_name}
      @@ -1176,7 +1176,7 @@ automatically when you exit the menu.

      ... -def node_view_sheet(caller): +def node_view_sheet(caller): text = f"Character sheet:\n {self.ndb._evmenu.charactersheet}" options = ({"key": "Accept", @@ -1204,7 +1204,7 @@ when the user exits the menu.

      Example: Repeating the same node

      Sometimes you want to make a chain of menu nodes one after another, but you don’t want the user to be able to continue to the next node until you have verified that what they input in the previous node is ok. A common example is a login menu:

      
      -def _check_username(caller, raw_string, **kwargs):
      +def _check_username(caller, raw_string, **kwargs):
           # we assume lookup_username() exists
           if not lookup_username(raw_string):
       	# re-run current node by returning `None`
      @@ -1215,14 +1215,14 @@ when the user exits the menu.

      return "node_password" -def node_username(caller): +def node_username(caller): text = "Please enter your user name." options = {"key": "_default", "goto": _check_username} return text, options -def _check_password(caller, raw_string, **kwargs): +def _check_password(caller, raw_string, **kwargs): nattempts = kwargs.get("nattempts", 0) if nattempts > 3: @@ -1235,7 +1235,7 @@ when the user exits the menu.

      # password accepted return "node_login" -def node_password(caller, raw_string, **kwargs): +def node_password(caller, raw_string, **kwargs): text = "Enter your password." options = {"key": "_default", "goto": _check_password} @@ -1277,7 +1277,7 @@ to iteration until too many attempts have been made.

      Defining nodes in a dictionary

      You can also define your nodes directly in a dictionary to feed into the EvMenu creator.

      -
      def mynode(caller):
      +
      def mynode(caller):
          # a normal menu node function
          return text, options
       
      diff --git a/docs/latest/Components/EvMore.html b/docs/latest/Components/EvMore.html
      index 7c3790500a..1bcfad8a31 100644
      --- a/docs/latest/Components/EvMore.html
      +++ b/docs/latest/Components/EvMore.html
      @@ -117,7 +117,7 @@ window. The evennia
       page of text at a time. It is usually used via its access function, evmore.msg.

      The name comes from the famous unix pager utility more which performs just this function.

      To use the pager, just pass the long text through it:

      -
      from evennia.utils import evmore
      +
      from evennia.utils import evmore
       
       evmore.msg(receiver, long_text)
       
      diff --git a/docs/latest/Components/FuncParser.html b/docs/latest/Components/FuncParser.html index 3d4fb5e5de..9669686631 100644 --- a/docs/latest/Components/FuncParser.html +++ b/docs/latest/Components/FuncParser.html @@ -146,9 +146,9 @@ You say "I g

      To escape the inlinefunc (e.g. to explain to someone how it works, use $$)

      While randint may look and work just like random.randint from the standard Python library, it is not. Instead it’s an inlinefunc named randint made available to Evennia (which in turn uses the standard library function). For security reasons, only functions explicitly assigned to be used as inlinefuncs are viable.

      You can apply the FuncParser manually. The parser is initialized with the inlinefunc(s) it’s supposed to recognize in that string. Below is an example of a parser only understanding a single $pow inlinefunc:

      -
      from evennia.utils.funcparser import FuncParser
      +
      from evennia.utils.funcparser import FuncParser
       
      -def _power_callable(*args, **kwargs):
      +def _power_callable(*args, **kwargs):
           """This will be callable as $pow(number, power=<num>) in string"""
           pow = int(kwargs.get('power', 2))
           return float(args[0]) ** pow
      @@ -185,7 +185,7 @@ should see “You” or the character’s name.

      You can apply inline function parsing to any string. The FuncParser is imported as evennia.utils.funcparser.

      -
      from evennia.utils import funcparser
      +
      from evennia.utils import funcparser
       
       parser = FuncParser(callables, **default_kwargs)
       parsed_string = parser.parse(input_string, raise_errors=False,
      @@ -234,7 +234,7 @@ back-reference to the raise_errors boolean given to FuncParser.parse.

      Here’s an example of using the default/reserved keywords:

      -
      def _test(*args, **kwargs):
      +
      def _test(*args, **kwargs):
           # do stuff
           return something
       
      @@ -253,7 +253,7 @@ The funcparser
       

      Defining custom callables

      All callables made available to the parser must have the following signature:

      -
      def funcname(*args, **kwargs):
      +
      def funcname(*args, **kwargs):
           # ...
           return something
       
      @@ -343,9 +343,9 @@ non-developer players/builders and some things (such as complex classes/callables etc) are just not safe/possible to convert from string representation.

      In evennia.utils.utils is a helper called safe_convert_to_types. This function automates the conversion of simple data types in a safe way:

      -
      from evennia.utils.utils import safe_convert_to_types
      +
      from evennia.utils.utils import safe_convert_to_types
       
      -def _process_callable(*args, **kwargs):
      +def _process_callable(*args, **kwargs):
           """
           $process(expression, local, extra1=34, extra2=foo)
       
      @@ -458,15 +458,15 @@ gendering. For example 
       

      Example

      Here’s an example of including the default callables together with two custom ones.

      -
      from evennia.utils import funcparser
      -from evennia.utils import gametime
      +
      from evennia.utils import funcparser
      +from evennia.utils import gametime
       
      -def _dashline(*args, **kwargs):
      +def _dashline(*args, **kwargs):
           if args:
               return f"\n-------- {args[0]} --------"
           return ''
       
      -def _uptime(*args, **kwargs):
      +def _uptime(*args, **kwargs):
           return gametime.uptime()
       
       callables = {
      diff --git a/docs/latest/Components/Help-System.html b/docs/latest/Components/Help-System.html
      index 3f120f8780..d3ff8b22fe 100644
      --- a/docs/latest/Components/Help-System.html
      +++ b/docs/latest/Components/Help-System.html
      @@ -192,7 +192,7 @@ Suggestions:
       

      This will create a new help entry in the database. Use the /edit switch to open the EvEditor for more convenient in-game writing (but note that devs can also create help entries outside the game using their regular code editor, see below).

      The HelpEntry stores database help. It is not a Typeclassed entity and can’t be extended using the typeclass mechanism.

      Here’s how to create a database-help entry in code:

      -
      from evennia import create_help_entry
      +
      from evennia import create_help_entry
       entry = create_help_entry("emote",
                       "Emoting is important because ...",
                       category="Roleplaying", locks="view:all()")
      @@ -262,9 +262,9 @@ server and the file-based help entries will be available to view.

      Command-help entries

      The __docstring__ of Command classes are automatically extracted into a help entry. You set help_category directly on the class.

      -
      from evennia import Command
      +
      from evennia import Command
       
      -class MyCommand(Command): 
      +class MyCommand(Command): 
           """ 
           This command is great! 
       
      @@ -304,7 +304,7 @@ also not show up in the index). If 
       
       

      For Commands you set the help-related locks the same way you would any lock:

      -
      class MyCommand(Command):
      +
      class MyCommand(Command):
           """
           <docstring for command>
           """
      diff --git a/docs/latest/Components/Inputfuncs.html b/docs/latest/Components/Inputfuncs.html
      index 4c611579b4..dd9d09f07c 100644
      --- a/docs/latest/Components/Inputfuncs.html
      +++ b/docs/latest/Components/Inputfuncs.html
      @@ -151,13 +151,13 @@
       

      Evennia will try to find and call an Inputfunc on the form

      -
      def commandname(session, *args, **kwargs):
      +
      def commandname(session, *args, **kwargs):
           # ...
       
       

      Or, if no match was found, it will call an inputfunc named “default” on this form

      -
      def default(session, cmdname, *args, **kwargs):
      +
      def default(session, cmdname, *args, **kwargs):
           # cmdname is the name of the mismatched inputcommand
       
       
      diff --git a/docs/latest/Components/Locks.html b/docs/latest/Components/Locks.html index 82777d774d..3f80f5d1ee 100644 --- a/docs/latest/Components/Locks.html +++ b/docs/latest/Components/Locks.html @@ -262,7 +262,7 @@ definitions to the object’s Lock functions

      A lock function is a normal Python function put in a place Evennia looks for such functions. The modules Evennia looks at is the list settings.LOCK_FUNC_MODULES. All functions in any of those modules will automatically be considered a valid lock function. The default ones are found in evennia/locks/lockfuncs.py and you can start adding your own in mygame/server/conf/lockfuncs.py. You can append the setting to add more module paths. To replace a default lock function, just add your own with the same name.

      This is the basic definition of a lock function:

      -
      def lockfunc_name(accessing_obj, accessed_obj, *args, **kwargs):
      +
      def lockfunc_name(accessing_obj, accessed_obj, *args, **kwargs):
           return True # or False
       
      @@ -273,7 +273,7 @@ definitions to the object’s
      # A simple example lock function. Called with e.g. `id(34)`. This is
       # defined in, say mygame/server/conf/lockfuncs.py
       
      -def id(accessing_obj, accessed_obj, *args, **kwargs):
      +def id(accessing_obj, accessed_obj, *args, **kwargs):
           if args:
               wanted_id = args[0]
               return accessing_obj.id == wanted_id
      @@ -387,7 +387,7 @@ called boxTry to get the object and you should get the message that we are not strong enough. Increase your strength above 50 however and you’ll pick it up no problem. Done! A very heavy box!

      If you wanted to set this up in python code, it would look something like this:

      
      - from evennia import create_object
      + from evennia import create_object
       
           # create, then set the lock
           box = create_object(None, key="box")
      diff --git a/docs/latest/Components/MonitorHandler.html b/docs/latest/Components/MonitorHandler.html
      index f0a9cc5a58..cf4e577584 100644
      --- a/docs/latest/Components/MonitorHandler.html
      +++ b/docs/latest/Components/MonitorHandler.html
      @@ -130,7 +130,7 @@ whenever it changes. This way the client could for example update its health bar
       

      The MontorHandler is accessed from the singleton evennia.MONITOR_HANDLER. The code for the handler is in evennia.scripts.monitorhandler.

      Here’s how to add a new monitor:

      -
      from evennia import MONITOR_HANDLER
      +
      from evennia import MONITOR_HANDLER
       
       MONITOR_HANDLER.add(obj, fieldname, callback,
                           idstring="", persistent=False, **kwargs)
      @@ -154,9 +154,9 @@ saving it.

    5. persistent (bool) - if True, the monitor will survive a server reboot.

    6. Example:

      -
      from evennia import MONITOR_HANDLER as monitorhandler
      +
      from evennia import MONITOR_HANDLER as monitorhandler
       
      -def _monitor_callback(fieldname="", obj=None, **kwargs):    
      +def _monitor_callback(fieldname="", obj=None, **kwargs):    
           # reporting callback that works both
           # for db-fields and Attributes
           if fieldname.startswith("db_"):
      diff --git a/docs/latest/Components/Msg.html b/docs/latest/Components/Msg.html
      index 9d8597754e..3f9454e778 100644
      --- a/docs/latest/Components/Msg.html
      +++ b/docs/latest/Components/Msg.html
      @@ -146,13 +146,13 @@ actual in-game letter-object based on the Msg)

      Working with Msg

      The Msg is intended to be used exclusively in code, to build other game systems. It is not a Typeclassed entity, which means it cannot (easily) be overridden. It doesn’t support Attributes (but it does support Tags). It tries to be lean and small since a new one is created for every message. You create a new message with evennia.create_message:

      -
          from evennia import create_message
      +
          from evennia import create_message
           message = create_message(senders, message, receivers,
                                    locks=..., tags=..., header=...)
       

      You can search for Msg objects in various ways:

      -
        from evennia import search_message, Msg
      +
        from evennia import search_message, Msg
       
         # args are optional. Only a single sender/receiver should be passed
         messages = search_message(sender=..., receiver=..., freetext=..., dbref=...)
      diff --git a/docs/latest/Components/Objects.html b/docs/latest/Components/Objects.html
      index a99eb23706..8b841f7094 100644
      --- a/docs/latest/Components/Objects.html
      +++ b/docs/latest/Components/Objects.html
      @@ -170,10 +170,10 @@
       
      # in mygame/typeclasses/objects.py
       # ... 
       
      -from evennia.objects.objects import DefaultObject 
      +from evennia.objects.objects import DefaultObject 
       
      -class ObjectParent:
      -    def at_pre_get(self, getter, **kwargs):
      +class ObjectParent:
      +    def at_pre_get(self, getter, **kwargs):
              # make all entities by default un-pickable
             return False
       
      @@ -186,13 +186,13 @@

      You can put your new typeclass directly in the relevant module, or you could organize your code in some other way. Here we assume we make a new module mygame/typeclasses/flowers.py:

          # mygame/typeclasses/flowers.py
       
      -    from typeclasses.objects import Object
      +    from typeclasses.objects import Object
       
      -    class Rose(Object):
      +    class Rose(Object):
               """
               This creates a simple rose object        
               """    
      -        def at_object_creation(self):
      +        def at_object_creation(self):
                   "this is called only once, when object is first created"
                   # add a persistent attribute 'desc' 
                   # to object (silly example).
      @@ -204,7 +204,7 @@
       

      What the create command actually does is to use the evennia.create_object function. You can do the same thing yourself in code:

      -
          from evennia import create_object
      +
          from evennia import create_object
           new_rose = create_object("typeclasses.flowers.Rose", key="MyRose")
       
      diff --git a/docs/latest/Components/OnDemandHandler.html b/docs/latest/Components/OnDemandHandler.html index ec34c0b930..b7dd058805 100644 --- a/docs/latest/Components/OnDemandHandler.html +++ b/docs/latest/Components/OnDemandHandler.html @@ -145,13 +145,13 @@

      This handler is found as evennia.ON_DEMAND_HANDLER. It is meant to be integrated into your other code. Here’s an example of a flower that goes through its stages of life in 12 hours.

      # e.g. in mygame/typeclasses/objects.py
       
      -from evennia import ON_DEMAND_HANDLER 
      +from evennia import ON_DEMAND_HANDLER 
       
       # ... 
       
      -class Flower(Object): 
      +class Flower(Object): 
       
      -    def at_object_creation(self):
      +    def at_object_creation(self):
       
               minute = 60
               hour = minute * 60
      @@ -167,7 +167,7 @@
                       12 * hour: "dead"
                   })
       
      -    def at_desc(self, looker):
      +    def at_desc(self, looker):
               """
               Called whenever someone looks at this object
               """ 
      @@ -196,7 +196,7 @@
       

      More usage examples

      The OnDemandHandler API describes how to use the handler in detail. While it’s available as evennia.ON_DEMAND_HANDLER, its code is located in evennia.scripts.ondemandhandler.py.

      -
      from evennia import ON_DEMAND_HANDLER 
      +
      from evennia import ON_DEMAND_HANDLER 
       
       ON_DEMAND_HANDLER.add("key", category=None, stages=None)
       time_passed = ON_DEMAND_HANDLER.get_dt("key", category=None)
      @@ -219,7 +219,7 @@
       
    7. .get_stage() - get the current state name, such as “flowering” or “seedling”. If you didn’t specify any stages, this will return None, and you need to interpret the dt yourself to determine which state you are in.

    8. Under the hood, the handler uses OnDemandTask objects. It can sometimes be practical to create tasks directly with these, and pass them to the handler in bulk:

      -
      from evennia import ON_DEMAND_HANDLER, OnDemandTask 
      +
      from evennia import ON_DEMAND_HANDLER, OnDemandTask 
       
       task1 = OnDemandTask("key1", {0: "state1", 100: ("state2", my_callable)})
       task2 = OnDemandTask("key2", category="state-category")
      @@ -244,22 +244,22 @@
       
    9. It may optionally take **kwargs . This will be passed down from your call of get_dt or get_stages.

    10. Here’s an example:

      -
      from evennia DefaultObject, ON_DEMAND_HANDLER
      +
      from evennia DefaultObject, ON_DEMAND_HANDLER
       
      -def mycallable(task, **kwargs)
      +def mycallable(task, **kwargs)
       	# this function is outside the class and is pickleable just fine
           obj = kwargs.get("obj")
           # do something with the object
       
      -class SomeObject(DefaultObject):
      +class SomeObject(DefaultObject):
       
      -    def at_object_creation(self):
      +    def at_object_creation(self):
               ON_DEMAND_HANDLER.add(
       	        "key1", 
       	        stages={0: "new", 10: ("old", mycallable)}
       	    )
       
      -	def do_something(self):
      +	def do_something(self):
       	    # pass obj=self into the handler; to be passed into
       	    # mycallable if we are in the 'old' stage.
       		state = ON_DEMAND_HANDLER.get_state("key1", obj=self)
      @@ -275,7 +275,7 @@
       

      Looping repeatedly

      Normally, when a sequence of stages have been cycled through, the task will just stop at the last stage indefinitely.

      evennia.OnDemandTask.stagefunc_loop is an included static-method stage callable you can use to make the task loop. Here’s an example of how to use it:

      -
      from evennia import ON_DEMAND_HANDLER, OnDemandTask 
      +
      from evennia import ON_DEMAND_HANDLER, OnDemandTask 
       
       ON_DEMAND_HANDLER.add(
           "trap_state", 
      @@ -297,7 +297,7 @@
       

      Bouncing back and forth

      evennia.OnDemandTask.stagefunc_bounce is an included static-method callable you can use to ‘bounce’ the sequence of stages. That is, it will cycle to the end of the cycle and then reverse direction and cycle through the sequence in reverse, keeping the same time intervals between each stage.

      To make this repeat indefinitely, you need to put these callables at both ends of the list:

      -
      from evennia import ON_DEMAND_HANDLER, OnDemandTask 
      +
      from evennia import ON_DEMAND_HANDLER, OnDemandTask 
       
       ON_DEMAND_HANDLER.add(
           "cycling reactor",
      diff --git a/docs/latest/Components/Permissions.html b/docs/latest/Components/Permissions.html
      index 791588bbab..e4ce8a40ed 100644
      --- a/docs/latest/Components/Permissions.html
      +++ b/docs/latest/Components/Permissions.html
      @@ -234,11 +234,11 @@ lock our red chests:

      then take the key and do an access check:

      # in some typeclass file where chest is defined
       
      -class TreasureChest(Object):
      +class TreasureChest(Object):
       
         # ...
       
      -  def open_chest(self, who, tried_key):
      +  def open_chest(self, who, tried_key):
       
             if not chest.access(who, tried_key, "unlock"):
                 who.msg("The key does not fit!")
      diff --git a/docs/latest/Components/Prototypes.html b/docs/latest/Components/Prototypes.html
      index bbae40212e..28067bbc3e 100644
      --- a/docs/latest/Components/Prototypes.html
      +++ b/docs/latest/Components/Prototypes.html
      @@ -245,7 +245,7 @@ easier later.

      At spawn time, the place of the protfunc will be replaced with the result of that protfunc being called (this is always a string). A protfunc is a FuncParser function run every time the prototype is used to spawn a new object. See the FuncParse for a lot more information.

      Here is how a protfunc is defined (same as other funcparser functions).

      # this is a silly example, you can just color the text red with |r directly!
      -def red(*args, **kwargs):
      +def red(*args, **kwargs):
          """
          Usage: $red(<text>)
          Returns the same text you entered, but red.
      diff --git a/docs/latest/Components/Scripts.html b/docs/latest/Components/Scripts.html
      index c5a14b4360..5eb825b5a9 100644
      --- a/docs/latest/Components/Scripts.html
      +++ b/docs/latest/Components/Scripts.html
      @@ -198,7 +198,7 @@ sections).

      # search through Evennia's GLOBAL_SCRIPTS container (based on # script's key only) -from evennia import GLOBAL_SCRIPTS +from evennia import GLOBAL_SCRIPTS myscript = GLOBAL_SCRIPTS.myscript GLOBAL_SCRIPTS.get("Timed script").db.foo = "bar" @@ -220,13 +220,13 @@ sections).

      can use this as a base for your own scripts.

      # in mygame/typeclasses/scripts.py
       
      -from evennia import DefaultScript
      +from evennia import DefaultScript
       
      -class Script(DefaultScript):
      +class Script(DefaultScript):
           # stuff common for all your scripts goes here
       
      -class MyScript(Script):
      -    def at_script_creation(self):
      +class MyScript(Script):
      +    def at_script_creation(self):
               """Called once, when script is first created"""
               self.key = "myscript"
               self.db.foo = "bar"
      @@ -260,14 +260,14 @@ you set in your at_
       

      There are several properties one can set on the Script to control its timer component.

      # in mygame/typeclasses/scripts.py
       
      -class TimerScript(Script):
      +class TimerScript(Script):
       
      -    def at_script_creation(self):
      +    def at_script_creation(self):
               self.key = "myscript"
               self.desc = "An example script"
               self.interval = 60  # 1 min repeat
       
      -    def at_repeat(self):
      +    def at_repeat(self):
               # do stuff every minute
       
       
      @@ -327,20 +327,20 @@ If so, the ‘parent object’ will be available to the script as either
          # mygame/typeclasses/scripts.py
           # Script class is defined at the top of this module
       
      -    import random
      +    import random
       
      -    class Weather(Script):
      +    class Weather(Script):
               """
               A timer script that displays weather info. Meant to
               be attached to a room.
       
               """
      -        def at_script_creation(self):
      +        def at_script_creation(self):
                   self.key = "weather_script"
                   self.desc = "Gives random weather messages."
                   self.interval = 60 * 5  # every 5 minutes
       
      -        def at_repeat(self):
      +        def at_repeat(self):
                   "called every self.interval seconds."
                   rand = random.random()
                   if rand < 0.5:
      @@ -394,13 +394,13 @@ it will also stop the timer (if it runs), leading to the 

      Errors inside a timed, executing script can sometimes be rather terse or point to parts of the execution mechanism that is hard to interpret. One way to make it easier to debug scripts is to import Evennia’s native logger and wrap your functions in a try/catch block. Evennia’s logger can show you where the traceback occurred in your script.

      
      -from evennia.utils import logger
      +from evennia.utils import logger
       
      -class Weather(Script):
      +class Weather(Script):
       
           # [...]
       
      -    def at_repeat(self):
      +    def at_repeat(self):
       
               try:
                   # [...]
      @@ -416,7 +416,7 @@ it will also stop the timer (if it runs), leading to the evennia.GLOBAL_SCRIPTS to help organize your global
       scripts. All you need is the Script’s key.

      -
      from evennia import GLOBAL_SCRIPTS
      +
      from evennia import GLOBAL_SCRIPTS
       
       # access as a property on the container, named the same as the key
       my_script = GLOBAL_SCRIPTS.my_script
      @@ -462,7 +462,7 @@ script keys are globally unique.

      Before setting up Evennia to manage your script like this, make sure that your Script typeclass does not have any critical errors (test it separately). If there are, you’ll see errors in your log and your Script will temporarily fall back to being a DefaultScript type.

      Moreover, a script defined this way is guaranteed to exist when you try to access it:

      -
      from evennia import GLOBAL_SCRIPTS
      +
      from evennia import GLOBAL_SCRIPTS
       # Delete the script
       GLOBAL_SCRIPTS.storagescript.delete()
       # running the `scripts` command now will show no storagescript
      diff --git a/docs/latest/Components/Sessions.html b/docs/latest/Components/Sessions.html
      index 990162644a..4f16b97232 100644
      --- a/docs/latest/Components/Sessions.html
      +++ b/docs/latest/Components/Sessions.html
      @@ -229,7 +229,7 @@ are global entities contain all methods for relaying data across the AMP bridge.
       Sessions hold a reference to their respective Sessionhandler (the property is called
       sessionhandler) so they can relay data. See protocols for more info on building new protocols.

      To get all Sessions in the game (i.e. all currently connected clients), you access the server-side Session handler, which you get by

      -
      from evennia.server.sessionhandler import SESSION_HANDLER
      +
      from evennia.server.sessionhandler import SESSION_HANDLER
       
      diff --git a/docs/latest/Components/Signals.html b/docs/latest/Components/Signals.html index 654689e227..c08154b34a 100644 --- a/docs/latest/Components/Signals.html +++ b/docs/latest/Components/Signals.html @@ -139,13 +139,13 @@ signal.

      Working with Signals

      First you create your handler

      
      -def myhandler(sender, **kwargs):
      +def myhandler(sender, **kwargs):
         # do stuff
       
       

      The **kwargs is mandatory. Then you attach it to the signal of your choice:

      -
      from evennia.server import signals
      +
      from evennia.server import signals
       
       signals.SIGNAL_OBJECT_POST_CREATE.connect(myhandler)
       
      @@ -155,8 +155,8 @@ signal.

      When that happens, myhandler will fire with the sender being the Account that just connected.

      If you want to respond only to the effects of a specific entity you can do so like this:

      -
      from evennia import search_account
      -from evennia import signals
      +
      from evennia import search_account
      +from evennia import signals
       
       account = search_account("foo")[0]
       signals.SIGNAL_ACCOUNT_POST_CONNECT.connect(myhandler, account)
      diff --git a/docs/latest/Components/Tags.html b/docs/latest/Components/Tags.html
      index 33cc407d9f..3dd335a854 100644
      --- a/docs/latest/Components/Tags.html
      +++ b/docs/latest/Components/Tags.html
      @@ -144,10 +144,10 @@
       
      In code, using TagProperty or TagCategoryProperty
      -
      from evennia import DefaultObject
      -from evennia import TagProperty, TagCategoryProperty
      +
      from evennia import DefaultObject
      +from evennia import TagProperty, TagCategoryProperty
       
      -class Sword(DefaultObject): 
      +class Sword(DefaultObject): 
           # name of property is the tagkey, category as argument
           can_be_wielded = TagProperty(category='combat')
           has_sharp_edge = TagProperty(category='combat')
      @@ -182,7 +182,7 @@ Another example would be a weather script affecting all rooms tagged as 

      As shown above, you can also have tags without a category (category of None).

      -
           import evennia
      +
           import evennia
            
            # all methods return Querysets
       
      @@ -207,7 +207,7 @@ Another example would be a weather script affecting all rooms tagged as 

      Using any of the search_tag variants will all return Django Querysets, including if you only have one match. You can treat querysets as lists and iterate over them, or continue building search queries with them.

      Remember when searching that not setting a category means setting it to None - this does not mean that category is undefined, rather None is considered the default, unnamed category.

      -
      import evennia 
      +
      import evennia 
       
       myobj1.tags.add("foo")  # implies category=None
       myobj2.tags.add("foo", category="bar")
      @@ -247,10 +247,10 @@ Another example would be a weather script affecting all rooms tagged as 
       

      TagProperty

      This is used as a property when you create a new class:

      -
      from evennia import TagProperty 
      -from typeclasses import Object 
      +
      from evennia import TagProperty 
      +from typeclasses import Object 
       
      -class MyClass(Object):
      +class MyClass(Object):
           mytag = TagProperty(tagcategory)
       
      @@ -260,10 +260,10 @@ Another example would be a weather script affecting all rooms tagged as

      TagCategoryProperty

      This is the inverse of TagProperty:

      -
      from evennia import TagCategoryProperty 
      -from typeclasses import Object 
      +
      from evennia import TagCategoryProperty 
      +from typeclasses import Object 
       
      -class MyClass(Object): 
      +class MyClass(Object): 
           tagcategory = TagCategroyProperty(tagkey1, tagkey2)
       
      diff --git a/docs/latest/Components/TickerHandler.html b/docs/latest/Components/TickerHandler.html index 07fe7618b2..987ec43e72 100644 --- a/docs/latest/Components/TickerHandler.html +++ b/docs/latest/Components/TickerHandler.html @@ -134,7 +134,7 @@ triggered at the same interval, unsubscribing when the updating is no longer des

      Usage

      Here is an example of importing TICKER_HANDLER and using it:

          # we assume that obj has a hook "at_tick" defined on itself
      -    from evennia import TICKER_HANDLER as tickerhandler    
      +    from evennia import TICKER_HANDLER as tickerhandler    
       
           tickerhandler.add(20, obj.at_tick)
       
      @@ -145,8 +145,8 @@ triggered at the same interval, unsubscribing when the updating is no longer des

      Everything you supply to TickerHandler.add will need to be pickled at some point to be saved into the database - also if you use persistent=False. Most of the time the handler will correctly store things like database objects, but the same restrictions as for Attributes apply to what the TickerHandler may store.

      You can also import a function and tick that:

      -
          from evennia import TICKER_HANDLER as tickerhandler
      -    from mymodule import myfunc
      +
          from evennia import TICKER_HANDLER as tickerhandler
      +    from mymodule import myfunc
       
           tickerhandler.add(30, myfunc)
       
      diff --git a/docs/latest/Components/Typeclasses.html b/docs/latest/Components/Typeclasses.html index a567ee6d4a..d7b3bc0299 100644 --- a/docs/latest/Components/Typeclasses.html +++ b/docs/latest/Components/Typeclasses.html @@ -178,14 +178,14 @@
      1. A typeclass can save itself to the database. This means that some properties (actually not that many) on the class actually represents database fields and can only hold very specific data types.

      2. Due to its connection to the database, the typeclass’ name must be unique across the entire server namespace. That is, there must never be two same-named classes defined anywhere. So the below code would give an error (since DefaultObject is now globally found both in this module and in the default library):

        -
        from evennia import DefaultObject as BaseObject
        -class DefaultObject(BaseObject):
        +
        from evennia import DefaultObject as BaseObject
        +class DefaultObject(BaseObject):
              pass
         
      3. A typeclass’ __init__ method should normally not be overloaded. This has mostly to do with the fact that the __init__ method is not called in a predictable way. Instead Evennia suggest you use the at_*_creation hooks (like at_object_creation for Objects) for setting things the very first time the typeclass is saved to the database or the at_init hook which is called every time the object is cached to memory. If you know what you are doing and want to use __init__, it must both accept arbitrary keyword arguments and use super to call its parent:

        -
        def __init__(self, **kwargs):
        +
        def __init__(self, **kwargs):
             # my content
             super().__init__(**kwargs)
             # my content
        @@ -200,9 +200,9 @@
         

        Creating a new typeclass

        It’s easy to work with Typeclasses. Either you use an existing typeclass or you create a new Python class inheriting from an existing typeclass. Here is an example of creating a new type of Object:

        -
            from evennia import DefaultObject
        +
            from evennia import DefaultObject
         
        -    class Furniture(DefaultObject):
        +    class Furniture(DefaultObject):
                 # this defines what 'furniture' is, like
                 # storing who sits on it or something.
                 pass
        @@ -218,7 +218,7 @@ convenient) way is to create an instance of the class and then save it manually
         

        To use this you must give the database field names as keywords to the call. Which are available depends on the entity you are creating, but all start with db_* in Evennia. This is a method you may be familiar with if you know Django from before.

        It is recommended that you instead use the create_* functions to create typeclassed entities:

        -
        from evennia import create_object
        +
        from evennia import create_object
         
         chair = create_object(Furniture, key="Chair")
         # or (if your typeclass is in a module furniture.py)
        @@ -287,7 +287,7 @@ database.

      To make sure to search, say, all Scripts regardless of typeclass, you need to query from the database model itself. So for Objects, this would be ObjectDB in the diagram above. Here’s an example for Scripts:

      -
      from evennia import ScriptDB
      +
      from evennia import ScriptDB
       matches = ScriptDB.objects.filter(db_key__contains="Combat")
       
      @@ -310,7 +310,7 @@ database.

      The above examples can be run in the command prompt created by evennia shell. You could also run it all in-game using @py. That however requires you to put the code (including imports) as one single line using ; and list comprehensions, like this (ignore the line break, that’s only for readability in the wiki):

      -
      py from typeclasses.furniture import Furniture;
      +
      py from typeclasses.furniture import Furniture;
       [obj.at_object_creation() for obj in Furniture.objects.all() if not obj.db.worth]
       
      diff --git a/docs/latest/Components/Web-Admin.html b/docs/latest/Components/Web-Admin.html index f9ee9e64ca..fc6dd53f36 100644 --- a/docs/latest/Components/Web-Admin.html +++ b/docs/latest/Components/Web-Admin.html @@ -267,7 +267,7 @@ following to your m # ... -from django.conf.admin import site +from django.conf.admin import site #... diff --git a/docs/latest/Components/Website.html b/docs/latest/Components/Website.html index bbe471bdce..165da90030 100644 --- a/docs/latest/Components/Website.html +++ b/docs/latest/Components/Website.html @@ -333,7 +333,7 @@ we find the view is in in # ... -from web.website.views import index +from web.website.views import index urlpatterns = [ path("", index.EvenniaIndexView.as_view(), name="index") @@ -385,9 +385,9 @@ add /test/in the Django docs):

      # mygame/web/website/views/testview.py
       
      -from django.views.generic import TemplateView
      +from django.views.generic import TemplateView
       
      -class MyTestView(TemplateView):
      +class MyTestView(TemplateView):
           template_name = "website/test.html"
       
       
      @@ -398,7 +398,7 @@ Django/Evennia will think it contains unit tests). Add a view there to process y
       
      # in mygame/web/website/urls.py
       
       # ...
      -from web.website.views import testview
      +from web.website.views import testview
       
       urlpatterns = [
           # ...
      diff --git a/docs/latest/Concepts/Async-Process.html b/docs/latest/Concepts/Async-Process.html
      index c36723e77a..d83c50b454 100644
      --- a/docs/latest/Concepts/Async-Process.html
      +++ b/docs/latest/Concepts/Async-Process.html
      @@ -152,11 +152,11 @@ executed in the same strict order they were coded.

      This is equivalent to something like time.sleep() except delay is asynchronous while sleep would lock the entire server for the duration of the sleep.

      The delay function is a much simpler sibling to run_async. It is in fact just a way to delay the execution of a command until a future time.

      -
           from evennia.utils import delay
      +
           from evennia.utils import delay
       
            # [...]
            # e.g. inside a Command, where `self.caller` is available
      -     def callback(obj):
      +     def callback(obj):
               obj.msg("Returning!")
            delay(10, callback, self.caller)
       
      @@ -171,10 +171,10 @@ executed in the same strict order they were coded.

      @utils.interactive decorator

      The @interactive [decorator](https://realpython.com/primer-on-python- decorators/) makes any function or method possible to ‘pause’ and/or await player input in an interactive way.

      -
          from evennia.utils import interactive
      +
          from evennia.utils import interactive
       
           @interactive
      -    def myfunc(caller):
      +    def myfunc(caller):
               
             while True:
                 caller.msg("Getting ready to wait ...")
      @@ -196,10 +196,10 @@ executed in the same strict order they were coded.

      • You can’t use return <value> from a generator (just an empty return works). To return a value from a function/method you have decorated with @interactive, you must instead use a special Twisted function twisted.internet.defer.returnValue. Evennia also makes this function conveniently available from evennia.utils:

      -
      from evennia.utils import interactive, returnValue
      +
      from evennia.utils import interactive, returnValue
       
       @interactive
      -def myfunc():
      +def myfunc():
       
           # ... 
           result = 10
      @@ -222,7 +222,7 @@ executed in the same strict order they were coded.

      Where function will be called asynchronously with *args and **kwargs. Example:

      -
          from evennia import utils
      +
          from evennia import utils
           print("before call ...")
           utils.run_async(long_running_function)
           print("after call ...")
      @@ -239,7 +239,7 @@ line quite pointless for processing any data from the function. Instead one has
       
    11. at_return(r) (the callback) is called when the asynchronous function (long_running_function above) finishes successfully. The argument r will then be the return value of that function (or None).

      -
          def at_return(r):
      +
          def at_return(r):
               print(r)
       
      @@ -250,7 +250,7 @@ This exception is passed to the errback wrapped in a Failure object
    12. -
              def at_err(e):
      +
              def at_err(e):
                   print("There was an error:", str(e))
       
      @@ -259,22 +259,22 @@ log. An example of an errback is found below:

      errback.

      An example of making an asynchronous call from inside a Command definition:

      -
          from evennia import utils, Command
      +
          from evennia import utils, Command
       
      -    class CmdAsync(Command):
      +    class CmdAsync(Command):
       
              key = "asynccommand"
           
      -       def func(self):     
      +       def func(self):     
                  
      -           def long_running_function():  
      +           def long_running_function():  
                      #[... lots of time-consuming code  ...]
                      return final_value
                  
      -           def at_return_function(r):
      +           def at_return_function(r):
                      self.caller.msg(f"The final value is {r}")
           
      -           def at_err_function(e):
      +           def at_err_function(e):
                      self.caller.msg(f"There was an error: {e}")
       
                  # do the async call, setting all callbacks
      diff --git a/docs/latest/Concepts/Connection-Styles.html b/docs/latest/Concepts/Connection-Styles.html
      index 42e1dcf964..a7a040dfeb 100644
      --- a/docs/latest/Concepts/Connection-Styles.html
      +++ b/docs/latest/Concepts/Connection-Styles.html
      @@ -143,27 +143,27 @@
       
       
      # in mygame/commands/mylogin_commands.py
       
      -from evennia import syscmdkeys, default_cmds, Command
      +from evennia import syscmdkeys, default_cmds, Command
       
       
      -class MyUnloggedinLook(Command):
      +class MyUnloggedinLook(Command):
       
           # this will now be the first command called when connecting
           key = syscmdkeys.CMD_LOGINSTART 
       
      -    def func(self):
      +    def func(self):
               # ... 
       

      Next, add this to the right place in the UnloggedinCmdSet:

      # in mygame/commands/default_cmdsets.py
       
      -from commands.mylogin_commands import MyUnloggedinLook
      +from commands.mylogin_commands import MyUnloggedinLook
       # ... 
       
      -class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
      +class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
           # ... 
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               super().at_cmdset_creation
               self.add(MyUnloggedinLook())
       
      diff --git a/docs/latest/Concepts/Models.html b/docs/latest/Concepts/Models.html index 8c181d2db4..7885150441 100644 --- a/docs/latest/Concepts/Models.html +++ b/docs/latest/Concepts/Models.html @@ -190,9 +190,9 @@ to version control.

      Defining your models

      A Django model is the Python representation of a database table. It can be handled like any other Python class. It defines fields on itself, objects of a special type. These become the “columns” of the database table. Finally, you create new instances of the model to add new rows to the database.

      We won’t describe all aspects of Django models here, for that we refer to the vast Django documentation on the subject. Here is a (very) brief example:

      -
      from django.db import models
      +
      from django.db import models
       
      -class MyDataStore(models.Model):
      +class MyDataStore(models.Model):
           "A simple model for storing some data"
           db_key = models.CharField(max_length=80, db_index=True)
           db_category = models.CharField(max_length=80, null=True, blank=True)
      @@ -227,9 +227,9 @@ point for your models.

    13. "help.HelpEntry" for Help Entries.

    14. Here’s an example:

      -
      from django.db import models
      +
      from django.db import models
       
      -class MySpecial(models.Model):
      +class MySpecial(models.Model):
           db_character = models.ForeignKey("objects.ObjectDB")
           db_items = models.ManyToManyField("objects.ObjectDB")
           db_account = modeles.ForeignKey("accounts.AccountDB")
      @@ -247,7 +247,7 @@ my_character = myspecial.db_character  # still a Character
       

      Creating a new model instance

      To create a new row in your table, you instantiate the model and then call its save() method:

      -
           from evennia.myapp import MyDataStore
      +
           from evennia.myapp import MyDataStore
       
            new_datastore = MyDataStore(db_key="LargeSword",
                                        db_category="weapons",
      @@ -292,9 +292,9 @@ my_character = myspecial.db_character  # still a Character
       

      Using the idmapper is both more intuitive and more efficient per object; it leads to a lot less reading from disk. The drawback is that this system tends to be more memory hungry overall. So if you know that you’ll never need to add new properties to running instances or know that you will create new objects all the time yet rarely access them again (like for a log system), you are probably better off making “plain” Django models rather than using SharedMemoryModel and its idmapper.

      To use the idmapper and the field-wrapper functionality you just have to have your model classes inherit from evennia.utils.idmapper.models.SharedMemoryModel instead of from the default django.db.models.Model:

      -
      from evennia.utils.idmapper.models import SharedMemoryModel
      +
      from evennia.utils.idmapper.models import SharedMemoryModel
       
      -class MyDataStore(SharedMemoryModel):
      +class MyDataStore(SharedMemoryModel):
           # the rest is the same as before, but db_* is important; these will
           # later be settable as .key, .category, .text ...
           db_key = models.CharField(max_length=80, db_index=True)
      @@ -308,7 +308,7 @@ reading from disk. The drawback is that this system tends to be more memory hung
       

      Searching for your models

      To search your new custom database table you need to use its database manager to build a query. Note that even if you use SharedMemoryModel as described in the previous section, you have to use the actual field names in the query, not the wrapper name (so db_key and not just key).

      -
           from world.myapp import MyDataStore
      +
           from world.myapp import MyDataStore
       
            # get all datastore objects exactly matching a given key
            matches = MyDataStore.objects.filter(db_key="Larger Sword")
      diff --git a/docs/latest/Concepts/Protocols.html b/docs/latest/Concepts/Protocols.html
      index 3748227dd1..0aed2cee76 100644
      --- a/docs/latest/Concepts/Protocols.html
      +++ b/docs/latest/Concepts/Protocols.html
      @@ -156,14 +156,14 @@
       
          # mygame/server/conf/portal_services_plugins.py
       
           # here the new Portal Twisted protocol is defined
      -    class MyOwnFactory( ... ):
      +    class MyOwnFactory( ... ):
              # [...]
       
           # some configs
           MYPROC_ENABLED = True # convenient off-flag to avoid having to edit settings all the time
           MY_PORT = 6666
       
      -    def start_plugin_services(portal):
      +    def start_plugin_services(portal):
               "This is called by the Portal during startup"
                if not MYPROC_ENABLED:
                    return
      @@ -195,21 +195,21 @@ Evennia-specific inputs.

      # In module that we'll later add to the system through PORTAL_SERVICE_PLUGIN_MODULES
       
       # pseudo code 
      -from twisted.something import TwistedClient
      +from twisted.something import TwistedClient
       # this class is used both for Portal- and Server Sessions
      -from evennia.server.session import Session 
      +from evennia.server.session import Session 
       
      -from evennia.server.portal.portalsessionhandler import PORTAL_SESSIONS
      +from evennia.server.portal.portalsessionhandler import PORTAL_SESSIONS
       
      -class MyCustomClient(TwistedClient, Session): 
      +class MyCustomClient(TwistedClient, Session): 
       
      -    def __init__(self, *args, **kwargs): 
      +    def __init__(self, *args, **kwargs): 
               super().__init__(*args, **kwargs)
               self.sessionhandler = PORTAL_SESSIONS
       
           # these are methods we must know that TwistedClient uses for 
           # communication. Name and arguments could vary for different Twisted protocols
      -    def onOpen(self, *args, **kwargs):
      +    def onOpen(self, *args, **kwargs):
               # let's say this is called when the client first connects
       
               # we need to init the session and connect to the sessionhandler. The .factory
      @@ -220,17 +220,17 @@ Evennia-specific inputs.

      self.init_session("mycustom_protocol", client_address, self.factory.sessionhandler) self.sessionhandler.connect(self) - def onClose(self, reason, *args, **kwargs): + def onClose(self, reason, *args, **kwargs): # called when the client connection is dropped # link to the Evennia equivalent self.disconnect(reason) - def onMessage(self, indata, *args, **kwargs): + def onMessage(self, indata, *args, **kwargs): # called with incoming data # convert as needed here self.data_in(data=indata) - def sendMessage(self, outdata, *args, **kwargs): + def sendMessage(self, outdata, *args, **kwargs): # called to send data out # modify if needed super().sendMessage(self, outdata, *args, **kwargs) @@ -239,7 +239,7 @@ Evennia-specific inputs.

      # The above twisted-methods call them and vice-versa. This connects the protocol # the Evennia internals. - def disconnect(self, reason=None): + def disconnect(self, reason=None): """ Called when connection closes. This can also be called directly by Evennia when manually closing the connection. @@ -247,12 +247,12 @@ Evennia-specific inputs.

      """ self.sessionhandler.disconnect(self) - def at_login(self): + def at_login(self): """ Called when this session authenticates by the server (if applicable) """ - def data_in(self, **kwargs): + def data_in(self, **kwargs): """ Data going into the server should go through this method. It should pass data into `sessionhandler.data_in`. THis will be called @@ -261,7 +261,7 @@ Evennia-specific inputs.

      """ self.sessionhandler.data_in(self, text=kwargs['data']) - def data_out(self, **kwargs): + def data_out(self, **kwargs): """ Data going out from the server should go through this method. It should hand off to the protocol's send method, whatever it's called. @@ -272,14 +272,14 @@ Evennia-specific inputs.

      # 'outputfuncs' are defined as `send_<outputfunc_name>`. From in-code, they are called # with `msg(outfunc_name=<data>)`. - def send_text(self, txt, *args, **kwargs): + def send_text(self, txt, *args, **kwargs): """ Send text, used with e.g. `session.msg(text="foo")` """ # we make use of the self.data_out(text=txt) - def send_default(self, cmdname, *args, **kwargs): + def send_default(self, cmdname, *args, **kwargs): """ Handles all outputfuncs without an explicit `send_*` method to handle them. """ diff --git a/docs/latest/Concepts/Zones.html b/docs/latest/Concepts/Zones.html index f533428546..d095f05cef 100644 --- a/docs/latest/Concepts/Zones.html +++ b/docs/latest/Concepts/Zones.html @@ -147,7 +147,7 @@ distinction.

      Henceforth you can then easily retrieve only objects with a given tag:

      -
           import evennia
      +
           import evennia
            rooms = evennia.search_tag("magicalforest", category="zone")
       
      diff --git a/docs/latest/Contribs/Contrib-Achievements.html b/docs/latest/Contribs/Contrib-Achievements.html index e1c47951c2..75171ecf78 100644 --- a/docs/latest/Contribs/Contrib-Achievements.html +++ b/docs/latest/Contribs/Contrib-Achievements.html @@ -160,12 +160,12 @@

      To allow players to check their achievements, you’ll also want to add the achievements command to your default Character and/or Account command sets.

      # in commands/default_cmdsets.py
       
      -from evennia.contrib.game_systems.achievements.achievements import CmdAchieve
      +from evennia.contrib.game_systems.achievements.achievements import CmdAchieve
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(CmdAchieve)
       
      @@ -276,12 +276,12 @@

      Adding achievement tracking to it could then look something like this:

      # in typeclasses/objects.py
       
      -from contrib.game_systems.achievements import track_achievements
      +from contrib.game_systems.achievements import track_achievements
       
      -class Object(ObjectParent, DefaultObject):
      +class Object(ObjectParent, DefaultObject):
           # ....
       
      -    def at_defeated(self, victor):
      +    def at_defeated(self, victor):
               """called when this object is defeated in combat"""
               # we'll use the "mob_type" tag-category as the tracked info
               # this way we can have rats named "black rat" and "brown rat" that are both rats
      @@ -293,12 +293,12 @@
       

      If a player defeats something tagged rat with a tag category of mob_type, it’d now count towards the rat-killing achievement.

      You can also have the tracking information hard-coded into your game, for special or unique situations. The achievement described earlier, FIRST_LOGIN_ACHIEVE, for example, would be tracked like this:

      # in typeclasses/accounts.py
      -from contrib.game_systems.achievements import track_achievements
      +from contrib.game_systems.achievements import track_achievements
       
      -class Account(DefaultAccount):
      +class Account(DefaultAccount):
           # ...
       
      -    def at_first_login(self, **kwargs):
      +    def at_first_login(self, **kwargs):
               # this function is only called on the first time the account logs in
               # so we already know and can just tell the tracker that this is the first
               track_achievements(self, category="login", tracking="first")
      @@ -317,9 +317,9 @@
       

      Example:

      -
      from evennia.contrib.game_systems.achievements import get_achievement
      +
      from evennia.contrib.game_systems.achievements import get_achievement
       
      -def toast(achiever, completed_list):
      +def toast(achiever, completed_list):
           if completed_list:
               # `completed_data` will be a list of dictionaries - unrecognized keys return empty dictionaries
               completed_data = [get_achievement(key) for key in args]
      @@ -336,7 +336,7 @@
       

      Example:

      The first example does a search for “fruit”, which returns the fruit medley achievement as it contains “fruit” in the key and name.

      The second example searches for “usual”, which returns the ten rats achievement due to its display name.

      -
      >>> from evennia.contrib.game_systems.achievements import search_achievement
      +
      >>> from evennia.contrib.game_systems.achievements import search_achievement
       >>> search_achievement("fruit")
       {'fruit_basket_achievement': {'name': 'Fruit Basket', 'desc': "One kind of fruit just isn't enough.", 'category': 'buy', 'tracking': ('apple', 'orange', 'pear'), 'count': 5, 'tracking_type': 'separate'}}
       >>> search_achievement("usual")
      diff --git a/docs/latest/Contribs/Contrib-Barter.html b/docs/latest/Contribs/Contrib-Barter.html
      index c99953f112..d79857c113 100644
      --- a/docs/latest/Contribs/Contrib-Barter.html
      +++ b/docs/latest/Contribs/Contrib-Barter.html
      @@ -138,12 +138,12 @@ cmdset. This will make the trade (or barter) command available
       in-game.

      # in mygame/commands/default_cmdsets.py
       
      -from evennia.contrib.game_systems import barter  # <---
      +from evennia.contrib.game_systems import barter  # <---
       
       # ...
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ...
      -    def at cmdset_creation(self):
      +    def at cmdset_creation(self):
               # ...
               self.add(barter.CmdsetTrade)  # <---
       
      diff --git a/docs/latest/Contribs/Contrib-Buffs.html b/docs/latest/Contribs/Contrib-Buffs.html
      index 5061a931e3..6ce2b17e53 100644
      --- a/docs/latest/Contribs/Contrib-Buffs.html
      +++ b/docs/latest/Contribs/Contrib-Buffs.html
      @@ -172,7 +172,7 @@ It is a common design pattern in RPGs, particularly action games.

      Quick Start

      Assign the handler to a property on the object, like so.

      @lazy_property
      -def buffs(self) -> BuffHandler:
      +def buffs(self) -> BuffHandler:
           return BuffHandler(self)
       
      @@ -191,12 +191,12 @@ on puppet/unpuppet. You have been warned!

      Let’s say you want another handler for an object, perks, which has a separate database and respects playtime buffs. You’d assign this new property as so:

      -
      class BuffableObject(Object):
      +
      class BuffableObject(Object):
           @lazy_property
      -    def perks(self) -> BuffHandler:
      +    def perks(self) -> BuffHandler:
               return BuffHandler(self, dbkey='perks', autopause=True)
       
      -    def at_init(self):
      +    def at_init(self):
               self.perks
       
      @@ -284,7 +284,7 @@ This will return the stat string).

      For example, let’s say you want to modify how much damage you take. That might look something like this:

      # The method we call to damage ourselves
      -def take_damage(self, source, damage):
      +def take_damage(self, source, damage):
           _damage = self.buffs.check(damage, 'taken_damage')
           self.db.health -= _damage
       
      @@ -320,7 +320,7 @@ applied through the following formula.

      Buff Strength Priority (Advanced)

      Sometimes you only want to apply the strongest modifier to a stat. This is supported by the optional strongest bool arg in the handler’s check method

      -
      def take_damage(self, source, damage):
      +
      def take_damage(self, source, damage):
           _damage = self.buffs.check(damage, 'taken_damage', strongest=True)
           self.db.health -= _damage
       
      @@ -332,10 +332,10 @@ applied through the following formula.

      Call the handler’s trigger(string) method when you want an event call. This will call the at_trigger hook method on all buffs with the relevant trigger string.

      For example, let’s say you want to trigger a buff to “detonate” when you hit your target with an attack. You’d write a buff that might look like this:

      -
      class Detonate(BaseBuff):
      +
      class Detonate(BaseBuff):
           ...
           triggers = ['take_damage']
      -    def at_trigger(self, trigger, *args, **kwargs)
      +    def at_trigger(self, trigger, *args, **kwargs)
               self.owner.take_damage(100)
               self.remove()
       
      @@ -350,10 +350,10 @@ You’d write a buff that might look like this:

      Ticking buffs are slightly special. They are similar to trigger buffs in that they run code, but instead of doing so on an event trigger, they do so on a periodic tick. A common use case for a buff like this is a poison, or a heal over time.

      -
      class Poison(BaseBuff):
      +
      class Poison(BaseBuff):
           ...
           tickrate = 5
      -    def at_tick(self, initial=True, *args, **kwargs):
      +    def at_tick(self, initial=True, *args, **kwargs):
               _dmg = self.dmg * self.stacks
               if not initial:
                   self.owner.location.msg_contents(
      @@ -378,7 +378,7 @@ dictionary (default: empty) to the buff hook methods as keyword arguments (
       

      For example, let’s say you want a “thorns” buff which damages enemies that attack you. Let’s take our take_damage method and add a context to the mix.

      -
      def take_damage(attacker, damage):
      +
      def take_damage(attacker, damage):
           context = {'attacker': attacker, 'damage': damage}
           _damage = self.buffs.check(damage, 'taken_damage', context=context)
           self.buffs.trigger('taken_damage', context=context)
      @@ -386,11 +386,11 @@ and add a context to the mix.

      Now we use the values that context passes to the buff kwargs to customize our logic.

      -
      class ThornsBuff(BaseBuff):
      +
      class ThornsBuff(BaseBuff):
           ...
           triggers = ['taken_damage']
           # This is the hook method on our thorns buff
      -    def at_trigger(self, trigger, attacker=None, damage=0, **kwargs):
      +    def at_trigger(self, trigger, attacker=None, damage=0, **kwargs):
               if not attacker: 
                   return
               attacker.db.health -= damage * 0.2
      @@ -471,7 +471,7 @@ mods of a specific stat string and apply their modifications to the value; howev
       
    15. perstack: How much value the modifier grants per stack, INCLUDING the first. (default: 0)

    16. The most basic way to add a Mod to a buff is to do so in the buff class definition, like this:

      -
      class DamageBuff(BaseBuff):
      +
      class DamageBuff(BaseBuff):
           mods = [Mod('damage', 'add', 10)]
       
      @@ -484,9 +484,9 @@ never permanently change a stat modified by a buff. To remove the modification,

      Generating Mods (Advanced)

      An advanced way to do mods is to generate them when the buff is initialized. This lets you create mods on the fly that are reactive to the game state.

      -
      class GeneratedStatBuff(BaseBuff):
      +
      class GeneratedStatBuff(BaseBuff):
           ...
      -    def at_init(self, *args, **kwargs) -> None:
      +    def at_init(self, *args, **kwargs) -> None:
               # Finds our "modgen" cache value, and generates a mod from it
               modgen = list(self.cache.get("modgen"))
               if modgen:
      @@ -501,10 +501,10 @@ never permanently change a stat modified by a buff. To remove the modification,
       

      When the handler’s trigger method is called, it searches all buffs on the handler for any with a matchingtrigger, then calls their at_trigger hooks. Buffs can have multiple triggers, and you can tell which trigger was used by the trigger argument in the hook.

      -
      class AmplifyBuff(BaseBuff):
      +
      class AmplifyBuff(BaseBuff):
           triggers = ['damage', 'heal'] 
       
      -    def at_trigger(self, trigger, **kwargs):
      +    def at_trigger(self, trigger, **kwargs):
               if trigger == 'damage': print('Damage trigger called!')
               if trigger == 'heal': print('Heal trigger called!')
       
      @@ -514,12 +514,12 @@ the trigger<

      Ticking

      A buff which ticks isn’t much different than one which triggers. You’re still executing arbitrary hooks on the buff class. To tick, the buff must have a tickrate of 1 or higher.

      -
      class Poison(BaseBuff):
      +
      class Poison(BaseBuff):
           ...
           # this buff will tick 6 times between application and cleanup.
           duration = 30
           tickrate = 5
      -    def at_tick(self, initial, **kwargs):
      +    def at_tick(self, initial, **kwargs):
               self.owner.take_damage(10)
       
      @@ -537,9 +537,9 @@ If you are adding new properties, try to ensure they do not end

      You can restrict whether or not the buff will check, trigger, or tick through defining the conditional hook. As long as it returns a “truthy” value, the buff will apply itself. This is useful for making buffs dependent on game state - for example, if you want a buff that makes the player take more damage when they are on fire:

      -
      class FireSick(BaseBuff):
      +
      class FireSick(BaseBuff):
           ...
      -    def conditional(self, *args, **kwargs):
      +    def conditional(self, *args, **kwargs):
               if self.owner.buffs.has(FireBuff): 
                   return True
               return False
      diff --git a/docs/latest/Contribs/Contrib-Building-Menu.html b/docs/latest/Contribs/Contrib-Building-Menu.html
      index e7935b7669..9927cc2239 100644
      --- a/docs/latest/Contribs/Contrib-Building-Menu.html
      +++ b/docs/latest/Contribs/Contrib-Building-Menu.html
      @@ -154,16 +154,16 @@ that will edit any default object, offering to change its key and description.
       
    17. Import the GenericBuildingCmd class from this contrib in your mygame/commands/default_cmdset.py file:

      -
      from evennia.contrib.base_systems.building_menu import GenericBuildingCmd
      +
      from evennia.contrib.base_systems.building_menu import GenericBuildingCmd
       
    18. Below, add the command in the CharacterCmdSet:

      # ... These lines should exist in the file
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               super().at_cmdset_creation()
               # ... add the line below
               self.add(GenericBuildingCmd())
      @@ -197,9 +197,9 @@ change the room title by simply entering text, and go back to the
       main menu entering @ (all this is customizable).  Press q to quit this menu.

      The first thing to do is to create a new module and place a class inheriting from BuildingMenu in it.

      -
      from evennia.contrib.base_systems.building_menu import BuildingMenu
      +
      from evennia.contrib.base_systems.building_menu import BuildingMenu
       
      -class RoomBuildingMenu(BuildingMenu):
      +class RoomBuildingMenu(BuildingMenu):
           # ...
       
       
      @@ -207,8 +207,8 @@ inheriting from Bui

      Next, override the init method (not __init__!). You can add choices (like the title, description, and exits choices as seen above) by using the add_choice method.

      -
      class RoomBuildingMenu(BuildingMenu):
      -    def init(self, room):
      +
      class RoomBuildingMenu(BuildingMenu):
      +    def init(self, room):
               self.add_choice("title", "t", attr="key")
       
       
      @@ -226,8 +226,8 @@ called when the menu element is triggered.

      add_choice, but add_choice_edit. This is a convenient shortcut which is available to quickly open an EvEditor when entering this choice and going back to the menu when the editor closes.

      -
      class RoomBuildingMenu(BuildingMenu):
      -    def init(self, room):
      +
      class RoomBuildingMenu(BuildingMenu):
      +    def init(self, room):
               self.add_choice("title", "t", attr="key")
               self.add_choice_edit("description", key="d", attr="db.desc")
       
      @@ -236,13 +236,13 @@ and going back to the menu when the editor closes.

      When you wish to create a building menu, you just need to import your class, create it specifying your intended caller and object to edit, then call open:

      -
      from <wherever> import RoomBuildingMenu
      +
      from <wherever> import RoomBuildingMenu
       
      -class CmdEdit(Command):
      +class CmdEdit(Command):
       
           key = "redit"
       
      -    def func(self):
      +    def func(self):
               menu = RoomBuildingMenu(self.caller, self.caller.location)
               menu.open()
       
      @@ -269,10 +269,10 @@ demonstration, but we will override it in this example, using the same code with
       

      Let’s begin by adding a new command. You could add or edit the following file (there’s no trick here, feel free to organize the code differently):

      # file: commands/building.py
      -from evennia.contrib.building_menu import BuildingMenu
      -from commands.command import Command
      +from evennia.contrib.building_menu import BuildingMenu
      +from commands.command import Command
       
      -class EditCmd(Command):
      +class EditCmd(Command):
       
           """
           Editing command.
      @@ -294,7 +294,7 @@ here, feel free to organize the code differently):

      locks = "cmd:id(1) or perm(Builders)" help_category = "Building" - def func(self): + def func(self): if not self.args.strip(): self.msg("|rYou should provide an argument to this function: the object to edit.|n") return @@ -343,14 +343,14 @@ at the top of the file:

      ... """ -from evennia import default_cmds +from evennia import default_cmds # The following line is to be added -from commands.building import EditCmd +from commands.building import EditCmd

      And in the class below (CharacterCmdSet), add the last line of this code:

      -
      class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +
      class CharacterCmdSet(default_cmds.CharacterCmdSet):
           """
           The `CharacterCmdSet` contains general in-game commands like `look`,
           `get`, etc available on in-game Character objects. It is merged with
      @@ -358,7 +358,7 @@ at the top of the file:

      """ key = "DefaultCharacter" - def at_cmdset_creation(self): + def at_cmdset_creation(self): """ Populates the cmdset """ @@ -377,7 +377,7 @@ the RoomBuildingMen
      # ... at the end of commands/building.py
       # Our building menu
       
      -class RoomBuildingMenu(BuildingMenu):
      +class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
      @@ -386,7 +386,7 @@ the RoomBuildingMen
       
           """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("key", "k", attr="key")
       
      @@ -469,7 +469,7 @@ class, with a method and a single line of code within, we’ve added a menu with

      Code explanation

      Let’s examine our code again:

      -
      class RoomBuildingMenu(BuildingMenu):
      +
      class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
      @@ -478,7 +478,7 @@ class, with a method and a single line of code within, we’ve added a menu with
       
           """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("key", "k", attr="key")
       
      @@ -536,13 +536,13 @@ can specify the title and key of this menu. You can also call a function when t

      So here’s a more complete example (you can replace your RoomBuildingMenu class in commands/building.py to see it):

      -
      class RoomBuildingMenu(BuildingMenu):
      +
      class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
           """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("key", "k", attr="key")
               self.add_choice_edit("description", "d")
               self.add_choice_quit("quit this editor", "q")
      @@ -639,7 +639,7 @@ choice.  This can be useful for cleanup as well.

    19. These are a lot of possibilities, and most of the time you won’t need them all. Here is a short example using some of these arguments (again, replace the RoomBuildingMenu class in commands/building.py with the following code to see it working):

      -
      class RoomBuildingMenu(BuildingMenu):
      +
      class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
      @@ -648,7 +648,7 @@ example using some of these arguments (again, replace the     """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("title", key="t", attr="key", glance="{obj.key}", text="""
                       -------------------------------------------------------------------------------
                       Editing the title of {{obj.key}}(#{{obj.id}})
      @@ -733,14 +733,14 @@ can we show that?

      great. Perhaps even add new exits?

      First of all, let’s write a function to display the glance on existing exits. Here’s the code, it’s explained below:

      -
      class RoomBuildingMenu(BuildingMenu):
      +
      class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
       
           """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("title", key="t", attr="key", glance="{obj.key}", text="""
                       -------------------------------------------------------------------------------
                       Editing the title of {{obj.key}}(#{{obj.id}})
      @@ -755,7 +755,7 @@ it’s explained below:

      # Menu functions -def glance_exits(room): +def glance_exits(room): """Show the room exits.""" if room.exits: glance = "" @@ -810,7 +810,7 @@ building menu.

    20. Anything else: any other argument will contain the object being edited by the building menu.

    21. So in our case:

      -
      def glance_exits(room):
      +
      def glance_exits(room):
       

      The only argument we need is room. It’s not present in the list of possible arguments, so the @@ -826,14 +826,14 @@ see how to edit them in the next section but this is a good opportunity to show callback. To see it in action, as usual, replace the class and functions in commands/building.py:

      # Our building menu
       
      -class RoomBuildingMenu(BuildingMenu):
      +class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
       
           """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("title", key="t", attr="key", glance="{obj.key}", text="""
                       -------------------------------------------------------------------------------
                       Editing the title of {{obj.key}}(#{{obj.id}})
      @@ -848,7 +848,7 @@ callback.  To see it in action, as usual, replace the class and functions in # Menu functions
      -def glance_exits(room):
      +def glance_exits(room):
           """Show the room exits."""
           if room.exits:
               glance = ""
      @@ -859,7 +859,7 @@ callback.  To see it in action, as usual, replace the class and functions in return "\n  |gNo exit yet|n"
       
      -def text_exits(caller, room):
      +def text_exits(caller, room):
           """Show the room exits in the choice itself."""
           text = "-" * 79
           text += "\n\nRoom exits:"
      @@ -1027,7 +1027,7 @@ explanations next!

      # ... from commands/building.py
       # Our building menu
       
      -class RoomBuildingMenu(BuildingMenu):
      +class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
      @@ -1036,7 +1036,7 @@ explanations next!

      """ - def init(self, room): + def init(self, room): self.add_choice("title", key="t", attr="key", glance="{obj.key}", text=""" ------------------------------------------------------------------------------- Editing the title of {{obj.key}}(#{{obj.id}}) @@ -1054,7 +1054,7 @@ explanations next!

      # Menu functions -def glance_exits(room): +def glance_exits(room): """Show the room exits.""" if room.exits: glance = "" @@ -1065,7 +1065,7 @@ explanations next!

      return "\n |gNo exit yet|n" -def text_exits(caller, room): +def text_exits(caller, room): """Show the room exits in the choice itself.""" text = "-" * 79 text += "\n\nRoom exits:" @@ -1085,7 +1085,7 @@ explanations next!

      return text -def nomatch_exits(menu, caller, room, string): +def nomatch_exits(menu, caller, room, string): """ The user typed something in the list of exits. Maybe an exit name? """ @@ -1100,7 +1100,7 @@ explanations next!

      return False # Exit sub-menu -def text_single_exit(menu, caller): +def text_single_exit(menu, caller): """Show the text to edit single exits.""" exit = menu.keys[1] if exit is None: @@ -1114,7 +1114,7 @@ explanations next!

      New exit key: """ -def nomatch_single_exit(menu, caller, room, string): +def nomatch_single_exit(menu, caller, room, string): """The user entered something in the exit sub-menu. Replace the exit key.""" # exit is the second key element: keys should contain ['e', <Exit object>] exit = menu.keys[1] @@ -1176,13 +1176,13 @@ depending on what you want to achieve. So let’s build two menus:

      # Still in commands/building.py, replace the menu class and functions by...
       # Our building menus
       
      -class RoomBuildingMenu(BuildingMenu):
      +class RoomBuildingMenu(BuildingMenu):
       
           """
           Building menu to edit a room.
           """
       
      -    def init(self, room):
      +    def init(self, room):
               self.add_choice("title", key="t", attr="key", glance="{obj.key}", text="""
                       -------------------------------------------------------------------------------
                       Editing the title of {{obj.key}}(#{{obj.id}})
      @@ -1198,7 +1198,7 @@ depending on what you want to achieve.  So let’s build two menus:

      # Menu functions -def glance_exits(room): +def glance_exits(room): """Show the room exits.""" if room.exits: glance = "" @@ -1209,7 +1209,7 @@ depending on what you want to achieve. So let’s build two menus:

      return "\n |gNo exit yet|n" -def text_exits(caller, room): +def text_exits(caller, room): """Show the room exits in the choice itself.""" text = "-" * 79 text += "\n\nRoom exits:" @@ -1229,7 +1229,7 @@ depending on what you want to achieve. So let’s build two menus:

      return text -def nomatch_exits(menu, caller, room, string): +def nomatch_exits(menu, caller, room, string): """ The user typed something in the list of exits. Maybe an exit name? """ @@ -1243,14 +1243,14 @@ depending on what you want to achieve. So let’s build two menus:

      menu.open_submenu("commands.building.ExitBuildingMenu", exit, parent_keys=["e"]) return False -class ExitBuildingMenu(BuildingMenu): +class ExitBuildingMenu(BuildingMenu): """ Building menu to edit an exit. """ - def init(self, exit): + def init(self, exit): self.add_choice("key", key="k", attr="key", glance="{obj.key}") self.add_choice_edit("description", "d")
      @@ -1403,7 +1403,7 @@ without giving it a key. If so, the menu system will try to “guess” the key change the minimum length of any key for security reasons.

      To set one of them just do so in your menu class(es):

      -
      class RoomBuildingMenu(BuildingMenu):
      +
      class RoomBuildingMenu(BuildingMenu):
           keys_go_back = ["/"]
           min_shortcut = 2
       
      diff --git a/docs/latest/Contribs/Contrib-Character-Creator.html b/docs/latest/Contribs/Contrib-Character-Creator.html index 53e04c97bf..7676ce4814 100644 --- a/docs/latest/Contribs/Contrib-Character-Creator.html +++ b/docs/latest/Contribs/Contrib-Character-Creator.html @@ -143,11 +143,11 @@

      In your game folder commands/default_cmdsets.py, import and add ContribChargenCmdSet to your AccountCmdSet.

      Example:

      -
      from evennia.contrib.rpg.character_creator.character_creator import ContribChargenCmdSet
      +
      from evennia.contrib.rpg.character_creator.character_creator import ContribChargenCmdSet
       
      -class AccountCmdSet(default_cmds.AccountCmdSet):
      +class AccountCmdSet(default_cmds.AccountCmdSet):
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               super().at_cmdset_creation()
               self.add(ContribChargenCmdSet)
       
      @@ -157,9 +157,9 @@ on your Account class.

      (Alternatively, you can copy the at_look method directly into your own class.)

      Example:

      -
      from evennia.contrib.rpg.character_creator.character_creator import ContribChargenAccount
      +
      from evennia.contrib.rpg.character_creator.character_creator import ContribChargenAccount
       
      -class Account(ContribChargenAccount):
      +class Account(ContribChargenAccount):
           # your Account class code
       
      diff --git a/docs/latest/Contribs/Contrib-Clothing.html b/docs/latest/Contribs/Contrib-Clothing.html index 00ad9a6eda..ffdbcd9980 100644 --- a/docs/latest/Contribs/Contrib-Clothing.html +++ b/docs/latest/Contribs/Contrib-Clothing.html @@ -143,18 +143,18 @@ a very pretty dress and a pair of regular ol' shoes.

      To install, import this module and have your default character inherit from ClothedCharacter in your game’s characters.py file:

      
      -from evennia.contrib.game_systems.clothing import ClothedCharacter
      +from evennia.contrib.game_systems.clothing import ClothedCharacter
       
      -class Character(ClothedCharacter):
      +class Character(ClothedCharacter):
       
       

      And then add ClothedCharacterCmdSet in your character set in mygame/commands/default_cmdsets.py:

      
      -from evennia.contrib.game_systems.clothing import ClothedCharacterCmdSet # <--
      +from evennia.contrib.game_systems.clothing import ClothedCharacterCmdSet # <--
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
            # ...
            at_cmdset_creation(self):
       
      diff --git a/docs/latest/Contribs/Contrib-Color-Markups.html b/docs/latest/Contribs/Contrib-Color-Markups.html
      index f94e6d81a7..d90786fd69 100644
      --- a/docs/latest/Contribs/Contrib-Color-Markups.html
      +++ b/docs/latest/Contribs/Contrib-Color-Markups.html
      @@ -151,7 +151,7 @@ markup, otherwise replace it completely.
       

      Example

      To add the {- “curly-bracket” style, add the following to your settings file, then reboot both Server and Portal:

      -
      from evennia.contrib.base_systems import color_markups
      +
      from evennia.contrib.base_systems import color_markups
       COLOR_ANSI_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_EXTRA_MAP
       COLOR_XTERM256_EXTRA_FG = color_markups.CURLY_COLOR_XTERM256_EXTRA_FG
       COLOR_XTERM256_EXTRA_BG = color_markups.CURLY_COLOR_XTERM256_EXTRA_BG
      @@ -162,7 +162,7 @@ then reboot both Server and Portal:

      To add the %c- “mux/mush” style, add the following to your settings file, then reboot both Server and Portal:

      -
      from evennia.contrib.base_systems import color_markups
      +
      from evennia.contrib.base_systems import color_markups
       COLOR_ANSI_EXTRA_MAP = color_markups.MUX_COLOR_ANSI_EXTRA_MAP
       COLOR_XTERM256_EXTRA_FG = color_markups.MUX_COLOR_XTERM256_EXTRA_FG
       COLOR_XTERM256_EXTRA_BG = color_markups.MUX_COLOR_XTERM256_EXTRA_BG
      diff --git a/docs/latest/Contribs/Contrib-Components.html b/docs/latest/Contribs/Contrib-Components.html
      index 82e69ab333..184b476b41 100644
      --- a/docs/latest/Contribs/Contrib-Components.html
      +++ b/docs/latest/Contribs/Contrib-Components.html
      @@ -153,22 +153,22 @@ It supports both persisted attributes and in-memory attributes by using Evennia
       

      How to install

      To enable component support for a typeclass, import and inherit the ComponentHolderMixin, similar to this

      -
      from evennia.contrib.base_systems.components import ComponentHolderMixin
      -class Character(ComponentHolderMixin, DefaultCharacter):
      +
      from evennia.contrib.base_systems.components import ComponentHolderMixin
      +class Character(ComponentHolderMixin, DefaultCharacter):
       # ...
       

      Components need to inherit the Component class and require a unique name. Components may inherit from other components but must specify another name. You can assign the same ‘slot’ to both components to have alternative implementations.

      -
      from evennia.contrib.base_systems.components import Component
      +
      from evennia.contrib.base_systems.components import Component
       
       
      -class Health(Component):
      +class Health(Component):
           name = "health"
       
           
      -class ItemHealth(Health):
      +class ItemHealth(Health):
           name = "item_health"
           slot = "health"
       
      @@ -179,9 +179,9 @@ NDBField will store its values in the host’s NDB and will not persist. The key used will be ‘component_name::field_name’. They use AttributeProperty under the hood.

      Example:

      -
      from evennia.contrib.base_systems.components import Component, DBField
      +
      from evennia.contrib.base_systems.components import Component, DBField
       
      -class Health(Component):
      +class Health(Component):
           health = DBField(default=1)
       
      @@ -194,9 +194,9 @@ TagField accepts a default value and can be used to store a single or multiple t Default values are automatically added when the component is added. Component Tags are cleared from the host if the component is removed.

      Example:

      -
      from evennia.contrib.base_systems.components import Component, TagField
      +
      from evennia.contrib.base_systems.components import Component, TagField
       
      -class Health(Component):
      +class Health(Component):
           resistances = TagField()
           vulnerability = TagField(default="fire", enforce_single=True)
       
      @@ -208,8 +208,8 @@ in the class via the ComponentProperty. These are components that will always be present in a typeclass. You can also pass kwargs to override the default values Example

      -
      from evennia.contrib.base_systems.components import ComponentHolderMixin
      -class Character(ComponentHolderMixin, DefaultCharacter):
      +
      from evennia.contrib.base_systems.components import ComponentHolderMixin
      +class Character(ComponentHolderMixin, DefaultCharacter):
           health = ComponentProperty("health", hp=10, max_hp=50)
       
      @@ -239,16 +239,16 @@ You can then import all your components in that package’s initinit.py’ file add the import to the folder, like

      -
      from typeclasses import components
      +
      from typeclasses import components
       

      This ensures that the components package will be imported when the typeclasses are imported. You will also need to import each components inside the package’s own ‘typeclasses/components/init.py’ file. You only need to import each module/file from there but importing the right class is a good practice.

      -
      from typeclasses.components.health import Health
      +
      from typeclasses.components.health import Health
       
      -
      from typeclasses.components import health
      +
      from typeclasses.components import health
       

      Both of the above examples will work.

      @@ -263,18 +263,18 @@ To avoid this, you must set autocreate=True on the field, like this.

      Full Example

      -
      from evennia.contrib.base_systems import components
      +
      from evennia.contrib.base_systems import components
       
       
       # This is the Component class
      -class Health(components.Component):
      +class Health(components.Component):
           name = "health"
       
           # Stores the current and max values as Attributes on the host, defaulting to 100
           current = components.DBField(default=100)
           max = components.DBField(default=100)
       
      -    def damage(self, value):
      +    def damage(self, value):
               if self.current <= 0:
                   return
       
      @@ -285,7 +285,7 @@ To avoid this, you must set autocreate=True on the field, like this.

      self.current = 0 self.on_death() - def heal(self, value): + def heal(self, value): hp = self.current hp += value if hp >= self.max_hp: @@ -294,25 +294,25 @@ To avoid this, you must set autocreate=True on the field, like this.

      self.current = hp @property - def is_dead(self): + def is_dead(self): return self.current <= 0 - def on_death(self): + def on_death(self): # Behavior is defined on the typeclass self.host.on_death() # This is how the Character inherits the mixin and registers the component 'health' -class Character(ComponentHolderMixin, DefaultCharacter): +class Character(ComponentHolderMixin, DefaultCharacter): health = ComponentProperty("health") # This is an example of a command that checks for the component -class Attack(Command): +class Attack(Command): key = "attack" aliases = ('melee', 'hit') - def at_pre_cmd(self): + def at_pre_cmd(self): caller = self.caller targets = self.caller.search(args, quiet=True) valid_target = None diff --git a/docs/latest/Contribs/Contrib-Containers.html b/docs/latest/Contribs/Contrib-Containers.html index b78867aa88..6bc7a27d20 100644 --- a/docs/latest/Contribs/Contrib-Containers.html +++ b/docs/latest/Contribs/Contrib-Containers.html @@ -131,12 +131,12 @@

      Installation

      To install, import and add the ContainerCmdSet to CharacterCmdSet in your default_cmdsets.py file:

      -
      from evennia.contrib.game_systems.containers import ContainerCmdSet
      +
      from evennia.contrib.game_systems.containers import ContainerCmdSet
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ...
           
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(ContainerCmdSet)
       
      diff --git a/docs/latest/Contribs/Contrib-Cooldowns.html b/docs/latest/Contribs/Contrib-Cooldowns.html index cd02c02385..80aa31a46d 100644 --- a/docs/latest/Contribs/Contrib-Cooldowns.html +++ b/docs/latest/Contribs/Contrib-Cooldowns.html @@ -143,11 +143,11 @@ object, or just put it on your By default the CooldownHandler will use the cooldowns property, but you can customize this if desired by passing a different value for the db_attribute parameter.

      -
      from evennia.contrib.game_systems.cooldowns import CooldownHandler
      -from evennia.utils.utils import lazy_property
      +
      from evennia.contrib.game_systems.cooldowns import CooldownHandler
      +from evennia.utils.utils import lazy_property
       
       @lazy_property
      -def cooldowns(self):
      +def cooldowns(self):
           return CooldownHandler(self, db_attribute="cooldowns")
       
      @@ -158,8 +158,8 @@ parameter.

      cooldown to limit how often you can perform a command. The following code snippet will limit the use of a Power Attack command to once every 10 seconds per character.

      -
      class PowerAttack(Command):
      -    def func(self):
      +
      class PowerAttack(Command):
      +    def func(self):
               if self.caller.cooldowns.ready("power attack"):
                   self.do_power_attack()
                   self.caller.cooldowns.add("power attack", 10)
      diff --git a/docs/latest/Contribs/Contrib-Crafting.html b/docs/latest/Contribs/Contrib-Crafting.html
      index 8699518e90..99d3253af6 100644
      --- a/docs/latest/Contribs/Contrib-Crafting.html
      +++ b/docs/latest/Contribs/Contrib-Crafting.html
      @@ -176,7 +176,7 @@ available to you:

      In code, you can craft using the evennia.contrib.game_systems.crafting.craft function:

      -
      from evennia.contrib.game_systems.crafting import craft
      +
      from evennia.contrib.game_systems.crafting import craft
       
       result = craft(caller, "recipename", *inputs)
       
      @@ -196,10 +196,10 @@ the above example, create 
       

      A quick example (read on for more details):

      
      -from evennia.contrib.game_systems.crafting import CraftingRecipe, CraftingValidationError
      +from evennia.contrib.game_systems.crafting import CraftingRecipe, CraftingValidationError
       
       
      -class RecipeBread(CraftingRecipe):
      +class RecipeBread(CraftingRecipe):
         """
         Bread is good for making sandwitches!
       
      @@ -218,14 +218,14 @@ there:

      ] - def pre_craft(self, **kwargs): + def pre_craft(self, **kwargs): # validates inputs etc. Raise `CraftingValidationError` if fails - def do_craft(self, **kwargs): + def do_craft(self, **kwargs): # performs the craft - report errors directly to user and return None (if # failed) and the created object(s) if successful. - def post_craft(self, result, **kwargs): + def post_craft(self, result, **kwargs): # any post-crafting effects. Always called, even if do_craft failed (the # result would be None then) @@ -251,9 +251,9 @@ as viable recipes.

      example setting:

      # in mygame/world/myrecipes.py
       
      -from evennia.contrib.game_systems.crafting import CraftingRecipe
      +from evennia.contrib.game_systems.crafting import CraftingRecipe
       
      -class WoodenPuppetRecipe(CraftingRecipe):
      +class WoodenPuppetRecipe(CraftingRecipe):
           """A puppet""""
           name = "wooden puppet"  # name to refer to this recipe as
           tool_tags = ["knife"]
      @@ -279,7 +279,7 @@ specific tag-categories.  The tag-category used can be set per-recipe using the
       are crafting_material and crafting_tool. For
       the puppet we need one object with the wood tag and another with the knife
       tag:

      -
      from evennia import create_object
      +
      from evennia import create_object
       
       knife = create_object(key="Hobby knife", tags=[("knife", "crafting_tool")])
       wood = create_object(key="Piece of wood", tags[("wood", "crafting_material")])
      @@ -302,7 +302,7 @@ in-game command:

      In code we would do

      -
      from evennia.contrib.game_systems.crafting import craft
      +
      from evennia.contrib.game_systems.crafting import craft
       puppet = craft(crafter, "wooden puppet", knife, wood)
       
       
      @@ -314,7 +314,7 @@ recipe will sort out which is which based on their tags.

      Deeper customization of recipes

      For customizing recipes further, it helps to understand how to use the recipe-class directly:

      -
      class MyRecipe(CraftingRecipe):
      +
      class MyRecipe(CraftingRecipe):
           # ...
       
       tools, consumables = MyRecipe.seed()
      @@ -354,15 +354,15 @@ into each crafting hook. These are unused by default but could be used to custom
       notion of being able to fail the craft if you are not skilled enough. Just how
       skills work is game-dependent, so to add this you need to make your own recipe
       parent class and have your recipes inherit from this.

      -
      from random import randint
      -from evennia.contrib.game_systems.crafting import CraftingRecipe
      +
      from random import randint
      +from evennia.contrib.game_systems.crafting import CraftingRecipe
       
      -class SkillRecipe(CraftingRecipe):
      +class SkillRecipe(CraftingRecipe):
          """A recipe that considers skill"""
       
           difficulty = 20
       
      -    def craft(self, **kwargs):
      +    def craft(self, **kwargs):
               """The input is ok. Determine if crafting succeeds"""
       
               # this is set at initialization
      diff --git a/docs/latest/Contribs/Contrib-Custom-Gametime.html b/docs/latest/Contribs/Contrib-Custom-Gametime.html
      index 5544985247..5da20d7b44 100644
      --- a/docs/latest/Contribs/Contrib-Custom-Gametime.html
      +++ b/docs/latest/Contribs/Contrib-Custom-Gametime.html
      @@ -135,7 +135,7 @@ example below).

      Usage:

      -
          from evennia.contrib.base_systems import custom_gametime
      +
          from evennia.contrib.base_systems import custom_gametime
       
           gametime = custom_gametime.realtime_to_gametime(days=23)
       
      diff --git a/docs/latest/Contribs/Contrib-Dice.html b/docs/latest/Contribs/Contrib-Dice.html
      index 19c6bb1c85..caf423183c 100644
      --- a/docs/latest/Contribs/Contrib-Dice.html
      +++ b/docs/latest/Contribs/Contrib-Dice.html
      @@ -138,11 +138,11 @@ rolls for use by a human game master.

      # in mygame/commands/default_cmdsets.py
       
       # ...
      -from evennia.contrib.rpg import dice  <---
      +from evennia.contrib.rpg import dice  <---
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(dice.CmdDice())  # <---
       
      @@ -178,7 +178,7 @@ was.

      Rolling dice from code

      You can specify the first argument as a string on standard RPG d-syntax (NdM, where N is the number of dice to roll, and M is the number sides per dice):

      -
      from evennia.contrib.rpg.dice import roll
      +
      from evennia.contrib.rpg.dice import roll
       
       roll("3d10 + 2")
       
      @@ -210,9 +210,9 @@ sets of dice and combine them in more advanced ways, you can do so with multiple roll() calls. Depending on what you need, you may just want to express this as helper functions specific for your game.

      Here’s how to roll a D&D advantage roll (roll d20 twice, pick highest):

      -
          from evennia.contrib.rpg.dice import roll
      +
          from evennia.contrib.rpg.dice import roll
       
      -    def roll_d20_with_advantage():
      +    def roll_d20_with_advantage():
               """Get biggest result of two d20 rolls"""
               return max(roll("d20"), roll("d20"))
       
      @@ -220,9 +220,9 @@ helper functions specific for your game.

      Here’s an example of a Free-League style dice pool, where you roll a pile of d6 and want to know how many 1s and sixes you get:

      -
      from evennia.contrib.rpg.dice import roll
      +
      from evennia.contrib.rpg.dice import roll
       
      -def roll_dice_pool(poolsize):
      +def roll_dice_pool(poolsize):
           """Return (number_of_ones, number_of_sixes)"""
           results = [roll("1d6") for _ in range(poolsize)]
           return results.count(1), results.count(6)
      diff --git a/docs/latest/Contribs/Contrib-Evscaperoom.html b/docs/latest/Contribs/Contrib-Evscaperoom.html
      index 1c3cba0622..5a5469129e 100644
      --- a/docs/latest/Contribs/Contrib-Evscaperoom.html
      +++ b/docs/latest/Contribs/Contrib-Evscaperoom.html
      @@ -157,9 +157,9 @@ dev blog, mygame/commands/default_cmdsets.py:

      
      -from evennia.contrib.full_systems.evscaperoom.commands import CmdEvscapeRoomStart
      +from evennia.contrib.full_systems.evscaperoom.commands import CmdEvscapeRoomStart
       
      -class CharacterCmdSet(...):
      +class CharacterCmdSet(...):
       
         # ...
       
      diff --git a/docs/latest/Contribs/Contrib-Extended-Room.html b/docs/latest/Contribs/Contrib-Extended-Room.html
      index 292089c406..7226f64bdc 100644
      --- a/docs/latest/Contribs/Contrib-Extended-Room.html
      +++ b/docs/latest/Contribs/Contrib-Extended-Room.html
      @@ -143,11 +143,11 @@ commands detail
       

      In more detail, in mygame/commands/default_cmdsets.py:

      ...
      -from evennia.contrib.grid import extended_room   # <---
      +from evennia.contrib.grid import extended_room   # <---
       
      -class CharacterCmdset(default_cmds.CharacterCmdSet):
      +class CharacterCmdset(default_cmds.CharacterCmdSet):
           ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               super().at_cmdset_creation()
               ...
               self.add(extended_room.ExtendedRoomCmdSet)  # <---
      @@ -168,11 +168,11 @@ this contrib overrides the Room typeclass inherit from the ExtendedRoom and then reload:

      # in mygame/typeclasses/rooms.py
       
      -from evennia.contrib.grid.extended_room import ExtendedRoom
      +from evennia.contrib.grid.extended_room import ExtendedRoom
       
       # ...
       
      -class Room(ObjectParent, ExtendedRoom):
      +class Room(ObjectParent, ExtendedRoom):
           # ...
       
       
      diff --git a/docs/latest/Contribs/Contrib-Gendersub.html b/docs/latest/Contribs/Contrib-Gendersub.html index 073d25cee2..1fb20708ae 100644 --- a/docs/latest/Contribs/Contrib-Gendersub.html +++ b/docs/latest/Contribs/Contrib-Gendersub.html @@ -141,13 +141,13 @@ inspiration and starting point to how to do stuff like this.

      # ... -from evennia.contrib.game_systems.gendersub import SetGender # <--- +from evennia.contrib.game_systems.gendersub import SetGender # <--- # ... -class CharacterCmdSet(default_cmds.CharacterCmdSet): +class CharacterCmdSet(default_cmds.CharacterCmdSet): # ... - def at_cmdset_creation(self): + def at_cmdset_creation(self): # ... self.add(SetGender()) # <---
      @@ -157,9 +157,9 @@ inspiration and starting point to how to do stuff like this.

      # ... -from evennia.contrib.game_systems.gendersub import GenderCharacter # <--- +from evennia.contrib.game_systems.gendersub import GenderCharacter # <--- -class Character(GenderCharacter): # <--- +class Character(GenderCharacter): # <--- # ...
      diff --git a/docs/latest/Contribs/Contrib-Git-Integration.html b/docs/latest/Contribs/Contrib-Git-Integration.html index 20bfd496e6..65a6baccf4 100644 --- a/docs/latest/Contribs/Contrib-Git-Integration.html +++ b/docs/latest/Contribs/Contrib-Git-Integration.html @@ -149,11 +149,11 @@ extra requirements:

      This utility adds a simple assortment of ‘git’ commands. Import the module into your commands and add it to your command set to make it available.

      Specifically, in mygame/commands/default_cmdsets.py:

      ...
      -from evennia.contrib.utils.git_integration import GitCmdSet   # <---
      +from evennia.contrib.utils.git_integration import GitCmdSet   # <---
       
      -class CharacterCmdset(default_cmds.Character_CmdSet):
      +class CharacterCmdset(default_cmds.Character_CmdSet):
           ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               ...
               self.add(GitCmdSet)  # <---
       
      diff --git a/docs/latest/Contribs/Contrib-Health-Bar.html b/docs/latest/Contribs/Contrib-Health-Bar.html
      index 51d935e0fa..e68f358ec0 100644
      --- a/docs/latest/Contribs/Contrib-Health-Bar.html
      +++ b/docs/latest/Contribs/Contrib-Health-Bar.html
      @@ -137,7 +137,7 @@ screen readers.

      Usage

      No installation, just import and use display_meter from this module:

      -
          from evennia.contrib.rpg.health_bar import display_meter
      +
          from evennia.contrib.rpg.health_bar import display_meter
       
           # health is 23/100
           health_bar = display_meter(23, 100)
      diff --git a/docs/latest/Contribs/Contrib-Ingame-Map-Display.html b/docs/latest/Contribs/Contrib-Ingame-Map-Display.html
      index d364d43d2d..89f8421407 100644
      --- a/docs/latest/Contribs/Contrib-Ingame-Map-Display.html
      +++ b/docs/latest/Contribs/Contrib-Ingame-Map-Display.html
      @@ -139,11 +139,11 @@ calculations anew each time.

      Adding the MapDisplayCmdSet to the default character cmdset will add the map command.

      Specifically, in mygame/commands/default_cmdsets.py:

      ...
      -from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet   # <---
      +from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet   # <---
       
      -class CharacterCmdset(default_cmds.CharacterCmdSet):
      +class CharacterCmdset(default_cmds.CharacterCmdSet):
           ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               ...
               self.add(MapDisplayCmdSet)  # <---
       
      diff --git a/docs/latest/Contribs/Contrib-Ingame-Python-Tutorial-Dialogue.html b/docs/latest/Contribs/Contrib-Ingame-Python-Tutorial-Dialogue.html
      index c9fdd17400..682ef98e27 100644
      --- a/docs/latest/Contribs/Contrib-Ingame-Python-Tutorial-Dialogue.html
      +++ b/docs/latest/Contribs/Contrib-Ingame-Python-Tutorial-Dialogue.html
      @@ -296,7 +296,7 @@ an entire dialogue, you might want to do a bit more than that.

      speaker.msg("{character} looks at you more closely.".format(character=character.get_display_name(speaker))) speaker.msg("{character} continues in a low voice: 'Ain't my place to say, but if you need to find -'em, they're encamped some distance away from the road, I guess near a cave or +'em, they're encamped some distance away from the road, I guess near a cave or something.'.".format(character=character.get_display_name(speaker)))
      diff --git a/docs/latest/Contribs/Contrib-Ingame-Python.html b/docs/latest/Contribs/Contrib-Ingame-Python.html index 0c3035cd71..e59401f1e7 100644 --- a/docs/latest/Contribs/Contrib-Ingame-Python.html +++ b/docs/latest/Contribs/Contrib-Ingame-Python.html @@ -351,10 +351,10 @@ the EVENTS_WITH_VAL

      You also have to add the @call command to your Character CmdSet. This command allows your users to add, edit and delete callbacks in-game. In your commands/default_cmdsets, it might look like this:

      -
      from evennia import default_cmds
      -from evennia.contrib.base_systems.ingame_python.commands import CmdCallback
      +
      from evennia import default_cmds
      +from evennia.contrib.base_systems.ingame_python.commands import CmdCallback
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           """
           The `CharacterCmdSet` contains general in-game commands like `look`,
           `get`, etc available on in-game Character objects. It is merged with
      @@ -362,7 +362,7 @@ this:

      """ key = "DefaultCharacter" - def at_cmdset_creation(self): + def at_cmdset_creation(self): """ Populates the cmdset """ @@ -376,9 +376,9 @@ this:

      Finally, to use the in-game Python system, you need to have your typeclasses inherit from the modified event classes. For instance, in your typeclasses/characters.py module, you should change inheritance like this:

      -
      from evennia.contrib.base_systems.ingame_python.typeclasses import EventCharacter
      +
      from evennia.contrib.base_systems.ingame_python.typeclasses import EventCharacter
       
      -class Character(EventCharacter):
      +class Character(EventCharacter):
       
           # ...
       
      @@ -830,8 +830,8 @@ also need to register this class, to tell the in-game Python system that it cont this typeclass.

      Here, we want to add a “push” event on objects. In your typeclasses/objects.py file, you should write something like:

      -
      from evennia.contrib.base_systems.ingame_python.utils import register_events
      -from evennia.contrib.base_systems.ingame_python.typeclasses import EventObject
      +
      from evennia.contrib.base_systems.ingame_python.utils import register_events
      +from evennia.contrib.base_systems.ingame_python.typeclasses import EventObject
       
       EVENT_PUSH = """
       A character push the object.
      @@ -844,7 +844,7 @@ write something like:

      """ @register_events -class Object(EventObject): +class Object(EventObject): """ Class representing objects. """ @@ -892,9 +892,9 @@ specified in the order chosen when
      from commands.command import Command
      +
      from commands.command import Command
       
      -class CmdPush(Command):
      +class CmdPush(Command):
       
           """
           Push something.
      @@ -908,7 +908,7 @@ of an object.  If this object is valid, it will call its “push” event.

      key = "push" - def func(self): + def func(self): """Called when pushing something.""" if not self.args.strip(): self.msg("Usage: push <something>") @@ -944,7 +944,7 @@ create a simple object:

      In the callback you could write:

      -
      from random import randint
      +
      from random import randint
       number = randint(1, 6)
       character.msg("You push a rock... is... it... going... to... move?")
       if number == 6:
      @@ -993,10 +993,10 @@ event definition.

    22. phrase_event should be used for phrase parameters.

    23. For example, here is the definition of the “say” event:

      -
      from evennia.contrib.base_systems.ingame_python.utils import register_events, phrase_event
      +
      from evennia.contrib.base_systems.ingame_python.utils import register_events, phrase_event
       # ...
       @register_events
      -class SomeTypeclass:
      +class SomeTypeclass:
           _events = {
               "say": (["speaker", "character", "message"], CHARACTER_SAY, phrase_event),
           }
      diff --git a/docs/latest/Contribs/Contrib-Ingame-Reports.html b/docs/latest/Contribs/Contrib-Ingame-Reports.html
      index 7e3c8868a1..70b740689f 100644
      --- a/docs/latest/Contribs/Contrib-Ingame-Reports.html
      +++ b/docs/latest/Contribs/Contrib-Ingame-Reports.html
      @@ -138,12 +138,12 @@
       

      To install the reports contrib, just add the provided cmdset to your default AccountCmdSet:

      # in commands/default_cmdset.py
       
      -from evennia.contrib.base_systems.ingame_reports import ReportsCmdSet
      +from evennia.contrib.base_systems.ingame_reports import ReportsCmdSet
       
      -class AccountCmdSet(default_cmds.AccountCmdSet):
      +class AccountCmdSet(default_cmds.AccountCmdSet):
           # ...
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(ReportsCmdSet)
       
      @@ -225,9 +225,9 @@

      Note: The contrib’s own commands - CmdBug, CmdIdea, and CmdReport - are implemented the same way, so you can review them as examples.

      Example:

      -
      from evennia.contrib.base_systems.ingame_reports.reports import ReportCmdBase
      +
      from evennia.contrib.base_systems.ingame_reports.reports import ReportCmdBase
       
      -class CmdCustomReport(ReportCmdBase):
      +class CmdCustomReport(ReportCmdBase):
           """
           file a custom report
       
      diff --git a/docs/latest/Contribs/Contrib-Llm.html b/docs/latest/Contribs/Contrib-Llm.html
      index fce93aa7d5..95ca53c2b8 100644
      --- a/docs/latest/Contribs/Contrib-Llm.html
      +++ b/docs/latest/Contribs/Contrib-Llm.html
      @@ -173,11 +173,11 @@ villager says (to You): We are in this strange place called 'Limbo'. Not
       
      # in mygame/commands/default_cmdsets.py
       
       # ... 
      -from evennia.contrib.rpg.llm import CmdLLMTalk  # <----
      +from evennia.contrib.rpg.llm import CmdLLMTalk  # <----
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet): 
      +class CharacterCmdSet(default_cmds.CharacterCmdSet): 
           # ...
      -    def at_cmdset_creation(self): 
      +    def at_cmdset_creation(self): 
               # ... 
               self.add(CmdLLMTalk())     # <-----
       
      diff --git a/docs/latest/Contribs/Contrib-Mail.html b/docs/latest/Contribs/Contrib-Mail.html
      index 82e0c7915b..3129417d5c 100644
      --- a/docs/latest/Contribs/Contrib-Mail.html
      +++ b/docs/latest/Contribs/Contrib-Mail.html
      @@ -141,7 +141,7 @@ IC and OOC mailing systems, with different lists of mail for IC and OOC modes.

      CmdMail (IC + OOC mail, sent between players)

      # mygame/commands/default_cmds.py
       
      -from evennia.contrib.game_systems import mail
      +from evennia.contrib.game_systems import mail
       
       # in AccountCmdSet.at_cmdset_creation:
           self.add(mail.CmdMail())
      @@ -151,7 +151,7 @@ IC and OOC mailing systems, with different lists of mail for IC and OOC modes.

      CmdMailCharacter (optional, IC only mail, sent between characters)

      # mygame/commands/default_cmds.py
       
      -from evennia.contrib.game_systems import mail
      +from evennia.contrib.game_systems import mail
       
       # in CharacterCmdSet.at_cmdset_creation:
           self.add(mail.CmdMailCharacter())
      diff --git a/docs/latest/Contribs/Contrib-Mapbuilder-Tutorial.html b/docs/latest/Contribs/Contrib-Mapbuilder-Tutorial.html
      index bae0063a8e..cb2f04d01e 100644
      --- a/docs/latest/Contribs/Contrib-Mapbuilder-Tutorial.html
      +++ b/docs/latest/Contribs/Contrib-Mapbuilder-Tutorial.html
      @@ -217,8 +217,8 @@ you are okay with them running arbitrary Python code on your server.

      mygame/world and create a new file there named batchcode_map.py:

      # mygame/world/batchcode_map.py
       
      -from evennia import create_object
      -from evennia import DefaultObject
      +from evennia import create_object
      +from evennia import DefaultObject
       
       # We use the create_object function to call into existence a 
       # DefaultObject named "Map" wherever you are standing.
      @@ -262,8 +262,8 @@ coastal beach and the crossroads which connects them. Create a new batchcode fil
       mygame/world, named batchcode_world.py.

      # mygame/world/batchcode_world.py
       
      -from evennia import create_object, search_object
      -from typeclasses import rooms, exits
      +from evennia import create_object, search_object
      +from typeclasses import rooms, exits
       
       # We begin by creating our rooms so we can detail them later.
       
      @@ -368,7 +368,7 @@ of characters allowing us to pick out the characters we need.

      # those characters with world_map[5][5] where world_map[row][column]. world_map = world_map.split('\n') -def return_map(): +def return_map(): """ This function returns the whole map """ @@ -381,7 +381,7 @@ of characters allowing us to pick out the characters we need.

      return map -def return_minimap(x, y, radius = 2): +def return_minimap(x, y, radius = 2): """ This function returns only part of the map. Returning all chars in a 2 char radius from (x,y) @@ -400,9 +400,9 @@ of characters allowing us to pick out the characters we need.

      a reference to our map module. Make sure to import our map_module!

      # mygame/world/batchcode_map.py
       
      -from evennia import create_object
      -from evennia import DefaultObject
      -from world import map_module
      +from evennia import create_object
      +from evennia import DefaultObject
      +from world import map_module
       
       map = create_object(DefaultObject, key="Map", location=caller.location)
       
      @@ -428,8 +428,8 @@ again

      # mygame\world\batchcode_world.py
       
       # Add to imports
      -from evennia.utils import evtable
      -from world import map_module
      +from evennia.utils import evtable
      +from world import map_module
       
       # [...]
       
      diff --git a/docs/latest/Contribs/Contrib-Mapbuilder.html b/docs/latest/Contribs/Contrib-Mapbuilder.html
      index 96b858146f..293013edd4 100644
      --- a/docs/latest/Contribs/Contrib-Mapbuilder.html
      +++ b/docs/latest/Contribs/Contrib-Mapbuilder.html
      @@ -180,7 +180,7 @@ The path you provide is relative to the evennia or mygame folder.

      For example:

          # mygame/commands/default_cmdsets.py
       
      -    from evennia.contrib.grid import mapbuilder
      +    from evennia.contrib.grid import mapbuilder
       
           ...
       
      @@ -211,18 +211,18 @@ convenience The below example code should be in 
       

      Example One

      
      -from django.conf import settings
      -from evennia.utils import utils
      +from django.conf import settings
      +from evennia.utils import utils
       
       # mapbuilder evennia.contrib.grid.mapbuilder.EXAMPLE1_MAP EXAMPLE1_LEGEND
       
       # -*- coding: utf-8 -*-
       
       # Add the necessary imports for your instructions here.
      -from evennia import create_object
      -from typeclasses import rooms, exits
      -from random import randint
      -import random
      +from evennia import create_object
      +from typeclasses import rooms, exits
      +from random import randint
      +import random
       
       
       # A map with a temple (▲) amongst mountains (n,∩) in a forest (♣,♠) on an
      @@ -236,7 +236,7 @@ convenience The below example code should be in ≈≈≈≈≈
       '''
       
      -def example1_build_forest(x, y, **kwargs):
      +def example1_build_forest(x, y, **kwargs):
           '''A basic example of build instructions. Make sure to include **kwargs
           in the arguments and return an instance of the room for exit generation.'''
       
      @@ -251,7 +251,7 @@ convenience The below example code should be in return room
       
       
      -def example1_build_mountains(x, y, **kwargs):
      +def example1_build_mountains(x, y, **kwargs):
           '''A room that is a little more advanced'''
       
           # Create the room.
      @@ -277,7 +277,7 @@ convenience The below example code should be in return room
       
       
      -def example1_build_temple(x, y, **kwargs):
      +def example1_build_temple(x, y, **kwargs):
           '''A unique room that does not need to be as general'''
       
           # Create the room.
      @@ -339,7 +339,7 @@ convenience The below example code should be in ≈ ≈ ≈ ≈ ≈
       '''
       
      -def example2_build_forest(x, y, **kwargs):
      +def example2_build_forest(x, y, **kwargs):
           '''A basic room'''
           # If on anything other than the first iteration - Do nothing.
           if kwargs["iteration"] > 0:
      @@ -352,7 +352,7 @@ convenience The below example code should be in return room
       
      -def example2_build_verticle_exit(x, y, **kwargs):
      +def example2_build_verticle_exit(x, y, **kwargs):
           '''Creates two exits to and from the two rooms north and south.'''
           # If on the first iteration - Do nothing.
           if kwargs["iteration"] == 0:
      @@ -373,7 +373,7 @@ convenience The below example code should be in kwargs["caller"].msg("Connected: " + north_room.key + " & " + south_room.key)
       
       
      -def example2_build_horizontal_exit(x, y, **kwargs):
      +def example2_build_horizontal_exit(x, y, **kwargs):
           '''Creates two exits to and from the two rooms east and west.'''
           # If on the first iteration - Do nothing.
           if kwargs["iteration"] == 0:
      diff --git a/docs/latest/Contribs/Contrib-Mux-Comms-Cmds.html b/docs/latest/Contribs/Contrib-Mux-Comms-Cmds.html
      index 3cb8cc6f70..b68e98f325 100644
      --- a/docs/latest/Contribs/Contrib-Mux-Comms-Cmds.html
      +++ b/docs/latest/Contribs/Contrib-Mux-Comms-Cmds.html
      @@ -172,11 +172,11 @@ main channel
       
      # in mygame/commands/default_cmdsets.py
       
       # ..
      -from evennia.contrib.base_systems.mux_comms_cmds import CmdSetLegacyComms   # <----
      +from evennia.contrib.base_systems.mux_comms_cmds import CmdSetLegacyComms   # <----
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(CmdSetLegacyComms)   # <----
       
      diff --git a/docs/latest/Contribs/Contrib-Name-Generator.html b/docs/latest/Contribs/Contrib-Name-Generator.html
      index 6e42c04c9e..6f293b166a 100644
      --- a/docs/latest/Contribs/Contrib-Name-Generator.html
      +++ b/docs/latest/Contribs/Contrib-Name-Generator.html
      @@ -158,7 +158,7 @@ information via your game’s 
       

      Usage

      Import the module where you need it with the following:

      -
      from evennia.contrib.utils.name_generator import namegen
      +
      from evennia.contrib.utils.name_generator import namegen
       

      By default, all of the functions will return a string with one generated name. diff --git a/docs/latest/Contribs/Contrib-RPSystem.html b/docs/latest/Contribs/Contrib-RPSystem.html index 2e8dbdbe07..0ffec13c3a 100644 --- a/docs/latest/Contribs/Contrib-RPSystem.html +++ b/docs/latest/Contribs/Contrib-RPSystem.html @@ -159,11 +159,11 @@ # ... -from evennia.contrib.rpg.rpsystem import RPSystemCmdSet <--- +from evennia.contrib.rpg.rpsystem import RPSystemCmdSet <--- -class CharacterCmdSet(default_cmds.CharacterCmdset): +class CharacterCmdSet(default_cmds.CharacterCmdset): # ... - def at_cmdset_creation(self): + def at_cmdset_creation(self): # ... self.add(RPSystemCmdSet()) # <--- @@ -173,27 +173,27 @@ the typeclasses in this module:

      # in mygame/typeclasses/characters.py
       
      -from evennia.contrib.rpg.rpsystem import ContribRPCharacter
      +from evennia.contrib.rpg.rpsystem import ContribRPCharacter
       
      -class Character(ContribRPCharacter):
      +class Character(ContribRPCharacter):
           # ...
       
       
      # in mygame/typeclasses/objects.py
       
      -from evennia.contrib.rpg.rpsystem import ContribRPObject
      +from evennia.contrib.rpg.rpsystem import ContribRPObject
       
      -class Object(ContribRPObject):
      +class Object(ContribRPObject):
           # ...
       
       
      # in mygame/typeclasses/rooms.py
       
      -from evennia.contrib.rpg.rpsystem import ContribRPRoom
      +from evennia.contrib.rpg.rpsystem import ContribRPRoom
       
      -class Room(ContribRPRoom):
      +class Room(ContribRPRoom):
           # ...
       
       
      @@ -261,7 +261,7 @@ The tavern is full of nice people

      Usage:

      -
      from evennia.contrib.rpg.rpsystem import rplanguage
      +
      from evennia.contrib.rpg.rpsystem import rplanguage
       
       # need to be done once, here we create the "default" lang
       rplanguage.add_language()
      diff --git a/docs/latest/Contribs/Contrib-Random-String-Generator.html b/docs/latest/Contribs/Contrib-Random-String-Generator.html
      index 00f9a14950..dbec3434c3 100644
      --- a/docs/latest/Contribs/Contrib-Random-String-Generator.html
      +++ b/docs/latest/Contribs/Contrib-Random-String-Generator.html
      @@ -130,7 +130,7 @@ passwords and so on. The strings generated will be stored and won’t be repeate
       

      Usage Example

      Here’s a very simple example:

      
      -from evennia.contrib.utils.random_string_generator import RandomStringGenerator
      +from evennia.contrib.utils.random_string_generator import RandomStringGenerator
       
       # Create a generator for phone numbers
       phone_generator = RandomStringGenerator("phone number", r"555-[0-9]{3}-[0-9]{4}")
      diff --git a/docs/latest/Contribs/Contrib-Simpledoor.html b/docs/latest/Contribs/Contrib-Simpledoor.html
      index bc5c785f32..ac8c30f010 100644
      --- a/docs/latest/Contribs/Contrib-Simpledoor.html
      +++ b/docs/latest/Contribs/Contrib-Simpledoor.html
      @@ -136,11 +136,11 @@ non-superuser account.

      and add it to your CharacterCmdSet:

      # in mygame/commands/default_cmdsets.py
       
      -from evennia.contrib.grid import simpledoor  <---
      +from evennia.contrib.grid import simpledoor  <---
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(simpledoor.SimpleDoorCmdSet)
       
      diff --git a/docs/latest/Contribs/Contrib-Slow-Exit.html b/docs/latest/Contribs/Contrib-Slow-Exit.html
      index b947b7d4b5..d8b92d5f5e 100644
      --- a/docs/latest/Contribs/Contrib-Slow-Exit.html
      +++ b/docs/latest/Contribs/Contrib-Slow-Exit.html
      @@ -138,9 +138,9 @@ to import this module and change the default SlowExit instead.

      # in mygame/typeclasses/exits.py
       
      -from evennia.contrib.grid.slowexit import SlowExit
      +from evennia.contrib.grid.slowexit import SlowExit
       
      -class Exit(SlowExit):
      +class Exit(SlowExit):
           # ...
       
       
      @@ -148,11 +148,11 @@ from SlowExitTo get the ability to change your speed and abort your movement, import

      # in mygame/commands/default_cmdsets.py
       
      -from evennia.contrib.grid import slow_exit  <---
      +from evennia.contrib.grid import slow_exit  <---
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(slow_exit.SlowDoorCmdSet)  <---
       
      diff --git a/docs/latest/Contribs/Contrib-Storage.html b/docs/latest/Contribs/Contrib-Storage.html
      index 829e378ff5..d2330d3009 100644
      --- a/docs/latest/Contribs/Contrib-Storage.html
      +++ b/docs/latest/Contribs/Contrib-Storage.html
      @@ -130,11 +130,11 @@
       

      This utility adds the storage-related commands. Import the module into your commands and add it to your command set to make it available.

      Specifically, in mygame/commands/default_cmdsets.py:

      ...
      -from evennia.contrib.game_systems.storage import StorageCmdSet   # <---
      +from evennia.contrib.game_systems.storage import StorageCmdSet   # <---
       
      -class CharacterCmdset(default_cmds.Character_CmdSet):
      +class CharacterCmdset(default_cmds.Character_CmdSet):
           ...
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               ...
               self.add(StorageCmdSet)  # <---
       
      diff --git a/docs/latest/Contribs/Contrib-Traits.html b/docs/latest/Contribs/Contrib-Traits.html
      index c8fa593cda..5c0add975f 100644
      --- a/docs/latest/Contribs/Contrib-Traits.html
      +++ b/docs/latest/Contribs/Contrib-Traits.html
      @@ -168,21 +168,21 @@ define it explicitly. You can combine both styles if you like.

      Here’s an example for adding the TraitHandler to the Character class:

      # mygame/typeclasses/objects.py
       
      -from evennia import DefaultCharacter
      -from evennia.utils import lazy_property
      -from evennia.contrib.rpg.traits import TraitHandler
      +from evennia import DefaultCharacter
      +from evennia.utils import lazy_property
      +from evennia.contrib.rpg.traits import TraitHandler
       
       # ...
       
      -class Character(DefaultCharacter):
      +class Character(DefaultCharacter):
           ...
           @lazy_property
      -    def traits(self):
      +    def traits(self):
               # this adds the handler as .traits
               return TraitHandler(self)
       
       
      -    def at_object_creation(self):
      +    def at_object_creation(self):
               # (or wherever you want)
               self.traits.add("str", "Strength", trait_type="static", base=10, mod=2)
               self.traits.add("hp", "Health", trait_type="gauge", min=0, max=100)
      @@ -202,13 +202,13 @@ fields. The drawback is that you must make sure that the name of your Traits don
       other properties/methods on your class.

      # mygame/typeclasses/objects.py
       
      -from evennia import DefaultObject
      -from evennia.utils import lazy_property
      -from evennia.contrib.rpg.traits import TraitProperty
      +from evennia import DefaultObject
      +from evennia.utils import lazy_property
      +from evennia.contrib.rpg.traits import TraitProperty
       
       # ...
       
      -class Object(DefaultObject):
      +class Object(DefaultObject):
           ...
           strength = TraitProperty("Strength", trait_type="static", base=10, mod=2)
           health = TraitProperty("Health", trait_type="gauge", min=0, base=100, mod=2)
      @@ -517,19 +517,19 @@ from poison slowly draining your health, to resting gradually increasing it.

      the existing Trait classes).

      # in a file, say, 'mygame/world/traits.py'
       
      -from evennia.contrib.rpg.traits import StaticTrait
      +from evennia.contrib.rpg.traits import StaticTrait
       
      -class RageTrait(StaticTrait):
      +class RageTrait(StaticTrait):
       
           trait_type = "rage"
           default_keys = {
               "rage": 0
           }
       
      -    def berserk(self):
      +    def berserk(self):
               self.mod = 100
       
      -    def sedate(self):
      +    def sedate(self):
               self.mod = 0
       
      @@ -551,9 +551,9 @@ default (0). Above we also added some helper methods.

      Remember that you can use .get_trait("name") to access other traits on the same handler. Let’s say that the rage modifier is actually limited by the characters’s current STR value times 3, with a max of 100:

      -
      class RageTrait(StaticTrait):
      +
      class RageTrait(StaticTrait):
           #...
      -    def berserk(self):
      +    def berserk(self):
               self.mod = min(100, self.get_trait("STR").value * 3)
       
      @@ -561,7 +561,7 @@ the characters’s current STR value times 3, with a max of 100:

      as TraitProperty

      -
      class Character(DefaultCharacter):
      +
      class Character(DefaultCharacter):
           rage = TraitProperty("A dark mood", rage=30, trait_type='rage')
       
      @@ -570,31 +570,31 @@ the characters’s current STR value times 3, with a max of 100:

      Sometimes, it is easier to top-level classify traits, such as stats, skills, or other categories of traits you want to handle independantly of each other. Here is an example showing an example on the object typeclass, expanding on the first installation example:

      # mygame/typeclasses/objects.py
       
      -from evennia import DefaultCharacter
      -from evennia.utils import lazy_property
      -from evennia.contrib.rpg.traits import TraitHandler
      +from evennia import DefaultCharacter
      +from evennia.utils import lazy_property
      +from evennia.contrib.rpg.traits import TraitHandler
       
       # ...
       
      -class Character(DefaultCharacter):
      +class Character(DefaultCharacter):
           ...
           @lazy_property
      -    def traits(self):
      +    def traits(self):
               # this adds the handler as .traits
               return TraitHandler(self)
       
           @lazy_property
      -    def stats(self):
      +    def stats(self):
               # this adds the handler as .stats
               return TraitHandler(self, db_attribute_key="stats")
       
           @lazy_property
      -    def skills(self):
      +    def skills(self):
               # this adds the handler as .skills
               return TraitHandler(self, db_attribute_key="skills")
       
       
      -    def at_object_creation(self):
      +    def at_object_creation(self):
               # (or wherever you want)
               self.stats.add("str", "Strength", trait_type="static", base=10, mod=2)
               self.traits.add("hp", "Health", trait_type="gauge", min=0, max=100)
      diff --git a/docs/latest/Contribs/Contrib-Unixcommand.html b/docs/latest/Contribs/Contrib-Unixcommand.html
      index 4792c0cd0a..1d83a0992c 100644
      --- a/docs/latest/Contribs/Contrib-Unixcommand.html
      +++ b/docs/latest/Contribs/Contrib-Unixcommand.html
      @@ -138,10 +138,10 @@ should normally not override the normal func method, called to execute the command once parsed (like any Command).

      Here’s a short example:

      -
      from evennia.contrib.base_systems.unixcommand import UnixCommand
      +
      from evennia.contrib.base_systems.unixcommand import UnixCommand
       
       
      -class CmdPlant(UnixCommand):
      +class CmdPlant(UnixCommand):
       
           '''
           Plant a tree or plant.
      @@ -157,7 +157,7 @@ should normally not override the normal key = "plant"
       
      -    def init_parser(self):
      +    def init_parser(self):
               "Add the arguments to the parser."
               # 'self.parser' inherits `argparse.ArgumentParser`
               self.parser.add_argument("key",
      @@ -167,7 +167,7 @@ should normally not override the normal self.parser.add_argument("--hidden", action="store_true",
                       help="should the newly-planted plant be hidden to players?")
       
      -    def func(self):
      +    def func(self):
               "func is called only if the parser succeeded."
               # 'self.opts' contains the parsed options
               key = self.opts.key
      diff --git a/docs/latest/Contribs/Contrib-Wilderness.html b/docs/latest/Contribs/Contrib-Wilderness.html
      index 510a54870f..700e559d03 100644
      --- a/docs/latest/Contribs/Contrib-Wilderness.html
      +++ b/docs/latest/Contribs/Contrib-Wilderness.html
      @@ -186,11 +186,11 @@ provided as a string: a “.” symbol is a location we can walk on.

      ....... ''' -from evennia.contrib.grid import wilderness +from evennia.contrib.grid import wilderness -class PyramidMapProvider(wilderness.WildernessMapProvider): +class PyramidMapProvider(wilderness.WildernessMapProvider): - def is_valid_coordinates(self, wilderness, coordinates): + def is_valid_coordinates(self, wilderness, coordinates): "Validates if these coordinates are inside the map" x, y = coordinates try: @@ -204,7 +204,7 @@ provided as a string: a “.” symbol is a location we can walk on.

      except IndexError: return False - def get_location_name(self, coordinates): + def get_location_name(self, coordinates): "Set the location name" x, y = coordinates if y == 3: @@ -212,7 +212,7 @@ provided as a string: a “.” symbol is a location we can walk on.

      else: return "Inside a pyramid." - def at_prepare_room(self, coordinates, caller, room): + def at_prepare_room(self, coordinates, caller, room): "Any other changes done to the room before showing it" x, y = coordinates desc = "This is a room in the pyramid." diff --git a/docs/latest/Contribs/Contrib-XYZGrid.html b/docs/latest/Contribs/Contrib-XYZGrid.html index a6dcdfad6c..7b5378eed2 100644 --- a/docs/latest/Contribs/Contrib-XYZGrid.html +++ b/docs/latest/Contribs/Contrib-XYZGrid.html @@ -744,9 +744,9 @@ custom directions outside of the cardinal directions + up/down. The exit’s key

      Below is an example that changes the map’s nodes to show up as red (maybe for a lava map?):

      -
      from evennia.contrib.grid.xyzgrid import xymap_legend
      +
      from evennia.contrib.grid.xyzgrid import xymap_legend
       
      -class RedMapNode(xymap_legend.MapNode):
      +class RedMapNode(xymap_legend.MapNode):
           display_symbol = "|r#|n"
       
       
      @@ -1105,7 +1105,7 @@ time.

      different (unused) unique symbol in your map legend:

      # in your map definition module
       
      -from evennia.contrib.grid.xyzgrid import xymap_legend
      +from evennia.contrib.grid.xyzgrid import xymap_legend
       
       MAPSTR = r"""
       
      @@ -1150,7 +1150,7 @@ must be customized in a child class for every transition.

      added, with different map-legend symbols:

      # in your map definition module (let's say this is mapB)
       
      -from evennia.contrib.grid.xyzgrid import xymap_legend
      +from evennia.contrib.grid.xyzgrid import xymap_legend
       
       MAPSTR = r"""
       
      @@ -1167,11 +1167,11 @@ added, with different map-legend symbols:

      """ -class TransitionToMapA(xymap_legend.MapTransitionNode): +class TransitionToMapA(xymap_legend.MapTransitionNode): """Transition to MapA""" target_map_xyz = (1, 4, "mapA") -class TransitionToMapC(xymap_legend.MapTransitionNode): +class TransitionToMapC(xymap_legend.MapTransitionNode): """Transition to MapB""" target_map_xyz = (12, 14, "mapC") diff --git a/docs/latest/Contributing-Docs.html b/docs/latest/Contributing-Docs.html index 05166ea6c3..1aca380c39 100644 --- a/docs/latest/Contributing-Docs.html +++ b/docs/latest/Contributing-Docs.html @@ -425,13 +425,13 @@ class CmdEcho(Command): ```
      -
      from evennia import Command
      -class CmdEcho(Command):
      +
      from evennia import Command
      +class CmdEcho(Command):
         """
         Usage: echo <arg>
         """
         key = "echo"
      -  def func(self):
      +  def func(self):
           self.caller.msg(self.args.strip())
       
      @@ -623,13 +623,13 @@ class CmdEcho(Command): 5 6 7 -8
      from evennia import Command
      -class CmdEcho(Command):
      +8
      from evennia import Command
      +class CmdEcho(Command):
           """
           Usage: echo <arg>
           """
           key = "echo"
      -    def func(self):
      +    def func(self):
               self.caller.msg(self.args.strip())
       
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.html index f260108a55..7267ce1274 100644 --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.html +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.html @@ -146,10 +146,10 @@ An example is look< (module docstring) """ -from evennia import Command as BaseCommand +from evennia import Command as BaseCommand # from evennia import default_cmds -class Command(BaseCommand): +class Command(BaseCommand): """ (class docstring) """ @@ -167,9 +167,9 @@ An example is look<

      We could modify this module directly, but let’s work in a separate module just for the heck of it. Open a new file mygame/commands/mycommands.py and add the following code:

      # in mygame/commands/mycommands.py
       
      -from commands.command import Command
      +from commands.command import Command
       
      -class CmdEcho(Command):
      +class CmdEcho(Command):
           key = "echo"
       
       
      @@ -178,16 +178,16 @@ An example is look<

      Next we need to put this in a CmdSet. It will be a one-command CmdSet for now! Change your file as such:

      # in mygame/commands/mycommands.py
       
      -from commands.command import Command
      -from evennia import CmdSet
      +from commands.command import Command
      +from evennia import CmdSet
       
      -class CmdEcho(Command):
      +class CmdEcho(Command):
           key = "echo"
       
       
      -class MyCmdSet(CmdSet):
      +class MyCmdSet(CmdSet):
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               self.add(CmdEcho)
       
       
      @@ -233,7 +233,7 @@ Command "echo" has no defined `func()`. Available properties ...
      # in mygame/commands/mycommands.py
       # ...
       
      -class CmdEcho(Command):
      +class CmdEcho(Command):
           """
           A simple echo command
       
      @@ -243,7 +243,7 @@ Command "echo" has no defined `func()`. Available properties ...
           """
           key = "echo"
       
      -    def func(self):
      +    def func(self):
               self.caller.msg(f"Echo: '{self.args}'")
       
       # ...
      @@ -271,7 +271,7 @@ Echo: ' Woo Tang!'
       
      # in mygame/commands/mycommands.py
       # ...
       
      -class CmdEcho(Command):
      +class CmdEcho(Command):
           """
           A simple echo command
       
      @@ -281,7 +281,7 @@ Echo: ' Woo Tang!'
           """
           key = "echo"
       
      -    def func(self):
      +    def func(self):
               self.caller.msg(f"Echo: '{self.args.strip()}'")
       
       # ...
      @@ -326,9 +326,9 @@ Echo: 'Woo Tang!'
       
       # ... 
       
      -from . import mycommands    # <-------  
      +from . import mycommands    # <-------  
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           """
           The `CharacterCmdSet` contains general in-game commands like `look`,
           `get`, etc available on in-game Character objects. It is merged with
      @@ -337,7 +337,7 @@ Echo: 'Woo Tang!'
        
           key = "DefaultCharacter"
        
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               """
               Populates the cmdset
               """
      @@ -409,7 +409,7 @@ You hit Bob with full force!
       
       # ...
       
      -class CmdHit(Command):
      +class CmdHit(Command):
           """
           Hit a target.
       
      @@ -419,7 +419,7 @@ You hit Bob with full force!
           """
           key = "hit"
       
      -    def func(self):
      +    def func(self):
               args = self.args.strip()
               if not args:
                   self.caller.msg("Who do you want to hit?")
      @@ -484,9 +484,9 @@ directly in-game or in your log (view it with 
      # in mygame/commands/mycommands.py
       
       # ...
      -class MyCmdSet(CmdSet):
      +class MyCmdSet(CmdSet):
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               self.add(CmdEcho)
               self.add(CmdHit)
       
      @@ -497,9 +497,9 @@ directly in-game or in your log (view it with # ,.. 
       
      -from . import mycommands    
      +from . import mycommands    
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           """
           The `CharacterCmdSet` contains general in-game commands like `look`,
           `get`, etc available on in-game Character objects. It is merged with
      @@ -508,7 +508,7 @@ directly in-game or in your log (view it with key = "DefaultCharacter"
        
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               """
               Populates the cmdset
               """
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Creating-Things.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Creating-Things.html
      index df1c0c59f0..0296f2c4d2 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Creating-Things.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Creating-Things.html
      @@ -209,7 +209,7 @@ are available on ev
       

      Since we didn’t specify the typeclass as the first argument, the default given by settings.BASE_OBJECT_TYPECLASS (typeclasses.objects.Object out of the box) will be used.

      The create_object has a lot of options. A more detailed example in code:

      -
      from evennia import create_object, search_object
      +
      from evennia import create_object, search_object
       
       meadow = search_object("Meadow")[0]
       
      @@ -237,8 +237,8 @@ Forest -> west -> Meadow
       

      In-game you do this with tunnel and dig commands, bit if you want to ever set up these links in code, you can do it like this:

      -
      from evennia import create_object 
      -from mygame.typeclasses import rooms, exits 
      +
      from evennia import create_object 
      +from mygame.typeclasses import rooms, exits 
       
       # rooms
       meadow = create_object(rooms.Room, key="Meadow")
      @@ -257,7 +257,7 @@ Forest -> west -> Meadow
       You can find the parent class for Accounts in typeclasses/accounts.py.

      Normally, you want to create the Account when a user authenticates. By default, this happens in the create account and login default commands in the UnloggedInCmdSet. This means that customizing this just means replacing those commands!

      So normally you’d modify those commands rather than make something from scratch. But here’s the principle:

      -
      from evennia import create_account 
      +
      from evennia import create_account 
       
       new_account = create_account(
                   accountname, email, password, 
      @@ -285,7 +285,7 @@ channel/sub channel name
       

      Creating channels follows a familiar syntax:

      -
      from evennia import create_channel
      +
      from evennia import create_channel
       
       new_channel = create_channel(channelname)
       
      @@ -296,7 +296,7 @@ channel/sub channel name

      10.5. Creating Scripts

      A Script is an entity that has no in-game location. It can be used to store arbitrary data and is often used for game systems that need persistent storage but which you can’t ‘look’ at in-game. Examples are economic systems, weather and combat handlers.

      Scripts are multi-use and depending on what they do, a given script can either be ‘global’ or be attached “to” another object (like a Room or Character).

      -
      from evennia import create_script, search_object 
      +
      from evennia import create_script, search_object 
       # global script 
       new_script = create_script("typeclasses.scripts.MyScript", key="myscript")
       
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.html
      index d636cceb88..f087899655 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.html
      @@ -280,7 +280,7 @@ possible.

      7 8 9 -10
      from typeclasses.characters import Character
      +10
      from typeclasses.characters import Character
       
       will_transform = (
           Character.objects
      @@ -349,7 +349,7 @@ of Daltons and non-prisoners.

      Let us expand our original werewolf query. Not only do we want to find all Characters in a moonlit room with a certain level of lycanthropy - we decide that if they have been newly bitten, they should also turn, regardless of their lycanthropy level (more dramatic that way!).

      Let’s say that getting bitten means that you’ll get assigned a Tag recently_bitten.

      This is how we’d change our query:

      -
      from django.db.models import Q
      +
      from django.db.models import Q
       
       will_transform = (
           Character.objects
      @@ -365,7 +365,7 @@ of Daltons and non-prisoners.

      That’s quite compact. It may be easier to see what’s going on if written this way:

      -
      from django.db.models import Q
      +
      from django.db.models import Q
       
       q_moonlit = Q(db_location__db_tags__db_key__iexact="moonlit")
       q_lycanthropic = Q(db_attributes__db_key="lycanthropy", db_attributes__db_value__eq=2)
      @@ -396,7 +396,7 @@ sure that there is only one instance of each Character in the result.

      What if we wanted to filter on some condition that isn’t represented easily by a field on the object? An example would wanting to find rooms only containing five or more objects.

      We could do it like this (don’t actually do it this way!):

      -
      from typeclasses.rooms import Room
      +
      from typeclasses.rooms import Room
       
         all_rooms = Rooms.objects.all()
       
      @@ -425,8 +425,8 @@ directly in the database:

      6 7 8 -9
      from typeclasses.rooms import Room
      -from django.db.models import Count
      +9
      from typeclasses.rooms import Room
      +from django.db.models import Count
       
       rooms = (
           Room.objects
      @@ -455,8 +455,8 @@ should be greater than or equal to 5.

      query? For example, what if instead of having 5 or more objects, we only wanted objects that had a bigger inventory than they had tags (silly example, but …)?

      This can be with Django’s F objects. So-called F expressions allow you to do a query that looks at a value of each object in the database.

      -
      from django.db.models import Count, F
      -from typeclasses.rooms import Room
      +
      from django.db.models import Count, F
      +from typeclasses.rooms import Room
       
       result = (
           Room.objects
      @@ -481,8 +481,8 @@ objects that had a bigger inventory than they had tags (silly example, but …)?
       6
       7
       8
      -9
      from django.db.models import Count
      -from typeclasses.rooms import Room
      +9
      from django.db.models import Count
      +from typeclasses.rooms import Room
       
       result = (
           Character.objects
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Evennia-Library-Overview.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Evennia-Library-Overview.html
      index 8d25af10c1..cc7409c369 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Evennia-Library-Overview.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Evennia-Library-Overview.html
      @@ -204,9 +204,9 @@ from evennia.some_module.other_module import SomeClass
       
      """
       module docstring
       """
      -from evennia import DefaultObject
      +from evennia import DefaultObject
       
      -class Object(DefaultObject):
      +class Object(DefaultObject):
           """
           class docstring
           """
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Learning-Typeclasses.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Learning-Typeclasses.html
      index fcf892bbae..04a7152e95 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Learning-Typeclasses.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Learning-Typeclasses.html
      @@ -146,28 +146,28 @@
       and Smaug and made them fly and breathe fire. So far our dragons are short-lived - whenever we restart the server or quit() out of python mode they are gone.

      This is what you should have in mygame/typeclasses/monsters.py so far:

      
      -class Monster:
      +class Monster:
           """
           This is a base class for Monsters.
           """
        
      -    def __init__(self, key):
      +    def __init__(self, key):
               self.key = key 
       
      -    def move_around(self):
      +    def move_around(self):
               print(f"{self.key} is moving!")
       
       
      -class Dragon(Monster):
      +class Dragon(Monster):
           """
           This is a dragon-specific monster.
           """
       
      -    def move_around(self):
      +    def move_around(self):
               super().move_around()
               print("The world trembles.")
       
      -    def firebreath(self):
      +    def firebreath(self):
               """ 
               Let our dragon breathe fire.
               """
      @@ -182,15 +182,15 @@ open it:

      """
       module docstring
       """
      -from evennia import DefaultObject
      +from evennia import DefaultObject
       
      -class ObjectParent:
      +class ObjectParent:
           """ 
           class docstring 
           """
           pass
       
      -class Object(ObjectParent, DefaultObject):
      +class Object(ObjectParent, DefaultObject):
           """
           class docstring
           """
      @@ -202,26 +202,26 @@ open it:

      One thing that Evennia classes offers and which you don’t get with vanilla Python classes is persistence - they survive a server reload since they are stored in the database.

      Go back to mygame/typeclasses/monsters.py. Change it as follows:

      
      -from typeclasses.objects import Object
      +from typeclasses.objects import Object
       
      -class Monster(Object):
      +class Monster(Object):
           """
           This is a base class for Monsters.
           """
      -    def move_around(self):
      +    def move_around(self):
               print(f"{self.key} is moving!")
       
       
      -class Dragon(Monster):
      +class Dragon(Monster):
           """
           This is a dragon-specific Monster.
           """
       
      -    def move_around(self):
      +    def move_around(self):
               super().move_around()
               print("The world trembles.")
       
      -    def firebreath(self):
      +    def firebreath(self):
               """ 
               Let our dragon breathe fire.
               """
      @@ -343,15 +343,15 @@ Cuddly is moving!
       
       
       

      The child classes under mygame/typeclasses/ are meant for you to conveniently modify and work with. Every class inheriting (at any distance) from a Evennia base typeclass is also considered a typeclass.

      -
      from somewhere import Something 
      -from evennia import DefaultScript 
      +
      from somewhere import Something 
      +from evennia import DefaultScript 
       
      -class MyOwnClass(Something): 
      +class MyOwnClass(Something): 
           # not inheriting from an Evennia core typeclass, so this 
           # is just a 'normal' Python class inheriting from somewhere
           pass 
       
      -class MyOwnClass2(DefaultScript):
      +class MyOwnClass2(DefaultScript):
           # inherits from one of the core Evennia typeclasses, so 
           # this is also considered a 'typeclass'.
           pass
      @@ -438,10 +438,10 @@ You create a new Object: box.
       
      """
       (module docstring)
       """
      -from evennia import DefaultCharacter
      -from .objects import ObjectParent
      +from evennia import DefaultCharacter
      +from .objects import ObjectParent
       
      -class Character(ObjectParent, DefaultCharacter):
      +class Character(ObjectParent, DefaultCharacter):
           """
           (class docstring)
           """
      @@ -491,7 +491,7 @@ Non-Persistent attributes:
       
       # ...
       
      -class Character(ObjectParent, DefaultCharacter):
      +class Character(ObjectParent, DefaultCharacter):
           """
           (class docstring)
           """
      @@ -500,7 +500,7 @@ Non-Persistent attributes:
           dexterity = 12
           intelligence = 15
       
      -    def get_stats(self):
      +    def get_stats(self):
               """
               Get the main stats of this character
               """
      @@ -555,12 +555,12 @@ Strength is 10.
       
       # ...
       
      -class Character(ObjectParent, DefaultCharacter):
      +class Character(ObjectParent, DefaultCharacter):
           """
           (class docstring)
           """
       
      -    def get_stats(self):
      +    def get_stats(self):
               """
               Get the main stats of this character
               """
      @@ -612,19 +612,19 @@ AttributeError: 'Character' object has no attribute 'strength'
       
      # in mygame/typeclasses/characters.py
       
       # ...
      -import random 
      +import random 
       
      -class Character(ObjectParent, DefaultCharacter):
      +class Character(ObjectParent, DefaultCharacter):
           """
           (class docstring)
           """
       
      -    def at_object_creation(self):       
      +    def at_object_creation(self):       
               self.db.strength = random.randint(3, 18)
               self.db.dexterity = random.randint(3, 18)
               self.db.intelligence = random.randint(3, 18)
           
      -    def get_stats(self):
      +    def get_stats(self):
               """
               Get the main stats of this character
               """
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.html
      index 339b1cc62e..4b4b8ecca2 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.html
      @@ -151,10 +151,10 @@ This requires a change to our Character typeclass. Open # ...
       
      -class Character(DefaultCharacter):
      +class Character(DefaultCharacter):
           # ...
       
      -    def at_pre_move(self, destination, **kwargs):
      +    def at_pre_move(self, destination, **kwargs):
              """
              Called by self.move_to when trying to move somewhere. If this returns
              False, the move is immediately cancelled.
      @@ -199,11 +199,11 @@ will call character
       24
       25
      # in mygame/typeclasses/sittables.py
       
      -from typeclasses.objects import Object
      +from typeclasses.objects import Object
       
      -class Sittable(Object):
      +class Sittable(Object):
       
      -    def do_sit(self, sitter):
      +    def do_sit(self, sitter):
               """
               Called when trying to sit on/in this object.
       
      @@ -253,7 +253,7 @@ will call character
       16
       17
      # add this right after the `do_sit method` in the same class 
       
      -    def do_stand(self, stander):
      +    def do_stand(self, stander):
               """
               Called when trying to stand from this object.
       
      @@ -333,11 +333,11 @@ will call character
       42
       43
      # in mygame/typeclasses/sittables.py
       
      -from typeclasses.objects import Object
      +from typeclasses.objects import Object
       
      -class Sittable(Object):
      +class Sittable(Object):
       
      -    def do_sit(self, sitter):
      +    def do_sit(self, sitter):
               """
               Called when trying to sit on/in this object.
       
      @@ -359,7 +359,7 @@ will call character
               sitter.db.is_sitting = self
               sitter.msg(f"You sit {adjective} {self.key}")
       
      -    def do_stand(self, stander):
      +    def do_stand(self, stander):
               """
               Called when trying to stand from this object.
       
      @@ -473,28 +473,28 @@ As you sit down in armchair, life feels easier.
       25
       26
      # in mygame/commands/sittables.py 
       
      -from evennia import Command, CmdSet
      +from evennia import Command, CmdSet
       
      -class CmdSit(Command):
      +class CmdSit(Command):
           """
           Sit down.
           """
           key = "sit"
      -    def func(self):
      +    def func(self):
               self.obj.do_sit(self.caller)
       
      -class CmdStand(Command):
      +class CmdStand(Command):
            """
            Stand up.
            """
            key = "stand"
      -     def func(self):
      +     def func(self):
                self.obj.do_stand(self.caller)
       
       
      -class CmdSetSit(CmdSet):
      +class CmdSetSit(CmdSet):
           priority = 1
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               self.add(CmdSit)
               self.add(CmdStand)
       
      @@ -518,14 +518,14 @@ As you sit down in armchair, life feels easier. 11 12
      # in mygame/typeclasses/sittables.py
       
      -from typeclasses.objects import Object
      -from commands.sittables import CmdSetSit 
      +from typeclasses.objects import Object
      +from commands.sittables import CmdSetSit 
       
      -class Sittable(Object):
      +class Sittable(Object):
           """
           (docstring)
           """
      -    def at_object_creation(self):
      +    def at_object_creation(self):
               self.cmdset.add_default(CmdSetSit)
           # ... 
       
      @@ -580,14 +580,14 @@ of mygame/commands/ 15
      # after the other commands in mygame/commands/sittables.py
       # ...
       
      -class CmdNoSitStand(Command):
      +class CmdNoSitStand(Command):
           """
           Sit down or Stand up
           """
           key = "sit"
           aliases = ["stand"]
       
      -    def func(self):
      +    def func(self):
               if self.cmdname == "sit":
                   self.msg("You have nothing to sit on.")
               else:
      @@ -602,13 +602,13 @@ of mygame/commands/
       
      # in mygame/commands/default_cmdsets.py
       
       # ...
      -from commands import sittables
      +from commands import sittables
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           """
           (docstring)
           """
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(sittables.CmdNoSitStand)
       
      @@ -664,14 +664,14 @@ More than one match for 'stand' (please narrow target):
       
       # ...
       
      -class CmdStand(Command):
      +class CmdStand(Command):
            """
            Stand up.
            """
            key = "stand"
            locks = "cmd:sitsonthis()"
       
      -     def func(self):
      +     def func(self):
                self.obj.do_stand(self.caller)
       # ...
       
      @@ -688,7 +688,7 @@ What will be checked is the """ # ... -def sitsonthis(accessing_obj, accessed_obj, *args, **kwargs): +def sitsonthis(accessing_obj, accessed_obj, *args, **kwargs): """ True if accessing_obj is sitting on/in the accessed_obj. """ @@ -781,17 +781,17 @@ You stand up from chair. 37 38
      # in mygame/commands/sittables.py
       
      -from evennia import Command, CmdSet
      -from evennia import InterruptCommand
      +from evennia import Command, CmdSet
      +from evennia import InterruptCommand
       
      -class CmdSit(Command):
      +class CmdSit(Command):
           # ...
       
       # ...
       
       # new from here
       
      -class CmdSit2(Command):
      +class CmdSit2(Command):
           """
           Sit down.
       
      @@ -801,13 +801,13 @@ You stand up from chair.
           """
           key = "sit"
       
      -    def parse(self):
      +    def parse(self):
               self.args = self.args.strip()
               if not self.args:
                   self.caller.msg("Sit on what?")
                   raise InterruptCommand
       
      -    def func(self):
      +    def func(self):
       
               # self.search handles all error messages etc.
               sittable = self.caller.search(self.args)
      @@ -851,7 +851,7 @@ You stand up from chair.
       19
       20
      # end of mygame/commands/sittables.py
       
      -class CmdStand2(Command):
      +class CmdStand2(Command):
           """
           Stand up.
       
      @@ -861,7 +861,7 @@ You stand up from chair.
           """
           key = "stand"
       
      -    def func(self):
      +    def func(self):
               caller = self.caller
               # if we are sitting, this should be set on us
               sittable = caller.db.is_sitting
      @@ -879,13 +879,13 @@ You stand up from chair.
       
      # in mygame/commands/default_cmdsets.py
       
       # ...
      -from commands import sittables
      +from commands import sittables
       
      -class CharacterCmdSet(CmdSet):
      +class CharacterCmdSet(CmdSet):
           """
           (docstring)
           """
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               # ...
               self.add(sittables.CmdSit2)
               self.add(sittables.CmdStand2)
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-More-on-Commands.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-More-on-Commands.html
      index e6ffe03e16..5c5e6be840 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-More-on-Commands.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-More-on-Commands.html
      @@ -193,7 +193,7 @@ you are in a hurry. Time to modify 42
       43
      #...
       
      -class CmdHit(Command):
      +class CmdHit(Command):
           """
           Hit a target.
       
      @@ -203,7 +203,7 @@ you are in a hurry. Time to modify     """
           key = "hit"
       
      -    def parse(self):
      +    def parse(self):
               self.args = self.args.strip()
               target, *weapon = self.args.split(" with ", 1)
               if not weapon:
      @@ -214,7 +214,7 @@ you are in a hurry. Time to modify else:
                   self.weapon = ""
       
      -    def func(self):
      +    def func(self):
               if not self.args:
                   self.caller.msg("Who do you want to hit?")
                   return
      @@ -346,10 +346,10 @@ Who do you want to hit?
       
       # ...
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
       
           # ... 
      -    def at_object_creation(self): 
      +    def at_object_creation(self): 
       
               # self.add(MyCmdSet)    # <---------
       
      @@ -418,46 +418,46 @@ Command 'hit' is not available. ..
       (module docstring)
       """
       
      -from evennia import default_cmds
      +from evennia import default_cmds
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
       
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
               # any commands you add below will overload the default ones
               #
       
      -class AccountCmdSet(default_cmds.AccountCmdSet):
      +class AccountCmdSet(default_cmds.AccountCmdSet):
       
           key = "DefaultAccount"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
               # any commands you add below will overload the default ones
               #
       
      -class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
      +class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
       
           key = "DefaultUnloggedin"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
               # any commands you add below will overload the default ones
               #
       
      -class SessionCmdSet(default_cmds.SessionCmdSet):
      +class SessionCmdSet(default_cmds.SessionCmdSet):
       
           key = "DefaultSession"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
      @@ -481,13 +481,13 @@ Command 'hit' is not available. ..
       

      For now, let’s add our own hit and echo commands to the CharacterCmdSet:

      # ...
       
      -from commands import mycommands
      +from commands import mycommands
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
       
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
      @@ -504,13 +504,13 @@ Who do you want to hit?
       

      Your new commands are now available for all player characters in the game. There is another way to add a bunch of commands at once, and that is to add your own CmdSet to the other cmdset.

      -
      from commands import mycommands
      +
      from commands import mycommands
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
       
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
      @@ -529,13 +529,13 @@ this is practical. A Command can be a part of any number of different CmdSets.at_cmdset_creation. For example, let’s remove the default get Command
       from Evennia. If you investigate the default_cmds.CharacterCmdSet parent, you’ll find that its class is default_cmds.CmdGet (the ‘real’ location is evennia.commands.default.general.CmdGet).

      # ...
      -from commands import mycommands
      +from commands import mycommands
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
       
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
      @@ -567,12 +567,12 @@ command with the same 7
       8
       9
      # up top, by the other imports
      -from evennia import default_cmds
      +from evennia import default_cmds
       
       # somewhere below
      -class MyCmdGet(default_cmds.CmdGet):
      +class MyCmdGet(default_cmds.CmdGet):
       
      -    def func(self):
      +    def func(self):
               super().func()
               self.caller.msg(str(self.caller.location.contents))
       
      @@ -595,13 +595,13 @@ has a special function get command. Open mygame/commands/default_cmdsets.py again:

      # ...
      -from commands import mycommands
      +from commands import mycommands
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
       
           key = "DefaultCharacter"
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
       
               super().at_cmdset_creation()
               #
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.html
      index cc639bb29e..2976eff0f8 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.html
      @@ -340,7 +340,7 @@ Hello World!
       

      3.3.1. Our first own function

      We want to be able to print our hello-world message at any time, not just once after a server reload. Change your mygame/world/test.py file to look like this:

      -
      def hello_world():
      +
      def hello_world():
           print("Hello World!")
       
      @@ -401,7 +401,7 @@ debugging and .msg(

      3.5. Parsing Python errors

      Let’s try this new text-sending in the function we just created. Go back to your test.py file and Replace the function with this instead:

      -
      def hello_world():
      +
      def hello_world():
           me.msg("Hello World!")
       
      @@ -439,7 +439,7 @@ sometimes be useful information, but reading from the bottom is always a good st

      3.6. Passing arguments to functions

      We know that me exists at the point when we run the py command, because we can do py me.msg("Hello World!") with no problem. So let’s pass that me along to the function so it knows what it should be. Go back to your test.py and change it to this:

      -
      def hello_world(who):
      +
      def hello_world(who):
           who.msg("Hello World!")
       
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-classes-and-objects.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-classes-and-objects.html index 344ae656a3..3f4af37c7e 100644 --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-classes-and-objects.html +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-classes-and-objects.html @@ -155,7 +155,7 @@ Hello World!

      If you followed earlier tutorial lessons, the mygame/world/test.py file should look like this (if not, make it so):

      -
      def hello_world(who):
      +
      def hello_world(who):
           who.msg("Hello World!")
       
      @@ -247,9 +247,9 @@ Closing the Python console. """ module docstring """ -from evennia import DefaultScript +from evennia import DefaultScript -class Script(DefaultScript): +class Script(DefaultScript): """ class docstring """ @@ -275,11 +275,11 @@ as a black box.

      A ‘class’ can be seen as a ‘template’ for a ‘type’ of object. The class describes the basic functionality of everyone of that class. For example, we could have a class Monster which has resources for moving itself from room to room.

      Open a new file mygame/typeclasses/monsters.py. Add the following simple class:

      
      -class Monster:
      +class Monster:
       
           key = "Monster"
       
      -    def move_around(self):
      +    def move_around(self):
               print(f"{self.key} is moving!")
       
       
      @@ -324,12 +324,12 @@ Monster is moving! instance. We can have them move as many times as we want. But no matter how many monsters we create, they will all show the same printout since key is always fixed as “Monster”.

      Let’s make the class a little more flexible:

      
      -class Monster:
      +class Monster:
       
      -    def __init__(self, key):
      +    def __init__(self, key):
               self.key = key
       
      -    def move_around(self):
      +    def move_around(self):
               print(f"{self.key} is moving!")
       
       
      @@ -362,7 +362,7 @@ Fluffy is moving!

      5.2.2. What’s so good about objects?

      So far all we’ve seen a class do is to behave like our first hello_world function but being more complex. We could just have made a function:

      -
           def monster_move_around(key):
      +
           def monster_move_around(key):
               print(f"{key} is moving!")
       
      @@ -389,27 +389,27 @@ Fluffy, the red dragon is moving!

      Classes can inherit from each other. A “child” class will inherit everything from its “parent” class. But if the child adds something with the same name as its parent, it will override whatever it got from its parent.

      Let’s expand mygame/typeclasses/monsters.py with another class:

      
      -class Monster:
      +class Monster:
           """
           This is a base class for Monster.
           """
       
      -    def __init__(self, key):
      +    def __init__(self, key):
               self.key = key
       
      -    def move_around(self):
      +    def move_around(self):
               print(f"{self.key} is moving!")
       
       
      -class Dragon(Monster):
      +class Dragon(Monster):
           """
           This is a dragon monster.
           """
       
      -    def move_around(self):
      +    def move_around(self):
               print(f"{self.key} flies through the air high above!")
       
      -    def firebreath(self):
      +    def firebreath(self):
               """
               Let our dragon breathe fire.
               """
      @@ -437,9 +437,9 @@ Smaug breathes fire!
       

      One can also force a class to use resources from the parent even if you are overriding some of it. This is done with the super() method. Modify your Dragon class as follows:

      # ...
       
      -class Dragon(Monster):
      +class Dragon(Monster):
       
      -    def move_around(self):
      +    def move_around(self):
               super().move_around()
               print("The world trembles.")
       
      @@ -468,14 +468,14 @@ The world trembles.
       
      """
       module docstring
       """
      -from evennia import DefaultObject
      +from evennia import DefaultObject
       
      -class ObjectParent:
      +class ObjectParent:
           """
           class docstring 
           """
       
      -class Object(ObjectParent, DefaultObject):
      +class Object(ObjectParent, DefaultObject):
           """
           class docstring
           """
      @@ -490,11 +490,11 @@ The world trembles.
       ...
       """
       
      -from evennia.objects.objects import DefaultRoom
      +from evennia.objects.objects import DefaultRoom
       
      -from .objects import ObjectParent
      +from .objects import ObjectParent
       
      -class Room(ObjectParent, DefaultRoom):
      +class Room(ObjectParent, DefaultRoom):
           """
       	...
           """
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.html b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.html
      index 5f1fdc41d6..ef0466b3f5 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.html
      @@ -161,12 +161,12 @@ Rose
       

      In other words, this method handles error messaging for you. A very common way to use it is in commands. You can put your command anywhere, but let’s try the pre-filled-in mygame/commands/command.py.

      # in for example mygame/commands/command.py
       
      -from evennia import Command as BaseCommand
      +from evennia import Command as BaseCommand
       
      -class Command(BaseCommand): 
      +class Command(BaseCommand): 
           # ... 
       
      -class CmdQuickFind(Command):
      +class CmdQuickFind(Command):
           """ 
           Find an item in your current location.
       
      @@ -177,7 +177,7 @@ Rose
       
           key = "quickfind"
       
      -    def func(self):
      +    def func(self):
               query = self.args
               result = self.caller.search(query)
               if not result:
      @@ -190,11 +190,11 @@ Rose
       
       # ...
       
      -from commands.command import CmdQuickFind    # <-------
      +from commands.command import CmdQuickFind    # <-------
       
      -class CharacterCmdSet(default_cmds.CharacterCmdSet):
      +class CharacterCmdSet(default_cmds.CharacterCmdSet):
           # ... 
      -    def at_cmdset_creation(self): 
      +    def at_cmdset_creation(self): 
               # ... 
               self.add(CmdQuickFind())   # <------
       
      @@ -237,7 +237,7 @@ inherits from Defau
       

      What is returned from the main search functions is actually a queryset. They can be treated like lists except that they can’t modified in-place. We’ll discuss querysets in the next lesson

      This searches for objects based on key or alias. The .search method we talked about in the previous section in fact wraps evennia.search_object and handles its output in various ways. Here’s the same example in Python code, for example as part of a command or coded system:

      -
      import evennia 
      +
      import evennia 
       
       roses = evennia.search_object("rose")
       accts = evennia.search_account("YourName")
      @@ -441,12 +441,12 @@ all_fantasy_books = evennia.search_tag("fantasy", category="books
       

      If you e.g. have the BlessedRose class already imported you can also pass it directly:

      -
      from typeclasses.flowers import BlessedFlower
      +
      from typeclasses.flowers import BlessedFlower
       blessed_roses = evennia.search_object("rose", typeclass=BlessedFlower)
       

      A common use case is finding all items of a given typeclass, no matter what they are named. For this you don’t use search_object, but search with the typeclass directly:

      -
      from typeclasses.objects.flowers import Rose
      +
      from typeclasses.objects.flowers import Rose
       all_roses = Rose.objects.all()
       
      @@ -475,7 +475,7 @@ eightball = evennia.search_object("#8")

      11.5. Summary

      Knowing how to find things is important and the tools from this section will serve you well. These tools will cover most of your regular needs.

      Not always though. If we go back to the example of a coin in a chest from before, you could use the following to dynamically figure out if there are any chests in the room with coins inside:

      -
      from evennia import search_object
      +
      from evennia import search_object
       
       # we assume only one match of each 
       dungeons = search_object("dungeon", typeclass="typeclasses.rooms.Room")
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.html
      index 653ec0d6d3..377b6a8c42 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.html
      @@ -200,25 +200,25 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic
       25
       26
      # in evadventure/ai.py
       
      -from evennia.logger import log_trace
      +from evennia.logger import log_trace
       
      -class AIHandler:
      +class AIHandler:
           attribute_name = "ai_state"
           attribute_category = "ai_state"
       
      -    def __init__(self, obj):
      +    def __init__(self, obj):
               self.obj = obj
               self.ai_state = obj.attributes.get(self.attribute_name,
                                                  category=self.attribute_category,
                                                  default="idle")
      -    def set_state(self, state):
      +    def set_state(self, state):
               self.ai_state = state
               self.obj.attributes.add(self.attribute_name, state, category=self.attribute_category)
       
      -    def get_state(self):
      +    def get_state(self):
               return self.ai_state
       
      -    def run(self):
      +    def run(self):
               try:
                   state = self.get_state()
                   getattr(self.obj, f"ai_{state}")()
      @@ -233,13 +233,13 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic
       
       
      # just an example, don't put this anywhere yet
       
      -from evennia.utils import lazy_property
      -from evadventure.ai import AIHandler 
      +from evennia.utils import lazy_property
      +from evadventure.ai import AIHandler 
       
      -class MyMob(SomeParent): 
      +class MyMob(SomeParent): 
       
           @lazy_property
      -    class ai(self): 
      +    class ai(self): 
               return AIHandler(self)
       
      @@ -307,20 +307,20 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic 52
      # in evadventure/ai.py 
       
       # ... 
      -import random
      +import random
       
      -class AIHandler:
      +class AIHandler:
       
           # ...
       
      -    def get_targets(self):
      +    def get_targets(self):
               """
               Get a list of potential targets for the NPC to combat.
       
               """
               return [obj for obj in self.obj.location.contents if hasattr(obj, "is_pc") and obj.is_pc]
       
      -    def get_traversable_exits(self, exclude_destination=None):
      +    def get_traversable_exits(self, exclude_destination=None):
               """
               Get a list of exits that the NPC can traverse. Optionally exclude a destination.
               
      @@ -334,7 +334,7 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic
                   if exi.destination != exclude_destination and exi.access(self, "traverse")
               ]
           
      -    def random_probability(self, probabilities):
      +    def random_probability(self, probabilities):
               """
               Given a dictionary of probabilities, return the key of the chosen probability.
       
      @@ -395,30 +395,30 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic
       
       # ... 
       
      -from evennia.utils import lazy_property 
      -from .ai import AIHandler
      +from evennia.utils import lazy_property 
      +from .ai import AIHandler
       
       # ... 
       
      -class EvAdventureMob(EvAdventureNPC):
      +class EvAdventureMob(EvAdventureNPC):
       
           @lazy_property
      -    def ai(self): 
      +    def ai(self): 
               return AIHandler(self)
       
      -    def ai_idle(self): 
      +    def ai_idle(self): 
               pass 
       
      -    def ai_roam(self): 
      +    def ai_roam(self): 
               pass 
       
      -    def ai_roam(self): 
      +    def ai_roam(self): 
               pass 
       
      -    def ai_combat(self): 
      +    def ai_combat(self): 
               pass 
       
      -    def ai_flee(self):
      +    def ai_flee(self):
               pass
       
       
      @@ -435,13 +435,13 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic # ... -import random +import random -class EvAdventureMob(EvAdventureNPC): +class EvAdventureMob(EvAdventureNPC): # ... - def ai_roam(self): + def ai_roam(self): """ roam, moving randomly to a new room. If a target is found, switch to combat state. @@ -466,11 +466,11 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic # ... -class EvAdventureMob(EvAdventureNPC): +class EvAdventureMob(EvAdventureNPC): # ... - def ai_flee(self): + def ai_flee(self): """ Flee from the current room, avoiding going back to the room from which we came. If no exits are found, switch to roam state. @@ -555,7 +555,7 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic # ... -class EvAdventureMob(EvAdventureNPC): +class EvAdventureMob(EvAdventureNPC): combat_probabilities = { "hold": 0.0, @@ -567,7 +567,7 @@ At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tic # ... - def ai_combat(self): + def ai_combat(self): """ Manage the combat/combat state of the mob. diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Characters.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Characters.html index c53db355f2..c00fb45d29 100644 --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Characters.html +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Characters.html @@ -145,29 +145,29 @@ 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 (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, we could use a class inheritance like this:

      -
      from evennia import DefaultCharacter 
      +
      from evennia import DefaultCharacter 
       
      -class EvAdventureCharacter(DefaultCharacter): 
      +class EvAdventureCharacter(DefaultCharacter): 
           # stuff 
           
      -class EvAdventureNPC(EvAdventureCharacter):
      +class EvAdventureNPC(EvAdventureCharacter):
           # more stuff 
           
      -class EvAdventureMob(EvAdventureNPC): 
      +class EvAdventureMob(EvAdventureNPC): 
           # more stuff 
       

      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 simplified to use a Hit-Die (HD) concept. So while still character-like, NPCs should be separate from PCs like this:

      -
      from evennia import DefaultCharacter 
      +
      from evennia import DefaultCharacter 
       
      -class EvAdventureCharacter(DefaultCharacter): 
      +class EvAdventureCharacter(DefaultCharacter): 
           # stuff 
       
      -class EvAdventureNPC(DefaultCharacter):
      +class EvAdventureNPC(DefaultCharacter):
           # separate stuff 
           
      -class EvAdventureMob(EvadventureNPC):
      +class EvAdventureMob(EvadventureNPC):
           # more separate stuff
       
      @@ -181,18 +181,18 @@ RPG, it will hold everything relevant to that PC.

    24. All can get looted when defeated.

    25. 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:

      -
      from evennia import DefaultCharacter 
      +
      from evennia import DefaultCharacter 
       
      -class LivingMixin:
      +class LivingMixin:
           # stuff common for all living things
       
      -class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
      +class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
           # stuff 
       
      -class EvAdventureNPC(LivingMixin, DefaultCharacter):
      +class EvAdventureNPC(LivingMixin, DefaultCharacter):
           # stuff 
           
      -class EvAdventureMob(LivingMixin, EvadventureNPC):
      +class EvAdventureMob(LivingMixin, EvadventureNPC):
           # more stuff
       
      @@ -210,15 +210,15 @@ is an example of a character class structure.

      Let’s get some useful common methods all living things should have in our game.

      # in mygame/evadventure/characters.py 
       
      -from .rules import dice 
      +from .rules import dice 
       
      -class LivingMixin:
      +class LivingMixin:
       
           # makes it easy for mobs to know to attack PCs
           is_pc = False  
       
       	@property
      -    def hurt_level(self):
      +    def hurt_level(self):
               """
               String describing how hurt this character is.
               """
      @@ -240,7 +240,7 @@ is an example of a character class structure.

      elif percent == 0: return "|RCollapsed!|n" - def heal(self, hp): + def heal(self, hp): """ Heal hp amount of health, not allowing to exceed our max hp @@ -251,34 +251,34 @@ is an example of a character class structure.

      self.msg(f"You heal for {healed} HP.") - def at_pay(self, amount): + def at_pay(self, amount): """When paying coins, make sure to never detract more than we have""" amount = min(amount, self.coins) self.coins -= amount return amount - def at_attacked(self, attacker, **kwargs): + def at_attacked(self, attacker, **kwargs): """Called when being attacked and combat starts.""" pass - def at_damage(self, damage, attacker=None): + def at_damage(self, damage, attacker=None): """Called when attacked and taking damage.""" self.hp -= damage - def at_defeat(self): + def at_defeat(self): """Called when defeated. By default this means death.""" self.at_death() - def at_death(self): + def at_death(self): """Called when this thing dies.""" # this will mean different things for different living things pass - def at_do_loot(self, looted): + def at_do_loot(self, looted): """Called when looting another entity""" looted.at_looted(self) - def at_looted(self, looter): + def at_looted(self, looter): """Called when looted by another entity""" # default to stealing some coins @@ -296,14 +296,14 @@ is an example of a character class structure.

      We will now start making the basic Character class, based on what we need from Knave.

      # in mygame/evadventure/characters.py
       
      -from evennia import DefaultCharacter, AttributeProperty
      -from .rules import dice 
      +from evennia import DefaultCharacter, AttributeProperty
      +from .rules import dice 
       
      -class LivingMixin:
      +class LivingMixin:
           # ... 
       
       
      -class EvAdventureCharacter(LivingMixin, DefaultCharacter):
      +class EvAdventureCharacter(LivingMixin, DefaultCharacter):
           """ 
           A character to use for EvAdventure. 
           """
      @@ -323,7 +323,7 @@ is an example of a character class structure.

      xp = AttributeProperty(0) coins = AttributeProperty(0) - def at_defeat(self): + def at_defeat(self): """Characters roll on the death table""" if self.location.allow_death: # this allow rooms to have non-lethal battles @@ -334,7 +334,7 @@ is an example of a character class structure.

      from_obj=self) self.heal(self.hp_max) - def at_death(self): + def at_death(self): """We rolled 'dead' on the death table.""" self.location.msg_contents( "$You() collapse in a heap, embraced by death.", @@ -373,11 +373,11 @@ is an example of a character class structure.

      We make our first use of the rules.dice roller to roll on the death table! As you may recall, in the previous lesson, we didn’t know just what to do when rolling ‘dead’ on this table. Now we know - we should be calling at_death on the character. So let’s add that where we had TODOs before:

      # mygame/evadventure/rules.py 
       
      -class EvAdventureRollEngine:
      +class EvAdventureRollEngine:
           
           # ... 
       
      -    def roll_death(self, character): 
      +    def roll_death(self, character): 
               ability_name = self.roll_random_table("1d8", death_table)
       
               if ability_name == "dead":
      @@ -426,17 +426,17 @@ instead.

      For testing, we just need to create a new EvAdventure character and check that calling the methods on it doesn’t error out.

      # mygame/evadventure/tests/test_characters.py 
       
      -from evennia.utils import create
      -from evennia.utils.test_resources import BaseEvenniaTest 
      +from evennia.utils import create
      +from evennia.utils.test_resources import BaseEvenniaTest 
       
      -from ..characters import EvAdventureCharacter 
      +from ..characters import EvAdventureCharacter 
       
      -class TestCharacters(BaseEvenniaTest):
      -    def setUp(self):
      +class TestCharacters(BaseEvenniaTest):
      +    def setUp(self):
               super().setUp()
               self.character = create.create_object(EvAdventureCharacter, key="testchar")
       
      -    def test_heal(self):
      +    def test_heal(self):
               self.character.hp = 0 
               self.character.hp_max = 8 
               
      @@ -446,7 +446,7 @@ instead.

      self.character.heal(100) self.assertEqual(self.character.hp, 8) - def test_at_pay(self): + def test_at_pay(self): self.character.coins = 100 result = self.character.at_pay(60) @@ -474,10 +474,10 @@ instead.

      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:

      # mygame/evadventure/characters.py
       
      -from evennia import DefaultCharacter, AttributeProperty
      +from evennia import DefaultCharacter, AttributeProperty
       # ... 
       
      -class EvAdventureCharacter(LivingMixin, DefaultCharacter):
      +class EvAdventureCharacter(LivingMixin, DefaultCharacter):
           
           # ... 
       
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.html
      index 8e0fcf7a2a..40cf2c171f 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.html
      @@ -244,15 +244,15 @@ keep in here.

      ‘temporary character sheet’.

      # in mygame/evadventure/chargen.py
       
      -from .random_tables import chargen_tables
      -from .rules import dice
      +from .random_tables import chargen_tables
      +from .rules import dice
       
      -class TemporaryCharacterSheet:
      +class TemporaryCharacterSheet:
       
      -    def _random_ability(self):
      +    def _random_ability(self):
               return min(dice.roll("1d6"), dice.roll("1d6"), dice.roll("1d6"))
       
      -    def __init__(self):
      +    def __init__(self):
               self.ability_changes = 0  # how many times we tried swap abilities
       
               # name will likely be modified later
      @@ -336,11 +336,11 @@ Abilities once. We will use this to know if it has been done or not.

      {equipment} """ -class TemporaryCharacterSheet: +class TemporaryCharacterSheet: # ... - def show_sheet(self): + def show_sheet(self): equipment = ( str(item) for item in [self.armor, self.helmet, self.shield, self.weapon] + self.backpack @@ -371,16 +371,16 @@ This is a bit more involved.

      # ... -from .characters import EvAdventureCharacter -from evennia import create_object -from evennia.prototypes.spawner import spawn +from .characters import EvAdventureCharacter +from evennia import create_object +from evennia.prototypes.spawner import spawn -class TemporaryCharacterSheet: +class TemporaryCharacterSheet: # ... - def apply(self): + def apply(self): # create character object with given abilities new_character = create_object( EvAdventureCharacter, @@ -436,7 +436,7 @@ armor” etc.

      EvMenu.

      # in mygame/evadventure/chargen.py
       
      -from evennia import EvMenu
      +from evennia import EvMenu
       
       # ...
       
      @@ -445,7 +445,7 @@ armor” etc.

      # this goes to the bottom of the module -def start_chargen(caller, session=None): +def start_chargen(caller, session=None): """ This is a start point for spinning up the chargen from a command later. @@ -482,7 +482,7 @@ actions.

      # at the end of the module, but before the `start_chargen` function -def node_chargen(caller, raw_string, **kwargs): +def node_chargen(caller, raw_string, **kwargs): tmp_character = kwargs["tmp_character"] @@ -549,7 +549,7 @@ to each node, since that contains our temporary character sheet.

      # after previous node -def _update_name(caller, raw_string, **kwargs): +def _update_name(caller, raw_string, **kwargs): """ Used by node_change_name below to check what user entered and update the name if appropriate. @@ -562,7 +562,7 @@ to each node, since that contains our temporary character sheet.

      return "node_chargen", kwargs -def node_change_name(caller, raw_string, **kwargs): +def node_change_name(caller, raw_string, **kwargs): """ Change the random name of the character. @@ -620,7 +620,7 @@ were at.

      } -def _swap_abilities(caller, raw_string, **kwargs): +def _swap_abilities(caller, raw_string, **kwargs): """ Used by node_swap_abilities to parse the user's input and swap ability values. @@ -652,7 +652,7 @@ were at.

      return "node_chargen", kwargs -def node_swap_abilities(caller, raw_string, **kwargs): +def node_swap_abilities(caller, raw_string, **kwargs): """ One is allowed to swap the values of two abilities around, once. @@ -713,7 +713,7 @@ we use caller.msg()

      6.9. Tying the nodes together

      -
      def start_chargen(caller, session=None):
      +
      def start_chargen(caller, session=None):
           """
           This is a start point for spinning up the chargen from a command later.
       
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.html
      index 0622b2416d..01586b5d06 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.html
      @@ -163,15 +163,15 @@
       

      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.

      # in evadventure/combat_base.py 
       
      -from evennia import DefaultScript
      +from evennia import DefaultScript
       
       
      -class CombatFailure(RuntimeError):
      +class CombatFailure(RuntimeError):
       	"""If some error happens in combat"""
           pass
       
       
      -class EvAdventureCombatBaseHandler(DefaultSCript): 
      +class EvAdventureCombatBaseHandler(DefaultSCript): 
           """ 
       	This should be created when combat starts. It 'ticks' the combat 
       	and tracks all sides of it.
      @@ -183,18 +183,18 @@
           fallback_action_dict = {}
       
           @classmethod 
      -    def get_or_create_combathandler(cls, obj, **kwargs): 
      +    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): 
      +    def msg(self, message, combatant=None, broadcast=True, location=True): 
               """ 
               Send a message to all combatants.
       		
               """
               pass  # TODO
            
      -    def get_combat_summary(self, combatant):
      +    def get_combat_summary(self, combatant):
               """ 
               Get a nicely formatted 'battle report' of combat, from the 
               perspective of the combatant.
      @@ -204,7 +204,7 @@
       
       	# implemented differently by Twitch- and Turnbased combat
       
      -    def get_sides(self, combatant):
      +    def get_sides(self, combatant):
               """ 
               Get who's still alive on the two sides of combat, as a 
               tuple `([allies], [enemies])` from the perspective of `combatant` 
      @@ -213,35 +213,35 @@
               """
               raise NotImplementedError 
       
      -    def give_advantage(self, recipient, target): 
      +    def give_advantage(self, recipient, target): 
               """ 
               Give advantage to recipient against target.
               
               """
               raise NotImplementedError 
       
      -    def give_disadvantage(self, recipient, target): 
      +    def give_disadvantage(self, recipient, target): 
               """
               Give disadvantage to recipient against target. 
       
               """
               raise NotImplementedError
       
      -    def has_advantage(self, combatant, target): 
      +    def has_advantage(self, combatant, target): 
               """ 
               Does combatant have advantage against target?
               
               """ 
               raise NotImplementedError 
       
      -    def has_disadvantage(self, combatant, target): 
      +    def has_disadvantage(self, combatant, target): 
               """ 
               Does combatant have disadvantage against target?
               
               """ 
               raise NotImplementedError
       
      -    def queue_action(self, combatant, action_dict):
      +    def queue_action(self, combatant, action_dict):
               """ 
               Queue an action for the combatant by providing 
               action dict.
      @@ -249,28 +249,28 @@
               """ 
               raise NotImplementedError
       
      -    def execute_next_action(self, combatant): 
      +    def execute_next_action(self, combatant): 
               """ 
               Perform a combatant's next action.
               
               """ 
               raise NotImplementedError
       
      -    def start_combat(self): 
      +    def start_combat(self): 
               """ 
               Start combat.
               
           	""" 
           	raise NotImplementedError
           
      -    def check_stop_combat(self): 
      +    def check_stop_combat(self): 
               """
               Check if the combat is over and if it should be stopped.
                
               """
               raise NotImplementedError 
               
      -    def stop_combat(self): 
      +    def stop_combat(self): 
               """ 
               Stop combat and do cleanup.
               
      @@ -288,16 +288,16 @@
       

      We expect to create the script “on” an object (which one we don’t know yet, but we expect it to be a typeclassed entity).

      # in evadventure/combat_base.py
       
      -from evennia import create_script
      +from evennia import create_script
       
       # ... 
       
      -class EvAdventureCombatBaseHandler(DefaultScript): 
      +class EvAdventureCombatBaseHandler(DefaultScript): 
       
           # ... 
       
           @classmethod
      -    def get_or_create_combathandler(cls, obj, **kwargs):
      +    def get_or_create_combathandler(cls, obj, **kwargs):
               """
               Get or create a combathandler on `obj`.
           
      @@ -351,10 +351,10 @@
       
       # ... 
       
      -class EvAdventureCombatBaseHandler(DefaultScript): 
      +class EvAdventureCombatBaseHandler(DefaultScript): 
       	# ... 
       
      -	def msg(self, message, combatant=None, broadcast=True, location=None):
      +	def msg(self, message, combatant=None, broadcast=True, location=None):
               """
               Central place for sending messages to combatants. This allows
               for adding any combat-specific text-decoration in one place.
      @@ -483,15 +483,15 @@
       
       # ...
       
      -from evennia import EvTable
      +from evennia import EvTable
       
       # ... 
       
      -class EvAdventureCombatBaseHandler(DefaultScript):
      +class EvAdventureCombatBaseHandler(DefaultScript):
       
       	# ... 
       
      -	def get_combat_summary(self, combatant):
      +	def get_combat_summary(self, combatant):
       
               allies, enemies = self.get_sides(combatant)
               nallies, nenemies = len(allies), len(enemies)
      @@ -619,9 +619,9 @@
       

      Once our action_dict identifies the particular action we should use, we need something that reads those keys/values and actually performs the action.

      # in evadventure/combat_base.py 
       
      -class CombatAction: 
      +class CombatAction: 
       
      -    def __init__(self, combathandler, combatant, action_dict):
      +    def __init__(self, combathandler, combatant, action_dict):
               self.combathandler = combathandler
               self.combatant = combatant
       
      @@ -634,23 +634,23 @@
       

      The setattr Python standard function assigns the keys/values of the action_dict to be properties “on” this action. This is very convenient to use in other methods. So for the stunt action, other methods could just access self.key, self.recipient, self.target and so on directly.

      # in evadventure/combat_base.py 
       
      -class CombatAction: 
      +class CombatAction: 
       
           # ... 
       
      -    def msg(self, message, broadcast=True):
      +    def msg(self, message, broadcast=True):
               "Send message to others in combat"
               self.combathandler.msg(message, combatant=self.combatant, broadcast=broadcast)
       
      -    def can_use(self): 
      +    def can_use(self): 
              """Return False if combatant can's use this action right now""" 
               return True 
       
      -    def execute(self): 
      +    def execute(self): 
               """Does the actional action"""
               pass
       
      -    def post_execute(self):
      +    def post_execute(self):
               """Called after `execute`"""
               pass 
       
      @@ -668,7 +668,7 @@ # ... -class CombatActionHold(CombatAction): +class CombatActionHold(CombatAction): """ Action that does nothing @@ -687,7 +687,7 @@ # ... -class CombatActionAttack(CombatAction): +class CombatActionAttack(CombatAction): """ A regular attack, using a wielded weapon. @@ -698,7 +698,7 @@ """ - def execute(self): + def execute(self): attacker = self.combatant weapon = attacker.weapon target = self.target @@ -718,7 +718,7 @@ # ... -class CombatActionStunt(CombatAction): +class CombatActionStunt(CombatAction): """ Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a target. Whenever performing a stunt that would affect another negatively (giving them @@ -737,7 +737,7 @@ """ - def execute(self): + def execute(self): combathandler = self.combathandler attacker = self.combatant recipient = self.recipient # the one to receive the effect of the stunt @@ -800,7 +800,7 @@ # ... -class CombatActionUseItem(CombatAction): +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. @@ -812,7 +812,7 @@ """ - def execute(self): + def execute(self): item = self.item user = self.combatant target = self.target @@ -835,7 +835,7 @@ # ... -class CombatActionWield(CombatAction): +class CombatActionWield(CombatAction): """ Wield a new weapon (or spell) from your inventory. This will swap out the one you are currently wielding, if any. @@ -847,7 +847,7 @@ """ - def execute(self): + def execute(self): self.combatant.equipment.move(self.item)
      @@ -903,18 +903,18 @@ 36 37
      # in evadventure/tests/test_combat.py 
       
      -from unittest.mock import Mock
      +from unittest.mock import Mock
       
      -from evennia.utils.test_resources import EvenniaTestCase
      -from evennia import create_object
      -from .. import combat_base
      -from ..rooms import EvAdventureRoom
      -from ..characters import EvAdventureCharacter
      +from evennia.utils.test_resources import EvenniaTestCase
      +from evennia import create_object
      +from .. import combat_base
      +from ..rooms import EvAdventureRoom
      +from ..characters import EvAdventureCharacter
       
       
      -class TestEvAdventureCombatBaseHandler(EvenniaTestCase):
      +class TestEvAdventureCombatBaseHandler(EvenniaTestCase):
       
      -    def setUp(self): 
      +    def setUp(self): 
       
       		self.location = create_object(EvAdventureRoom, key="testroom")
       		self.combatant = create_object(EvAdventureCharacter, key="testchar")
      @@ -922,7 +922,7 @@
       
               self.combathandler = combat_base.get_combat_summary(self.location)
       
      -    def test_get_combat_summary(self):
      +    def test_get_combat_summary(self):
       
               # do the test from perspective of combatant
       	    self.combathandler.get_sides = Mock(return_value=([], [self.target]))
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Turnbased.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Turnbased.html
      index e188d83055..9dbafa6000 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Turnbased.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Turnbased.html
      @@ -244,7 +244,7 @@ You use Potion 
       
      # in evadventure/combat_turnbased.py
       
      -from .combat_base import (
      +from .combat_base import (
          CombatActionAttack,
          CombatActionHold,
          CombatActionStunt,
      @@ -253,9 +253,9 @@ You use Potion EvAdventureCombatBaseHandler,
       )
       
      -from .combat_base import EvAdventureCombatBaseHandler
      +from .combat_base import EvAdventureCombatBaseHandler
       
      -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
      +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
       
           action_classes = {
               "hold": CombatActionHold,
      @@ -301,11 +301,11 @@ You use Potion # ...
       
      -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
      +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
       
       	# ...
       
      -    def get_sides(self, combatant):
      +    def get_sides(self, combatant):
                  """
                  Get a listing of the two 'sides' of this combat,
                  m the perspective of the provided combatant.
      @@ -338,22 +338,22 @@ You use Potion # ...
       
      -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
      +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
       
       	# ...
       
      -    def give_advantage(self, combatant, target):
      +    def give_advantage(self, combatant, target):
               self.advantage_matrix[combatant][target] = True
       
      -    def give_disadvantage(self, combatant, target, **kwargs):
      +    def give_disadvantage(self, combatant, target, **kwargs):
               self.disadvantage_matrix[combatant][target] = True
       
      -    def has_advantage(self, combatant, target, **kwargs):
      +    def has_advantage(self, combatant, target, **kwargs):
               return (
       	        target in self.fleeing_combatants
       	        or bool(self.advantage_matrix[combatant].pop(target, False))
               )
      -    def has_disadvantage(self, combatant, target):
      +    def has_disadvantage(self, combatant, target):
               return bool(self.disadvantage_matrix[combatant].pop(target, False))
       
      @@ -373,11 +373,11 @@ This is new compared to the base handler.

      # ... -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): # ... - def add_combatant(self, combatant): + def add_combatant(self, combatant): """ Add a new combatant to the battle. Can be called multiple times safely. """ @@ -386,7 +386,7 @@ This is new compared to the base handler.

      return True return False - def remove_combatant(self, combatant): + def remove_combatant(self, combatant): """ Remove a combatant from the battle. """ @@ -403,11 +403,11 @@ This is new compared to the base handler.

      Since you can’t just move away from the room to flee turnbased combat, we need to add a new CombatAction subclass like the ones we created in the base combat lesson.

      # in evadventure/combat_turnbased.py
       
      -from .combat_base import CombatAction
      +from .combat_base import CombatAction
       
       # ...
       
      -class CombatActionFlee(CombatAction):
      +class CombatActionFlee(CombatAction):
           """
           Start (or continue) fleeing/disengaging from combat.
       
      @@ -416,7 +416,7 @@ This is new compared to the base handler.

      } """ - def execute(self): + def execute(self): combathandler = self.combathandler if self.combatant not in combathandler.fleeing_combatants: @@ -436,7 +436,7 @@ This is new compared to the base handler.

      ) -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): action_classes = { "hold": CombatActionHold, @@ -459,11 +459,11 @@ This is new compared to the base handler.

      # ... -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): # ... - def queue_action(self, combatant, action_dict): + def queue_action(self, combatant, action_dict): self.combatants[combatant] = action_dict # track who inserted actions this turn (non-persistent) @@ -533,15 +533,15 @@ This is new compared to the base handler.

      51 52
      # in evadventure/combat_turnbased.py
       
      -import random
      +import random
       
       # ...
       
      -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
      +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
       
           # ...
       
      -    def execute_next_action(self, combatant):
      +    def execute_next_action(self, combatant):
               # this gets the next dict and rotates the queue
               action_dict = self.combatants.get(combatant, self.fallback_action_dict)
       
      @@ -564,7 +564,7 @@ This is new compared to the base handler.

      self.combatants[combatant] = self.fallback_action_dict - def at_repeat(self): + def at_repeat(self): """ This method is called every time Script repeats (every `interval` seconds). Performs a full turn of @@ -673,16 +673,16 @@ This is new compared to the base handler.

      70 71
      # in evadventure/combat_turnbased.py
       
      -import random
      -from evennia.utils.utils import list_to_string
      +import random
      +from evennia.utils.utils import list_to_string
       
       # ...
       
      -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
      +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
       
           # ...
       
      -     def stop_combat(self):
      +     def stop_combat(self):
               """
               Stop the combat immediately.
       
      @@ -692,7 +692,7 @@ This is new compared to the base handler.

      self.stop() self.delete() - def check_stop_combat(self): + def check_stop_combat(self): """Check if it's time to stop combat""" # check if anyone is defeated @@ -756,17 +756,17 @@ This is new compared to the base handler.

      11.2.8. Start combat

      Since we are using the timer-component of the Script to tick our combat, we also need a helper method to ‘start’ that.

      -
      from evennia.utils.utils import list_to_string
      +
      from evennia.utils.utils import list_to_string
       
       # in evadventure/combat_turnbased.py
       
       # ...
       
      -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
      +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
       
           # ...
       
      -    def start_combat(self, **kwargs):
      +    def start_combat(self, **kwargs):
               """
               This actually starts the combat. It's safe to run this multiple times
               since it will only start combat if it isn't already running.
      @@ -784,7 +784,7 @@ This is new compared to the base handler.

      11.3. Using EvMenu for the combat menu

      The EvMenu used to create in-game menues in Evennia. We used a simple EvMenu already in the Character Generation Lesson. This time we’ll need to be a bit more advanced. While The EvMenu documentation describe its functionality in more detail, we will give a quick overview of how it works here.

      An EvMenu is made up of nodes, which are regular functions on this form (somewhat simplified here, there are more options):

      -
      def node_somenodename(caller, raw_string, **kwargs):
      +
      def node_somenodename(caller, raw_string, **kwargs):
       
           text = "some text to show in the node"
           options = [
      @@ -814,7 +814,7 @@ This is new compared to the base handler.

      Each option dict has a key "goto" that determines which node the player should jump to if they choose that option. Inside the menu, each node needs to be referenced with these names (like "start", "node1" etc).

      The "goto" value of each option can either specify the name directly (like "node1") or it can be given as a tuple (callable, {keywords}). This callable is called and is expected to in turn return the next node-name to use (like "node1").

      The callable (often called a “goto callable”) looks very similar to a node function:

      -
      def _goto_when_choosing_option1(caller, raw_string, **kwargs):
      +
      def _goto_when_choosing_option1(caller, raw_string, **kwargs):
           # do whatever is needed to determine the next node
           return nodename  # also nodename, dict works
       
      @@ -827,7 +827,7 @@ This is new compared to the base handler.

      The goto-callable must return the name of the next node. Optionally, you can return both nodename, {kwargs}. If you do the next node will get those kwargs as ingoing **kwargs. This way you can pass information from one node to the next. A special feature is that if nodename is returned as None, then the current node will be rerun again.

      Here’s a (somewhat contrived) example of how the goto-callable and node-function hang together:

      # goto-callable
      -def _my_goto_callable(caller, raw_string, **kwargs):
      +def _my_goto_callable(caller, raw_string, **kwargs):
           info_number = kwargs["info_number"]
           if info_number > 0:
               return "node1"
      @@ -836,7 +836,7 @@ This is new compared to the base handler.

      # node function -def node_somenodename(caller, raw_string, **kwargs): +def node_somenodename(caller, raw_string, **kwargs): text = "Some node text" options = [ { @@ -942,11 +942,11 @@ This is new compared to the base handler.

      11.4.2. Getting or setting the combathandler

      # in evadventure/combat_turnbased.py
       
      -from evennia import EvMenu
      +from evennia import EvMenu
       
       # ...
       
      -def _get_combathandler(caller, turn_timeout=30, flee_time=3, combathandler_key="combathandler"):
      +def _get_combathandler(caller, turn_timeout=30, flee_time=3, combathandler_key="combathandler"):
           return EvAdventureTurnbasedCombatHandler.get_or_create_combathandler(
               caller.location,
               interval=turn_timeout,
      @@ -964,7 +964,7 @@ This is new compared to the base handler.

      # ... -def _queue_action(caller, raw_string, **kwargs): +def _queue_action(caller, raw_string, **kwargs): action_dict = kwargs["action_dict"] _get_combathandler(caller).queue_action(caller, action_dict) return "node_combat" @@ -980,7 +980,7 @@ This is new compared to the base handler.

      # ... -def _rerun_current_node(caller, raw_string, **kwargs): +def _rerun_current_node(caller, raw_string, **kwargs): return None, kwargs
      @@ -1009,7 +1009,7 @@ This is new compared to the base handler.

      Furthermore, we want the ability to go “back” to the previous node like this:

      # in some other node (shown only as an example)
       
      -def some_node(caller, raw_string, **kwargs):
      +def some_node(caller, raw_string, **kwargs):
       
           # ...
       
      @@ -1030,7 +1030,7 @@ This is new compared to the base handler.

      # ... -def _step_wizard(caller, raw_string, **kwargs): +def _step_wizard(caller, raw_string, **kwargs): # get the steps and count them steps = kwargs.get("steps", []) @@ -1132,7 +1132,7 @@ This is new compared to the base handler.

      # ... -def node_choose_enemy_target(caller, raw_string, **kwargs): +def node_choose_enemy_target(caller, raw_string, **kwargs): text = "Choose an enemy to target" @@ -1154,15 +1154,15 @@ This is new compared to the base handler.

      return text, options -def node_choose_enemy_recipient(caller, raw_string, **kwargs): +def node_choose_enemy_recipient(caller, raw_string, **kwargs): # almost the same, except storing "recipient" -def node_choose_allied_target(caller, raw_string, **kwargs): +def node_choose_allied_target(caller, raw_string, **kwargs): # almost the same, except using allies + yourself -def node_choose_allied_recipient(caller, raw_string, **kwargs): +def node_choose_allied_recipient(caller, raw_string, **kwargs): # almost the same, except using allies + yourself and storing "recipient"
      @@ -1189,11 +1189,11 @@ This is new compared to the base handler.

      For Stunts, we need to be able to select which Knave Ability (STR, DEX etc) you want to boost/foil.

      # in evadventure/combat_turnbased.py
       
      -from .enums import Ability
      +from .enums import Ability
       
       # ...
       
      -def node_choose_ability(caller, raw_string, **kwargs):
      +def node_choose_ability(caller, raw_string, **kwargs):
           text = "Choose the ability to apply"
           action_dict = kwargs["action_dict"]
       
      @@ -1233,7 +1233,7 @@ This is new compared to the base handler.

      # ... -def node_choose_use_item(caller, raw_string, **kwargs): +def node_choose_use_item(caller, raw_string, **kwargs): text = "Select the item" action_dict = kwargs["action_dict"] @@ -1254,7 +1254,7 @@ This is new compared to the base handler.

      return text, options -def node_choose_wield_item(caller, raw_string, **kwargs): +def node_choose_wield_item(caller, raw_string, **kwargs): # same except using caller.equipment.get_wieldable_objects_from_backpack()
      @@ -1269,7 +1269,7 @@ This is new compared to the base handler.

      # ... -def node_combat(caller, raw_string, **kwargs): +def node_combat(caller, raw_string, **kwargs): """Base combat menu""" combathandler = _get_combathandler(caller) @@ -1370,11 +1370,11 @@ This is new compared to the base handler.

      We will only need one single Command to run the Turnbased combat system. This is the attack command. Once you use it once, you will be in the menu.

      # in evadventure/combat_turnbased.py
       
      -from evennia import Command, CmdSet, EvMenu
      +from evennia import Command, CmdSet, EvMenu
       
       # ...
       
      -class CmdTurnAttack(Command):
      +class CmdTurnAttack(Command):
           """
           Start or join combat.
       
      @@ -1389,11 +1389,11 @@ This is new compared to the base handler.

      turn_timeout = 30 # seconds flee_time = 3 # rounds - def parse(self): + def parse(self): super().parse() self.args = self.args.strip() - def func(self): + def func(self): if not self.args: self.msg("What are you attacking?") return @@ -1445,12 +1445,12 @@ This is new compared to the base handler.

      ) -class TurnCombatCmdSet(CmdSet): +class TurnCombatCmdSet(CmdSet): """ CmdSet for the turn-based combat. """ - def at_cmdset_creation(self): + def at_cmdset_creation(self): self.add(CmdTurnAttack())
      @@ -1467,10 +1467,10 @@ This is new compared to the base handler.

      # ... -class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): +class EvadventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): # ... - def remove_combatant(self, combatant): + def remove_combatant(self, combatant): """ Remove a combatant from the battle. """ @@ -1526,11 +1526,11 @@ This is new compared to the base handler.

      # HEADER -from evennia import DefaultExit, create_object, search_object -from evennia.contrib.tutorials.evadventure.characters import EvAdventureCharacter -from evennia.contrib.tutorials.evadventure.combat_turnbased import TurnCombatCmdSet -from evennia.contrib.tutorials.evadventure.npcs import EvAdventureNPC -from evennia.contrib.tutorials.evadventure.rooms import EvAdventureRoom +from evennia import DefaultExit, create_object, search_object +from evennia.contrib.tutorials.evadventure.characters import EvAdventureCharacter +from evennia.contrib.tutorials.evadventure.combat_turnbased import TurnCombatCmdSet +from evennia.contrib.tutorials.evadventure.npcs import EvAdventureNPC +from evennia.contrib.tutorials.evadventure.rooms import EvAdventureRoom # CODE diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Twitch.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Twitch.html index 1f1570195a..1cad074905 100644 --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Twitch.html +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Twitch.html @@ -223,7 +223,7 @@ The battle is We will make use of the Combat Actions, Action dicts and the parent EvAdventureCombatBaseHandler we created previously.

      # in evadventure/combat_twitch.py
       
      -from .combat_base import (
      +from .combat_base import (
          CombatActionAttack,
          CombatActionHold,
          CombatActionStunt,
      @@ -232,9 +232,9 @@ The battle is EvAdventureCombatBaseHandler,
       )
       
      -from .combat_base import EvAdventureCombatBaseHandler
      +from .combat_base import EvAdventureCombatBaseHandler
       
      -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
      +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
           """
           This is created on the combatant when combat starts. It tracks only 
           the combatant's side of the combat and handles when the next action 
      @@ -242,7 +242,7 @@ The battle is  
           """
        
      -    def msg(self, message, broadcast=True):
      +    def msg(self, message, broadcast=True):
               """See EvAdventureCombatBaseHandler.msg"""
               super().msg(message, combatant=self.obj, 
                           broadcast=broadcast, location=self.obj.location)
      @@ -253,15 +253,15 @@ The battle is 10.2.1. Getting the sides of combat
       
      # in evadventure/combat_twitch.py 
       
      -from evennia.utils import inherits_from
      +from evennia.utils import inherits_from
       
       # ...
       
      -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
      +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
       
           # ... 
       
      -    def get_sides(self, combatant):
      +    def get_sides(self, combatant):
                """
                Get a listing of the two 'sides' of this combat, from the 
                perspective of the provided combatant. The sides don't need 
      @@ -320,30 +320,30 @@ The battle is 10.2.2. Tracking Advantage / Disadvantage
       
      # in evadventure/combat_twitch.py 
       
      -from evennia import AttributeProperty
      +from evennia import AttributeProperty
       
       # ... 
       
      -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
      +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
       
           self.advantage_against = AttributeProperty(dict) 
           self.disadvantage_against = AttributeProperty(dict)
       
           # ... 
       
      -    def give_advantage(self, recipient, target):
      +    def give_advantage(self, recipient, target):
               """Let a recipient gain advantage against the target."""
               self.advantage_against[target] = True
       
      -    def give_disadvantage(self, recipient, target):
      +    def give_disadvantage(self, recipient, target):
               """Let an affected party gain disadvantage against a target."""
               self.disadvantage_against[target] = True
       
      -    def has_advantage(self, combatant, target):
      +    def has_advantage(self, combatant, target):
               """Check if the combatant has advantage against a target."""
               return self.advantage_against.get(target, False)
       
      -    def has_disadvantage(self, combatant, target):
      +    def has_disadvantage(self, combatant, target):
               """Check if the combatant has disadvantage against a target."""
               return self.disadvantage_against.get(target, False)1
       
      @@ -415,8 +415,8 @@ a given combatant has advantage.

      57 58
      # in evadventure/combat_twitch.py 
       
      -from evennia.utils import repeat, unrepeat
      -from .combat_base import (
      +from evennia.utils import repeat, unrepeat
      +from .combat_base import (
           CombatActionAttack,
           CombatActionHold,
           CombatActionStunt,
      @@ -427,7 +427,7 @@ a given combatant has advantage.

      # ... -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler): +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler): action_classes = { "hold": CombatActionHold, @@ -442,7 +442,7 @@ a given combatant has advantage.

      # ... - def queue_action(self, action_dict, combatant=None): + def queue_action(self, action_dict, combatant=None): """ Schedule the next action to fire. @@ -514,13 +514,13 @@ a given combatant has advantage.

      26 27
      # in evadventure/combat_twitch.py
       
      -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
      +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
       
           fallback_action_dict = AttributeProperty({"key": "hold", "dt": 0})
       
           # ... 
       
      -    def execute_next_action(self):
      +    def execute_next_action(self):
                   """
                   Triggered after a delay by the command
                   """
      @@ -596,11 +596,11 @@ a given combatant has advantage.

      31 32
      # in evadventure/combat_twitch.py 
       
      -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
      +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
       
           # ... 
       
      -    def check_stop_combat(self):
      +    def check_stop_combat(self):
               """
               Check if the combat is over.
               """
      @@ -624,7 +624,7 @@ a given combatant has advantage.

      self.msg("The combat is over. You won!", broadcast=False) self.stop_combat() - def stop_combat(self): + def stop_combat(self): pass # We'll finish this last
      @@ -711,20 +711,20 @@ a given combatant has advantage.

      60 61
      # in evadventure/combat_twitch.py
       
      -from evennia import Command
      -from evennia import InterruptCommand 
      +from evennia import Command
      +from evennia import InterruptCommand 
       
       # ... 
       
       # after the combat handler class
       
      -class _BaseTwitchCombatCommand(Command):
      +class _BaseTwitchCombatCommand(Command):
           """
           Parent class for all twitch-combat commnads.
       
           """
       
      -    def at_pre_command(self):
      +    def at_pre_command(self):
               """
               Called before parsing.
       
      @@ -733,7 +733,7 @@ a given combatant has advantage.

      self.msg("Can't fight here!") raise InterruptCommand() - def parse(self): + def parse(self): """ Handle parsing of most supported combat syntaxes (except stunts). @@ -757,7 +757,7 @@ a given combatant has advantage.

      rhs = " ".join(rhs) self.lhs, self.rhs = lhs.strip(), rhs.strip() - def get_or_create_combathandler(self, target=None, combathandler_name="combathandler"): + def get_or_create_combathandler(self, target=None, combathandler_name="combathandler"): """ Get or create the combathandler assigned to this combatant. @@ -781,13 +781,13 @@ a given combatant has advantage.

      10.3.2. In-combat look command

      # in evadventure/combat_twitch.py 
       
      -from evennia import default_cmds
      -from evennia.utils import pad
      +from evennia import default_cmds
      +from evennia.utils import pad
       
       # ...
       
      -class CmdLook(default_cmds.CmdLook, _BaseTwitchCombatCommand):
      -    def func(self):
      +class CmdLook(default_cmds.CmdLook, _BaseTwitchCombatCommand):
      +    def func(self):
               # get regular look, followed by a combat summary
               super().func()
               if not self.args:
      @@ -807,7 +807,7 @@ You (Wounded)
       

      10.3.3. Hold command

      -
      class CmdHold(_BaseTwitchCombatCommand):
      +
      class CmdHold(_BaseTwitchCombatCommand):
           """
           Hold back your blows, doing nothing.
       
      @@ -818,7 +818,7 @@ You (Wounded)key = "hold"
       
      -    def func(self):
      +    def func(self):
               combathandler = self.get_or_create_combathandler()
               combathandler.queue_action({"key": "hold"})
               combathandler.msg("$You() $conj(hold) back, doing nothing.", self.caller)
      @@ -837,7 +837,7 @@ You (Wounded)# ... 
       
      -class CmdAttack(_BaseTwitchCombatCommand):
      +class CmdAttack(_BaseTwitchCombatCommand):
           """
           Attack a target. Will keep attacking the target until
           combat ends or another combat action is taken.
      @@ -851,7 +851,7 @@ You (Wounded)aliases = ["hit"]
           help_category = "combat"
       
      -    def func(self):
      +    def func(self):
               target = self.caller.search(self.lhs)
               if not target:
                   return
      @@ -869,11 +869,11 @@ You (Wounded)The attack command becomes quite simple because we do all the heavy lifting in the combathandler and in the ActionAttack class. Note that we set dt to a fixed 3 here, but in a more complex system one could imagine your skills, weapon and circumstance affecting how long your attack will take.

      # in evadventure/combat_twitch.py 
       
      -from .enums import ABILITY_REVERSE_MAP
      +from .enums import ABILITY_REVERSE_MAP
       
       # ... 
       
      -class CmdStunt(_BaseTwitchCombatCommand):
      +class CmdStunt(_BaseTwitchCombatCommand):
           """
           Perform a combat stunt, that boosts an ally against a target, or
           foils an enemy, giving them disadvantage against an ally.
      @@ -900,7 +900,7 @@ You (Wounded))
           help_category = "combat"
       
      -    def parse(self):
      +    def parse(self):
               args = self.args
       
               if not args or " " not in args:
      @@ -951,7 +951,7 @@ You (Wounded)self.recipient = recipient.strip()
               self.target = target.strip()
       
      -    def func(self):
      +    def func(self):
               target = self.caller.search(self.target)
               if not target:
                   return
      @@ -986,7 +986,7 @@ You (Wounded)# ... 
       
      -class CmdUseItem(_BaseTwitchCombatCommand):
      +class CmdUseItem(_BaseTwitchCombatCommand):
           """
           Use an item in combat. The item must be in your inventory to use.
       
      @@ -1004,7 +1004,7 @@ You (Wounded)key = "use"
           help_category = "combat"
       
      -    def parse(self):
      +    def parse(self):
               super().parse()
       
               if not self.args:
      @@ -1014,7 +1014,7 @@ You (Wounded)self.item = self.lhs
               self.target = self.rhs or "me"
       
      -    def func(self):
      +    def func(self):
               item = self.caller.search(
                   self.item,
                   candidates=self.caller.equipment.get_usable_objects_from_backpack()
      @@ -1047,7 +1047,7 @@ You (Wounded)# ... 
       
      -class CmdWield(_BaseTwitchCombatCommand):
      +class CmdWield(_BaseTwitchCombatCommand):
           """
           Wield a weapon or spell-rune. You will the wield the item, 
               swapping with any other item(s) you were wielded before.
      @@ -1069,13 +1069,13 @@ You (Wounded)key = "wield"
           help_category = "combat"
       
      -    def parse(self):
      +    def parse(self):
               if not self.args:
                   self.msg("What do you want to wield?")
                   raise InterruptCommand()
               super().parse()
       
      -    def func(self):
      +    def func(self):
               item = self.caller.search(
                   self.args, candidates=self.caller.equipment.get_wieldable_objects_from_backpack()
               )
      @@ -1096,18 +1096,18 @@ You (Wounded)To make these commands available to use we must add them to a Command Set.

      # in evadventure/combat_twitch.py 
       
      -from evennia import CmdSet
      +from evennia import CmdSet
       
       # ... 
       
       # after the commands 
       
      -class TwitchCombatCmdSet(CmdSet):
      +class TwitchCombatCmdSet(CmdSet):
           """
           Add to character, to be able to attack others in a twitch-style way.
           """
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               self.add(CmdAttack())
               self.add(CmdHold())
               self.add(CmdStunt())
      @@ -1115,12 +1115,12 @@ You (Wounded)self.add(CmdWield())
       
       
      -class TwitchLookCmdSet(CmdSet):
      +class TwitchLookCmdSet(CmdSet):
           """
           This will be added/removed dynamically when in combat.
           """
       
      -    def at_cmdset_creation(self):
      +    def at_cmdset_creation(self):
               self.add(CmdLook())
       
       
      @@ -1150,14 +1150,14 @@ You (Wounded)# ... 
       
      -class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
      +class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
       
           # ... 
       
      -    def at_init(self): 
      +    def at_init(self): 
               self.obj.cmdset.add(TwitchLookCmdSet, persistent=False)
       
      -    def stop_combat(self): 
      +    def stop_combat(self): 
               self.queue_action({"key": "hold", "dt": 0})  # make sure ticker is killed
               del self.obj.ndb.combathandler
               self.obj.cmdset.remove(TwitchLookCmdSet)
      @@ -1186,16 +1186,16 @@ You (Wounded)Here’s an example:

      # in evadventure/tests/test_combat.py 
       
      -from unittest.mock import Mock, patch
      -from evennia.utils.test_resources import EvenniaCommandTestMixin
      +from unittest.mock import Mock, patch
      +from evennia.utils.test_resources import EvenniaCommandTestMixin
       
      -from .. import combat_twitch
      +from .. import combat_twitch
       
       # ...
       
      -class TestEvAdventureTwitchCombat(EvenniaCommandTestMixin)
      +class TestEvAdventureTwitchCombat(EvenniaCommandTestMixin)
       
      -    def setUp(self): 
      +    def setUp(self): 
               self.combathandler = (
                       combat_twitch.EvAdventureCombatTwitchHandler.get_or_create_combathandler(
                   self.char1, key="combathandler") 
      @@ -1203,7 +1203,7 @@ You (Wounded)@patch("evadventure.combat_twitch.unrepeat", new=Mock())
           @patch("evadventure.combat_twitch.repeat", new=Mock())
      -    def test_hold_command(self): 
      +    def test_hold_command(self): 
               self.call(combat_twitch, CmdHold(), "", "You hold back, doing nothing")
               self.assertEqual(self.combathandler.action_dict, {"key": "hold"})
                   
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html
      index 032e51ba9f..4cd8633e7e 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html
      @@ -417,11 +417,11 @@ We will expand on this for dungeon rooms.

      45 46
      # in evadventure/dungeon.py 
       
      -from evennia import AttributeProperty
      -from .rooms import EvAdventureRoom 
      +from evennia import AttributeProperty
      +from .rooms import EvAdventureRoom 
       
       
      -class EvAdventureDungeonRoom(EvAdventureRoom):
      +class EvAdventureDungeonRoom(EvAdventureRoom):
           """
           Dangerous dungeon room.
       
      @@ -434,7 +434,7 @@ We will expand on this for dungeon rooms.

      dungeon_branch = AttributeProperty(None, autocreate=False) xy_coords = AttributeProperty(None, autocreate=False) - def at_object_creation(self): + def at_object_creation(self): """ Set the `not_clear` tag on the room. This is removed when the room is 'cleared', whatever that means for each room. @@ -445,14 +445,14 @@ We will expand on this for dungeon rooms.

      """ self.tags.add("not_clear", category="dungeon_room") - def clear_room(self): + def clear_room(self): self.tags.remove("not_clear", category="dungeon_room") @property - def is_room_clear(self): + def is_room_clear(self): return not bool(self.tags.get("not_clear", category="dungeon_room")) - def get_display_footer(self, looker, **kwargs): + def get_display_footer(self, looker, **kwargs): """ Show if the room is 'cleared' or not as part of its description. @@ -487,27 +487,27 @@ We will expand on this for dungeon rooms.

      # ... -from evennia import DefaultExit +from evennia import DefaultExit # ... -class EvAdventureDungeonExit(DefaultExit): +class EvAdventureDungeonExit(DefaultExit): """ Dungeon exit. This will not create the target room until it's traversed. """ - def at_object_creation(self): + def at_object_creation(self): """ We want to block progressing forward unless the room is clear. """ self.locks.add("traverse:not objloctag(not_clear, dungeon_room)") - def at_traverse(self, traversing_object, target_location, **kwargs): + def at_traverse(self, traversing_object, target_location, **kwargs): pass # to be implemented! - def at_failed_traverse(self, traversing_object, **kwargs): + def at_failed_traverse(self, traversing_object, **kwargs): """ Called when failing to traverse. @@ -638,12 +638,12 @@ We will expand on this for dungeon rooms.

      70 71
      # in evadventure/dungeon.py 
       
      -from evennia.utils import create
      -from evennia import DefaultScript
      +from evennia.utils import create
      +from evennia import DefaultScript
       
       # ... 
       
      -class EvAdventureDungeonBranch(DefaultScript):
      +class EvAdventureDungeonBranch(DefaultScript):
           """
           One script is created for every dungeon 'instance' created. The branch is
           responsible for determining what is created next when a character enters an
      @@ -668,7 +668,7 @@ We will expand on this for dungeon rooms.

      start_room = AttributeProperty(None, autocreate=False) - def register_exit_traversed(self, exit): + def register_exit_traversed(self, exit): """ Tell the system the given exit was traversed. This allows us to track how many unvisited paths we have so as to not have it grow @@ -678,7 +678,7 @@ We will expand on this for dungeon rooms.

      if exit.id in self.unvisited_exits: self.unvisited_exits.remove(exit.id) - def create_out_exit(self, location, exit_direction="north"): + def create_out_exit(self, location, exit_direction="north"): """ Create outgoing exit from a room. The target room is not yet created. @@ -691,14 +691,14 @@ We will expand on this for dungeon rooms.

      ) self.unvisited_exits.append(out_exit.id) - def delete(self): + def delete(self): """ Clean up the dungeon branch. """ pass # to be implemented - def new_room(self, from_exit): + def new_room(self, from_exit): """ Create a new Dungeon room leading from the provided exit. @@ -728,7 +728,7 @@ We will expand on this for dungeon rooms.

      # ... -def room_generator(dungeon_branch, depth, coords): +def room_generator(dungeon_branch, depth, coords): """ Plugin room generator @@ -790,15 +790,15 @@ We will expand on this for dungeon rooms.

      If have done that it will be easy to find all characters and rooms associated with the branch in order to do this cleanup operation.

      # in evadventure/dungeon.py 
       
      -from evennia import search
      +from evennia import search
       
       # ... 
       
      -class EvAdventureDungeonBranch(DefaultScript):
      +class EvAdventureDungeonBranch(DefaultScript):
       
           # ...
       
      -    def delete(self):
      +    def delete(self):
               """
               Clean up the dungeon branch, removing players safely
       
      @@ -921,16 +921,16 @@ We will expand on this for dungeon rooms.

      82 83
      # in evadventure/dungeon.py 
       
      -from datetime import datetime
      -from random import shuffle
      +from datetime import datetime
      +from random import shuffle
       
       # ... 
       
      -class EvAdventureDungeonBranch(DefaultScript):
      +class EvAdventureDungeonBranch(DefaultScript):
       
           # ...
       
      -    def new_room(self, from_exit):
      +    def new_room(self, from_exit):
               """
               Create a new Dungeon room leading from the provided exit.
       
      @@ -1026,10 +1026,10 @@ We will expand on this for dungeon rooms.

      # ... -class EvAdventureDungeonExit(DefaultExit): +class EvAdventureDungeonExit(DefaultExit): # ... - def at_traverse(self, traversing_object, target_location, **kwargs): + def at_traverse(self, traversing_object, target_location, **kwargs): """ Called when traversing. `target_location` will be pointing back to ourselves if the target was not yet created. It checks the current @@ -1093,16 +1093,16 @@ We will expand on this for dungeon rooms.

      # ... -class EvAdventureDungeonStartRoomExit(DefaultExit): +class EvAdventureDungeonStartRoomExit(DefaultExit): - def reset_exit(self): + def reset_exit(self): """ Flush the exit, so next traversal creates a new dungeon branch. """ self.destination = self.location - def at_traverse(self, traversing_object, target_location, **kwargs): + def at_traverse(self, traversing_object, target_location, **kwargs): """ When traversing create a new branch if one is not already assigned. @@ -1143,21 +1143,21 @@ We will expand on this for dungeon rooms.

      Both of these scripts are expected to be created ‘on’ the start room, so self.obj will be the start room.

      # in evadventure/dungeon.py
       
      -from evennia.utils.utils import inherits_from
      +from evennia.utils.utils import inherits_from
       
       # ... 
       
      -class EvAdventureStartRoomResetter(DefaultScript):
      +class EvAdventureStartRoomResetter(DefaultScript):
           """
           Simple ticker-script. Introduces a chance of the room's exits cycling every
           interval.
       
           """
       
      -    def at_script_creation(self):
      +    def at_script_creation(self):
               self.key = "evadventure_dungeon_startroom_resetter"
       
      -    def at_repeat(self):
      +    def at_repeat(self):
               """
               Called every time the script repeats.
       
      @@ -1173,7 +1173,7 @@ We will expand on this for dungeon rooms.

      # ... -class EvAdventureDungeonBranchDeleter(DefaultScript): +class EvAdventureDungeonBranchDeleter(DefaultScript): """ Cleanup script. After some time a dungeon branch will 'collapse', forcing all players in it back to the start room. @@ -1183,10 +1183,10 @@ We will expand on this for dungeon rooms.

      # set at creation time when the start room is created branch_max_life = AttributeProperty(0, autocreate=False) - def at_script_creation(self): + def at_script_creation(self): self.key = "evadventure_dungeon_branch_deleter" - def at_repeat(self): + def at_repeat(self): """ Go through all dungeon-branchs and find which ones are too old. @@ -1210,7 +1210,7 @@ We will expand on this for dungeon rooms.

      # ... -class EvAdventureDungeonStartRoom(EvAdventureDungeonRoom): +class EvAdventureDungeonStartRoom(EvAdventureDungeonRoom): recycle_time = 60 * 5 # 5 mins branch_check_time = 60 * 60 # one hour @@ -1219,13 +1219,13 @@ We will expand on this for dungeon rooms.

      # allow for a custom room_generator function room_generator = AttributeProperty(lambda: room_generator, autocreate=False) - def get_display_footer(self, looker, **kwargs): + def get_display_footer(self, looker, **kwargs): return ( "|yYou sense that if you want to team up, " "you must all pick the same path from here ... or you'll quickly get separated.|n" ) - def at_object_creation(self): + def at_object_creation(self): # want to set the script interval on creation time, so we use create_script with obj=self # instead of self.scripts.add() here create.create_script( @@ -1239,7 +1239,7 @@ We will expand on this for dungeon rooms.

      attributes=(("branch_max_life", self.branch_max_life),), ) - def at_object_receive(self, obj, source_location, **kwargs): + def at_object_receive(self, obj, source_location, **kwargs): """ Make sure to clean the dungeon branch-tag from characters when leaving a dungeon branch. diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.html index 94e2f93ec7..504313a5b2 100644 --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.html +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.html @@ -148,7 +148,7 @@ # ... -class WieldLocation(Enum): +class WieldLocation(Enum): BACKPACK = "backpack" WEAPON_HAND = "weapon_hand" @@ -173,17 +173,17 @@

      This is the start of our handler:

      # in mygame/evadventure/equipment.py 
       
      -from .enums import WieldLocation
      +from .enums import WieldLocation
       
      -class EquipmentHandler: 
      +class EquipmentHandler: 
           save_attribute = "inventory_slots"
           
      -    def __init__(self, obj): 
      +    def __init__(self, obj): 
               # here obj is the character we store the handler on 
               self.obj = obj 
               self._load() 
               
      -    def _load(self):
      +    def _load(self):
               """Load our data from an Attribute on `self.obj`"""
               self.slots = self.obj.attributes.get(
                   self.save_attribute,
      @@ -198,7 +198,7 @@
                   } 
               )
           
      -    def _save(self):
      +    def _save(self):
               """Save our data back to the same Attribute"""
               self.obj.attributes.add(self.save_attribute, self.slots, category="inventory") 
       
      @@ -209,17 +209,17 @@ we will add it to the Character:

      # ... -from evennia.utils.utils import lazy_property -from .equipment import EquipmentHandler +from evennia.utils.utils import lazy_property +from .equipment import EquipmentHandler # ... -class EvAdventureCharacter(LivingMixin, DefaultCharacter): +class EvAdventureCharacter(LivingMixin, DefaultCharacter): # ... @lazy_property - def equipment(self): + def equipment(self): return EquipmentHandler(self)
      @@ -262,25 +262,25 @@ we will skip that for this tutorial.

      # ... -class EvAdventureCharacter(LivingMixin, DefaultCharacter): +class EvAdventureCharacter(LivingMixin, DefaultCharacter): # ... - def at_pre_object_receive(self, moved_object, source_location, **kwargs): + def at_pre_object_receive(self, moved_object, source_location, **kwargs): """Called by Evennia before object arrives 'in' this character (that is, if they pick up something). If it returns False, move is aborted. """ return self.equipment.validate_slot_usage(moved_object) - def at_object_receive(self, moved_object, source_location, **kwargs): + def at_object_receive(self, moved_object, source_location, **kwargs): """ Called by Evennia when an object arrives 'in' the character. """ self.equipment.add(moved_object) - def at_object_leave(self, moved_object, destination, **kwargs): + def at_object_leave(self, moved_object, destination, **kwargs): """ Called by Evennia when object leaves the Character. @@ -299,22 +299,22 @@ we will skip that for this tutorial.

      Let’s start with implementing the first method we came up with above, validate_slot_usage:

      # mygame/evadventure/equipment.py 
       
      -from .enums import WieldLocation, Ability
      +from .enums import WieldLocation, Ability
       
      -class EquipmentError(TypeError):
      +class EquipmentError(TypeError):
           """All types of equipment-errors"""
           pass
       
      -class EquipmentHandler: 
      +class EquipmentHandler: 
       
           # ... 
           
           @property
      -    def max_slots(self):
      +    def max_slots(self):
               """Max amount of slots, based on CON defense (CON + 10)""" 
               return getattr(self.obj, Ability.CON.value, 1) + 10
               
      -    def count_slots(self):
      +    def count_slots(self):
               """Count current slot usage""" 
               slots = self.slots
               wield_usage = sum(
      @@ -327,7 +327,7 @@ we will skip that for this tutorial.

      ) return wield_usage + backpack_usage - def validate_slot_usage(self, obj): + def validate_slot_usage(self, obj): """ Check if obj can fit in equipment, based on its size. @@ -407,15 +407,15 @@ together.

      We will make it so .add puts something in the BACKPACK location and remove drops it, wherever it is (even if it was in your hands).

      # mygame/evadventure/equipment.py 
       
      -from .enums import WieldLocation, Ability
      +from .enums import WieldLocation, Ability
       
       # ... 
       
      -class EquipmentHandler: 
      +class EquipmentHandler: 
       
           # ... 
            
      -    def add(self, obj):
      +    def add(self, obj):
               """
               Put something in the backpack.
               """
      @@ -423,7 +423,7 @@ together.

      self.slots[WieldLocation.BACKPACK].append(obj) self._save() - def remove(self, obj_or_slot): + def remove(self, obj_or_slot): """ Remove specific object or objects from a slot. @@ -468,15 +468,15 @@ double-check we can actually fit the thing, then we add the item to the backpack

      With the help of .remove() and .add() we can get things in and out of the BACKPACK equipment location. We also need to grab stuff from the backpack and wield or wear it. We add a .move method on the EquipmentHandler to do this:

      # mygame/evadventure/equipment.py 
       
      -from .enums import WieldLocation, Ability
      +from .enums import WieldLocation, Ability
       
       # ... 
       
      -class EquipmentHandler: 
      +class EquipmentHandler: 
       
           # ... 
           
      -    def move(self, obj): 
      +    def move(self, obj): 
                """Move object from backpack to its intended `inventory_use_slot`.""" 
                
               # make sure to remove from equipment/backpack first, to avoid double-adding
      @@ -522,15 +522,15 @@ double-check we can actually fit the thing, then we add the item to the backpack
       

      In order to visualize our inventory, we need some method to get everything we are carrying.

      # mygame/evadventure/equipment.py 
       
      -from .enums import WieldLocation, Ability
      +from .enums import WieldLocation, Ability
       
       # ... 
       
      -class EquipmentHandler: 
      +class EquipmentHandler: 
       
           # ... 
       
      -    def all(self):
      +    def all(self):
               """
               Get all objects in inventory, regardless of location.
               """
      @@ -553,17 +553,17 @@ double-check we can actually fit the thing, then we add the item to the backpack
       

      It’s convenient to have the EquipmentHandler easily tell you what weapon is currently wielded and what armor level all worn equipment provides. Otherwise you’d need to figure out what item is in which wield-slot and to add up armor slots manually every time you need to know.

      # mygame/evadventure/equipment.py 
       
      -from .enums import WieldLocation, Ability
      -from .objects import get_bare_hand
      +from .enums import WieldLocation, Ability
      +from .objects import get_bare_hand
       
       # ... 
       
      -class EquipmentHandler: 
      +class EquipmentHandler: 
       
           # ... 
           
           @property
      -    def armor(self):
      +    def armor(self):
               slots = self.slots
               return sum(
                   (
      @@ -577,7 +577,7 @@ double-check we can actually fit the thing, then we add the item to the backpack
               )
       
           @property
      -    def weapon(self):
      +    def weapon(self):
               # first checks two-handed wield, then one-handed; the two
               # should never appear simultaneously anyhow (checked in `move` method).
               slots = self.slots
      @@ -603,11 +603,11 @@ double-check we can actually fit the thing, then we add the item to the backpack
       
      # mygame/evadventure/characters.py
       # ... 
       
      -class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
      +class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
       
           # ... 
       
      -    def at_object_receive(self, moved_object, source_location, **kwargs): 
      +    def at_object_receive(self, moved_object, source_location, **kwargs): 
               """ 
               Called by Evennia when an object arrives 'in' the character.
               
      @@ -618,14 +618,14 @@ double-check we can actually fit the thing, then we add the item to the backpack
       

      This means that the equipmenthandler will check the NPC, and since it’s not a equippable thing, an EquipmentError will be raised, failing the creation. Since we want to be able to create npcs etc easily, we will handle this error with a try...except statement like so:

      # mygame/evadventure/characters.py
       # ... 
      -from evennia import logger 
      -from .equipment import EquipmentError
      +from evennia import logger 
      +from .equipment import EquipmentError
       
      -class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
      +class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
       
           # ... 
       
      -    def at_object_receive(self, moved_object, source_location, **kwargs): 
      +    def at_object_receive(self, moved_object, source_location, **kwargs): 
               """ 
               Called by Evennia when an object arrives 'in' the character.
               
      @@ -668,21 +668,21 @@ have EquipmentHandl
       passing these into the handler’s methods.

      # mygame/evadventure/tests/test_equipment.py 
       
      -from evennia.utils import create 
      -from evennia.utils.test_resources import BaseEvenniaTest 
      +from evennia.utils import create 
      +from evennia.utils.test_resources import BaseEvenniaTest 
       
      -from ..objects import EvAdventureObject, EvAdventureHelmet, EvAdventureWeapon
      -from ..enums import WieldLocation
      -from ..characters import EvAdventureCharacter
      +from ..objects import EvAdventureObject, EvAdventureHelmet, EvAdventureWeapon
      +from ..enums import WieldLocation
      +from ..characters import EvAdventureCharacter
       
      -class TestEquipment(BaseEvenniaTest): 
      +class TestEquipment(BaseEvenniaTest): 
           
      -    def setUp(self): 
      +    def setUp(self): 
               self.character = create.create_object(EvAdventureCharacter, key='testchar')
               self.helmet = create.create_object(EvAdventureHelmet, key="helmet") 
               self.weapon = create.create_object(EvAdventureWeapon, key="weapon") 
                
      -    def test_add_remove): 
      +    def test_add_remove): 
               self.character.equipment.add(self.helmet)
               self.assertEqual(
                   self.character.equipment.slots[WieldLocation.BACKPACK],
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.html
      index c1c6dea491..b8616d2b21 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.html
      @@ -206,13 +206,13 @@
       65
       66
      # in evadventure/npcs.py 
       
      -from evennia import DefaultCharacter, AttributeProperty
      +from evennia import DefaultCharacter, AttributeProperty
       
      -from .characters import LivingMixin
      -from .enums import Ability
      +from .characters import LivingMixin
      +from .enums import Ability
       
       
      -class EvAdventureNPC(LivingMixin, DefaultCharacter): 
      +class EvAdventureNPC(LivingMixin, DefaultCharacter): 
       	"""Base class for NPCs""" 
       
           is_pc = False
      @@ -229,34 +229,34 @@
           is_idle = AttributeProperty(default=False, autocreate=False)
           
           @property
      -    def strength(self):
      +    def strength(self):
               return self.hit_dice
               
           @property
      -    def dexterity(self):
      +    def dexterity(self):
               return self.hit_dice
        
           @property
      -    def constitution(self):
      +    def constitution(self):
               return self.hit_dice
        
           @property
      -    def intelligence(self):
      +    def intelligence(self):
               return self.hit_dice
        
           @property
      -    def wisdom(self):
      +    def wisdom(self):
               return self.hit_dice
        
           @property
      -    def charisma(self):
      +    def charisma(self):
               return self.hit_dice
        
           @property
      -    def hp_max(self):
      +    def hp_max(self):
               return self.hit_dice * self.hp_multiplier
           
      -    def at_object_creation(self):
      +    def at_object_creation(self):
                """
                Start with max health.
         
      @@ -265,7 +265,7 @@
                self.tags.add("npcs", category="group")
       
       
      -class EvAdventureMob(EvAdventureNPC): 
      +class EvAdventureMob(EvAdventureNPC): 
           """
           Mob(ile) NPC to be used for enemies.
            
      @@ -290,15 +290,15 @@
       

      Not so much to test yet, but we will be using the same module to test other aspects of NPCs in the future, so let’s create it now.

      # in evadventure/tests/test_npcs.py
       
      -from evennia import create_object                                           
      -from evennia.utils.test_resources import EvenniaTest                        
      +from evennia import create_object                                           
      +from evennia.utils.test_resources import EvenniaTest                        
                                                                                   
      -from .. import npcs                                                         
      +from .. import npcs                                                         
                                                                                   
      -class TestNPCBase(EvenniaTest):                                             
      +class TestNPCBase(EvenniaTest):                                             
       	"""Test the NPC base class""" 
       	
      -    def test_npc_base(self):
      +    def test_npc_base(self):
               npc = create_object(
                   npcs.EvAdventureNPC,
                   key="TestNPC",
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.html
      index 5de73ddfc1..284c1bd8be 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.html
      @@ -151,7 +151,7 @@ Before we continue, let’s expand with enums for use-slots and object types.

      # ... -class WieldLocation(Enum): +class WieldLocation(Enum): BACKPACK = "backpack" WEAPON_HAND = "weapon_hand" @@ -160,7 +160,7 @@ Before we continue, let’s expand with enums for use-slots and object types.

      BODY = "body" # armor HEAD = "head" # helmets -class ObjType(Enum): +class ObjType(Enum): WEAPON = "weapon" ARMOR = "armor" @@ -188,13 +188,13 @@ a full set of objects implemented.

      We will make a base EvAdventureObject class off Evennia’s standard DefaultObject. We will then add child classes to represent the relevant types:

      # mygame/evadventure/objects.py
       
      -from evennia import AttributeProperty, DefaultObject 
      -from evennia.utils.utils import make_iter
      -from .utils import get_obj_stats 
      -from .enums import WieldLocation, ObjType
      +from evennia import AttributeProperty, DefaultObject 
      +from evennia.utils.utils import make_iter
      +from .utils import get_obj_stats 
      +from .enums import WieldLocation, ObjType
       
       
      -class EvAdventureObject(DefaultObject): 
      +class EvAdventureObject(DefaultObject): 
           """ 
           Base for all evadventure objects. 
           
      @@ -209,40 +209,40 @@ a full set of objects implemented.

      # default evennia hooks - def at_object_creation(self): + def at_object_creation(self): """Called when this object is first created. We convert the .obj_type property to a database tag.""" for obj_type in make_iter(self.obj_type): self.tags.add(self.obj_type.value, category="obj_type") - def get_display_header(self, looker, **kwargs): + def get_display_header(self, looker, **kwargs): """The top of the description""" return "" - def get_display_desc(self, looker, **kwargs): + def get_display_desc(self, looker, **kwargs): """The main display - show object stats""" return get_obj_stats(self, owner=looker) # custom evadventure methods - def has_obj_type(self, objtype): + def has_obj_type(self, objtype): """Check if object is of a certain type""" return objtype.value in make_iter(self.obj_type) - def at_pre_use(self, *args, **kwargs): + def at_pre_use(self, *args, **kwargs): """Called before use. If returning False, can't be used""" return True - def use(self, *args, **kwargs): + def use(self, *args, **kwargs): """Use this object, whatever that means""" pass - def post_use(self, *args, **kwargs): + def post_use(self, *args, **kwargs): """Always called after use.""" pass - def get_help(self): + def get_help(self): """Get any help text for this item""" return "No help for this item"
      @@ -251,7 +251,7 @@ a full set of objects implemented.

      4.2.1. Using Attributes or not

      In theory, size and value does not change and could also be just set as a regular Python property on the class:

      -
      class EvAdventureObject(DefaultObject):
      +
      class EvAdventureObject(DefaultObject):
           inventory_use_slot = WieldLocation.BACKPACK 
           size = 1 
           value = 0 
      @@ -270,8 +270,8 @@ all objects of a particular size. So we should be safe.

      The at_object_creation is a method Evennia calls on every child of DefaultObject whenever it is first created.

      We do a tricky thing here, converting our .obj_type to one or more Tags. Tagging the object like this means you can later efficiently find all objects of a given type (or combination of types) with Evennia’s search functions:

      -
          from .enums import ObjType 
      -    from evennia.utils import search 
      +
          from .enums import ObjType 
      +    from evennia.utils import search 
           
           # get all shields in the game
           all_shields = search.search_object_by_tag(ObjType.SHIELD.value, category="obj_type")
      @@ -285,18 +285,18 @@ types) with Evennia’s search functions:

      Some of the other object types are very simple so far.

      # mygame/evadventure/objects.py 
       
      -from evennia import AttributeProperty, DefaultObject
      -from .enums import ObjType 
      +from evennia import AttributeProperty, DefaultObject
      +from .enums import ObjType 
       
      -class EvAdventureObject(DefaultObject): 
      +class EvAdventureObject(DefaultObject): 
           # ... 
           
           
      -class EvAdventureQuestObject(EvAdventureObject):
      +class EvAdventureQuestObject(EvAdventureObject):
           """Quest objects should usually not be possible to sell or trade."""
           obj_type = ObjType.QUEST
        
      -class EvAdventureTreasure(EvAdventureObject):
      +class EvAdventureTreasure(EvAdventureObject):
           """Treasure is usually just for selling for coin"""
           obj_type = ObjType.TREASURE
           value = AttributeProperty(100, autocreate=False)
      @@ -311,14 +311,14 @@ types) with Evennia’s search functions:

      # ... -class EvAdventureConsumable(EvAdventureObject): +class EvAdventureConsumable(EvAdventureObject): """An item that can be used up""" obj_type = ObjType.CONSUMABLE value = AttributeProperty(0.25, autocreate=False) uses = AttributeProperty(1, autocreate=False) - def at_pre_use(self, user, target=None, *args, **kwargs): + def at_pre_use(self, user, target=None, *args, **kwargs): """Called before using. If returning False, abort use.""" if target and user.location != target.location: user.msg("You are not close enough to the target!") @@ -328,11 +328,11 @@ types) with Evennia’s search functions:

      user.msg(f"|w{self.key} is used up.|n") return False - def use(self, user, *args, **kwargs): + def use(self, user, *args, **kwargs): """Called when using the item""" pass - def at_post_use(self, user, *args, **kwargs): + def at_post_use(self, user, *args, **kwargs): """Called after using the item""" # detract a usage, deleting the item if used up. self.uses -= 1 @@ -349,11 +349,11 @@ types) with Evennia’s search functions:

      All weapons need properties that describe how efficient they are in battle. To ‘use’ a weapon means to attack with it, so we can let the weapon itself handle all logic around performing an attack. Having the attack code on the weapon also means that if we in the future wanted a weapon doing something special on-attack (for example, a vampiric sword that heals the attacker when hurting the enemy), we could easily add that on the weapon subclass in question without modifying other code.

      # mygame/evadventure/objects.py 
       
      -from .enums import WieldLocation, ObjType, Ability
      +from .enums import WieldLocation, ObjType, Ability
       
       # ... 
       
      -class EvAdventureWeapon(EvAdventureObject): 
      +class EvAdventureWeapon(EvAdventureObject): 
           """Base class for all weapons"""
       
           obj_type = ObjType.WEAPON 
      @@ -366,7 +366,7 @@ types) with Evennia’s search functions:

      damage_roll = AttributeProperty("1d6", autocreate=False) -def at_pre_use(self, user, target=None, *args, **kwargs): +def at_pre_use(self, user, target=None, *args, **kwargs): if target and user.location != target.location: # we assume weapons can only be used in the same location user.msg("You are not close enough to the target!") @@ -377,7 +377,7 @@ types) with Evennia’s search functions:

      return False return super().at_pre_use(user, target=target, *args, **kwargs) - def use(self, attacker, target, *args, advantage=False, disadvantage=False, **kwargs): + def use(self, attacker, target, *args, advantage=False, disadvantage=False, **kwargs): """When a weapon is used, it attacks an opponent""" location = attacker.location @@ -421,7 +421,7 @@ types) with Evennia’s search functions:

      self.quality -= 1 location.msg_contents(message, from_obj=attacker, mapping={target.key: target}) - def at_post_use(self, user, *args, **kwargs): + def at_post_use(self, user, *args, **kwargs): if self.quality is not None and self.quality <= 0: user.msg(f"|r{self.get_display_name(user)} breaks and can no longer be used!")
      @@ -474,13 +474,13 @@ types) with Evennia’s search functions:

      # mygame/evadventure/objects.py 
       
       # ... 
      -class EvAdventureConsumable(EvAdventureObject): 
      +class EvAdventureConsumable(EvAdventureObject): 
           # ... 
       
      -class EvAdventureWeapon(EvAdventureObject): 
      +class EvAdventureWeapon(EvAdventureObject): 
           # ... 
       
      -class EvAdventureRuneStone(EvAdventureWeapon, EvAdventureConsumable): 
      +class EvAdventureRuneStone(EvAdventureWeapon, EvAdventureConsumable): 
           """Base for all magical rune stones"""
           
           obj_type = (ObjType.WEAPON, ObjType.MAGIC)
      @@ -492,13 +492,13 @@ types) with Evennia’s search functions:

      damage_roll = AttributeProperty("1d8", autocreate=False) - def at_post_use(self, user, *args, **kwargs): + def at_post_use(self, user, *args, **kwargs): """Called after usage/spell was cast""" self.uses -= 1 # we don't delete the rune stone here, but # it must be reset on next rest. - def refresh(self): + def refresh(self): """Refresh the rune stone (normally after rest)""" self.uses = 1
      @@ -515,7 +515,7 @@ types) with Evennia’s search functions:

      # ... -class EvAdventureAmor(EvAdventureObject): +class EvAdventureAmor(EvAdventureObject): obj_type = ObjType.ARMOR inventory_use_slot = WieldLocation.BODY @@ -523,12 +523,12 @@ types) with Evennia’s search functions:

      quality = AttributeProperty(3, autocreate=False) -class EvAdventureShield(EvAdventureArmor): +class EvAdventureShield(EvAdventureArmor): obj_type = ObjType.SHIELD inventory_use_slot = WieldLocation.SHIELD_HAND -class EvAdventureHelmet(EvAdventureArmor): +class EvAdventureHelmet(EvAdventureArmor): obj_type = ObjType.HELMET inventory_use_slot = WieldLocation.HEAD
      @@ -540,13 +540,13 @@ types) with Evennia’s search functions:

      We will use this in the upcoming Equipment tutorial lesson to represent when you have ‘nothing’ in your hands. This way we don’t need to add any special case for this.

      # mygame/evadventure/objects.py
       
      -from evennia import search_object, create_object
      +from evennia import search_object, create_object
       
       _BARE_HANDS = None 
       
       # ... 
       
      -class WeaponBareHands(EvAdventureWeapon):
      +class WeaponBareHands(EvAdventureWeapon):
            obj_type = ObjType.WEAPON
            inventory_use_slot = WieldLocation.WEAPON_HAND
            attack_type = Ability.STR
      @@ -555,7 +555,7 @@ types) with Evennia’s search functions:

      quality = None # let's assume fists are indestructible ... -def get_bare_hands(): +def get_bare_hands(): """Get the bare hands""" global _BARE_HANDS if not _BARE_HANDS: diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html index dc7a942cc5..5e7e03aba2 100644 --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html @@ -202,17 +202,17 @@ 44 45
      # in evadventure/quests.py
       
      -class EvAdventureQuestHandler:
      +class EvAdventureQuestHandler:
           quest_storage_attribute_key = "_quests"
           quest_storage_attribute_category = "evadventure"
       
      -    def __init__(self, obj):
      +    def __init__(self, obj):
               self.obj = obj
               self.quest_classes = {}
               self.quests = {}
               self._load()
       
      -    def _load(self):
      +    def _load(self):
               self.quest_classes = self.obj.attributes.get(
                   self.quest_storage_attribute_key,
                   category=self.quest_storage_attribute_category,
      @@ -222,25 +222,25 @@
               for quest_key, quest_class in self.quest_classes.items():
                   self.quests[quest_key] = quest_class(self.obj)
       
      -    def _save(self):
      +    def _save(self):
               self.obj.attributes.add(
                   self.quest_storage_attribute_key,
                   self.quest_classes,
                   category=self.quest_storage_attribute_category,
               )
           
      -    def get(self, quest_key):
      +    def get(self, quest_key):
               return self.quests.get(quest_key)
       
      -    def all(self):
      +    def all(self):
               return list(self.quests.values())
       
      -    def add(self, quest_class):
      +    def add(self, quest_class):
               self.quest_classes[quest_class.key] = quest_class
               self.quests[quest_class.key] = quest_class(self.obj)
               self._save()
       
      -    def remove(self, quest_key):
      +    def remove(self, quest_key):
               quest = self.quests.pop(quest_key, None)
               self.quest_classes.pop(quest_key, None)
               self.quests.pop(quest_key, None)
      @@ -263,14 +263,14 @@
       

      This is how it would be used in practice:

      # in some questing code 
       
      -from evennia import search_object
      -from evadventure import quests 
      +from evennia import search_object
      +from evadventure import quests 
       
      -class EvAdventureSuperQuest(quests.EvAdventureQuest):
      +class EvAdventureSuperQuest(quests.EvAdventureQuest):
           key = "superquest"
           # quest implementation here
       
      -def start_super_quest(character):
      +def start_super_quest(character):
           character.quests.add(EvAdventureSuperQuest)
       
       
      @@ -283,7 +283,7 @@ Instead we store only the classes, instantiate those classes with the Character, and let the quest store its state flags separately, like this:

      # in evadventure/quests.py 
       
      -class EvAdventureQuestHandler: 
      +class EvAdventureQuestHandler: 
       
           # ... 
           quest_data_attribute_template = "_quest_data_{quest_key}"
      @@ -291,7 +291,7 @@ Instead we store only the classes, instantiate those classes with the Character,
       
           # ... 
       
      -    def save_quest_data(self, quest_key):
      +    def save_quest_data(self, quest_key):
               quest = self.get(quest_key)
               if quest:
                   self.obj.attributes.add(
      @@ -300,7 +300,7 @@ Instead we store only the classes, instantiate those classes with the Character,
                       category=self.quest_data_attribute_category,
                   )
       
      -    def load_quest_data(self, quest_key):
      +    def load_quest_data(self, quest_key):
               return self.obj.attributes.get(
                   self.quest_data_attribute_template.format(quest_key=quest_key),
                   category=self.quest_data_attribute_category,
      @@ -315,14 +315,14 @@ Instead we store only the classes, instantiate those classes with the Character,
       
       # ...
       
      -from evennia.utils import lazy_property
      -from evadventure.quests import EvAdventureQuestHandler
      +from evennia.utils import lazy_property
      +from evadventure.quests import EvAdventureQuestHandler
       
      -class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
      +class EvAdventureCharacter(LivingMixin, DefaultCharacter): 
           # ...
       
           @lazy_property
      -    def quests(self): 
      +    def quests(self): 
               return EvAdventureQuestHandler(self)
       
           # ...
      @@ -377,13 +377,13 @@ Instead we store only the classes, instantiate those classes with the Character,
       
       # ...
       
      -class EvAdventureQuest:
      +class EvAdventureQuest:
       
           key = "base-quest"
           desc = "Base quest"
           start_step = "start"
           
      -    def __init__(self, quester):
      +    def __init__(self, quester):
               self.quester = quester
               self.data = self.questhandler.load_quest_data(self.key)
               self._current_step = self.get_data("current_step")
      @@ -391,27 +391,27 @@ Instead we store only the classes, instantiate those classes with the Character,
               if not self.current_step:
                   self.current_step = self.start_step
       
      -    def add_data(self, key, value):
      +    def add_data(self, key, value):
               self.data[key] = value
               self.questhandler.save_quest_data(self.key)
       
      -    def get_data(self, key, default=None):
      +    def get_data(self, key, default=None):
               return self.data.get(key, default)
       
      -    def remove_data(self, key):
      +    def remove_data(self, key):
               self.data.pop(key, None)
               self.questhandler.save_quest_data(self.key)
           
           @property
      -    def questhandler(self):
      +    def questhandler(self):
               return self.quester.quests
       
           @property
      -    def current_step(self):
      +    def current_step(self):
               return self._current_step
       
           @current_step.setter
      -    def current_step(self, step_name):
      +    def current_step(self, step_name):
               self._current_step = step_name
               self.add_data("current_step", step_name)
       
      @@ -428,37 +428,37 @@ Instead we store only the classes, instantiate those classes with the Character, # ... -class EvAdventureQuest: +class EvAdventureQuest: # ... @property - def status(self): + def status(self): return self.get_data("status", "started") @status.setter - def status(self, value): + def status(self, value): self.add_data("status", value) @property - def is_completed(self): + def is_completed(self): return self.status == "completed" @property - def is_abandoned(self): + def is_abandoned(self): return self.status == "abandoned" @property - def is_failed(self): + def is_failed(self): return self.status == "failed" - def complete(self): + def complete(self): self.status = "completed" - def abandon(self): + def abandon(self): self.status = "abandoned" - def fail(self): + def fail(self): self.status = "failed" @@ -473,10 +473,10 @@ Instead we store only the classes, instantiate those classes with the Character, help_start = "You need to start first" help_end = "You need to end the quest" - def progress(self, *args, **kwargs): + def progress(self, *args, **kwargs): getattr(self, f"step_{self.current_step}")(*args, **kwargs) - def help(self, *args, **kwargs): + def help(self, *args, **kwargs): if self.status in ("abandoned", "completed", "failed"): help_resource = getattr(self, f"help_{self.status}", f"You have {self.status} this quest.") @@ -502,19 +502,19 @@ Instead we store only the classes, instantiate those classes with the Character,

      14.2.1. Example quest

      # in some quest module, like world/myquests.py
       
      -from evadventure.quests import EvAdventureQuest 
      +from evadventure.quests import EvAdventureQuest 
       
      -class ShortQuest(EvAdventureQuest): 
      +class ShortQuest(EvAdventureQuest): 
       
           key = "simple-quest"
           desc = "A very simple quest."
       
      -    def step_start(self, *args, **kwargs): 
      +    def step_start(self, *args, **kwargs): 
               """Example step!"""
               self.quester.msg("Quest started!")
               self.current_step = "end"
       
      -    def step_end(self, *args, **kwargs): 
      +    def step_end(self, *args, **kwargs): 
               if not self.is_completed:
                   self.quester.msg("Quest ended!")
                   self.complete()
      @@ -524,8 +524,8 @@ Instead we store only the classes, instantiate those classes with the Character,
       

      This is a very simple quest that will resolve on its own after two .progress() checks. Here’s the full life cycle of this quest:

      # in some module somewhere, using evennia shell or in-game using py
       
      -from evennia import search_object 
      -from world.myquests import ShortQuest 
      +from evennia import search_object 
      +from world.myquests import ShortQuest 
       
       character = search_object("MyCharacterName")[0]
       character.quests.add(ShortQuest)
      @@ -543,7 +543,7 @@ Instead we store only the classes, instantiate those classes with the Character,
       

      The player must know which quests they have and be able to inspect them. Here’s a simple quests command to handle this:

      # in evadventure/quests.py
       
      -class CmdQuests(Command):
      +class CmdQuests(Command):
           """
           List all quests and their statuses as well as get info about the status of
           a specific quest.
      @@ -556,10 +556,10 @@ Instead we store only the classes, instantiate those classes with the Character,
           key = "quests"
           aliases = ["quest"]
       
      -    def parse(self):
      +    def parse(self):
               self.quest_name = self.args.strip()
       
      -    def func(self):
      +    def func(self):
               if self.quest_name:
                   quest = self.caller.quests.get(self.quest_name)
                   if not quest:
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.html
      index 34913f6eb3..e5dd13d27c 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.html
      @@ -140,9 +140,9 @@
       
      # in evadventure/rooms.py
       
      -from evennia import AttributeProperty, DefaultRoom
      +from evennia import AttributeProperty, DefaultRoom
       
      -class EvAdventureRoom(DefaultRoom):
      +class EvAdventureRoom(DefaultRoom):
       	"""
           Simple room supporting some EvAdventure-specifics.
        
      @@ -169,7 +169,7 @@
       
       # ... 
       
      -class EvAdventurePvPRoom(EvAdventureRoom):
      +class EvAdventurePvPRoom(EvAdventureRoom):
           """
           Room where PvP can happen, but noone gets killed.
           
      @@ -178,7 +178,7 @@
           allow_combat = AttributeProperty(True, autocreate=False)
           allow_pvp = AttributeProperty(True, autocreate=False)
           
      -    def get_display_footer(self, looker, **kwargs):
      +    def get_display_footer(self, looker, **kwargs):
               """
               Customize footer of description.
               """
      @@ -276,9 +276,9 @@ Exits: north, northeast,# ... 
       
      -from copy import deepcopy
      -from evennia import DefaultCharacter
      -from evennia.utils.utils import inherits_from
      +from copy import deepcopy
      +from evennia import DefaultCharacter
      +from evennia.utils.utils import inherits_from
       
       CHAR_SYMBOL = "|w@|n"
       CHAR_ALT_SYMBOL = "|w>|n"
      @@ -303,15 +303,15 @@ Exits: north, northeast,"northwest": (-1, 1, "\\"),
       }
       
      -class EvAdventureRoom(DefaultRoom): 
      +class EvAdventureRoom(DefaultRoom): 
       
           # ... 
       
      -    def format_appearance(self, appearance, looker, **kwargs):
      +    def format_appearance(self, appearance, looker, **kwargs):
               """Don't left-strip the appearance string"""
               return appearance.rstrip()
        
      -    def get_display_header(self, looker, **kwargs):
      +    def get_display_header(self, looker, **kwargs):
               """
               Display the current location as a mini-map.
        
      @@ -390,26 +390,26 @@ Exits: north, northeast,# ... 
       
      -from random import choice, random
      -from evennia import TICKER_HANDLER
      +from random import choice, random
      +from evennia import TICKER_HANDLER
       
       # ... 
       
      -class EchoingRoom(EvAdventureRoom):
      +class EchoingRoom(EvAdventureRoom):
           """A room that randomly echoes messages to everyone inside it"""
       
           echoes = AttributeProperty(list, autocreate=False)
       	echo_rate = AttributeProperty(60 * 2, autocreate=False)
       	echo_chance = AttributeProperty(0.1, autocreate=False)
       
      -	def send_echo(self): 
      +	def send_echo(self): 
       		if self.echoes and random() < self.echo_chance: 
       			self.msg_contents(choice(self.echoes))
       
      -	def start_echo(self): 
      +	def start_echo(self): 
       		TICKER_HANDLER.add(self.echo_rate, self.send_echo)
       
      -	def stop_echo(self): 
      +	def stop_echo(self): 
       		TICKER_HANDLER.remove(self.echo_rate, self.send_echo)
       
      @@ -437,14 +437,14 @@ Exits: north, northeast,The main thing to test with our new rooms is the map. Here’s the basic principle for how to do this testing:

      # in evadventure/tests/test_rooms.py
       
      -from evennia import DefaultExit, create_object
      -from evennia.utils.test_resources import EvenniaTestCase
      -from ..characters import EvAdventureCharacter 
      -from ..rooms import EvAdventureRoom
      +from evennia import DefaultExit, create_object
      +from evennia.utils.test_resources import EvenniaTestCase
      +from ..characters import EvAdventureCharacter 
      +from ..rooms import EvAdventureRoom
       
      -class EvAdventureRoomTest(EvenniaTestCase): 
      +class EvAdventureRoomTest(EvenniaTestCase): 
       
      -    def test_map(self): 
      +    def test_map(self): 
               center_room = create_object(EvAdventureRoom, key="room_center")
               
               n_room = create_object(EvAdventureRoom, key="room_n)
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rules.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rules.html
      index a66ab71f09..b59404da27 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rules.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rules.html
      @@ -200,30 +200,30 @@ in a rulebook.

      2.3. Rolling dice

      We will start by making a dice roller. Let’s group all of our dice rolling into a structure like this (not functional code yet):

      -
      class EvAdventureRollEngine:
      +
      class EvAdventureRollEngine:
       
      -   def roll(...):
      +   def roll(...):
              # get result of one generic roll, for any type and number of dice
              
      -   def roll_with_advantage_or_disadvantage(...)
      +   def roll_with_advantage_or_disadvantage(...)
              # get result of normal d20 roll, with advantage/disadvantage (or not)
              
      -   def saving_throw(...):
      +   def saving_throw(...):
              # do a saving throw against a specific target number
              
      -   def opposed_saving_throw(...):
      +   def opposed_saving_throw(...):
              # do an opposed saving throw against a target's defense
       
      -   def roll_random_table(...):
      +   def roll_random_table(...):
              # make a roll against a random table (loaded elsewere)
         
      -   def morale_check(...):
      +   def morale_check(...):
              # roll a 2d6 morale check for a target
             
      -   def heal_from_rest(...):
      +   def heal_from_rest(...):
              # heal 1d8 when resting+eating, but not more than max value.
              
      -   def roll_death(...):
      +   def roll_death(...):
              # roll to determine penalty when hitting 0 HP. 
              
              
      @@ -239,7 +239,7 @@ module if you wanted.

      This structure (called a singleton) means we group all dice rolls into one class that we then initiate into a variable dice at the end of the module. This means that we can do the following from other modules:

      -
          from .rules import dice 
      +
          from .rules import dice 
       
           dice.roll("1d8")
       
      @@ -249,11 +249,11 @@ modules:

      We want to be able to do roll("1d20") and get a random result back from the roll.

      # in mygame/evadventure/rules.py 
       
      -from random import randint
      +from random import randint
       
      -class EvAdventureRollEngine:
      +class EvAdventureRollEngine:
           
      -    def roll(self, roll_string):
      +    def roll(self, roll_string):
               """ 
               Roll XdY dice, where X is the number of dice 
               and Y the number of sides per die. 
      @@ -316,12 +316,12 @@ crazy big so the loop takes forever!

      # ... -class EvAdventureRollEngine: +class EvAdventureRollEngine: - def roll(roll_string): + def roll(roll_string): # ... - def roll_with_advantage_or_disadvantage(self, advantage=False, disadvantage=False): + def roll_with_advantage_or_disadvantage(self, advantage=False, disadvantage=False): if not (advantage or disadvantage) or (advantage and disadvantage): # normal roll - advantage/disadvantage not set or they cancel @@ -361,17 +361,17 @@ Attribute is available as character.strength, character.constitution, character.charisma etc to get the relevant Abilities.

      # in mygame/evadventure/rules.py 
       # ...
      -from .enums import Ability
      +from .enums import Ability
       
      -class EvAdventureRollEngine: 
      +class EvAdventureRollEngine: 
       
      -    def roll(...)
      +    def roll(...)
               # ...
          
      -    def roll_with_advantage_or_disadvantage(...)
      +    def roll_with_advantage_or_disadvantage(...)
               # ...
              
      -    def saving_throw(self, character, bonus_type=Ability.STR, target=15, 
      +    def saving_throw(self, character, bonus_type=Ability.STR, target=15, 
                            advantage=False, disadvantage=False):
               """ 
               Do a saving throw, trying to beat a target.
      @@ -417,20 +417,20 @@ to beat is always the relevant bonus + 10 in Knave. So if the enemy def
       roll higher than 13.

      # in mygame/evadventure/rules.py 
       
      -from .enums import Ability
      +from .enums import Ability
       
      -class EvAdventureRollEngine:
      +class EvAdventureRollEngine:
           
      -    def roll(...):
      +    def roll(...):
               # ... 
       
      -    def roll_with_advantage_or_disadvantage(...):
      +    def roll_with_advantage_or_disadvantage(...):
               # ... 
       
      -    def saving_throw(...):
      +    def saving_throw(...):
               # ... 
       
      -    def opposed_saving_throw(self, attacker, defender, 
      +    def opposed_saving_throw(self, attacker, defender, 
                                    attack_type=Ability.STR, defense_type=Ability.ARMOR,
                                    advantage=False, disadvantage=False):
               defender_defense = getattr(defender, defense_type.value, 1) + 10 
      @@ -450,11 +450,11 @@ roll higher than 13
       when things go south. The standard morale value is 9.

      # in mygame/evadventure/rules.py 
       
      -class EvAdventureRollEngine:
      +class EvAdventureRollEngine:
       
           # ...
           
      -    def morale_check(self, defender): 
      +    def morale_check(self, defender): 
               return self.roll("2d6") <= getattr(defender, "morale", 9)
           
       
      @@ -469,13 +469,13 @@ health on game entities. We will need 1d8 + CON HP.

      # in mygame/evadventure/rules.py 
       
      -from .enums import Ability
      +from .enums import Ability
       
      -class EvAdventureRollEngine: 
      +class EvAdventureRollEngine: 
       
           # ... 
           
      -    def heal_from_rest(self, character): 
      +    def heal_from_rest(self, character): 
               """ 
               A night's rest retains 1d8 + CON HP  
               
      @@ -548,13 +548,13 @@ be obvious, and in some games you could be asked to roll a lower dice to only ge
       early table results, for example).

      # in mygame/evadventure/rules.py 
       
      -from random import randint, choice
      +from random import randint, choice
       
      -class EvAdventureRollEngine:
      +class EvAdventureRollEngine:
           
           # ... 
       
      -    def roll_random_table(self, dieroll, table_choices): 
      +    def roll_random_table(self, dieroll, table_choices): 
               """ 
               Args: 
                    dieroll (str): A die roll string, like "1d20".
      @@ -661,14 +661,14 @@ So the result for 1
       )
           
           
      -class EvAdventureRollEngine:
      +class EvAdventureRollEngine:
           
           # ... 
       
      -    def roll_random_table(...)
      +    def roll_random_table(...)
               # ... 
               
      -    def roll_death(self, character): 
      +    def roll_death(self, character): 
               ability_name = self.roll_random_table("1d8", death_table)
       
               if ability_name == "dead":
      @@ -709,19 +709,19 @@ a message if they survive, to let them know what happened.

      Testing the rules module will also showcase some very useful tools when testing.

      # mygame/evadventure/tests/test_rules.py 
       
      -from unittest.mock import patch 
      -from evennia.utils.test_resources import BaseEvenniaTest
      -from .. import rules 
      +from unittest.mock import patch 
      +from evennia.utils.test_resources import BaseEvenniaTest
      +from .. import rules 
       
      -class TestEvAdventureRuleEngine(BaseEvenniaTest):
      +class TestEvAdventureRuleEngine(BaseEvenniaTest):
          
      -    def setUp(self):
      +    def setUp(self):
               """Called before every test method"""
               super().setUp()
               self.roll_engine = rules.EvAdventureRollEngine()
           
           @patch("evadventure.rules.randint")
      -    def test_roll(self, mock_randint):
      +    def test_roll(self, mock_randint):
               mock_randint.return_value = 4 
               self.assertEqual(self.roll_engine.roll("1d6"), 4)     
               self.assertEqual(self.roll_engine.roll("2d6"), 2 * 4)     
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.html b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.html
      index deb947797d..becb7bfc65 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.html
      @@ -156,18 +156,18 @@ utilities that will be useful later. We will also learn how to write tests
       

      Importing anything from inside this folder from anywhere else under mygame will be done by

      # from anywhere in mygame/
      -from evadventure.yourmodulename import whatever
      +from evadventure.yourmodulename import whatever
       

      This is the ‘absolute path` type of import.

      Between two modules both in evadventure/, you can use a ‘relative’ import with .:

      # from a module inside mygame/evadventure
      -from .yourmodulename import whatever
      +from .yourmodulename import whatever
       

      From e.g. inside mygame/evadventure/tests/ you can import from one level above using ..:

      # from mygame/evadventure/tests/
      -from ..yourmodulename import whatever
      +from ..yourmodulename import whatever
       
      @@ -181,9 +181,9 @@ utilities that will be useful later. We will also learn how to write testsAn enum (enumeration) is a way to establish constants in Python. For example:

      # in a file mygame/evadventure/enums.py
       
      -from enum import Enum
      +from enum import Enum
       
      -class Ability(Enum):
      +class Ability(Enum):
       
           STR = "strength"
       
      @@ -192,7 +192,7 @@ utilities that will be useful later. We will also learn how to write testsYou can then access an enum like this:

      # from another module in mygame/evadventure
       
      -from .enums import Ability
      +from .enums import Ability
       
       Ability.STR   # the enum itself
       Ability.STR.value  # this is the string "strength"
      @@ -206,7 +206,7 @@ utilities that will be useful later. We will also learn how to write testsBelow is the enum.py module needed for Knave. It covers the basic aspects of the rule system we need to track. (Check out the Knave rules.) Should you later use another rule system, you’ll likely expand on your enums gradually as you figure out what you’ll need.

      # mygame/evadventure/enums.py
       
      -class Ability(Enum):
      +class Ability(Enum):
           """
           The six base ability-bonuses and other
           abilities
      @@ -286,7 +286,7 @@ utilities that will be useful later. We will also learn how to write tests""".strip()
       
       
      -def get_obj_stats(obj, owner=None):
      +def get_obj_stats(obj, owner=None):
           """
           Get a string of stats about the object.
       
      @@ -336,13 +336,13 @@ is an example of the testing module. To dive deeper into unit testing in Evennia
       

      Here’s a module for testing get_obj_stats.

      # mygame/evadventure/tests/test_utils.py
       
      -from evennia.utils import create
      -from evennia.utils.test_resources import EvenniaTest
      +from evennia.utils import create
      +from evennia.utils.test_resources import EvenniaTest
       
      -from ..import utils
      +from ..import utils
       
      -class TestUtils(EvenniaTest):
      -    def test_get_obj_stats(self):
      +class TestUtils(EvenniaTest):
      +    def test_get_obj_stats(self):
               # make a simple object to test with
               obj = create.create_object(
                   key="testobj",
      diff --git a/docs/latest/Howtos/Beginner-Tutorial/Part5/Add-a-simple-new-web-page.html b/docs/latest/Howtos/Beginner-Tutorial/Part5/Add-a-simple-new-web-page.html
      index 60a92907e3..c55089a767 100644
      --- a/docs/latest/Howtos/Beginner-Tutorial/Part5/Add-a-simple-new-web-page.html
      +++ b/docs/latest/Howtos/Beginner-Tutorial/Part5/Add-a-simple-new-web-page.html
      @@ -146,9 +146,9 @@ imported from the new folder. For this tutorial, here’s what the example conte
       module should look like:

      # in mygame/web/website/story.py
       
      -from django.shortcuts import render
      +from django.shortcuts import render
       
      -def storypage(request):
      +def storypage(request):
           return render(request, "story.html")
       
      @@ -203,11 +203,11 @@ and corresponding path to The main web/urls.py includes these routes for all urls (the root of the url) so it can reroute to all website pages. """ -from django.urls import path +from django.urls import path -from web.website import story +from web.website import story -from evennia.web.website.urls import urlpatterns as evennia_website_urlpatterns +from evennia.web.website.urls import urlpatterns as evennia_website_urlpatterns # add patterns here urlpatterns = [ diff --git a/docs/latest/Howtos/Evennia-for-Diku-Users.html b/docs/latest/Howtos/Evennia-for-Diku-Users.html index cfe13419b8..687f04fb0d 100644 --- a/docs/latest/Howtos/Evennia-for-Diku-Users.html +++ b/docs/latest/Howtos/Evennia-for-Diku-Users.html @@ -173,16 +173,16 @@ object that calls the command is denoted by a class property as