In order for your Jython code to make things happen in Wyvern,
it has to call game functions. The game functions are all
written in Java. This tutorial will show you how to access
those functions.
First, make a bookmark to this link:
The Wyvern Game API
You'll need it.
Next, take a look at that API. It's automatically generated
from the Wyvern source code comments using a program called
"javadoc". When we change Wyvern, we update the API on the
website — usually every few weeks. The API is shown as a bunch
of things called packages. We'll cover those first.
Packages
A package is a collection of classes. We're not going
to cover classes here — if you're not familiar with them, you
should read up on Python programming. They'll teach you all
about objects, classes, methods, and object-oriented programming.
Then you can come back and finish this tutorial.
Back to packages. In Java, classes are all grouped into
packages. For instance, if you have a collection of classes
to implement a Zoo (such as Elephant, Rhino, and Lion),
then you might put them in a package called "zoo". If you
want to use Elephant in your program, you have to import
it from the zoo package first. In Jython, importing looks like this:
You put this line at the top of your program. Then you can
make and use Elephants in your program.
Wyvern has several dozen packages available to you, and
we'll cover a few of the important ones next.
The Wyvern API
Wyvern has over a thousand classes in its API. You don't
need to use all of them. In fact, even if you write a whole
bunch of Wyvern Jython code, you'll probably only use a few
dozen of the same Wyvern classes, over and over.
However, it's extremely difficult to tell which classes you
need to use just by looking at those API docs. That's sort
of a problem with javadoc - it doesn't assign any relative
importance to one class or package over another one. So what
do you do? Well, two things:
- You read all our tutorials carefully, and copy them
as much as you can.
- You ask fellow Wizards for help if you get stuck.
With that in mind, we'll mention a few of the classes and
packages you'll be using most frequently in your Jython code.
First, be aware that Wyvern packages are heirarchical,
and they all start with "wyvern". There's a
wyvern.lib , and underneath
that there's a wyvern.lib.classes ,
a wyvern.lib.commands , a
wyvern.lib.classes.armor , and so on. All the packages
are listed in the API docs on the first page.
So as an example, if you want to use the class Door, which is
located in wyvern.lib.classes.construct, you'd do:
from wyvern.lib.classes.construct import Door
|
Then you can create and use Doors in your Jython program.
You have to list every class you want to import
in the import statement, so if you also wanted to use SpiralStair,
you'd do this:
from wyvern.lib.classes.construct import Door, SpiralStair
|
You typically have multiple import statements in your program, such as:
<span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>java<span class='keyword'></span>.<span class='keyword'></span>util<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>Iterator<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>GameMap<span class='keyword'></span>, <span class='keyword'></span>Kernel<span class='keyword'></span>, <span class='keyword'></span>Timed<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span>.<span class='keyword'></span>predicates<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>ArchetypePredicate<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span>.<span class='keyword'></span>properties<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>Applyable<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span>.<span class='keyword'></span>classes<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>StaticObject<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>kernel<span class='keyword'></span>.<span class='keyword'></span>maps<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>MapLoader<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>world<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>World<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>common<span class='keyword'></span>.<span class='keyword'></span>config<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>Wyvern<span class='keyword'></span>
|
It's a common mistake to forget to import a class you're
using. In Jython, pretty much everything is lowercased,
so if you're using something that's capitalized, it's
probably a Java class that you need to import.
Important Wyvern Packages and classes
This table shows a few of the classes you'll use most frequently:
Class or Package
|
Description
|
everything in wyvern.lib
|
This has lots of useful game interfaces, and you should familiarize
yourself with just about all of them. It has
Monster,
Player,
GameMap, and most of the other
really important ones covered in the Tutorials.
|
wyvern.lib.classes.DynamicObject
|
This is the most common class to subclass for your Jython
objects that can be picked up by players.
|
wyvern.lib.classes.StaticObject
|
This is the most common class to subclass for your Jython
objects that can't be picked up, such as trees or fountains.
|
wyvern.world.World
| The World keeps track of all the current players and maps in the game. |
wyvern.common.config.Wyvern
|
This class keeps track of the directory structure for Wyvern,
so you use it to find where certain files are located.
|
wyvern.kernel.combat.Combat
|
This class provides functions for damaging and killing
monsters and players.
|
There are plenty of others as well, but nearly all of the classes
you'll use on a regular basis are in wyvern.lib or one of its
sub-packages (like wyvern.lib.properties
or wyvern.lib.classes ).
Using Java Classes
OK, so now you know how to import Java classes into your
Jython program. What next?
You can do three different things with a Java class in your program:
- You can subclass it.
- You can instantiate it.
- You can call methods on it.
- You can set properties on it.
You'll use one or more of these, depending on what you're
trying to accomplish.
Subclassing
This is where you start with an existing class, and
make a Jython version of it that extends its functionality.
You'll need to do this in almost every Jython program
you write for Wyvern, because almost everything you create
will be a GameObject or a GameMap. GameObjects are the
things players interact with - other players, monsters, bags,
armor, weapons, terrain, trees, buildings, teleporters,
spells, and so on. GameMaps are, well, maps, of course.
Whenever you subclass a GameObject, you should provide an initialize
method for it, which sets up its default properties. For example,
this is a fully-functional Jython program:
<span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span>.<span class='keyword'></span>classes<span class='keyword'></span>.<span class='keyword'></span>weapons<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>Sword<span class='keyword'></span><br><br><span class='keyword'>class</span> <span class='function'>my_sword</span>(Sword):<br> <span class='keyword'>def</span> <span class='function'>initialize</span>(self):<br> <span class='keyword'></span><span class='instance'>self</span><span class='keyword'></span>.<span class='keyword'></span>setProperty<span class='keyword'></span>('<span class='keyword'></span>short<span class='keyword'></span>', '<span class='keyword'></span>My<span class='keyword'></span> <span class='keyword'></span>Sword<span class='keyword'></span>')
|
If you created one of these in the game, you'd have
a regular old sword, except its name would be "My Sword".
You can do this using a regular old archetype, as well:
<arch class="wyvern.lib.classes.weapons.Sword">
<string name="short" value="My Sword"/>
</arch>
|
So our Jython example above doesn't gain you anything
by being written in Jython. You should only use
Jython if you're attempting to do something fancier than
setting properties on an object.
How did we know Sword is a GameObject? Well, the
Wyvern Game API documentation tells you. Go to your
trusty bookmark,
click on wyvern.lib.classes.weapons ,
scroll down, and click on Sword.
Near the top of the page, you'll see Class Sword with a
tree underneath it showing all the superclasses for Sword:
java.lang.Object
|
+--wyvern.kernel.properties.PList
|
+--wyvern.kernel.maps.MapObject
|
+--wyvern.lib.classes.weapons.WeaponImpl
|
+--wyvern.lib.classes.weapons.MeleeWeapon
|
+--wyvern.lib.classes.weapons.Blade
|
+--wyvern.lib.classes.weapons.Sword
- All Implemented Interfaces:
- Applyable, Attack, Damageable, Drawable, GameObject, Locatable,
MethodHookable, Movable, PickupInterest, PropertyList, Readyable,
Weapon
What does this mean? Well, for starters, it means swords are
pretty complicated beasts. A Sword's direct superclasses are:
- Blade - includes swords, rapiers, knives, etc.
- MeleeWeapon - includes clubs, blades, axes, etc.
- WeaponImpl - includes range weapons, hurled weapons and melee weapons
- MapObject - includes weapons, armor, players, terrain, etc.
- PList - includes all game objects and game maps (they both have property lists)
- Object - the superclass of all classes in Java
As you can see, the superclasses get more and more generic
as they go up the chain.
Below the class tree, you see a list of All Implemented
Interfaces. Interfaces are pretty much like classes, except they
don't have any code in them. They simply specify that your class has
to behave a certain way. For instance, Sword implements the
Damageable interface, meaning swords have hit points and can take
damage.
Finally, you'll see that GameObject is
in the list of interfaces for the Sword class. That's how we knew. =)
So where were we? Ah yes, subclassing. Well, we pointed out that
subclasses are more specialized than their superclasses. That's how
you make Jython objects that do unique things. You pick a game object
that's sort of like the one you want to make, and you
subclass it.
In our example above, we just wanted to make a Sword that has a
special description. Our sword is basically just a sword, but has
some extra "stuff" in it, so we subclassed
wyvern.lib.classes.weapons.Sword . Make sense?
When you read through the other tutorials, take a look at the
classes that the tutorials subclass. The most popular ones to
subclass are:
-
wyvern.lib.classes.DynamicObject —
any time you just want to make something that players
can pick up and carry, use this.
-
wyvern.lib.classes.StaticObject —
a popular choice for non-teleporters that are part of
the scenery.
-
wyvern.lib.classes.Structure —
used for buildings and other visible teleporters. Its
superclass is wyvern.lib.classes.Teleporter ,
which is useful for invisible teleporters.
However, don't feel obligated to use these. If you want
to make a special MagicMouth, feel free to subclass
wyvern.lib.classes.construct.MagicMouth ! There are
dozens of useful game classes that you can use as the starting point
for your Jython class.
You should browse through the wyvern.lib.classes
and wyvern.lib.classes.construct packages in the
Wyvern API Docs, since these two
packages contain 90% of the classes you'll want to subclass when
you're programming.
Instantiating
Sometimes you'll want to create objects and stick them
in a map, a player's inventory, a bag, or whatever. This
is called instantiation. You do it by putting ()
(parentheses) after the name of the class to instantiate.
You don't actually use plain old object instantiation for
GameObjects. Instead, you ask the Kernel to do it for you.
It's hard to come up with a 3- to 5-line example showing
when you'd want to do this, but we'll do our best. The
following example is a simple Monster Dispenser - you pull
the lever and a monster comes out. It's what every player
dreams about!
<span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span>.<span class='keyword'></span>classes<span class='keyword'></span>.<span class='keyword'></span>construct<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>Lever<span class='keyword'></span><br><span class='keyword'></span>from<span class='keyword'></span> <span class='keyword'></span>wyvern<span class='keyword'></span>.<span class='keyword'></span>lib<span class='keyword'></span> <span class='keyword'></span>import<span class='keyword'></span> <span class='keyword'></span>Kernel<span class='keyword'></span><br><br><span class='keyword'>class</span> <span class='function'>monster_dispenser</span>(Lever):<br> <span class='keyword'>def</span> <span class='function'>apply</span>(self, agent):<br> <span class='keyword'></span>mon<span class='keyword'></span> = <span class='keyword'></span>Kernel<span class='keyword'></span>.<span class='keyword'></span>instantiate<span class='keyword'></span>('<span class='keyword'></span>monsters<span class='keyword'></span>/<span class='keyword'></span>goblin<span class='keyword'></span>/<span class='keyword'></span>orc<span class='keyword'></span>')<br> <span class='keyword'></span>map<span class='keyword'></span> = <span class='keyword'></span><span class='instance'>self</span><span class='keyword'></span>.<span class='keyword'></span>getMap<span class='keyword'></span>()<br> <span class='keyword'></span>ref<span class='keyword'></span> = <span class='keyword'></span><span class='instance'>self</span><span class='keyword'></span>.<span class='keyword'></span>getReferenceLoc<span class='keyword'></span>()<br> <span class='keyword'></span>mon<span class='keyword'></span>.<span class='keyword'></span>setMap<span class='keyword'></span>(<span class='keyword'></span>map<span class='keyword'></span>, <span class='keyword'></span>ref<span class='keyword'></span>.<span class='keyword'></span>x<span class='keyword'></span>, <span class='keyword'></span>ref<span class='keyword'></span>.<span class='keyword'></span>y<span class='keyword'></span>)<br> <span class='keyword'></span>mon<span class='keyword'></span>.<span class='keyword'></span>start<span class='keyword'></span>()
|
Notice we skipped the instantiate method
here, because we didn't want to give it any special properties.
It's going to look like a regular old lever to a player.
Notice also that Jython class names
typically_look_like_this ,
whereas Java class names
TypicallyLookLikeThis .
We imported two handy-dandy Wyvern classes:
Lever, which we subclassed,
and also Kernel, which we use
to instantiate an orc.
You can ignore all the lines in the example for now, except
for this one:
mon = Kernel.instantiate('monsters/goblin/orc')
|
We're going to focus a bit on that line. What's it doing? It's
creating a variable called mon, and instantiating an Orc, and then
assigning the Orc to the mon variable so we can do stuff with it
afterwards (like stick it in the map and start it up.)
We've invoked a built-in game function on this line. The function
is called instantiate, and it exists in the
class wyvern.lib.Kernel . Its purpose in life is to take
the path to an archetype file, turn it into a Game Object, and return
it to you.
We didn't really cover regular instantiation here, because
you don't often need to do it. You usually ask the Kernel to
instantiate archetypes for you instead. However, in later
tutorials we may show you specific examples of when you'd
want to instantiate an object directly. No point in learning
how until you need to do it!
Calling Functions
This is the third thing you can do with Java objects.
You call functions (also called "methods") on the objects, and
the functions do things for you.
If you've been reading carefully, you'll have noticed that
this is exactly what we did in the MonsterDispenser example above.
How many times did we call a game function? We actually called
five functions:
- we called instantiate on the Kernel
class, to get ourselves a monster.
- we called getMap on ourself, to
figure out what map we're in. This is a function that
we inherited from GameObject.
- we called getReferenceLoc on ourself,
to get our upper-left corner location. Since we're only
a 1x1 object, this is the only location we have. This
function is also inherited from our GameObject superclass.
- we called setMap on the orc, to
put it in our map at our location. You guessed it, this
one is also inherited from GameObject. Hmmm, maybe you
should take a look at that interface!
- lastly, we called start on the
orc, to make it start moving, attacking and so on.
This method is actually inherited from the
orc's Monster superclass (superinterface, actually, but
who's counting?) You can start monsters, but you can't
start buildings or other non-commandable objects.
If you want to be picky, you'll notice that the player is
standing on the lever, and we put the Orc on that location,
so the Orc will actually be in the same square as the player.
Oops! Well, we tried to keep the example simple. There's
actually another function you can call to find the
free spot nearest to a given location. It's in the
GameMap interface, and it's
called findFreeSpot , if you really care.
Setting Properties
This is the fourth and final thing you can do to a Wyvern
object, and it's by far the most common.
Setting properties is covered, appropriately, in the
Properties tutorial, so we
won't cover it here.
Wrapping Things Up
We have to end this tutorial now (alas!), but hopefully you
should feel like you have a better understanding of how to
use that
bookmark you made.
Read all the tutorials, ask for help from fellow Wizards, and
study the Python/Jython manuals and Wyvern APIs, and it should
all eventually start to become clear.
Good luck!
|