2020-04-07 23:13:24 +02:00
|
|
|
# Objects
|
|
|
|
|
|
|
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
All in-game objects in Evennia, be it characters, chairs, monsters, rooms or hand grenades are
|
|
|
|
|
represented by an Evennia *Object*. Objects form the core of Evennia and is probably what you'll
|
2021-10-21 21:04:14 +02:00
|
|
|
spend most time working with. Objects are [Typeclassed](./Typeclasses.md) entities.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
2022-03-05 00:44:32 +01:00
|
|
|
An Evennia Object is, by definition, a Python class that includes
|
|
|
|
|
[evennia.objects.objects.DefaultObject](evennia.objects.objects.DefaultObject) among its
|
|
|
|
|
parents. Evennia defines several subclasses of `DefaultObject`:
|
|
|
|
|
|
|
|
|
|
- [evennia.objects.objects.DefaultCharacter](evennia.objects.objects.DefaultCharacter) -
|
|
|
|
|
the normal in-game Character, controlled by a player.
|
|
|
|
|
- [evennia.objects.objects.DefaultRoom](evennia.objects.objects.DefaultRoom) - a location in the game world.
|
|
|
|
|
- [evennia.objects.objects.DefaultExit](evennia.objects.objects.DefaultExit) - an entity that (usually) sits
|
|
|
|
|
in a room and represents a one-way connection to another location.
|
|
|
|
|
|
|
|
|
|
You will usually not use the `Default*` parents themselves. In `mygame/typeclasses/` there are
|
|
|
|
|
convenient subclasses to use. They are empty, and thus identical to
|
|
|
|
|
the defaults. Tweaking them is one of the main ways to customize you game!
|
|
|
|
|
|
|
|
|
|
- `mygame.typeclasses.objects.Object` (inherits from `DefaultObject`)
|
|
|
|
|
- `mygame.typeclasses.characters.Character` (inherits from `DefaultCharacter`)
|
|
|
|
|
- `mygame.typeclasses.rooms.Room` (inherits from `DefaultRoom`)
|
|
|
|
|
- `mygame.typeclasses.exits.Exit` (inherits from `DefaultExit`)
|
|
|
|
|
|
2020-04-07 23:13:24 +02:00
|
|
|
## How to create your own object types
|
|
|
|
|
|
2022-03-05 00:44:32 +01:00
|
|
|
You can easily add your own in-game behavior by either modifying one of the typeclasses in
|
|
|
|
|
your game dir or by inheriting from them.
|
|
|
|
|
|
|
|
|
|
You can put your new typeclass directly in the relevant parent
|
|
|
|
|
module, or you could organize your code in some other way. Here we assume we make a new module
|
2020-06-16 16:53:35 +02:00
|
|
|
`mygame/typeclasses/flowers.py`:
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# mygame/typeclasses/flowers.py
|
|
|
|
|
|
|
|
|
|
from typeclasses.objects import Object
|
|
|
|
|
|
|
|
|
|
class Rose(Object):
|
|
|
|
|
"""
|
|
|
|
|
This creates a simple rose object
|
|
|
|
|
"""
|
|
|
|
|
def at_object_creation(self):
|
|
|
|
|
"this is called only once, when object is first created"
|
|
|
|
|
# add a persistent attribute 'desc'
|
|
|
|
|
# to object (silly example).
|
|
|
|
|
self.db.desc = "This is a pretty rose with thorns."
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-05 00:44:32 +01:00
|
|
|
Now you just need to point to the class *Rose* with the `create` command
|
2020-06-16 16:53:35 +02:00
|
|
|
to make a new rose:
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
@create/drop MyRose:flowers.Rose
|
|
|
|
|
|
2022-03-05 00:44:32 +01:00
|
|
|
What the `create` command actually *does* is to use the [evennia.create_object](evennia.utils.create.create_object)
|
|
|
|
|
function. You can do the same thing yourself in code:
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
from evennia import create_object
|
|
|
|
|
new_rose = create_object("typeclasses.flowers.Rose", key="MyRose")
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-05 00:44:32 +01:00
|
|
|
(The `create` command will auto-append the most likely path to your typeclass, if you enter the
|
2020-06-16 16:53:35 +02:00
|
|
|
call manually you have to give the full path to the class. The `create.create_object` function is
|
|
|
|
|
powerful and should be used for all coded object creating (so this is what you use when defining
|
2022-03-05 00:44:32 +01:00
|
|
|
your own building commands).
|
2020-04-07 23:13:24 +02:00
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
This particular Rose class doesn't really do much, all it does it make sure the attribute
|
|
|
|
|
`desc`(which is what the `look` command looks for) is pre-set, which is pretty pointless since you
|
2022-03-05 00:44:32 +01:00
|
|
|
will usually want to change this at build time (using the `desc` command or using the
|
|
|
|
|
[Spawner](./Prototypes.md)).
|
|
|
|
|
|
|
|
|
|
## Adding common functionality
|
|
|
|
|
|
|
|
|
|
`Object`, `Character`, `Room` and `Exit` also inherit from `mygame.typeclasses.objects.ObjectParent`.
|
|
|
|
|
This is an empty 'mixin' class. Optionally, you can modify this class if you want to easily add some _common_ functionality to all
|
|
|
|
|
your Objects, Characters, Rooms and Exits at once. You can still customize each subclass separately (see the Python
|
|
|
|
|
docs on [multiple inheritance](https://docs.python.org/3/tutorial/classes.html#multiple-inheritance) for details).
|
|
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# in mygame/typeclasses/objects.py
|
|
|
|
|
# ...
|
|
|
|
|
|
|
|
|
|
from evennia.objects.objects import DefaultObject
|
|
|
|
|
|
|
|
|
|
class ObjectParent:
|
|
|
|
|
def at_pre_get(self, getter, **kwargs):
|
|
|
|
|
# make all entities by default un-pickable
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
class Object(ObjectParent, DefaultObject):
|
|
|
|
|
# replaces at_pre_get with its own
|
|
|
|
|
def at_pre_get(self, getter, **kwargs):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
# each in their respective modules ...
|
|
|
|
|
|
|
|
|
|
class Character(ObjectParent, DefaultCharacter):
|
|
|
|
|
# will inherit at_pre_get from ObjectParent
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
class Exit(ObjectParent, DefaultExit):
|
|
|
|
|
# Overrides and uses the DefaultExit version of at_pre_get instead
|
|
|
|
|
def at_pre_get(self, getter, **kwargs):
|
|
|
|
|
return DefaultExit.at_pre_get(self, getter, **kwargs)
|
|
|
|
|
|
|
|
|
|
```
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
## Properties and functions on Objects
|
|
|
|
|
|
2021-10-21 21:04:14 +02:00
|
|
|
Beyond the properties assigned to all [typeclassed](./Typeclasses.md) objects (see that page for a list
|
2020-06-16 16:53:35 +02:00
|
|
|
of those), the Object also has the following custom properties:
|
2020-04-07 23:13:24 +02:00
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
- `aliases` - a handler that allows you to add and remove aliases from this object. Use
|
|
|
|
|
`aliases.add()` to add a new alias and `aliases.remove()` to remove one.
|
2020-04-07 23:13:24 +02:00
|
|
|
- `location` - a reference to the object currently containing this object.
|
2020-06-16 16:53:35 +02:00
|
|
|
- `home` is a backup location. The main motivation is to have a safe place to move the object to if
|
|
|
|
|
its `location` is destroyed. All objects should usually have a home location for safety.
|
|
|
|
|
- `destination` - this holds a reference to another object this object links to in some way. Its
|
2021-10-21 21:04:14 +02:00
|
|
|
main use is for [Exits](./Objects.md#exits), it's otherwise usually unset.
|
|
|
|
|
- `nicks` - as opposed to aliases, a [Nick](./Nicks.md) holds a convenient nickname replacement for a
|
2020-06-16 16:53:35 +02:00
|
|
|
real name, word or sequence, only valid for this object. This mainly makes sense if the Object is
|
|
|
|
|
used as a game character - it can then store briefer shorts, example so as to quickly reference game
|
|
|
|
|
commands or other characters. Use nicks.add(alias, realname) to add a new one.
|
2021-10-21 21:04:14 +02:00
|
|
|
- `account` - this holds a reference to a connected [Account](./Accounts.md) controlling this object (if
|
2020-06-16 16:53:35 +02:00
|
|
|
any). Note that this is set also if the controlling account is *not* currently online - to test if
|
|
|
|
|
an account is online, use the `has_account` property instead.
|
|
|
|
|
- `sessions` - if `account` field is set *and the account is online*, this is a list of all active
|
|
|
|
|
sessions (server connections) to contact them through (it may be more than one if multiple
|
|
|
|
|
connections are allowed in settings).
|
|
|
|
|
- `has_account` - a shorthand for checking if an *online* account is currently connected to this
|
|
|
|
|
object.
|
|
|
|
|
- `contents` - this returns a list referencing all objects 'inside' this object (i,e. which has this
|
|
|
|
|
object set as their `location`).
|
|
|
|
|
- `exits` - this returns all objects inside this object that are *Exits*, that is, has the
|
|
|
|
|
`destination` property set.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
The last two properties are special:
|
|
|
|
|
|
2021-10-21 21:04:14 +02:00
|
|
|
- `cmdset` - this is a handler that stores all [command sets](./Command-Sets.md) defined on the
|
2020-06-16 16:53:35 +02:00
|
|
|
object (if any).
|
2021-10-21 21:04:14 +02:00
|
|
|
- `scripts` - this is a handler that manages [Scripts](./Scripts.md) attached to the object (if any).
|
2020-04-07 23:13:24 +02:00
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
The Object also has a host of useful utility functions. See the function headers in
|
|
|
|
|
`src/objects/objects.py` for their arguments and more details.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
- `msg()` - this function is used to send messages from the server to an account connected to this
|
|
|
|
|
object.
|
2020-04-07 23:13:24 +02:00
|
|
|
- `msg_contents()` - calls `msg` on all objects inside this object.
|
2020-06-16 16:53:35 +02:00
|
|
|
- `search()` - this is a convenient shorthand to search for a specific object, at a given location
|
|
|
|
|
or globally. It's mainly useful when defining commands (in which case the object executing the
|
|
|
|
|
command is named `caller` and one can do `caller.search()` to find objects in the room to operate
|
|
|
|
|
on).
|
2020-04-07 23:13:24 +02:00
|
|
|
- `execute_cmd()` - Lets the object execute the given string as if it was given on the command line.
|
2020-06-16 16:53:35 +02:00
|
|
|
- `move_to` - perform a full move of this object to a new location. This is the main move method
|
|
|
|
|
and will call all relevant hooks, do all checks etc.
|
2021-10-21 21:04:14 +02:00
|
|
|
- `clear_exits()` - will delete all [Exits](./Objects.md#exits) to *and* from this object.
|
2020-06-16 16:53:35 +02:00
|
|
|
- `clear_contents()` - this will not delete anything, but rather move all contents (except Exits) to
|
|
|
|
|
their designated `Home` locations.
|
2020-04-07 23:13:24 +02:00
|
|
|
- `delete()` - deletes this object, first calling `clear_exits()` and
|
|
|
|
|
`clear_contents()`.
|
|
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
The Object Typeclass defines many more *hook methods* beyond `at_object_creation`. Evennia calls
|
|
|
|
|
these hooks at various points. When implementing your custom objects, you will inherit from the
|
|
|
|
|
base parent and overload these hooks with your own custom code. See `evennia.objects.objects` for an
|
2021-10-21 21:04:14 +02:00
|
|
|
updated list of all the available hooks or the [API for DefaultObject here](evennia.objects.objects.DefaultObject).
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
## Subclasses of `Object`
|
|
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
There are three special subclasses of *Object* in default Evennia - *Characters*, *Rooms* and
|
|
|
|
|
*Exits*. The reason they are separated is because these particular object types are fundamental,
|
|
|
|
|
something you will always need and in some cases requires some extra attention in order to be
|
|
|
|
|
recognized by the game engine (there is nothing stopping you from redefining them though). In
|
|
|
|
|
practice they are all pretty similar to the base Object.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
### Characters
|
|
|
|
|
|
2021-10-21 21:04:14 +02:00
|
|
|
Characters are objects controlled by [Accounts](./Accounts.md). When a new Account
|
2020-04-07 23:13:24 +02:00
|
|
|
logs in to Evennia for the first time, a new `Character` object is created and
|
|
|
|
|
the Account object is assigned to the `account` attribute. A `Character` object
|
2021-10-21 21:04:14 +02:00
|
|
|
must have a [Default Commandset](./Command-Sets.md) set on itself at
|
2020-04-07 23:13:24 +02:00
|
|
|
creation, or the account will not be able to issue any commands! If you just
|
|
|
|
|
inherit your own class from `evennia.DefaultCharacter` and make sure to use
|
|
|
|
|
`super()` to call the parent methods you should be fine. In
|
|
|
|
|
`mygame/typeclasses/characters.py` is an empty `Character` class ready for you
|
|
|
|
|
to modify.
|
|
|
|
|
|
|
|
|
|
### Rooms
|
|
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
*Rooms* are the root containers of all other objects. The only thing really separating a room from
|
|
|
|
|
any other object is that they have no `location` of their own and that default commands like `@dig`
|
|
|
|
|
creates objects of this class - so if you want to expand your rooms with more functionality, just
|
|
|
|
|
inherit from `ev.DefaultRoom`. In `mygame/typeclasses/rooms.py` is an empty `Room` class ready for
|
|
|
|
|
you to modify.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
### Exits
|
|
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
*Exits* are objects connecting other objects (usually *Rooms*) together. An object named *North* or
|
|
|
|
|
*in* might be an exit, as well as *door*, *portal* or *jump out the window*. An exit has two things
|
|
|
|
|
that separate them from other objects. Firstly, their *destination* property is set and points to a
|
|
|
|
|
valid object. This fact makes it easy and fast to locate exits in the database. Secondly, exits
|
2021-10-21 21:04:14 +02:00
|
|
|
define a special [Transit Command](./Commands.md) on themselves when they are created. This command is
|
2020-06-16 16:53:35 +02:00
|
|
|
named the same as the exit object and will, when called, handle the practicalities of moving the
|
|
|
|
|
character to the Exits's *destination* - this allows you to just enter the name of the exit on its
|
|
|
|
|
own to move around, just as you would expect.
|
|
|
|
|
|
|
|
|
|
The exit functionality is all defined on the Exit typeclass, so you could in principle completely
|
|
|
|
|
change how exits work in your game (it's not recommended though, unless you really know what you are
|
2021-10-21 21:04:14 +02:00
|
|
|
doing). Exits are [locked](./Locks.md) using an access_type called *traverse* and also make use of a few
|
2020-06-16 16:53:35 +02:00
|
|
|
hook methods for giving feedback if the traversal fails. See `evennia.DefaultExit` for more info.
|
|
|
|
|
In `mygame/typeclasses/exits.py` there is an empty `Exit` class for you to modify.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
|
|
|
|
The process of traversing an exit is as follows:
|
|
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
1. The traversing `obj` sends a command that matches the Exit-command name on the Exit object. The
|
2021-10-21 21:04:14 +02:00
|
|
|
[cmdhandler](./Commands.md) detects this and triggers the command defined on the Exit. Traversal always
|
2020-06-16 16:53:35 +02:00
|
|
|
involves the "source" (the current location) and the `destination` (this is stored on the Exit
|
|
|
|
|
object).
|
2020-04-07 23:13:24 +02:00
|
|
|
1. The Exit command checks the `traverse` lock on the Exit object
|
|
|
|
|
1. The Exit command triggers `at_traverse(obj, destination)` on the Exit object.
|
2020-06-16 16:53:35 +02:00
|
|
|
1. In `at_traverse`, `object.move_to(destination)` is triggered. This triggers the following hooks,
|
|
|
|
|
in order:
|
2021-10-31 22:18:58 +01:00
|
|
|
1. `obj.at_pre_move(destination)` - if this returns False, move is aborted.
|
|
|
|
|
1. `origin.at_pre_leave(obj, destination)`
|
2020-04-07 23:13:24 +02:00
|
|
|
1. `obj.announce_move_from(destination)`
|
|
|
|
|
1. Move is performed by changing `obj.location` from source location to `destination`.
|
|
|
|
|
1. `obj.announce_move_to(source)`
|
|
|
|
|
1. `destination.at_object_receive(obj, source)`
|
2021-10-31 22:18:58 +01:00
|
|
|
1. `obj.at_post_move(source)`
|
|
|
|
|
1. On the Exit object, `at_post_traverse(obj, source)` is triggered.
|
2020-04-07 23:13:24 +02:00
|
|
|
|
2020-06-16 16:53:35 +02:00
|
|
|
If the move fails for whatever reason, the Exit will look for an Attribute `err_traverse` on itself
|
|
|
|
|
and display this as an error message. If this is not found, the Exit will instead call
|
|
|
|
|
`at_failed_traverse(obj)` on itself.
|