Wiz Programming Tutorials

This tutorial covers creating new Player Guilds. You can create them in Java or Jython, and we'll focus on Jython since the code is more elegant, easier to understand, and easier to code and test.

Once your guild becomes stable, you should email Rhialto and ask for your python files to be compiled, which will speed them up considerably.

Overview

In Wyvern, a Guild is an organization that grants its members unique abilities and penalties. The list of currently-available guilds can be found in the Guilds Section of the Player Manual.

Guilds tend to have these things in common:

  • They require exclusive membership — i.e. you can't be a member of any other Guilds. There are a few guilds in Wyvern (such as the PK Guild) that allow you to be members of other guilds.

  • They require you to meet certain requirements before you can join, such as completing a quest, paying some money, and/or being at least a certain level. Each Guild establishes its own requirements for joining.

  • They grant some custom abilities to the guild member. These abilities are all provided by a special "Guild Skills" object that's attached to the player as a property. We'll talk about this more later.

  • There's a Guild Headquarters map somewhere that lets you join and leave the Guild. The map contains an invisible Guild object, usually near an informational sign, that listens for "join guild" and "leave guild" commands.

  • Guilds typically have levels and titles. You start off as an apprentice of some sort, and you gain Guild XP by doing whatever it is the Guild wants you to do. For instance, Archers get Guild XP when they kill monsters by using missile weapons. A Guild typically grants Guild XP for killing monsters, but Guilds may decide to award Guild XP for anything they like.

  • Guilds provide their own chat-channel and "guildwho" command for their members.

If you want to create a new Guild in Wyvern, you'll have to write some code — no two ways about it. You'll want to familiarize yourself with Java or Jython, and make sure you're pretty comfortable with one or the other before embarking on writing a Guild, since it's a big task.

Guild Coding Overview

Here are the steps involved in creating a new Guild:
  1. Write up a description of your Guild and its abilities and penalties. Be thorough and thoughtful. Make sure you figure out how it will affect each race, and write up detailed specifications on how each ability or penalty should work (from a player's perspective).

  2. Send your description to an Arch Wizard, and make sure that your design is feasible, balanced, and worthwhile. If the Wizard approves it, then you can start on your coding. Make sure Rhialto knows about it too.

  3. Create a Guild Headquarters map. This is just a normal map.

  4. Create a Guild archetype for your Guild. This is a little wrapper archetype that you'll need to put in your map. We cover how to do this below.

  5. Create a GuildSkills implementation class. This is the majority of the work. You can do it in Java or Jython. We'll show you a fully-functional example guild later in the tutorial, written in Jython.

The sections below will show you how to do each of these steps.

Blazing Your Own Trail

The Guild-creation framework we're discussing in this Tutorial is the simplest way to create new Guilds. It relies heavily on two helper classes: All the existing Wyvern guilds are currently written by simply subclassing these two classes, and adding in a few customizations.

However, you're not required to use this framework. You can create a Guild using Jython or Java and the Wyvern APIs, and it can work any way you like, provided that it gets approved by the Wyvern staff.

We wouldn't recommend trying this, though, unless you have a good reason for it, and you're a very experienced programmer. The existing Guild framework is pretty flexible, and should give you a good jumpstart on your own Guild.

The Guild Class

The Guild class provides the functionality joining and leaving the guild. The actual abilities for the guild are provided by a GuildSkills object.

Usually, you don't have to create a subclass of Guild. You can just create an archetype for it that sets various properties that control joining the guild.

The following table lists the properties you can set to configure your Guild. The Default Value column shows what the value will be if you don't explicitly set the property in your archetype.

Property Name Property Type Default Value Meaning
admission-fee int 100k fee to charge for joining, in gold
min-level int 10 minimum experience level to join the guild
guild-skills bean none (required property) the Skills object — has to extend the GuildSkills class
width int 1 width of the area to cover, listening for "join guild"/"leave guild"
height int 1 height of the area to cover, listening for "join guild"/"leave guild"
custom-image boolean false If true, the guild has a custom image, specified in the following two properties.
male-image string none the image to use for male members, e.g. "wiz/you/images/male_guildguy"
female-image string none the image to use for female members, e.g. "wiz/you/images/male_guildgal"
quest-tag string none the short name of the quest you have to solve to get into the guild. If not present, you don't have to solve a quest to get in.
quest-name string none the long name of the required quest, if any. This is what's displayed to the Player if they try to join the guild, but haven't solved the quest yet.

Here's an example, the Paladins Guild, in a file called

wiz/wyvern/guilds/paladins/paladins_guild.arch:

<arch class="wyvern.wiz.wyvern.guilds.paladins.PaladinGuild">
  <image cat="special" bmp="Generator"/>
  <int name="width" value="4"/>
  <int name="height" value="4"/>
  <string name="guild-name" value="Paladins"/>
  <boolean name="custom-image"/>
  <string name="male-image" value="players/paladin"/>
  <string name="female-image" value="players/femalepaladin"/>
  <string name="quest-tag" value="legolas-demon"/>
  <string name="quest-name" value="Demon Hordes"/>
</arch>

Note that in the case of the Paladins Guild, we actually did write a subclass of Guild, and it's referenced as the class for this archetype. Compare it to the (simpler) Archers Guild:

<arch class="wyvern.lib.classes.construct.Guild">
  <image cat="special" bmp="Generator"/>
  <int name="width" value="3"/>
  <int name="height" value="3"/>
  <string name="guild-skills" value="wyvern.wiz.wyvern.guilds.archers.ArcherSkills"/>
  <string name="guild-name" value="Archers"/>
  <string name="quest-tag" value="legolas-pirate-quest"/>
  <string name="quest-name" value="Pirate Island"/>
</arch>

Notice that the Archers Guild is just a plain old Guild, and that it specifies a "guild-skills" property in the archetype. The PaladinsGuild also specifies a "guild-skills" property, but it does it in the class. Either way is fine, as long as you provide one.

The GuildSkills Class

This is the heart of writing a Guild. All the Guild's abilities and penalties reside in this class.

In our example, you'll get to walk through the construction of a fairly simple example guild, called the Coders Guild. It's written in Jython, and is, in fact, the first-ever Wyvern guild written in Jython.

Just so you can get your eyes on it, take a look at the Coders Guild source code. We recommend opening up the source code in a separate window, so you can follow along while we go through it.

Coders Guild

The first thing you should do when designing a guild is, well, design the guild! Don't start coding until you know exactly how you want the guild to work. And then it's still not time to start coding yet — after you know how it will work, spend some time figuring out how it will work technically, and write that down as well.

These two documents are known as the "Functional Spec" (how it will work for Players) and the "Technical Spec" (how you'll implement it in code). You should do these two docs for every object you write, even if it's just a few lines in the comment header for the class.

Coders Guild Func Spec

In order to keep you focused on the bare minimum required for a Guild, our Coders Guild doesn't do much. It gives you a new command, and it monitors your kills and decides whether you should get Guild XP for them.

We should probably also note that we're getting some functionality "for free", simply by extending the GuildSkills class:

  • we get a "guildwho" and "guildtell" command
  • we get a "guildscore" command and a special Guild Title
  • we'll get 10 guild levels and advance automatically if we handle the awardXP() function properly
Every Func Spec worth its salt should also mention anything major that's out of scope. Most Guilds have some code that lets you advance Guild levels until you reach the maximum level (there are typically 10 levels in a Guild, but you don't have to adhere to that strictly.)

In deciding whether to award Guild XP, we'll check if the player is wielding a Pen at the time the monster died. We'll create a Pen class just for the guild — actually just an archetype. We can do all this simply by overriding some methods in the GuildSkills class, so we'll move on to the tech spec.

Coders Guild Tech Spec

This isn't a full technical spec, but it covers a few of the important details of creating the guild.

First, here's the Pen archetype:

[File:  wiz/rhialto/misc/pen.arch]

<arch class="wyvern.lib.classes.weapons.MeleeWeapon">
  <image path="objects/cigar"/>   <!-- close enough... -->
  <string name="short" value="pen"/>
  <string name="desc" value="This is mightier than the Sword."/>
  <bean name="material" class="wyvern.lib.properties.materials.Plastic"/>
  <int name="wc-stab" value="1000"/>
  <int name="max-hp" value="1500"/>
  <int name="hp" value="1500"/>
  <weight value="1oz"/>
</arch>

We can test to see if something is a Pen by calling Kernel.isInstance() on it.

Our Guild will be very straightforward, so here it is:

[File:  wiz/rhialto/misc/coders_guild.arch]

<arch class="wyvern.lib.classes.construct.Guild">
  <image cat="special" bmp="Generator"/>
  <int name="width" value="3"/>
  <int name="height" value="3"/>
  <string name="guild-skills" value="wiz/rhialto/python/coders_guild.py"/>
  <string name="guild-name" value="Coders"/>
  <int name="admission-fee" value="1"/>
  <int name="min-level" value="1"/>
</arch>

Notice that we set the admission fee to 1 gold coin, to make it easy to test. Your guild will want to set appropriate values for admission-fee, min-level, and the other properties.

To create new commands for players, you have to create an object that implements the Command interface. There's a tutorial on the Command interface, so we won't talk about it much here. All we have to decide is which object will implement the interface. The simplest approach might be to have our GuildSkills subclass implement it, but it's slightly clearer and more Object-Oriented to declare a nested class that implements the interface. We'll call it the code_command class.

Note that the class names in Jython are like_this, and the Java class names are LikeThis. This is because Python and Java have different naming conventions, and we prefer to stick with each language's recommending naming convention while we're working in that language.

JythonBean Interface

Our GuildSkills object is a "bean" property, meaning it's a named property whose value is a Java object, and not one of our standard types (string, int, boolean, double, etc.)

Jython bean properties present a bit of a problem, because (due to some technical issues with the Jython language implementation that you probably shouldn't care about) you can't ask a Jython object what Java classes or interfaces it extends or implements. You have to tell it what class you're extending.

This is an issue because we want Wyvern to be able to cast it to a GuildSkills object, so it can call the Java methods: startup(), shutdown(), getGuildName(), etc.

The solution Wyvern has chosen is to provide a JythonBean interface, in which you return the name of the class or interface that you want to be known as on the Java side of the house. Note that you're required to implement this interface for all Jython Bean properties. They won't save and restore properly if you don't provide a valid Java classname, even if it's just "java.lang.Object".

In our case, we're a GuildSkills, so that's what we'll return from the getJavaClass() method.

GuildSkills Required Methods

The GuildSkills class has eleven abstract methods, meaning you are required to implement them in your subclass. In a nutshell, the methods are:
  • startup: adds abilities/penalties to player
  • shutdown: removes abilities/penalties from player
  • getGuildName: returns official guild name ("Coders")
  • getGuildShortName: returns short guild name ("coder")
  • getGuildPluralName: returns guild plural name ("Coders")
  • getGuildTitle: return guild title ("Coder")
  • getGuildTag: return guild tag-property ("coders-guild")
  • getTitle: gets the level title for a member
  • getWhoCommand: returns the command for guild-who ("coderwho")
  • getTellCommand: returns the command for guild-tell ("codertell")
  • awardXP: decides whether to give the player Guild XP for killing a monster
Additionally, the GuildSkills class has several other methods that you can override in your subclass, if you choose. Take a look at the API Docs for the class as you're figuring out how to design your guild.

Even though there isn't much code for most of them, at least in the Coders Guild, we'll cover them each briefly so you know what they're supposed to do.

startup

This method adds the guild abilities to the player. This happens at two different times:
  1. when the player joins the guild for the first time
  2. whenever the player logs into the game after that
It's invoked via the AddRemoveNotify interface, an extremely powerful extension mechanism in which the property is notified when it's added to or removed from a player's property list. This just happens to happen when we log in, or join the guild for the first time. Perfect!

shutdown

The opposite of startup, this method removes all special abilities (and penalties) from the player. It's called when the player logs out, or when the player permanently quits (or is fired from) the guild.

getGuildName

Returns the "official" guild name. Used whenever the game is referring to the guild. For instance, when a new member joins a guild, the Guildmaster will shout a message to all the members, saying "Foobaz has joined the Coders Guild!".

Note that the " Guild" is not returned — the system will add that to the name that you return from this function.

getGuildShortName

Returns a short name for the guild — used in constructing messages such as: Foobaz coder-tells: hi everyone!

In our case, we return "coder".

getGuildPluralName

Some guilds have different singular and plural names. Ours is the same: "Coders" for both.

getGuildTitle

This returns the generic, singular guild name to show in the High Score List or Who List. We return "Coder", so the Who list would show "Foobaz the High-Elven Coder".

getGuildTag

This returns a unique "tag-property" (just a boolean property), that tells the system we're in the Coders Guild. Tag props are almost of the form "wizname-shortname", e.g. "legolas-oldmage" or "rhialto-moonquest". In our case, we'll just call it "coders-guild", but in a real guild you'd want to put your wizard name in front, to avoid any collisions with identifiers from other Wizards.

getTitle

This method examines the player's Guild Level and returns an appropriate Guild-title for the player. This is almost always implemented just the way we did it, by having a table of 10 titles, and using the guild level to index into the table.

Some guilds have different titles for male and female members. In this case, the guild would have two title-lists, and use the correct one based on the agent's gender.

getWhoCommand

Returns the word to use for our Who command — in our case, "coderwho". We could have used anything at all, but this seemed most appropriate.

getTellCommand

Returns the word to use for the guild-specific tell command, which can be used as an individual chat-channel or a group-chat channel. We return "codertell".

awardXP

This is the doozy.

When a monster is killed, all of the group members who are in a Guild have their guild's awardXP() function called. That's this function. awardXP() looks at the group member, the monster that was killed, the weapon that was used, and/or other properties, and figures out whether it should award any guild experience to the group member.

If we're going to award experience, we do it in this method. It's up to this method to decide how much guild XP to award. In most guilds, you get 1 guild XP for each regular XP, but you could define your own ratio.

This function should return 1 if we awarded some guild XP.

Pass: event — a TargetedEvent that encapsulates the final blow. It may or may not have actually killed the monster, but you should assume that it has, and you'll usually be right.

The passed event can be null in some (rare) cases, so check if it's null and don't award any guild XP in that case.

Properties set on the event include:

  • target — the thing that was killed, obtained via event.getTarget()
  • "member" — a member of the group who killed the monster. This is the player we're considering for the XP award. You can also get this player by calling event.getAgent().
  • "xp" — the regular xp being awarded to this group member for the kill.
  • "total-xp" — the total regular xp for the kill, shared by the whole group. If there's no group, this member gets all of it.
  • "dtypes" — a wyvern.kernel.combat.DamageType[] containing all of the damage types that contributed to the fatal blow.
  • "damages" — an int[] containing the damage amounts done by each damage type in dtypes.
  • "total-damage" — the total damage done in the event (i.e., the sum of all the array values in the damages array).
  • "hit-loc" — the BodyPart hit location for the attack.
  • "attacker-msg" — message to print to the attacker.
  • "defender-msg" — message to print to the defender.
  • "weapon" — the game object (spell or weapon) used in the attack.

NOTE: "weapon" is the attack used by the player who killed the opponent, who is not necessarily the member getting awarded experience in this call. If the killer is different from the member, then you have no way of knowing if they used their guild skill to fight the monster. It's OK to assume that they did, though. For fighter classes, you can check the @attacks list on the member to see if they're wielding a weapon of the correct type, if you want. The event is reused to give XP to every group member, so don't modify the event — treat it as read-only.

The property can be null, so make sure to do null checks on the values after you extract them from the event.

There are additional comments in the source code, explaining some further nuances of how we decide what to do.

Putting It All Together

It'll take some careful study of this document and the Coders Guild source code before it all starts making sense.

To get started, you can simply copy the source code into your own Guild source file, change the name of the class, and start changing the functions one at a time to do what you want your Guild to do.

Be sure to ask an Elder Wizard or Arch Wizard for help, if you're having difficulties.

<< Previous Chapter Next Chapter >>