func _on_data():
- # The following two lines will get us the data from Evennia.
- var data = _client.get_peer(1).get_packet().get_string_from_utf8()
- var json_data = JSON.parse(data).result
- # The json_data is an array
+To do this, we will add a new function to dispatch the messages properly.
+Here is an example
+func _handle_data(data):
+ print(data) # Print for debugging
+ var data_array = JSON.parse_string(data)
+ # The first element can be used to see if its text
+ if data_array[0] == 'text':
+ # The second element contains the messages
+ for msg in data_array[1]: write_to_rtb(msg)
- # The first element informs us this is simple text
- # so we add it to the RichTextlabel
- if json_data[0] == 'text':
- for msg in json_data[1]: label.append_bbcode(msg)
-
- # Always useful to print the data and see what we got.
- print(data)
+func write_to_rtb(msg):
+ output_label.append_text(msg)
The first element is the type, it will be text if it is a message
It can be anything you would provide to the Evennia msg function.
The second element will be the data related to the type of message, in this case it is a list of text to display.
-Since it is parsed BBCode, we can add that directly to a RichTextLabel by calling its append_bbcode method.
+Since it is parsed BBCode, we can add that directly to a RichTextLabel by calling its append_text method.
If you want anything better than fancy text in Godot, you will have
to leverage Evennia’s OOB to send extra data.
You can read more on OOB here.
-In this example, we send coordinates whenever we message our character.
-Evennia
-caller.msg(coordinates=(9, 2))
+Now to send data, we connect the Button pressed Signal to a method,
+read the label input and send it via the websocket, then clear the label.
+func _on_button_pressed():
+ var msg = text_edit.text
+ var msg_arr = ['text', [msg], {}]
+ var msg_str = JSON.stringify(msg_arr)
+ socket.send_text(msg_str)
+ text_edit.text = ""
-Godot
-func _on_data():
- ...
- if json_data[0] == 'text':
- for msg in json_data[1]: label.append_bbcode(msg)
-
- # Notice the first element is the name of the kwarg we used from evennia.
- elif json_data[0] == 'coordinates':
- var coords_data = json_data[2]
- player.set_pos(coords_data)
-
- ...
-
-
-A good idea would be to set up Godot Signals you can trigger based on the data
-you receive, so you can manage the code better.
Known Issues
Sending SaverDicts and similar objects straight from Evennia .DB will cause issues,
cast them to dict() or list() before doing so.
-Background colors are only supported by Godot 4.
-
-Godot 3 Example
-This is an example of a Script to use in Godot 3.
-The script can be attached to the root UI node.
-extends Node
+
+Full Example Script
+extends Node
-# The URL to connect to, should be your mud.
-export var websocket_url = "ws://127.0.0.1:4008"
+# The URL we will connect to.
+var websocket_url = "ws://localhost:4008"
+var socket := WebSocketPeer.new()
-# These are references to controls in the scene
-onready var parent = get_parent()
-onready var label = parent.get_node("%ChatLog")
-onready var txtEdit = parent.get_node("%ChatInput")
-
-onready var room = get_node("/root/World/Room")
-
-# Our WebSocketClient instance
-var _client = WebSocketClient.new()
-
-var is_connected = false
-
-func _ready():
- # Connect base signals to get notified of connection open, close, errors and messages
- _client.connect("connection_closed", self, "_closed")
- _client.connect("connection_error", self, "_closed")
- _client.connect("connection_established", self, "_connected")
- _client.connect("data_received", self, "_on_data")
- print('Ready')
-
- # Initiate connection to the given URL.
- var err = _client.connect_to_url(websocket_url)
- if err != OK:
- print("Unable to connect")
- set_process(false)
-
-func _closed(was_clean = false):
- # was_clean will tell you if the disconnection was correctly notified
- # by the remote peer before closing the socket.
- print("Closed, clean: ", was_clean)
- set_process(false)
-
-func _connected(proto = ""):
- is_connected = true
- print("Connected with protocol: ", proto)
-
-func _on_data():
- # This is called when Godot receives data from evennia
- var data = _client.get_peer(1).get_packet().get_string_from_utf8()
- var json_data = JSON.parse(data).result
- # Here we have the data from Evennia which is an array.
- # The first element will be text if it is a message
- # and would be the key of the OOB data you passed otherwise.
- if json_data[0] == 'text':
- # In this case, we simply append the data as bbcode to our label.
- for msg in json_data[1]: label.append_bbcode(msg)
- elif json_data[0] == 'coordinates':
- # Dummy signal emitted if we wanted to handle the new coordinates
- # elsewhere in the project.
- self.emit_signal('updated_coordinates', json_data[1])
+@onready var output_label = $"../Panel/VBoxContainer/RichTextLabel"
+@onready var text_edit = $"../Panel/VBoxContainer/HBoxContainer/TextEdit"
- # We only print this for easier debugging.
- print(data)
+func _ready():
+ if socket.connect_to_url(websocket_url) != OK:
+ print("Unable to connect.")
+ set_process(false)
-func _process(delta):
- # Required for websocket to properly react
- _client.poll()
+func _process(_delta):
+ socket.poll()
+ match socket.get_ready_state():
+ WebSocketPeer.STATE_OPEN:
+ while socket.get_available_packet_count():
+ var data = socket.get_packet().get_string_from_ascii()
+ _handle_data(data)
+
+ WebSocketPeer.STATE_CLOSED:
+ var code = socket.get_close_code()
+ var reason = socket.get_close_reason()
+ print("WebSocket closed with code: %d, reason %s. Clean: %s" % [code, reason, code != -1])
+ set_process(false)
-func _on_button_send():
- # This is called when we press the button in the scene
- # with a connected signal, it sends the written message to Evennia.
- var msg = txtEdit.text
- var msg_arr = ['text', [msg], {}]
- var msg_str = JSON.print(msg_arr)
- _client.get_peer(1).put_packet(msg_str.to_utf8())
+func _handle_data(data):
+ print(data) # Print for debugging
+ var data_array = JSON.parse_string(data)
+ # The first element can be used to see if its text
+ if data_array[0] == 'text':
+ # The second element contains the messages
+ for msg in data_array[1]: write_to_rtb(msg)
-func _notification(what):
- # This is a special method that allows us to notify Evennia we are closing.
- if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST:
- if is_connected:
- var msg_arr = ['text', ['quit'], {}]
- var msg_str = JSON.print(msg_arr)
- _client.get_peer(1).put_packet(msg_str.to_utf8())
- get_tree().quit() # default behavior
+func write_to_rtb(msg):
+ output_label.append_text(msg)
-
-
-
-
-Godot 4 Example
-This is an example of a Script to use in Godot 4.
-Note that the version is not final so the code may break.
-It requires a WebSocketClientNode as a child of the root node.
-The script can be attached to the root UI node.
-extends Control
+func _on_button_pressed():
+ var msg = text_edit.text
+ var msg_arr = ['text', [msg], {}]
+ var msg_str = JSON.stringify(msg_arr)
+ socket.send_text(msg_str)
+ text_edit.text = ""
-# The URL to connect to, should be your mud.
-var websocket_url = "ws://127.0.0.1:4008"
-
-# These are references to controls in the scene
-@onready
-var label: RichTextLabel = get_node("%ChatLog")
-@onready
-var txtEdit: TextEdit = get_node("%ChatInput")
-@onready
-var websocket = get_node("WebSocketClient")
-
-func _ready():
- # We connect the various signals
- websocket.connect('connected_to_server', self._connected)
- websocket.connect('connection_closed', self._closed)
- websocket.connect('message_received', self._on_data)
-
- # We attempt to connect and print out the error if we have one.
- var result = websocket.connect_to_url(websocket_url)
- if result != OK:
- print('Could not connect:' + str(result))
-
-
-func _closed():
- # This emits if the connection was closed by the remote host or unexpectedly
- print('Connection closed.')
- set_process(false)
-
-func _connected():
- # This emits when the connection succeeds.
- print('Connected!')
-
-func _on_data(data):
- # This is called when Godot receives data from evennia
- var json_data = JSON.parse_string(data)
- # Here we have the data from Evennia which is an array.
- # The first element will be text if it is a message
- # and would be the key of the OOB data you passed otherwise.
- if json_data[0] == 'text':
- # In this case, we simply append the data as bbcode to our label.
- for msg in json_data[1]: # Here we include a newline at every message.
- label.append_text("\n" + msg)
- elif json_data[0] == 'coordinates':
- # Dummy signal emitted if we wanted to handle the new coordinates
- # elsewhere in the project.
- self.emit_signal('updated_coordinates', json_data[1])
-
- # We only print this for easier debugging.
- print(data)
-
-func _on_button_pressed():
- # This is called when we press the button in the scene
- # with a connected signal, it sends the written message to Evennia.
- var msg = txtEdit.text
- var msg_arr = ['text', [msg], {}]
- var msg_str = JSON.stringify(msg_arr)
- websocket.send(msg_str)
+func _exit_tree():
+ socket.close()
diff --git a/docs/latest/Contribs/Contrib-RPSystem.html b/docs/latest/Contribs/Contrib-RPSystem.html
index e70bf7b7cc..8a4dd00e10 100644
--- a/docs/latest/Contribs/Contrib-RPSystem.html
+++ b/docs/latest/Contribs/Contrib-RPSystem.html
@@ -68,11 +68,16 @@
Roleplaying base system for Evennia
@@ -197,7 +202,11 @@ your objects, if you originally created them without this.
> type/reset/force me = typeclasses.characters.Character
-Examples:
+
+
+Usage
+
+Sdescs
> look
Tavern
@@ -220,6 +229,15 @@ The tavern is full of nice people
Note that by default, the case of the tag matters, so /tall will lead to ‘tall man’ while /Tall will become ‘Tall man’ and /TALL becomes /TALL MAN. If you don’t want this behavior, you can pass case_sensitive=False to the send_emote function.
+
+Language integration
+Speech can be identified as a particular language by prefixing it with the language key.
+emote says with a growl, orcish"Hello".
+
+
+This will identify the speech “Hello” as being spoken in orcish, and then pass that information on to process_language on your Character. By default, it doesn’t do much, but you can hook in a language system such as the rplanguage module below to do more interesting things.
+
+
Language and whisper obfuscation system
@@ -232,8 +250,8 @@ The tavern is full of nice people
Installation
This module adds no new commands; embed it in your say/emote/whisper commands.
-
-Usage:
+
+Usage:
from evennia.contrib.rpg.rpsystem import rplanguage
# need to be done once, here we create the "default" lang
diff --git a/docs/latest/Contribs/Contrib-Traits.html b/docs/latest/Contribs/Contrib-Traits.html
index 08a2058465..3c4010a668 100644
--- a/docs/latest/Contribs/Contrib-Traits.html
+++ b/docs/latest/Contribs/Contrib-Traits.html
@@ -72,8 +72,9 @@
Using traits
-
Trait types
-
Static trait
+
+
+Trait
+A single value of any type.
+This is the ‘base’ Trait, meant to inherit from if you want to invent
+trait-types from scratch (most of the time you’ll probably inherit from some of
+the more advanced trait-type classes though).
+Unlike other Trait-types, the single .value property of the base Trait can
+be editied. The value can hold any data that can be stored in an Attribute. If
+it’s an integer/float you can do arithmetic with it, but otherwise this acts just
+like a glorified Attribute.
+> obj.traits.add("mytrait", "My Trait", trait_type="trait", value=30)
+> obj.traits.mytrait.value
+30
+
+> obj.traits.mytrait.value = "stringvalue"
+> obj.traits.mytrait.value
+"stringvalue"
+
-Static trait
+Static trait
value = base + mod
The static trait has a base value and an optional mod-ifier. A typical use
of a static trait would be a Strength stat or Skill value. That is, something
@@ -319,6 +339,7 @@ that varies slowly or not at all, and which may be modified in-place.
+
Counter
min/unset base base+mod max/unset
@@ -477,27 +498,6 @@ get how filled it is as a percentage etc.
The .rate is particularly relevant for gauges - useful for everything
from poison slowly draining your health, to resting gradually increasing it.
-
-Trait
-A single value of any type.
-This is the ‘base’ Trait, meant to inherit from if you want to invent
-trait-types from scratch (most of the time you’ll probably inherit from some of
-the more advanced trait-type classes though).
-Unlike other Trait-types, the single .value property of the base Trait can
-be editied. The value can hold any data that can be stored in an Attribute. If
-it’s an integer/float you can do arithmetic with it, but otherwise this acts just
-like a glorified Attribute.
-> obj.traits.add("mytrait", "My Trait", trait_type="trait", value=30)
-> obj.traits.mytrait.value
-30
-
-> obj.traits.mytrait.value = "stringvalue"
-> obj.traits.mytrait.value
-"stringvalue"
-
-
-
-
Expanding with your own Traits
diff --git a/docs/latest/Howtos/Beginner-Tutorial/Beginner-Tutorial-Overview.html b/docs/latest/Howtos/Beginner-Tutorial/Beginner-Tutorial-Overview.html
index bc80beb3e1..cdbcb27ea5 100644
--- a/docs/latest/Howtos/Beginner-Tutorial/Beginner-Tutorial-Overview.html
+++ b/docs/latest/Howtos/Beginner-Tutorial/Beginner-Tutorial-Overview.html
@@ -329,8 +329,8 @@ Click here to see the full index of all parts and lessons of the Beginner-Tutori
11. Searching for things
-- 11.1. Main search functions
-- 11.2. Searching using Object.search
+- 11.1. Searching using Object.search
+- 11.2. Main search functions
- 11.3. What can be searched for
- 11.3.1. Search by key
- 11.3.2. Search by aliases
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 f8bfd4235f..70e9f46cfc 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
@@ -132,9 +132,7 @@ An example is look<
what is in it.
In Evennia, a Command is a Python class. If you are unsure about what a class is, review the
previous lesson about it! A Command inherits from evennia.Command or from one of the alternative command- classes, such as MuxCommand which is what most default commands use.
@@ -316,8 +314,7 @@ Echo: 'Woo Tang!'
You will get the docstring you put in your Command-class!
8.1.1. Making our cmdset persistent
-It’s getting a little annoying to have to re-add our cmdset every time we reload, right? It’s simple
-enough to make echo a persistent change though:
+It’s getting a little annoying to have to re-add our cmdset every time we reload, right? It’s simple enough to make echo a persistent change though:
> py self.cmdset.add("commands.mycommands.MyCmdSet", persistent=True)
@@ -431,10 +428,7 @@ You hit Bob with full force!
Line 3: The normal class header. We inherit from Command which we imported at the top of this file.
Lines 4-10: The docstring and help-entry for the command. You could expand on this as much as you wanted.
Line 11: We want to write hit to use this command.
-Line 14: We strip the whitespace from the argument like before. Since we don’t want to have to do
-self.args.strip() over and over, we store the stripped version
-in a local variable args. Note that we don’t modify self.args by doing this, self.args will still
-have the whitespace and is not the same as args in this example.
+Line 14: We strip the whitespace from the argument like before. Since we don’t want to have to do self.args.strip() over and over, we store the stripped version in a local variable args. Note that we don’t modify self.args by doing this, self.args will still have the whitespace and is not the same as args in this example.
-So we have a class Object that inherits from ObjectParent (which is empty) and DefaultObject, which we have imported from Evennia. The ObjectParent acts as a place to put code you want all
-of your Objects to have. We’ll focus on Object and DefaultObject for now.
+So we have a class Object that inherits from ObjectParent (which is empty) and DefaultObject, which we have imported from Evennia. The ObjectParent acts as a place to put code you want all of your Objects to have. We’ll focus on Object and DefaultObject for now.
The class itself doesn’t do anything (it just passes) but that doesn’t mean it’s useless. As we’ve seen, it inherits all the functionality of its parent. It’s in fact an exact replica of DefaultObject right now. Once we know what kind of methods and resources are available on DefaultObject we could add our own and change the way it works!
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:
@@ -262,8 +261,7 @@ Python Console is closing.
He’s still there… What we just did was to create a new entry in the database for Smaug. We gave the object its name (key) and set its location to our current location.
-
To make use of Smaug in code we must first find him in the database. For an object in the current
-location we can easily do this in py by using me.search():
+
To make use of Smaug in code we must first find him in the database. For an object in the current location we can easily do this in py by using me.search():
> py smaug = me.search("Smaug") ; smaug.firebreath()
Smaug breathes fire!
@@ -271,16 +269,12 @@ Smaug breathes fire!
7.1.2. Creating using create_object
-Creating Smaug like we did above is nice because it’s similar to how we created non-database
-bound Python instances before. But you need to use db_key instead of key and you also have to
-remember to call .save() afterwards. Evennia has a helper function that is more common to use,
-called create_object. Let’s recreate Cuddly this time:
+Creating Smaug like we did above is nice because it’s similar to how we created non-database bound Python instances before. But you need to use db_key instead of key and you also have to remember to call .save() afterwards. Evennia has a helper function that is more common to use, called create_object. Let’s recreate Cuddly this time:
> py evennia.create_object('typeclasses.monsters.Monster', key="Cuddly", location=here)
> look
-Boom, Cuddly should now be in the room with you, a little less scary than Smaug. You specify the
-python-path to the code you want and then set the key and location (if you had the Monster class already imported, you could have passed that too). Evennia sets things up and saves for you.
+Boom, Cuddly should now be in the room with you, a little less scary than Smaug. You specify the python-path to the code you want and then set the key and location (if you had the Monster class already imported, you could have passed that too). Evennia sets things up and saves for you.
If you want to find Cuddly from anywhere (not just in the same room), you can use Evennia’s search_object function:
> py cuddly = evennia.search_object("Cuddly")[0] ; cuddly.move_around()
Cuddly is moving!
@@ -346,8 +340,7 @@ 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.
+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
@@ -396,8 +389,7 @@ Persistent attributes:
-------------------------------------------------------------------------------
-We used the examine command briefly in the lesson about building in-game. Now these lines
-may be more useful to us:
+We used the examine command briefly in the lesson about building in-game. Now these lines may be more useful to us:
Name/key - The name of this thing. The value (#14) is probably different for you. This is the
unique ‘primary key’ or dbref for this entity in the database.
@@ -435,8 +427,7 @@ You create a new Object: box.
While it’s tempting to change folders around to your liking, this can make it harder to follow tutorials and may confuse if you are asking others for help. So don’t overdo it unless you really know what you are doing.
-So if you wanted the creation commands and methods to default to some other class you could
-add your own BASE_OBJECT_TYPECLASS line to mygame/server/conf/settings.py. The same is true for all the other typeclasseses, like characters, rooms and accounts. This way you can change the layout of your game dir considerably if you wanted. You just need to tell Evennia where everything is.
+So if you wanted the creation commands and methods to default to some other class you could add your own BASE_OBJECT_TYPECLASS line to mygame/server/conf/settings.py. The same is true for all the other typeclasseses, like characters, rooms and accounts. This way you can change the layout of your game dir considerably if you wanted. You just need to tell Evennia where everything is.
@@ -446,15 +437,16 @@ add your own BASE_O
(module docstring)
"""
from evennia import DefaultCharacter
+from .objects import ObjectParent
-class Character(DefaultCharacter):
+class Character(ObjectParent, DefaultCharacter):
"""
(class docstring)
"""
pass
-This looks quite familiar now - an empty class inheriting from the Evennia base typeclass (it’s even easier than Object since there is no equvalent ParentObject mixin class here). As you would expect, this is also the default typeclass used for creating Characters if you don’t specify it. You can verify it:
+This looks quite familiar now - an empty class inheriting from the Evennia base typeclassObjectParent. The ObjectParent (empty by default) is also here for adding any functionality shared by all types of Objects. As you would expect, this is also the default typeclass used for creating Characters if you don’t specify it. You can verify it:
> examine me
------------------------------------------------------------------------------
Name/key: YourName (#1)
@@ -486,16 +478,18 @@ Non-Persistent attributes:
Session id(s): This identifies the Session (that is, the individual connection to a player’s game client).
Account shows, well the Account object associated with this Character and Session.
-Stored/Merged Cmdsets and Commands available is related to which Commands are stored on you. We will get to them in the next lesson. For now it’s enough to know these consitute all the
-commands available to you at a given moment.
+Stored/Merged Cmdsets and Commands available is related to which Commands are stored on you. We will get to them in the next lesson. For now it’s enough to know these consitute all the commands available to you at a given moment.
Non-Persistent attributes are Attributes that are only stored temporarily and will go away on next reload.
Look at the Typeclass field and you’ll find that it points to typeclasses.character.Character as expected. So if we modify this class we’ll also modify ourselves.
7.3.1. A method on ourselves
Let’s try something simple first. Back in mygame/typeclasses/characters.py:
-
-class Character(DefaultCharacter):
+# in mygame/typeclasses/characters.py
+
+# ...
+
+class Character(ObjectParent, DefaultCharacter):
"""
(class docstring)
"""
@@ -555,8 +549,11 @@ Strength is 10.
After a reload all our changes were forgotten. When we change properties like this, it only changes in memory, not in the database (nor do we modify the python module’s code). So when we reloaded, the ‘fresh’ Character class was loaded, and it still has the original stats we wrote in it.
In principle we could change the python code. But we don’t want to do that manually every time. And more importantly since we have the stats hardcoded in the class, every character instance in the game will have exactly the same str, dex and int now! This is clearly not what we want.
Evennia offers a special, persistent type of property for this, called an Attribute. Rework your mygame/typeclasses/characters.py like this:
-
-class Character(DefaultCharacter):
+# in mygame/typeclasses/characters.py
+
+# ...
+
+class Character(ObjectParent, DefaultCharacter):
"""
(class docstring)
"""
@@ -610,10 +607,12 @@ AttributeError: 'Character' object has no attribute 'strength'
7.3.3. Setting things on new Characters
Things are looking better, but one thing remains strange - the stats start out with a value None and we have to manually set them to something reasonable. In a later lesson we will investigate character-creation in more detail. For now, let’s give every new character some random stats to start with.
We want those stats to be set only once, when the object is first created. For the Character, this method is called at_object_creation.
-# up by the other imports
+# in mygame/typeclasses/characters.py
+
+# ...
import random
-class Character(DefaultCharacter):
+class Character(ObjectParent, DefaultCharacter):
"""
(class docstring)
"""
@@ -630,8 +629,7 @@ AttributeError: 'Character' object has no attribute 'strength'
return self.db.strength, self.db.dexterity, self.db.intelligence
-We imported a new module, random. This is part of Python’s standard library. We used random.randint to
-set a random value from 3 to 18 to each stat. Simple, but for some classical RPGs this is all you need!
+We imported a new module, random. This is part of Python’s standard library. We used random.randint to set a random value from 3 to 18 to each stat. Simple, but for some classical RPGs this is all you need!
> reload
> py self.get_stats()
(12, 12, 15)
@@ -648,8 +646,7 @@ set a random value from 3 to 18 to each stat. Simple, but for some classical RPG
(5, 4, 8)
-Lady luck didn’t smile on us for this example; maybe you’ll fare better. Evennia has a helper command
-update that re-runs the creation hook and also cleans up any other Attributes not re-created by at_object_creation:
+Lady luck didn’t smile on us for this example; maybe you’ll fare better. Evennia has a helper command update that re-runs the creation hook and also cleans up any other Attributes not re-created by at_object_creation:
> update self
> py self.get_stats()
(8, 16, 14)
@@ -683,9 +680,7 @@ foo
Character.objects.all() is an example of a database query expressed in Python. This will be converted into a database query under the hood. This syntax is part of Django’s query language. You don’t need to know Django to use Evennia, but if you ever need more specific database queries, this is always available when you need it. We’ll get back to database queries in a later lesson.
-We import the Character class and then we use .objects.all() to get all Character instances. Simplified,
-.objects is a resource from which one can query for all Characters. Using .all() gets us a listing
-of all of them that we then immediately loop over. Boom, we just updated all Characters, including ourselves:
+We import the Character class and then we use .objects.all() to get all Character instances. Simplified, .objects is a resource from which one can query for all Characters. Using .all() gets us a listing of all of them that we then immediately loop over. Boom, we just updated all Characters, including ourselves:
> quit()
Closing the Python console.
> py self.get_stats()
@@ -696,8 +691,7 @@ Closing the Python console.
7.5. Conclusions
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 9f0b4e6a33..538e4c9673 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
@@ -280,8 +280,8 @@ will call character
13.2.1. Sitting on or in?
It’s fine to sit ‘on’ a chair. But what if our Sittable is an armchair?
-> py armchair = evennia.create_object("typeclasses.sittables.Sittable", key="armchair", location=here)
-> py armchair.do_sit(me)
+> py evennia.create_object("typeclasses.sittables.Sittable", key="armchair", location=here)
+> py self.search("armchair").do_sit(me)
You sit on armchair.
@@ -383,7 +383,7 @@ will call character
Since we haven’t added the sit command yet, we must still use py to test:
-> py armchair = evennia.search_object("armchair")[0];armchair.do_sit(me)
+> py self.search("armchair").do_sit(me)
You sit in armchair.
@@ -395,8 +395,8 @@ will call character
You can make this happen by tweaking your Sittable class having the return messages be replaceable by Attributes that you can set on the object you create. You want something like this:
-> py
-> chair = evennia.create_object("typeclasses.sittables.Sittable", key="pallet")
+> py
+> chair = evennia.create_object("typeclasses.sittables.Sittable", key="pallet", location=here)
> chair.do_sit(me)
You sit down on pallet.
> chair.do_stand(me)
@@ -406,7 +406,8 @@ You stand up from pallet.
You sit down and a whoopie cushion makes a loud fart noise!
-That is, if you are not setting the Attribute, you should get a default value. We leave this implementation up to the reader.
+That is, if you are not setting the Attribute, you should get a default value.
+We leave this implementation up to the reader.
@@ -846,8 +847,7 @@ You stand up from chair.
17
18
19
-20
-21# end of mygame/commands/sittables.py
+20 | # end of mygame/commands/sittables.py
class CmdStand2(Command):
"""
@@ -860,15 +860,14 @@ You stand up from chair.
key = "stand"
def func(self):
-
- caller = self.caller
- # if we are sitting, this should be set on us
- sittable = caller.db.is_sitting
- if not sittable:
- caller.msg("You are not sitting down.")
- else:
- sittable.do_stand(caller)
- |
+ caller = self.caller
+ # if we are sitting, this should be set on us
+ sittable = caller.db.is_sitting
+ if not sittable:
+ caller.msg("You are not sitting down.")
+ else:
+ sittable.do_stand(caller)
+
Line 17: We didn’t need the is_sitting Attribute for the first version of these Commands, but we do need it now. Since we have this, we don’t need to search and know just which chair we sit on. If we don’t have this Attribute set, we are not sitting anywhere.
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 428607fffa..d0c70b0fb6 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
@@ -134,8 +134,7 @@
also learn how to add, modify and extend Evennia’s default commands.
9.1. More advanced parsing
-In the last lesson we made a hit Command and struck a dragon with it. You should have the code
-from that still around.
+In the last lesson we made a hit Command and struck a dragon with it. You should have the code from that still around.
Let’s expand our simple hit command to accept a little more complex input:
hit <target> [[with] <weapon>]
@@ -280,29 +279,26 @@ under two conditions (from the example above):
-
+
Lines 29 and 35 - We make use of the previously parsed search terms for the target and weapon to find the
respective resource.
Lines 34-39 - Since the weapon is optional, we need to supply a default (use our fists!) if it’s not set. We
use this to create a weaponstr that is different depending on if we have a weapon or not.
-Lines 41-42 - We merge the weaponstr with our attack texts and send it to attacker and target respectively.
-Let’s try it out!
-
-reload
-hit smaug with sword
-Could not find ‘sword’.
-You hit smaug with bare fists!
-
-
+Lines 41-42 - We merge the weaponstr with our attack texts and send it to attacker and target respectively.
+Let’s try it out!
+> reload
+> hit smaug with sword
+Could not find 'sword'.
+You hit smaug with bare fists!
+
+
Oops, our self.caller.search(self.weapon) is telling us that it found no sword. This is reasonable (we don’t have a sword). Since we are not returning when failing to find a weapon in the way we do if we find no target, we still continue fighting with our bare hands.
This won’t do. Let’s make ourselves a sword:
-Since we didn’t specify /drop, the sword will end up in our inventory and can seen with the i or
-inventory command. The .search helper will still find it there. There is no need to reload to see this
-change (no code changed, only stuff in the database).
+Since we didn’t specify /drop, the sword will end up in our inventory and can seen with the i or inventory command. The .search helper will still find it there. There is no need to reload to see this change (no code changed, only stuff in the database).
> hit smaug with sword
You hit smaug with sword!
@@ -341,9 +337,24 @@ Who do you want to hit?
Who do you want to hit?
-In this case we don’t need both command-sets, so let’s just keep the one on the sword:
-> py self.cmdset.remove("commands.mycommands.MyCmdSet")
-> hit
+In this case we don’t need both command-sets, we should drop the version of hit sitting on our ourselves.
+Go to mygame/commands/default_cmdsets.py and find the line where you added
+MyCmdSet in the previous lesson. Delete or comment it out:
+# mygame/commands/default_cmdsets.py
+
+# ...
+
+class CharacterCmdSet(default_cmds.CharacterCmdSet):
+
+ # ...
+ def at_object_creation(self):
+
+ # self.add(MyCmdSet) # <---------
+
+
+
+Next reload and you’ll only have one hit command available:
+> hit
Who do you want to hit?
@@ -387,7 +398,7 @@ Command 'hit' is not available. ..
> Who do you want to hit?
-Finally, we get rid of ours sword so we have a clean slate with no more hit commands floating around. We can do that in two ways:
+After we’ve waved the sword around (hit a dragon or two), we will get rid of ours sword so we have a clean slate with no more hit commands floating around. We can do that in two ways:
@@ -456,8 +467,8 @@ Command 'hit' is not available. ..
The super() function refers to the parent of the current class and is commonly used to call same-named methods on the parent.
-evennia.default_cmds is a container that holds all of Evennia’s default commands and cmdsets. In this module we can see that this was imported and then a new child class was made for each cmdset. Each class looks familiar (except the key, that’s mainly used to easily identify the cmdset in listings). In each at_cmdset_creation all we do is call super().at_cmdset_creation which means that we call `at_cmdset_creation() on the parent CmdSet.
-This is what adds all the default commands to each CmdSet.
+evennia.default_cmds is a container that holds all of Evennia’s default commands and cmdsets. In this module we can see that this was imported and then a new child class was made for each cmdset. Each class looks familiar (except the key, that’s mainly used to easily identify the cmdset in listings). In each at_cmdset_creation all we do is call super().at_cmdset_creation which means that we call `at_cmdset_creation() on the parent CmdSet.
+This is what adds all the default commands to each CmdSet.
When the DefaultCharacter (or a child of it) is created, you’ll find that the equivalence of self.cmdset.add("default_cmdsets.CharacterCmdSet, persistent=True") gets called. This means that all new Characters get this cmdset. After adding more commands to it, you just need to reload to have all characters see it.
- 11. Searching for things