346 lines
14 KiB
Plaintext
346 lines
14 KiB
Plaintext
chapter 8 "Understanding the Lib"
|
|
|
|
One of the most common questions I get goes something
|
|
like this: "I'd like to change combat so that it is
|
|
turns-based, with actions. How would I do this?" Another
|
|
example might be "I'm setting up a farming system, with
|
|
livestock and stuff. What should I look at?"
|
|
|
|
To me, the questions are the same. Translated into
|
|
my language, this is the meaning: "I have great ideas that
|
|
require advanced knowledge of the lib to implement. How do I do it?"
|
|
|
|
I'm usually at a loss when I get one of these, because
|
|
I want to set people straight, but I don't want to hurt
|
|
their feelings, either. In the FAQ's, my response is
|
|
something along the lines of:
|
|
|
|
"If there's anything in the Creator's Manual you don't
|
|
understand, you aren't ready to try this."
|
|
|
|
I hate to say that, because I think it's probably discouraging
|
|
to hear. After all, whatever the project, it is very likely
|
|
doable. You can make an LP mud do pretty much anything
|
|
you want...that's the beauty of the flexibility of LPC.
|
|
However, as they say, with great power comes great responsibility,
|
|
and in this case, it is your responsibility to understand the
|
|
lib, if you want to make major changes to it. 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.
|
|
|
|
|
|
Section VI: Summary
|
|
-------------------
|
|
|
|
At this point in my lib coder development, I have
|
|
a hard time distinguishing what is easy and what is
|
|
hard for new people. I have been surprised by people
|
|
who take a long time to grasp simple concepts. I have
|
|
been surprised by people who grasp complex concepts
|
|
so quickly that I can't answer their questions.
|
|
|
|
Where you stand in that continuum I can't say.
|
|
What I can say is that if this chapter seems like it
|
|
went mostly over your head, you shouldn't worry too
|
|
much about it. It took me years of coding experience
|
|
and months of obsessed lib analysis to reach my
|
|
current level of understanding. You should not expect
|
|
yourself to grok everything in this guidebook the
|
|
first time around.
|
|
|
|
As suggested in the previous chapters, it's best
|
|
to start small, slow, and steady. As you build simple
|
|
things, more complex things will make more sense, and
|
|
you'll eventually reach the level of technical
|
|
expertise you need.
|
|
|
|
This chapter was not written to make you feel
|
|
overwhelmed by what you don't know. It was written so
|
|
that you understand what you're asking when you
|
|
say "How do I revamp bodies and limbs so they have
|
|
knees and elbows you can poke people with?"
|
|
|
|
Once you understand the lib, it really really isn't
|
|
that hard to do. But if you are a beginner, don't
|
|
set yourself up for failure by taking a leap at a
|
|
project you don't have the experience to tackle.
|