mirror of
https://github.com/evennia/evennia.git
synced 2026-03-27 10:16:32 +01:00
196 lines
7.2 KiB
Markdown
196 lines
7.2 KiB
Markdown
# Evennia for Diku Users
|
|
|
|
|
|
Evennia represents a learning curve for those who used to code on
|
|
[Diku](https://en.wikipedia.org/wiki/DikuMUD) type MUDs. While coding in Python is easy if you
|
|
already know C, the main effort is to get rid of old C programming habits. Trying to code Python the
|
|
way you code C will not only look ugly, it will lead to less optimal and harder to maintain code.
|
|
Reading Evennia example code is a good way to get a feel for how different problems are approached
|
|
in Python.
|
|
|
|
Overall, Python offers an extensive library of resources, safe memory management and excellent
|
|
handling of errors. While Python code does not run as fast as raw C code does, the difference is not
|
|
all that important for a text-based game. The main advantages of Python are an extremely fast
|
|
development cycle and easy ways to create game systems. Doing the same with C can take many times
|
|
more code and be harder to make stable and maintainable.
|
|
|
|
## Core Differences
|
|
|
|
- As mentioned, the main difference between Evennia and a Diku-derived codebase is that Evennia is
|
|
written purely in Python. Since Python is an interpreted language there is no compile stage. It is
|
|
modified and extended by the server loading Python modules at run-time. It also runs on all computer
|
|
platforms Python runs on (which is basically everywhere).
|
|
- Vanilla Diku type engines save their data in custom *flat file* type storage solutions. By
|
|
contrast, Evennia stores all game data in one of several supported SQL databases. Whereas flat files
|
|
have the advantage of being easier to implement, they (normally) lack many expected safety features
|
|
and ways to effectively extract subsets of the stored data. For example, if the server loses power
|
|
while writing to a flatfile it may become corrupt and the data lost. A proper database solution is
|
|
not susceptible to this - at no point is the data in a state where it cannot be recovered. Databases
|
|
are also highly optimized for querying large data sets efficiently.
|
|
|
|
## Some Familiar Things
|
|
|
|
Diku expresses the character object referenced normally by:
|
|
|
|
`struct char ch*` then all character-related fields can be accessed by `ch->`. In Evennia, one must
|
|
pay attention to what object you are using, and when you are accessing another through back-
|
|
handling, that you are accessing the right object. In Diku C, accessing character object is normally
|
|
done by:
|
|
|
|
```c
|
|
/* creating pointer of both character and room struct */
|
|
|
|
void(struct char ch*, struct room room*){
|
|
int dam;
|
|
if (ROOM_FLAGGED(room, ROOM_LAVA)){
|
|
dam = 100;
|
|
ch->damage_taken = dam;
|
|
}
|
|
}
|
|
```
|
|
|
|
As an example for creating Commands in Evennia via the `from evennia import Command` the character
|
|
object that calls the command is denoted by a class property as `self.caller`. In this example
|
|
`self.caller` is essentially the 'object' that has called the Command, but most of the time it is an
|
|
Account object. For a more familiar Diku feel, create a variable that becomes the account object as:
|
|
|
|
```python
|
|
#mygame/commands/command.py
|
|
|
|
from evennia import Command
|
|
|
|
class CmdMyCmd(Command):
|
|
"""
|
|
This is a Command Evennia Object
|
|
"""
|
|
|
|
[...]
|
|
|
|
def func(self):
|
|
ch = self.caller
|
|
# then you can access the account object directly by using the familiar ch.
|
|
ch.msg("...")
|
|
account_name = ch.name
|
|
race = ch.db.race
|
|
|
|
```
|
|
|
|
As mentioned above, care must be taken what specific object you are working with. If focused on a
|
|
room object and you need to access the account object:
|
|
|
|
```python
|
|
#mygame/typeclasses/room.py
|
|
|
|
from evennia import DefaultRoom
|
|
|
|
class MyRoom(DefaultRoom):
|
|
[...]
|
|
|
|
def is_account_object(self, object):
|
|
# a test to see if object is an account
|
|
[...]
|
|
|
|
def myMethod(self):
|
|
#self.caller would not make any sense, since self refers to the
|
|
# object of 'DefaultRoom', you must find the character obj first:
|
|
for ch in self.contents:
|
|
if self.is_account_object(ch):
|
|
# now you can access the account object with ch:
|
|
account_name = ch.name
|
|
race = ch.db.race
|
|
```
|
|
|
|
|
|
## Emulating Evennia to Look and Feel Like A Diku/ROM
|
|
|
|
To emulate a Diku Mud on Evennia some work has to be done before hand. If there is anything that all
|
|
coders and builders remember from Diku/Rom days is the presence of VNUMs. Essentially all data was
|
|
saved in flat files and indexed by VNUMs for easy access. Evennia has the ability to emulate VNUMS
|
|
to the extent of categorising rooms/mobs/objs/trigger/zones[...] into vnum ranges.
|
|
|
|
Evennia has objects that are called Scripts. As defined, they are the 'out of game' instances that
|
|
exist within the mud, but never directly interacted with. Scripts can be used for timers, mob AI,
|
|
and even stand alone databases.
|
|
|
|
Because of their wonderful structure all mob, room, zone, triggers, etc.. data can be saved in
|
|
independently created global scripts.
|
|
|
|
Here is a sample mob file from a Diku Derived flat file.
|
|
|
|
```text
|
|
#0
|
|
mob0~
|
|
mob0~
|
|
mob0
|
|
~
|
|
Mob0
|
|
~
|
|
10 0 0 0 0 0 0 0 0 E
|
|
1 20 9 0d0+10 1d2+0
|
|
10 100
|
|
8 8 0
|
|
E
|
|
#1
|
|
Puff dragon fractal~
|
|
Puff~
|
|
Puff the Fractal Dragon is here, contemplating a higher reality.
|
|
~
|
|
Is that some type of differential curve involving some strange, and unknown
|
|
calculus that she seems to be made out of?
|
|
~
|
|
516106 0 0 0 2128 0 0 0 1000 E
|
|
34 9 -10 6d6+340 5d5+5
|
|
340 115600
|
|
8 8 2
|
|
BareHandAttack: 12
|
|
E
|
|
T 95
|
|
```
|
|
Each line represents something that the MUD reads in and does something with it. This isn't easy to
|
|
read, but let's see if we can emulate this as a dictionary to be stored on a database script created
|
|
in Evennia.
|
|
|
|
First, let's create a global script that does absolutely nothing and isn't attached to anything. You
|
|
can either create this directly in-game with the @py command or create it in another file to do some
|
|
checks and balances if for whatever reason the script needs to be created again. It
|
|
can be done like so:
|
|
|
|
```python
|
|
from evennia import create_script
|
|
|
|
mob_db = create_script("typeclasses.scripts.DefaultScript", key="mobdb",
|
|
persistent=True, obj=None)
|
|
mob_db.db.vnums = {}
|
|
```
|
|
Just by creating a simple script object and assigning it a 'vnums' attribute as a type dictionary.
|
|
Next we have to create the mob layout..
|
|
|
|
```python
|
|
# vnum : mob_data
|
|
|
|
mob_vnum_1 = {
|
|
'key' : 'puff',
|
|
'sdesc' : 'puff the fractal dragon',
|
|
'ldesc' : 'Puff the Fractal Dragon is here, ' \
|
|
'contemplating a higher reality.',
|
|
'ddesc' : ' Is that some type of differential curve ' \
|
|
'involving some strange, and unknown calculus ' \
|
|
'that she seems to be made out of?',
|
|
[...]
|
|
}
|
|
|
|
# Then saving it to the data, assuming you have the script obj stored in a variable.
|
|
mob_db.db.vnums[1] = mob_vnum_1
|
|
```
|
|
|
|
This is a very 'caveman' example, but it gets the idea across. You can use the keys in the
|
|
`mob_db.vnums` to act as the mob vnum while the rest contains the data.
|
|
|
|
Much simpler to read and edit. If you plan on taking this route, you must keep in mind that by
|
|
default evennia 'looks' at different properties when using the `look` command for instance. If you
|
|
create an instance of this mob and make its `self.key = 1`, by default evennia will say:
|
|
|
|
`Here is : 1`
|
|
|
|
You must restructure all default commands so that the mud looks at different properties defined on
|
|
your mob.
|