Verbs & Skills

Compiled from: LPC guide ch08, and verb tutorial.

************************************************
Part 0: How Do Skills Work?
************************************************

Skills are generally class-based, meaning that they are
specified in the class files found in /secure/cfg/classes.
Skills are only meaningful in terms of library objects that
understand them.

For example, a fighter’s blade attack is useful because
player.c and combat.c make use of this skill as a
modifier.

But adding a basketweaving skill to a class is not
helpful unless there are library objects (looms, perhaps,
or straw) and verbs (weaving, maybe?) that make
use of that skill.

There are also race-based skills, such
as poison bite or breath attack. These skills are
specified in /secure/cfg/races.

How do I find the location of a verb or command?

Use the which command. For example:

which zap

which update

If ‘which’ doesn’t find it, the command is probably built into
some object you inherit, like the command shell or the
hooks for the chat daemon. Or it may be an add_action bound to a
nearby object. To see what commands objects or inherited
files might be providing you, type:

localcmds

I moved a command from one directory to another. How do I get the new location recognized?

First, run update on the command in question.
then:
If it’s not a verb: update /daemon/command
If it’s a verb: update /daemon/verbs

************************************************
Part 1:The Verb Itself
************************************************

Let’s take a look at the code for a verb, and dissect
it for meaning.
#include

inherit LIB_VERB;

static void create() {
verb::create();
SetVerb(“buy”);
SetSynonyms(“purchase”);
SetRules(“STR from LIV”);
SetErrorMessage(“Buy what from whom?”);
SetHelp(“Syntax: \n\n”
“Blah blah this is the help message.”);
}

mixed can_buy_str_from_liv(string str) {
if( this_player()->GetParalyzed() ) {
return “You cannot do anything.”;
}
return this_player()->CanManipulate();
}

mixed do_buy_str_from_liv(string str, object vendor) {
return vendor->eventSell(this_player(), remove_article(lower_case(str)));
}

If you never looked at a verb before, it can
look rather alien and disgusting, like a dead facehugger.
But like the facehugger, it’s actually quite beautiful
in its own way.
Let’s dissect and comment it now:

#include // This include statement allows us to use macros, or nicknames,
// for certain things. Specifically, the lib include gives us
// knowledge of what we mean by LIB_VERB.

inherit LIB_VERB;
// This line tells us that we will be inheriting all the
// functions and variables defined in /lib/verb.c .

static void create() {
// The create function is like main() in C/C++. A function that
// is *always* called when an object is loaded. Therefore,
// anything important goes inside here.

verb::create();
// The :: operator is called the “scope resolution operator”.
// That’s a fancy way of saying that we are trying to use
// a function that exists somewhere in the inheritance tree, and
// not in this individual file. The verb::create() line above
// is saying “At this point, execute any directives in the
// create() function of LIB_VERB”. This is necessary because
// the create() function we define here will override the create()
// function we inherit, and that overridden function might have
// had important stuff in it we need to have happen.
// If you didn’t understand any of that, you should probably
// stop reading this tutorial and get started on reading
// the Creator’s manual.

SetVerb(“buy”);
// This identifies what word the parser should associate
// with this verb.

SetSynonyms(“purchase”);
// Self explanatory. This is where you specify words with a
// similar meaning that should also be associated with this verb.

SetRules(“STR from LIV”);
// Here’s one of the new and exotic features of verbs. Rules and tokens.
// When the parser catches the “buy” word from the user’s input,
// it will check to see if the rest of the words in the
// command line conform the the known set of rules for the verb.
// if they don’t, the parser will error. We’ll go over the rules
// a bit further on.

SetErrorMessage(“Buy what from whom?”);
// By default, the parser’s error messages aren’t terribly
// descriptive or helpful…usually it’s along the lines
// of “You can’t X the Y”. The line above lets you provide the
// user a clue as to how to better avail themselves of the
// verb’s functionality.

SetHelp(“Blah blah this is the help message.”);
// When the user types “help buy”, this is what they get.

// The create function ends here, with the close brace
// below this line.
}

mixed can_buy_str_from_liv(string str) {
// This function does some very rudimentary checks of
// whether the user can use the verb. The can_* type
// functions will be discussed in more detail a bit
// further on.

if( this_player()->GetParalyzed() ) {
return “You cannot do anything.”;
}
// Pretty self-explanatory. An obvious check.

return this_player()->CanManipulate();
// The player object has a CanManipulate() function. If
// the player has no prehensile limbs (i.e. “wielding limbs”,
// like hands) then the CanManipulate() function in their
// object returns 0. Since the line above returns whatever
// CanManipulate() returns, that means that if the player
// is missing both hands, he can’t sell anything. The verb
// will fail.

mixed do_buy_str_from_liv(string str, object vendor) {
// Like the can_* functions, do_ functions will get more
// treatment further on. This is generally where the actual
// “doing” of the verb gets fired off, if appropriate.

return vendor->eventSell(this_player(), remove_article(lower_case(str)));
// Did you notice that this function is declared as type “mixed”?
// This allows us to return weird stuff, like in this case where
// we’re returning the result of a function call. We’re not
// 100% sure what data type eventSell() in the vendor returns,
// so mixed covers all the bases. Whatever eventSell() returns,
// that’s what gets returned here. If the parser got this far,
// then the verb’s execution was successful.
// That doesn’t necessarily mean that the player successfully
// sold anything. It may be he lacks enough quatloos for the
// shizbat. But that is no concern of the verb. That is the
// business of the eventSell() function it called on the vendor.
}

Rules and tokens

By a magic I do not fully understand myself, the
parser (which lives in the compiled driver binary, meaning
it was written in C and therefore inscrutable to me) can
actually work out from your command line what object you meant
to do what to with what.

For example, if you are in a room full of orcs,
and you type:

kill orcs

The parser actually susses out that orcs are objects
in your environment, and that you intend to act on more than
one of them. The parser will go on to check the attack verb
for its known rules. The rules for “attack” look like this:

SetRules(“LVS”, “only LVS”,”LVS only”)

The parser sees that you meant two objects that
are living, and sure enough, the attack verb will accept
“multiple living things” as a rule. In this case, the
matched rule is the first one, with the token LVS.

Hold on there, you say. What if I typed kill orc ,
in the singular? This would still match the first rule,
because LVS doesn’t just stand for “multiple living things”.
It’s rather more like “one or more living things”.

If the first attack token were LIV instead, then
killing a singular orc would succeed in the parser, but
killing plural orcs would probably fail.

The second rule has two tokens. One is an object
token, LVS, where object is meant in a semi-grammatical sense.
The other is a prepositional token, which is also meant in
a semi-grammatical sense.

The way “object” and “preposition” are handled in
the parser’s grammar is not the way they are handled in
English grammar. For example, you may be feeling indignant
at being told that “only” is a preposition. Obviously, in
English, it isn’t. However, the parser isn’t as linguistically
sophisticated as you. For the parser, anything used
periverbally that isn’t an object is a preposition. Please don’t
email me telling me I don’t know my English grammar. When I
talk subject, object, and preposition here, I am using
the parser’s simplified grammar.

As you might have guessed, an “object token” in this case
is the noun that the verb will be working with. Note that the
subject is assumed to be the caller, or player, that invoked the
verb’s execution, and does not have an explicit token identified.

So, back to our second rule. “only LVS” means that
I can issue a command like this:

kill only the second orc

And the parser will not reject it. However, failing
to use the preposition token explicitly allowed in a rule
will cause the parser to error. Trying to:

kill merely the second orc

will not bring joy.

The known object tokens are:

LIV – one living thing
LVS – one or more living things
OBJ – one object
OBS – one or more objects
STR – a string of characters
WRD – a generic thing that may be a string, object, or living thing

To get a list of known preposition tokens, type:

eval return MASTER_D->parse_command_prepos_list()

can’s and do’s

Following the create() fun you saw two types of functions:
can_* and do_*. These are sometimes called “applies”. When you see
someone referring to an apply, they mean a function called by the driver.

Notionally, the can_ applies are meant to test the
validity of the verb’s execution, and the do_ applies are
meant to perform the actions required.

In practice, this is only somewhat the case. Whether
it’s a bug or feature, the can_ applies tend to mangle the
arguments they receive. Pretty much no matter what the
argument is, it gets turned into a string. It’s a peculiar
“gotcha” that will have you spinning your wheels for a long time
if you don’t happen to know it.

That doesn’t mean the can_ applies are useless. They are
in fact necessary. If the can_ apply doesn’t exist, or doesn’t
return a positive integer, the do_ apply won’t happen, meaning your
verb won’t execute. Since the can_ applies are somewhat hobbled,
use them for quick, obvious sanity checks, like “is this
player dead” or “does she have hands to manipulate anything with”.

If you need to do a check, for example, to ensure that
the vendor is an elf, that means that you need to make a
call to that vendor object. Since the can_ applies won’t
handle object arguments, this check will need to happen in the
do_ function.

Some examples of can_ and do_ verb applies:

can_throw_obj()

Pretty obvious. The command in question would be something
like “throw switch” perhaps.

can_throw_obj_word_obj()

A little trickier. Now we’re allowing for a wildcard prepositional token. Here
the command might be “throw ball at benny”, but because the token
is not explicitly listed (that would be something like can_throw_obj_at_obj())
and the target is not explicitly a living thing, this apply
also matches “throw the sword in the lake”. Note it won’t match
“throw the jacks on the floor”.

do_read_str_on_obj(string str, object ob)

If your verb has a can_read_str_word_obj(string str, object ob) apply that
returned 1, then this sweet baby gets called. Presumably here you’d
have the verb call the object and tell it to do something with the
arguments provided. Here is where the actual reading action begins.

Alas, the work of parser applies is not done. The object in
question must have matching direct_ and indirect_ applies, otherwise,
error occurs. These applies are discussed in the next section.

The object: direct_ and indirect_

As you can see, the thing that gives a do_ apply
its whammy is a call to an object. That’s the point of a verb,
is doing something, and in the case above, its:

return vendor->eventSell(this_player(), remove_article(lower_case(str)));

However, if you make a verb like this, and then
just try to use it, it’ll fail. This is because the driver
needs the objects it acts on to have applies that
correspond to the do_ applies.

The corresponding applies are direct_ and indirect_ ,
and the appropriate one must exist in the object.

Verb applies are usually added to the lib
objects that are appropriate. For example, let’s take
the “press” verb. When you “push the button”, the parser
checks with the verb daemon, VERBS_D, to find out if
“push” means anything. Sure enough, it’s a synonym for
“press”, so the parser checks out the rules for press.

Eventually the parser decides all the t’s are
crossed and i’s are dotted, and it’s time to actually
see whether the “button” you’re talking about is
pressable. It does this by calling the following apply
in the button object:

direct_press_obj()

If the button does not have this function,
or if the function returns 0, the parser errors, assuming
that you’re trying to press something that isn’t
supposed to be pressable. Therefore, if you’re going to
make a button somewhere, or a wall or anything that
should do something when pushed, it will need to
have that function in it. The way it looks is something
like this:

mixed direct_press_obj(object target) {
return CanPress(this_player());
}

Now, this gets complicated, because the CanPress()
fun is a function that checks to see whether the
relationship between the button, the presser, and the lib
permits the pressing.

If you’re going to have an object that is
pressed, it’s going to be a right pain in the butt to
code all the necessary checks into that object. Fortunately,
you don’t need to. If you make a pressable thing, you don’t
need to code all that stuff yourself. Just have it
inherit LIB_PRESS, which is a file that already
contains the applies you need.

But, we’re not done. You’ll need to understand
a couple more things to be able to construct your new verb.

direct and indirect refer to the relationship
of two objects on a command line. If all you have is
“throw ball”, there’s no need to worry about object relationship.
But what if the command is “shoot nice guy eddy with pistol”?
Or, suppose the player types “shoot pistol at eddy”?

The parser needs to differentiate between the two,
but it isn’t nearly sophisticated enough to understand the
concept of who is acting on whom. The parser just needs to
know which is first and which is second. therefore, in
LIB_SELL you’ll see:

mixed direct_sell_obj_to_liv()

where the direct object is obj and the indirect object
is liv, and:

mixed indirect_sell_liv_obj()

where the direct object is liv and the indirect object is obj.
Again, this is not English grammar, it’s parser grammar.

************************************************
Part 2: Examples!
************************************************

Let’s take the
example of farming.

Section I: Verbs
—————-

It is critical to understand how verbs work in order to
do anything in Dead Souls of an advanced nature. Verbs are
basically commands that do something to your environment,
something in your environment, your “body”, or something
in your inventory.

For example, “who” is not a verb. It’s a standard command,
which doesn’t act on any cloned items. All it does is communicate
with the lib to query the list of users logged on, and displays
it to you in a particular manner.

Then there’s something like “zap”. That *is* a verb, and
it takes cloned items as arguments. When you “zap orc” this
has a special meaning to the parsing system. The parser is the
part of the game driver that tries to interpret your input
and attempts to do something useful with it. When the parser
catches a verb at the beginning of your input, it gets to
work on figuring out how the rest of the words in the input
relate to that verb.

This is done through “rules”. You can take a look
at /verbs/creators/zap.c for the specific rules in this case.
If the word or words (for example “first orc”, “orcs”, “an orc”)
match one or more objects in the room, the parser then
sends the arguments to the verb object. The verb object is
the loaded code from /verbs/creators/zap.c in this case.

Depending on how the verb is coded, your command line will
succeed or fail.

For your new farming system, you’re going to need some new
verbs, so the first thing you need to do is understand verbs.
You’re going to have to build new verbs like “plow”, and “plant”,
and “harvest”. Therefore, you’ll need to go over the verb
tutorial, which is at http://dead-souls.net/verbs.html

Section II: Lib Event Objects
—————————–

In the verb tutorial, you read that when a verb acts on
an object, the parser requires that the object have a function
that handles that verb. If a chair object lacks a function
like direct_sit() or something similar, the parser will assume
your sit verb doesn’t apply to chairs, and the command line
will fail with something like “You can’t sit on the chair”.

It would be incredibly tedious to have to code a sit verb
handler in every piece of furniture you create. Similarly,
your farmer’s field plow *could* have a plow verb handler
coded in it, but it is much better to create a lib object that
your plow will inherit. That way, other objects can inherit
that functionality without having to reinvent the wheel, and
plowing in general will be a uniform experience across the mud.

For example, one of the first systems I made when
I started my lib obsession was the inheritable flashlight
system. The original Dead Souls lib had regular old torches
you’d light with a match, but it seemed to me that not every
Dead Souls mud would be Sword & Sandals style, and a modern
illumination system should be available. So I set about
making a “turn” verb, so that once I had flashlights,
you could “turn on the flashlight”.

I then created the lib object /lib/events/turn.c (when
referring to lib objects, I often use the macro name. In
this case, if I’d said LIB_TURN, it would be the same thing
as saying /lib/events/turn.c). The lib object doesn’t really
*do* much of anything. That object isn’t really where you
need to be checking for validity of commands. What that
object does, almost *all* it does, is to have functions that
correspond to the verb “turn”. That’s it. It’s kind of like
a socket for a plug. The verb is the plug and you’re trying
to use it on something. If that something has a socket
that fits your plug, then it’ll work.

Lib event objects come in different flavors, and some
really do perform a bunch of thinking. But for the most part,
for simple verbs, all you need is a lib event object that
says “yes, I understand that verb”.

LIB_TURN is inherited by LIB_FLASHLIGHT. That means
that when you clone an object that inherits LIB_FLASHLIGHT,
it contains all the functions of /lib/flashlight.c plus
all the functions that LIB_FLASHLIGHT inherits from LIB_TURN.

Because your flashlight inherits LIB_FLASHLIGHT,
which inherits LIB_TURN, when you issue the command line
“turn on flashlight”, the parser checks with the
flashlight to see if it knows what you’re talking about,
and gets a “yes, I know that verb” response. At that point
the parser says “fine, here’s the rest of what this
player thinks he can do with you and the turn verb” and now
it’s up to LIB_FLASHLIGHT to figure out whether it has
enough batteries, of the right kind, with sufficient
charge, and so on.

For your new farming system, you’ll need to implement
a similar scheme. Your “plow” and “hoe” verbs will need
lib event objects that can be inherited by the cloned
objects you want to plow and hoe with.

In this case, LIB_FLASHLIGHT and the turn verb
aren’t the best models for your new plowing system. This
is because your plow is something you plow *with*,
as opposed to something that *is plowed*.

To see how a plowing system might be implemented,
take a look at the “dig” verb, LIB_DIGGING, and
LIB_DIG_WITH. This is what a shovel would use, so
that you can “dig in sand with the shovel”. After
studying the dig system, and lots of trial and error,
you will hopefully eventually come up with a
plow system that will let you “plow field with plow”,
for example.

Section III: Daemons
——————–

So, now you’ve created a plow verb, and a plow lib
event object, it works, and now you’re happily plowing
along. Let’s say that the rooms field1.c and field2.c
are plowable rooms. Presumably, you don’t want people
to be able to plow here all the time. The fields need
time to do their thing, and constant plowing would
slow down the growth of your tender young corn stalks.

Normally, you might deal with this by having
a local variable in the room, so that “harvest time is
50 hours, unless someone plows again, which
makes it take longer”, this sort of thing. Let’s call
that variable PlowedTimes.

But, oh noes! The mud rebooted! Now all the rooms
have reset, and the planting and plowing variables
have reset!
You might avoid this problem by just not rebooting,
but even if you manage never ever to reboot your mud,
the mud periodically does resets of unused objects,
retiring them from memory and resetting their values
to zero.
You might avoid *that* problem by setting your
fields to be “NoClean”, to avoid resets, but this is
very inelegant. Rather than ensuring the integrity
of your game data, you’re just crossing your fingers
and hoping it doesn’t go away.

The solution is to use a daemon. A daemon is an
object loaded into memory that acts like an arbiter
of information. For example, STARGATE_D keeps track
of where stargates are, and which gates are in
what state, and which gates are connected to each
other. It is important to have one location where
this data can be accessed, because a new gate must
be able to know what other valid gates there are, and
it must be able to know what gates are idle and
therefore accessible. STARGATE_D is a central
repository of this data, and serves as a mediator
for connection requests, keeping things working right.

In this case, the daemon’s job would be to
keep track of which fields have been plowed, how
many times, and how long it’ll take to get to harvest
time. Dead Souls daemons typically use object
persistence files ( http://dead-souls.net/ds-admin-faq.html#80 )
to avoid losing information during object reloads or
mud reboots. A FARMING_D is exactly what you need to
keep track of and manage this kind of data.

Section IV: Skills
——————

To what extent should people be able to plow? How
well should they do it? If you care enough about farming
to have come this far, you’ve probably got ideas about
what good plowing is and what criteria a player should
have for extracting the most from their land.

This is where skills can play an important role.
What you have to understand about skills is that they
are simply variables in a player’s body. Skills don’t
have to be gained by joining a class, guild, or being
member of a race. Adding a skill to a player is as
simple as having an object do something like this:

this_player()->SetSkill(“scuba diving”,1);

And if just strapping on a scuba tank does it, then
now that player has that skill.

Now, *normally* players are granted skills through
something more sensible than just picking up an object.
It makes more sense to have skills granted when a
player is taught something by an npc, or joins a guild,
or whatever, which is why traditionally that’s how
it has worked.

So let’s say you have a Farmer’s Guild, then. When
you show up and sign the registry, some npc pops out,
“teaches” you the farming skills you need (by simply
adding the skills “farming” and “plowing” and “sowing”
to the player) and now you have the skills. If you
want, you can even create a Farmer class, like Fighters,
but that’s up to you and not in the scope of this chapter.

This plowing skill is totally useless right
now. It does nothing at all, because you haven’t yet
coded anything that makes use of it. This is the key
concept of the skills system that you must understand.

Just giving a player a skill does not mean that
it has any use. For a skill to be useful, there
must be lib verbs and/or objects that evaluate the
skill and perform calculations based on it.

It is therefore time to add these skill checks to the
objects that need them. For example, suppose our
farmer’s plowing skill is at level 5. This doesn’t
mean he’s a level 5 player necessarily, just that
at plowing, his skill level is 5.
You might have a function in your /verbs/items/plow.c
verb that checks that skill, and determines how long
the field will take to grow based on it. Perhaps
for a level 5 plower, the field will be ready for
harvest in 45 hours. Perhaps for a level 10 plower,
it would be 35. You might have either the plow verb
or the plow lib event object do something like:

int PlowFunction(string field){
int skill_level = this_player()->GetSkillLevel(“plowing”);

if(skill_level) skill_level *= 2;
else skill_level = 1;
FARMING_D->eventModHarvestTime(field, skill_level);
return 1;
}

It’s a silly example, but you get the idea. The
“plowing” skill is valuable because the lib uses it
in some way to modify events the player performs. If
the lib doesn’t know about it, the skill has no value.

In the case of, for example, “blade attack”, the
lib checks for this if you’re wielding a sword and
you’re in combat. Based on how good you are at blade
attack, combat.c will modify how much damage you
inflict when you hit your opponent.

Section V: Special Abilities
—————————-

Perhaps “plant” and “sow” are verbs that should
only be available to players with the skills “planting”
and “sowing”.
Or, if farming isn’t your thing and you want to
enhance combat, you might want Fighters who are members
of the Viking Guild to have a special ability called
“massacre” that can do extra special damage.

This is best done by simply creating the sow,
plant, and/or massacre verbs, then coding the verbs to
work only for those people you designate. If the player
isn’t a Fighter and a Viking, perhaps the massacre
verb would return something like “You have no idea
how to do that.” and do no more.

You are, of course, free to implement a Special
Abilities System along the lines of the existing Dead
Souls spell system. I encourage you to do so, if
you’re so inclined, and to share that code with me,
if it works. But it isn’t necessary. The existing
verb system is plenty sophisticated enough to
handle such special events.