2011-09-10 23:44:49 +02:00
|
|
|
Scripts
|
|
|
|
|
=======
|
|
|
|
|
|
2012-09-30 10:32:41 +02:00
|
|
|
*Scripts* are the out-of-character siblings to the in-character
|
|
|
|
|
`Objects <Objects.html>`_. The name "Script" might suggest that they can
|
|
|
|
|
only be used to script the game but this is only part of their
|
|
|
|
|
usefulness (in the end we had to pick a single name for them). Scripts
|
|
|
|
|
are full Typeclassed database entities, just like Objects - with all the
|
|
|
|
|
advantages this entails. Likewise they can also store arbitrary
|
|
|
|
|
*Attributes*.
|
|
|
|
|
|
|
|
|
|
Scripts can be used for many different things in Evennia:
|
|
|
|
|
|
|
|
|
|
- They can attach to Objects to influence them in various ways - or
|
|
|
|
|
exist independently of any one in-game entity.
|
|
|
|
|
- They can work as timers and tickers - anything that may change with
|
|
|
|
|
Time. But they can also have no time dependence at all.
|
|
|
|
|
- They can describe State changes.
|
|
|
|
|
- They can act as data stores for storing game data persistently in the
|
2013-05-15 23:49:38 +02:00
|
|
|
database.
|
|
|
|
|
- They can be used as OOC stores for sharing data between groups of
|
|
|
|
|
objects.
|
2011-09-10 23:44:49 +02:00
|
|
|
|
|
|
|
|
The most obvious use of Scripts may be to use them as *timers* or
|
|
|
|
|
*Events*. Consider a script running on the object ``Grandfather Clock``.
|
|
|
|
|
The script has a timer that tells it to fire every hour. This allows the
|
|
|
|
|
clock to chime regularly. The script might even regulate how often the
|
|
|
|
|
clock must be rewound so it won't stop.
|
|
|
|
|
|
|
|
|
|
Scripts may act as changeable *States*. Consider for example creating a
|
|
|
|
|
'dark' room. It has two scripts assigned on it - one ``DarkState``
|
2012-09-30 10:32:41 +02:00
|
|
|
script, and one ``BrightState`` script. When characters enter the dark
|
|
|
|
|
room, it assigns a custom `Cmdset <Commands.html>`_ to them. This
|
|
|
|
|
command set defines the parameters of the state they describe. In this
|
|
|
|
|
case it limits their actions because of the darkness. After the
|
|
|
|
|
characters have stumbled around for a while, someone brings up a torch.
|
|
|
|
|
As a light source is now in the room, ``DarkState`` reacts to this by
|
|
|
|
|
shutting down itself and handing over control to the ``BrightState``
|
|
|
|
|
script that restores normal commands. Finally, when the character with
|
|
|
|
|
the torch leaves the room, the ``BrightState`` script detects this and
|
|
|
|
|
obediently hands control back to the ``DarkState``, leaving the
|
|
|
|
|
remaining poor characters in darkness once again.
|
2011-09-10 23:44:49 +02:00
|
|
|
|
|
|
|
|
By combining state-changes with timers one can make a room look
|
|
|
|
|
different during nighttime than it does during the day. Weather and
|
|
|
|
|
seasons might come and go. But one can also achieve more complex things
|
2011-12-20 18:54:27 +01:00
|
|
|
such as state-AI systems that make mobs move around and possibly pursue
|
2011-09-10 23:44:49 +02:00
|
|
|
characters between rooms.
|
|
|
|
|
|
2012-09-30 10:32:41 +02:00
|
|
|
Scripts are also excellent places to store game data in an OOC way. A
|
|
|
|
|
groupd of objects may share date by use of a Script object they all hold
|
|
|
|
|
references to.
|
|
|
|
|
|
|
|
|
|
In short, Scripts can be used for a lot of things.
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
How to create and test your own Script types
|
|
|
|
|
--------------------------------------------
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
In-game you can try out scripts using the ``@script`` command. Try the
|
|
|
|
|
following:
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
::
|
|
|
|
|
|
|
|
|
|
> @script self = examples.bodyfunctions.BodyFunctions
|
|
|
|
|
|
|
|
|
|
This should cause some random messages. Add the ``/stop`` switch to the
|
|
|
|
|
above command to kill the script again. You can use the ``@scripts``
|
|
|
|
|
command to list all active scripts in the game. Evennia creates a few
|
|
|
|
|
default ones.
|
2011-09-10 23:44:49 +02:00
|
|
|
|
|
|
|
|
Custom script modules are usually stored in ``game/gamesrc/scripts``. As
|
2012-05-01 17:37:37 +02:00
|
|
|
a convenience you can inherit sripts from ``ev.Script``.
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
If you add scripts to `Objects <Objects.html>`_ the script can then
|
|
|
|
|
manipulate the object as desired. The script is added to the object's
|
|
|
|
|
*script handler*, called simply ``scripts``. The handler takes care of
|
|
|
|
|
all initialization and startup of the script for you.
|
2011-09-10 23:44:49 +02:00
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
# add script to myobj's scripthandler
|
|
|
|
|
myobj.scripts.add("game.gamesrc.scripts.myscripts.CoolScript")
|
|
|
|
|
# alternative way
|
|
|
|
|
from ev import create_script
|
|
|
|
|
create_script("game.gamesrc.scripts.myscripts.CoolScript", obj=myobj)
|
|
|
|
|
|
|
|
|
|
A script does not have to be connected to an in-game object. Such
|
|
|
|
|
scripts are called *Global scripts*. You can create global scripts by
|
|
|
|
|
simply not supplying an object to store it on:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
# adding a global script
|
|
|
|
|
from ev import create_script
|
|
|
|
|
create_script("game.gamesrc.scripts.globals.MyGlobalEconomy", key="economy", obj=None)
|
|
|
|
|
|
|
|
|
|
Assuming the Script ``game.gamesrc.scripts.globals.MyGlobalEconomy``
|
|
|
|
|
exists, this will create and start it as a global script.
|
|
|
|
|
|
|
|
|
|
Properties and functions defined on Scripts
|
|
|
|
|
-------------------------------------------
|
|
|
|
|
|
|
|
|
|
A Script has all the properties of a typeclassed object, such as ``db``
|
|
|
|
|
and ``ndb``\ (see `Typeclasses <Typeclasses.html>`_). Setting ``key`` is
|
|
|
|
|
useful in order to manage scripts (delete them by name etc). These are
|
|
|
|
|
usually set up in the Script's typeclass, but can also be assigned on
|
|
|
|
|
the fly as keyword arguments to ``ev.create_script``.
|
|
|
|
|
|
|
|
|
|
- ``desc`` - an optional description of the script's function. Seen in
|
|
|
|
|
script listings.
|
|
|
|
|
- ``interval`` - how often the script should run. If ``interval == 0``
|
|
|
|
|
(default), it runs forever, without any repeating (it will not accept
|
|
|
|
|
a negative value).
|
|
|
|
|
- ``start_delay`` - (bool), if we should wait ``interval`` seconds
|
|
|
|
|
before firing for the first time or not.
|
|
|
|
|
- ``repeats`` - How many times we should repeat, assuming
|
|
|
|
|
``interval > 0``. If repeats is set to ``<= 0``, the script will
|
|
|
|
|
repeat indefinitely.
|
|
|
|
|
- ``persistent``- if this script should survive a server *reset* or
|
|
|
|
|
server *shutdown*. (You don't need to set this for it to survive a
|
|
|
|
|
normal reload - the script will be paused and seamlessly restart
|
|
|
|
|
after the reload is complete).
|
|
|
|
|
|
|
|
|
|
There is one special property:
|
|
|
|
|
|
|
|
|
|
- ``obj`` - the `Object <Objects.html>`_ this script is attached to (if
|
|
|
|
|
any). You should not need to set this manually. If you add the script
|
|
|
|
|
to the Object with ``myobj.scripts.add(myscriptpath)`` or give
|
|
|
|
|
``myobj`` as an argument to the ``utils.create.create_script``
|
|
|
|
|
function, the ``obj`` property will be set to ``myobj`` for you.
|
|
|
|
|
|
|
|
|
|
It's also imperative to know the hook functions. Normally, overriding
|
|
|
|
|
these are all the customization you'll need to do in Scripts. You can
|
|
|
|
|
find longer descriptions of these in ``src/scripts/scripts.py``.
|
|
|
|
|
|
|
|
|
|
- ``at_script_creation()`` - this is usually where the script class
|
|
|
|
|
sets things like ``interval`` and ``repeats``; things that control
|
|
|
|
|
how the script runs. It is only called once - when the script is
|
|
|
|
|
first created.
|
|
|
|
|
- ``is_valid()`` - determines if the script should still be running or
|
|
|
|
|
not. This is called when running ``obj.scripts.validate()``, which
|
|
|
|
|
you can run manually, but which also Evennia calls during certain
|
|
|
|
|
situations such as reloads. This is also useful for using scripts as
|
|
|
|
|
state managers. If the method returns ``False``, the script is
|
|
|
|
|
stopped and cleanly removed.
|
2013-12-02 16:43:44 +01:00
|
|
|
- ``at_start()`` - this is called when the script starts or is
|
|
|
|
|
unpaused. For persistent scripts this is at least once ever server
|
|
|
|
|
startup. Note that this will *always* be called right away, also if
|
|
|
|
|
``start_delay`` is ``True``.
|
2013-05-15 23:49:38 +02:00
|
|
|
- ``at_repeat()`` - this is called every ``interval`` seconds, or not
|
|
|
|
|
at all. It is called right away at startup, unless ``start_delay`` is
|
|
|
|
|
``True``, in which case the system will wait ``interval`` seconds
|
|
|
|
|
before calling.
|
|
|
|
|
- ``at_stop()`` - this is called when the script stops for whatever
|
|
|
|
|
reason. It's a good place to do custom cleanup.
|
|
|
|
|
- ``at_server_reload()`` - this is called whenever the server is
|
|
|
|
|
warm-rebooted (e.g. with the ``@reload`` command). It's a good place
|
|
|
|
|
to save non-persistent data you might want to survive a reload.
|
|
|
|
|
- ``at_server_shutdown()`` - this is called when a system reset or
|
|
|
|
|
systems shutdown is invoked.
|
|
|
|
|
|
|
|
|
|
Running methods (usually called automatically by the engine, but
|
|
|
|
|
possible to also invoke manually)
|
|
|
|
|
|
|
|
|
|
- ``start()`` - this will start the script. This is called
|
|
|
|
|
automatically whenever you add a new script to a handler.
|
|
|
|
|
``at_start()`` will be called.
|
|
|
|
|
- ``stop()`` - this will stop the script and delete it. Removing a
|
|
|
|
|
script from a handler will stop it automatically. ``at_stop()`` will
|
|
|
|
|
be called.
|
|
|
|
|
- ``pause()`` - this pauses a running script, rendering it inactive,
|
|
|
|
|
but not deleting it. All properties are saved and timers can be
|
|
|
|
|
resumed. This is called automatically when the server reloads. No
|
|
|
|
|
hooks are called - as far as the script knows, it never stopped -
|
|
|
|
|
this is a suspension of the script, not a change of state.
|
2013-12-02 16:43:44 +01:00
|
|
|
- ``unpause()`` - resumes a previously paused script. The at\_start()
|
|
|
|
|
hook will be called to allow it to reclaim its internal state. Timers
|
|
|
|
|
etc are restored to what they were before pause. The server unpauses
|
|
|
|
|
all paused scripts after a server reload.
|
2013-05-15 23:49:38 +02:00
|
|
|
- ``time_until_next_repeat()`` - for timed scripts, this returns the
|
|
|
|
|
time in seconds until it next fires. Returns ``None`` if
|
|
|
|
|
``interval==0``.
|
|
|
|
|
|
|
|
|
|
Example script
|
|
|
|
|
--------------
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
::
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
import random
|
|
|
|
|
from ev import Script
|
|
|
|
|
class Weather(Script):
|
|
|
|
|
"Displays weather info. Meant to be attached to a room."
|
|
|
|
|
def at_script_creation(self):
|
|
|
|
|
"Called once, during initial creation"
|
|
|
|
|
self.key = "weather_script"
|
|
|
|
|
self.desc = "Gives random weather messages."
|
|
|
|
|
self.interval = 60 * 5 # every 5 minutes
|
|
|
|
|
self.persistent = True
|
|
|
|
|
def at_repeat(self):
|
|
|
|
|
"called every self.interval seconds."
|
|
|
|
|
rand = random.random()
|
|
|
|
|
if rand < 0.5:
|
|
|
|
|
weather = "A faint breeze is felt."
|
|
|
|
|
elif rand < 0.7:
|
|
|
|
|
weather = "Clouds sweep across the sky."
|
|
|
|
|
else:
|
|
|
|
|
weather = "There is a light drizzle of rain."
|
|
|
|
|
# send this message to everyone inside the object this
|
|
|
|
|
# script is attached to (likely a room)
|
|
|
|
|
self.obj.msg_contents(weather)
|
|
|
|
|
|
|
|
|
|
This is a simple weather script that we can put on an object. Every 5
|
|
|
|
|
minutes it will tell everyone inside that object how the weather is.
|
|
|
|
|
|
|
|
|
|
To activate it, just add it to the script handler (``scripts``) on an
|
|
|
|
|
`Room <Objects.html>`_. That object becomes ``self.obj`` in the example
|
|
|
|
|
above. Here we put it on a room called ``myroom``:
|
2011-09-10 23:44:49 +02:00
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
myroom.scripts.add(weather.Weather)
|
|
|
|
|
|
|
|
|
|
In code you can also use the create function directly if you know how to
|
|
|
|
|
locate the room you want:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
from ev import create_script
|
|
|
|
|
create_script('game.gamesrc.scripts.weather.Weather', obj=myroom)
|
2011-09-10 23:44:49 +02:00
|
|
|
|
2013-05-15 23:49:38 +02:00
|
|
|
Or, from in-game, use the ``@script`` command:
|
2011-12-20 18:54:27 +01:00
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2012-06-26 17:45:12 +02:00
|
|
|
@script here = weather.Weather
|
2011-09-10 23:44:49 +02:00
|
|
|
|