Chapter 4. Creating a skill

The task of creating a skill is really not much harder than creating a command. In some cases there is very little difference. The two main differences are a couple extra fields in the define for the skill and the fact that you must make a skill check or some kind of check to see if the person is able to do the skill. In commands you have already had to make checks to see if a character is in the correct position or location now we just add the check to see if the character is strong enough or skilled enough.

As we have done in the chapter on commands we will first have to pick a skill to write. The diagnose skill seems to be a very good example of a non-combat skill so we will use it for our example. It really doesn't matter if you create the dil first or the define but we like to have the define done so that once the dil compiles we can reboot.

Example 4-1. Diagnose Define

index         = SKI_DIAGNOSTICS
name          = diagnostics
command       = diagnose
minpos        = POSITION_RESTING
turns         = PULSE_VIOLENCE/2
func          = diagnose@skills
type = TYPE_SKILL
race human     =   0
race elf       =   0
race dwarf     =   0
race halfling  =   0
race gnome     =   0
race half-orc  =   -2
race half-ogre =   0
race half-elf  =   0
race brownie   =   0
race groll     =   -3
race darkelf   =   0

As you can see from Example 4-1, the define for diagnose, the first line has 'SKI_DIAGNOSE' as the skills index value. This value is defined in the values.h When ever you create a skill you must add a value to the values.h that corresponds to your skill index. Next the name is what you want the skill to be known by for teachers and for the skills command. Finally we have set the diagnose skill to take up a half round of combat that way a person will have to decide wether he wants to fight or diagnose. Notice also we have picked 'Half-Orc' and 'Groll' to be the stupid races that will have a hard time learning diagnose.

Note: If there are any 'Groll' or 'Half-Orc' readers please do not be offended we just picked your race because we needed someone to use as an example

Now you must add the define to the commands.def and compile it. If you haven't been reading this straight through you can find the information on compiling the defines in Chapter 2.

Now with the define written and compiled it is time to move on to designing and coding our command. We will use the same method as we did in commands so we first need to start by asking ourselves some questions about how this command is going to work.

There could probably be more questions asked but the idea is to take time and think about what you want your skill to do before you start writing anything down. If you think better in your head than on paper these could all be questions floating around the Neural pathways.

After you have come up with not only the questions but the answers to the questions so that you think you know how you want the command to work you should write out some Pseudo code so that your logic is clear before you start writing the command.

Example 4-2. Diagnose Pseudo Code

if character is not skilled and is npc
	act you must learn first
		quit

if argument is empty
	if argument is fighting and
		target is person you are fighting
	otherwise if character is not fighting
		act what would you like to diagnose
		quit


if target has not been found yet and argument is not empty
	find target and set it

if activator of skill can not see target
	act no one found by that name
	quit

if target found
	do calculations
	act calculations

if not fighting
	find weight and size
	act weight and size
		quit

quit diagnose

There is two things we need to point out in Example 4-2. In the first line it says if person is not skilled and person is a PC fail. The reason for this is currently NPC's skill is based only on their abilities. Thus if a NPC does this skill it should just continue on with out this check. That way if some crazy Admin wants to switch into an NPC and diagnose all day long he or she could do that. Also notice we check to see if the character we are diagnosing is visible. You would be surprised at the amount of players that use commands that don't make this check to find wizinv admins so make sure to always check and see that what you are letting the players do is what you want them to do.

With the pseudo code created it is a simple matter to convert the logic into code. The resulting conversion created the dil in Example 4-3.

Example 4-3. Diagnose DIL

dilbegin diagnose(arg : string);
external
   string sizestring (cm : integer);
   string weightstring (cm : integer);
   integer skillresist (aa : integer, ad : integer,
			     sa : integer, sd : integer);

var
   percent : integer;
   hm      : integer;
   s1      : string;
   s2      : string;
   vict    : unitptr;
   skilla  : integer;
code
{
   if ((self.type == UNIT_ST_PC) and
       (self.skills[SKI_DIAGNOSTICS] == 0))
   {
      act("You must practice first.",
	  A_ALWAYS, self, null, null, TO_CHAR);
      quit;
   }

   if (arg == "")
   {
      vict := self.fighting;
      if (vict == null)
      {
	 act("Diagnose who?",
             A_ALWAYS, self, null, null, TO_CHAR);
	 quit;
      }
   }
   else
   {
      vict := findunit(self, arg, FIND_UNIT_SURRO, null);
      if ((vict == null) or not visible(self, vict))
      {
	 act("Nobody here by that name.",
	     A_ALWAYS, self, null, vict, TO_CHAR);
	 quit;
      }
   }

   if (not (vict.type & (UNIT_ST_PC | UNIT_ST_NPC)))
   {
      act("It seems to be dead?",
	  A_ALWAYS, self, null, null, TO_CHAR);
      return;
   }

   if (vict.max_hp > 0)
     percent := (100 * vict.hp) / vict.max_hp;
   else
     percent := -1; /* How could MAX_HIT be < 1?? */

   if (self.type == UNIT_ST_PC)
     skilla := self.skills[SKI_DIAGNOSTICS];
   else
     skilla := self.abilities[ABIL_BRA];

   hm := skillresist (self.abilities[ABIL_BRA], 20,
				skilla, 50);

   if (hm > 0)
     hm := 0;

   percent := percent + ((percent * (-hm))/100);

   if (percent >= 100)
     act("$3n is in an excellent condition.",
	 A_ALWAYS, self, null, vict, TO_CHAR);
   else if (percent >= 90)
     act("$3n has a few scratches.",
	 A_ALWAYS, self, null, vict, TO_CHAR);
   else if (percent >= 75)
     act("$3n has some small wounds and bruises.",
         A_ALWAYS, self, null, vict, TO_CHAR);
   else if (percent >= 50)
     act("$3n has quite a few wounds.",
         A_ALWAYS, self, null, vict, TO_CHAR);
   else if (percent >= 30)
     act("$3n has some big nasty wounds and scratches.",
         A_ALWAYS, self, null, vict, TO_CHAR);
   else if (percent >= 15)
     act("$3n looks pretty hurt.",
         A_ALWAYS, self, null, vict, TO_CHAR);
   else if (percent >= 0)
     act("$3n is in an awful condition.",
	 A_ALWAYS, self, null, vict, TO_CHAR);
   else
     act("$3n is bleeding awfully from big wounds.",
	 A_ALWAYS, self, null, vict, TO_CHAR);

   if (self.fighting == null)
   {
      s1 := weightstring (vict.baseweight);
      s2 := sizestring (vict.height);
      act("$3e weighs "+s1+" and is "+s2+" tall.",
	  A_SOMEONE, self, null, vict, TO_CHAR);
   }

   quit;
}
dilend

The entire command should be rather easy to figure out if its not let me know and I can add more documentation to this chapter. The only parts that may be difficult about this command a are the external functions called by the command. The sizestring and the weightstring are rather simple hey just return the size and weight of whatever is passed into the function back in a string to be displayed. The code for these two functions can be found in Appendix E.

skillresist on the other hand is a little harder to understand with out knowing what the openroll function does. see Example 4-4 for the listing of the skillresist

Example 4-4. Skill Resist DIL


dilbegin integer skillresist(Abil_self : integer, abil_vict : integer,
			     skill_self : integer, skill_vict : integer);
code
{
   return (openroll(100, 5) + abil_self + skill_self - abil_vict - skill_vict - 50);
}
dilend

The openroll seems to be the thing that stumps writers of skills because of its cryptic name. The reason it is called open roll is because it will reroll till it hits a certain range of numbers. For example in skillresist the following is the openroll call.

openroll(100,5);
This means roll a 100 sided dice until it reaches a range from 5 to 95. If the roll hits 5 to 95 first roll it stops and returns the value. If the roll on the first roll is greater than 95 it rolls again adding the next value to the last roll it will continue to roll again till it hits a number between 5 to 95. If however the first roll was 5 to 0 it will roll again subtracting the number that it gets until it hits the range 5 to 95. The function will continue till it hits either the range 5 to 95 or the threshold of about plus or minus 4 billion. Lets take a couple examples in case that explanation just totally messed up your mind.

Example 4-5. Positive Open Roll


	first roll = 97  /*Greater than 95 roll again*/
	second roll = 98 /* greater than 95 roll again but add to first roll*/
	roll value now  is  195
	third roll = 28 /* third roll in value 5 to 95 stop*/
	end roll value = 223

Example 4-6. Negative Open Roll


	First roll = 4 /*Less than 5 to 95 so we continue to roll subtracting now*/
	second roll = 98 /*outside the range 4 to 95 continue but subtract*/
	value is now -94
	third roll = 3 /*still outside continue*/
	roll is now  -97
	forth roll = 58
	end roll value = -155

Before going on make sure you clearly understand the openroll you will use it a lot.