285 lines
11 KiB
Plaintext
285 lines
11 KiB
Plaintext
Building Non-Player Characters
|
|
The Nightmare IV Object Library
|
|
written by Descartes of Borg 951201
|
|
|
|
This document outlines the creation of non-player characters (NPC's).
|
|
On other muds, NPC's are sometimes referred to as monsters. Like the
|
|
rooms document, this document is divided up into two sections: basic
|
|
NPC building and complex NPC building. NPC's are living things which
|
|
inherit all the behaviours of living things. Documentation on living
|
|
specific functionality may be found in /doc/build/Livings.
|
|
|
|
************************************************
|
|
Part 1: Basic NPC Building
|
|
************************************************
|
|
|
|
*****
|
|
I. The simplest NPC
|
|
*****
|
|
|
|
#include <lib.h>
|
|
|
|
inherit LIB_NPC;
|
|
|
|
static void create() {
|
|
npc::create();
|
|
SetKeyName("praxis peasant");
|
|
SetId( ({ "peasant", "praxis peasant" }) );
|
|
SetShort("a local peasant");
|
|
SetLong("Dirty and totally disheveled, this poor inhabitant of Praxis "
|
|
"still somehow maintains an air of dignity that nothing can "
|
|
"break.");
|
|
SetLevel(1);
|
|
SetRace("elf");
|
|
SetClass("fighter");
|
|
SetGender("male");
|
|
}
|
|
|
|
There are two things you should note. The first is that an NPC is
|
|
also a general object, meaning that you have available to you all the
|
|
things you can do with general objects, like setting descriptions and
|
|
ID's. The second is that a basic NPC does not require a heck of a lot
|
|
more. I will cover the NPC specific functions here.
|
|
|
|
SetLevel(1)
|
|
SetRace("elf")
|
|
SetClass("fighter")
|
|
Level, race, and class are the three most important settings in any
|
|
NPC. Together they determine how powerful the NPC is. You are
|
|
absolutely required to set a level and a race. For those who
|
|
absolutely do not want to give the NPC a class, you do not have to.
|
|
But, you must instead manually set the NPC's skill levels, which is
|
|
described in the second part of this document. In general, however,
|
|
you always want to set the class.
|
|
|
|
Together, the class and race and level determine which skills and
|
|
stats are considered important for the monster, and how good at those
|
|
skills and stats the monster is. The order in which you call these
|
|
functions is irrelevant, as everything is recalculated any time one of
|
|
the above changes.
|
|
|
|
Also, note that SetRace() may only be called with a race listed in the
|
|
mraces command with simple NPC's. If you wish to build an NPC with a
|
|
unique race, you need to do some limb manipulation, which is described
|
|
in the advanced section.
|
|
|
|
SetGender("male")
|
|
While not required, you will normally want to give an NPC a gender.
|
|
The default is neutral. However, in this world, very little is
|
|
neuter. Your choices for this function are male, female, and neuter.
|
|
|
|
*****
|
|
II. Other NPC Configuration Functions
|
|
*****
|
|
|
|
Function: int SetMorality(int amount);
|
|
Example: SetMorality(100);
|
|
|
|
This is a number between -2000 and 2000 which determines the morality
|
|
of an individual with respect to good and evil. -2000 is absolute
|
|
evil, and 2000 is absolute good. The actions of players determine
|
|
their morality, and often those actions are relative to a target.
|
|
Thus killing an evil being can be considered good, while killing a bad
|
|
one evil.
|
|
|
|
Function: int SetUnique(int x);
|
|
Example: SetUnique(1)
|
|
|
|
Marks the NPC as a unique monster. This allows the room which clones
|
|
your NPC to use the negative values to SetInventory() (see
|
|
/doc/build/Rooms) to make sure the NPC only gets cloned every few
|
|
days.
|
|
|
|
************************************************
|
|
Part 2: Advanced NPC Building
|
|
************************************************
|
|
|
|
*****
|
|
I. Functions
|
|
*****
|
|
|
|
You may use these functions to make your NPC's a bit more interesting
|
|
than the simple variety.
|
|
|
|
Function: void SetAction(int chance, mixed val);
|
|
Examples: SetAction(5, (: DoSomething :));
|
|
SetAction(5, ({ "!smile", "!frown" }));
|
|
SetAction(5, ({ "The peasant looks unhappy." }));
|
|
|
|
Sets something to randomly happen every few heart beats while the NPC
|
|
is not in combat. In the above examples, the NPC has a 5% chance each
|
|
heart beat of performing the action you provided with the second
|
|
argument. The action can be a call to a function, a list of potential
|
|
commands, or a list of strings to be echoed to the room.
|
|
|
|
If you pass a function, that function will be called each time an
|
|
action is supposed to occur. If you pass a list of strings, one of
|
|
those strings will be randomly chosen as the target action for this
|
|
heart beat. If the chosen string begins with a !, it is treated as a
|
|
command. Otherwise, it is simply echoed to the room. Note that you
|
|
can mix commands and echo strings.
|
|
|
|
*****
|
|
|
|
Function: void SetCombatAction(int chance, mixed val);
|
|
Examples: SetCombatAction(5, (: DoSomething :));
|
|
SetCombatAction(5, ({ "!missile", "!fireball" }));
|
|
SetAction(5, ({ "The peasant looks angry." }));
|
|
|
|
This function works exactly the same as SetAction(), except that these
|
|
actions only get triggered while the NPC is in combat. This is the
|
|
best place to have the NPC cast spells.
|
|
|
|
*****
|
|
|
|
Function: varargs void SetCurrency(mixed val, int amount);
|
|
Examples: SetCurrency("gold", 100);
|
|
SetCurrency( ([ "gold" : 100, "electrum" : 1000 ]) );
|
|
|
|
This function allows you to set how much money an NPC is carrying.
|
|
The first syntax allows you to set one currency at a time. The second
|
|
allows you to set multiple currencies at once. Not that if you use
|
|
the second syntax, it will blow away any currencies the NPC might
|
|
already be carrying.
|
|
|
|
*****
|
|
|
|
Function: mixed SetDie(mixed val);
|
|
Examples: SetDie("The black knight bleeds on you as he drops dead.");
|
|
SetDie((: CheckDie :));
|
|
|
|
If you pass a string, that string will be echoed as the NPC's death
|
|
message when it dies. If you pass a function, that function gets
|
|
called with the agent doing the killing, if any, as an argument. For
|
|
example, with the above example, the function that you write:
|
|
|
|
int CheckDie(object killer);
|
|
|
|
gets called. If you return 1, the NPC goes on to die. If you return
|
|
0, the NPC does not die. In the event you prevent death, you need to
|
|
make some arrangements with the NPC's health points and such to make
|
|
sure it really is still alive.
|
|
|
|
*****
|
|
|
|
Function: mixed SetEncounter(mixed val);
|
|
Examples: SetEncounter(40);
|
|
SetEncounter( (: CheckDwarf :) );
|
|
SetEncounter( ({ str1, str2 }) );
|
|
|
|
This allows you to set up e behaviour for an NPC upon encountering
|
|
another living thing. Note that this behaviour occurrs for both
|
|
players and other NPC's. Using the first syntax, the NPC will simply
|
|
attack any other living thing with a charisma less than 40. The
|
|
second syntax calls the function you specify. You may have it do any
|
|
number of things, however, you must also return a 1 or a 0 from that
|
|
function. A 1 means that after the function is called, the NPC should
|
|
initiate combat against the thing it just encountered. A 0 means
|
|
carry on as usual.
|
|
|
|
Finally, the third syntax is likely to be used in places other than
|
|
the create() funciton in the NPC. This syntax lets you set a list
|
|
names which are simply enemies to the NPC. More likely, you will be
|
|
using AddEncounter() and RemoveEncounter() for this.
|
|
|
|
*****
|
|
|
|
Function: string *AddEncounter(string name);
|
|
Example: AddEncounter((string)this_player()->GetKeyName());
|
|
|
|
Adds a name to the list of names an NPC will attack on sight.
|
|
|
|
*****
|
|
|
|
Function: string *RemoveEncounter(string name);
|
|
Example: RemoveEncounter((string)this_player()->GetKeyName());
|
|
|
|
Removes a name from the list of names an NPC will attack on sight.
|
|
|
|
*****
|
|
|
|
Function: SetInventory(mapping inventory);
|
|
Examples:
|
|
SetInventory( ([ "/domains/Praxis/weapon/sword" : "wield sword" ]) );
|
|
SetInventory( ([ "/domains/Praxix/etc/ruby" : 1 ]) );
|
|
SetInventory( ([ "/domains/Praxis/etc/emerald" : -10 ]) );
|
|
|
|
This functions behaves almost identically to SetInventory() for rooms
|
|
(see /doc/build/Rooms). The big difference is that you may pass a
|
|
string in addition to a number as the value for any item you want in
|
|
the inventory. In the first example above, that string is the command
|
|
the NPC issues when the sword is cloned into its inventory. In other
|
|
words, if you want an NPC to do something special with an item it has
|
|
in its inventory, in this case wield the sword, you pass the command
|
|
string as the value instead of a number.
|
|
|
|
Note that this means only one of such item will be cloned, and it
|
|
cannot be unique.
|
|
|
|
*****
|
|
II. Events
|
|
*****
|
|
|
|
The following events exist in NPC's. You should have a good grasp of
|
|
function overriding before overriding these functions.
|
|
|
|
Event: varargs int eventDie(object target);
|
|
|
|
This event is triggered any time the NPC is killed. The event returns
|
|
1 if the NPC dies, 0 if it fails to die, and -1 on error. If you
|
|
intend to allow the NPC to die, you should call npc::eventDie(target)
|
|
and make sure it returns 1.
|
|
|
|
*****
|
|
|
|
Event: int eventFollow(object dest, int chance);
|
|
|
|
This event is triggered whenever an NPC is following another living
|
|
thing and that thing leaves the room. Returnung 1 means that the NPC
|
|
successfully followed the other being, and 0 means the NPC did not.
|
|
|
|
*****
|
|
3. Manipulating limbs
|
|
*****
|
|
|
|
The basic set of limbs an NPC gets is generally set when you set its
|
|
race. You can get a list of supported NPC races through the mraces
|
|
command. Occassionally, however, you may want to create NPCs of
|
|
unique races, or with unique body structures. Or perhaps you want a
|
|
human whose right hand is already amputated. This section deals with
|
|
doing those things.
|
|
|
|
Amputating a limb is simple. Call RemoveLimb("limb"). Note that that
|
|
is not useful for removing a limb that should not be there. Instead,
|
|
it is used for amputating a limb that looks amputated.
|
|
|
|
If, on the other hand, you wish to remove a limb which simply should
|
|
not have been there in the first place, call DestLimb("limb").
|
|
|
|
The most simple case of actual limb manipulation, however, is to
|
|
change the basic structure of an individual NPC around for some
|
|
reason. For example, perhaps you wanted to add a tail to a human.
|
|
For this, you use the AddLimb() function.
|
|
|
|
Function: varargs int AddLimb(string limb, string parent, int class, int *armours);
|
|
Examples: AddLimb("tail", "torso", 4)
|
|
AddLimb("head", "torso", 1, ({ A_HELMET, A_VISOR, A_AMULET }));
|
|
|
|
This function adds a new limb to the NPC's body. The first argument
|
|
is the name of the limb to be added. The second argument is the name
|
|
of the limb to which it is attached. The third argument is the limb
|
|
class. Limb class is a number between 1 and 5. The lower the number,
|
|
the harder the limb is to remove. A limb class of 1 also means that
|
|
removal of the limb is fatal. The fourth, optional argument is a list
|
|
of armour types which may be worn on that limb.
|
|
|
|
In some cases, you may wish to create a new race from scratch. This
|
|
requires adding every single limb manually. You first call SetRace()
|
|
with a special second argument to note that you are creating a new
|
|
race:
|
|
|
|
SetRace("womble", 1);
|
|
|
|
Then, you add the limbs for that race one by one. Make sure you call
|
|
SetRace() first.
|