Slash's Merc Snippets

Here is some code which is in my Windows Merc 2.2 server that I thought I would release as snippets as I'm not really maintaining that server.

Have fun. Note: Because of the << in the C code, you may not be able to see the code correctly. I don't have a good solution to this at this point. You want to use the ViewSource option or save this page to disk to get the code.


Wandering sage who identifies objects for a fee

/*
   Wandering Sage Code

   Ok, you have probably seen MUDs that have a wandering banker mobile.
This is a little different.  This is a wandering sage who will IDENTIFY
objects for non-mage players.  (I've never been a God on a MUD that had it,
so I don't know if it has any wierd social dynamics.  My suggestion is to
make it hard to find this guy.)

   Anyway, players find the guy and type "IDENTIFY object" and the guy
tells them what it is, like the IDENTIFY spell does.  He also relieves
them of 100 gp * the level of the object.
*/

/* Add this line to the cmd_table in interp.c */
/*    { "identify", do_identify, POS_STANDING,  0,  LOG_NORMAL   }, */

/* Define this to be some mobile in your zone. */
#define MOB_VNUM_SAGE	   8911
void do_identify( CHAR_DATA *ch, char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *rch;

    if ( ( obj = get_obj_carry( ch, argument ) ) == NULL )
    {
       send_to_char( "You are not carrying that.\n\r", ch );
       return;
    }

    for ( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
       if (IS_NPC(rch) && rch->pIndexData->vnum == MOB_VNUM_SAGE)
          break;

    if (!rch)
    {
       send_to_char("No one here seems to know much about that.\n\r", ch);
       return;
    }

    if (IS_IMMORTAL(ch))
       act( "$n cackles and comps you!\n\r", rch, obj, ch, TO_VICT );
    else if (ch->gold < obj->level * 100 + 50)
       {
       act( "$n resumes tossing bones without looking at $p.",
             rch, obj, 0, TO_ROOM );
       return;
       }
    else
       {
       ch->gold -= obj->level * 100 + 50;
       send_to_char("Your purse feels lighter.\n\r", ch);
       }

    act( "$n fondles $p and tosses some chicken bones to the ground.",
       rch, obj, 0, TO_ROOM );
    spell_identify( 0, 0, ch, obj );
}

/* Example mobile to be the indentification guy.  I added a sage to the Holy
   Grove.

#8911
man cripple~
an old man~
A crippled old man is kneeling here, tossing chicken bones onto the ground.
~
When you look down at him, he raises his head to you and you can see that one
of his eyes is hideously enlarged and blue, while the other is a blackened
empty socket.  The old man beams a toothless grin at you.
~
64|128 1|8|128|8192 +1000 S
32 0 -5 20d40+150 1d8+12
50 60000
8 8 1
*/

Falling objects

/* 
   Falling objects

   Ok, what this does it causes objects in air sectors to fall.  This is
a real nice touch if you have lots of airy zones, like my Aarakocran city,
or the air above Midgaard.

   Everything falls.  This is pretty funny if you are standing in the
square in Midgaard below the air and corpses of flying monsters start
falling down.  It is also pretty funny if you have flying mobiles in air
zones who have treasure and as players loot the corpse the corpse is falling.

   Put this code in UPDATE.C in the function obj_update() where you see

	if ( obj->timer <= 0 || --obj->timer > 0 )
	    continue;

   Put it before the continue and put braces around the whole thing and
the continue.

            if (obj->in_room &&
                obj->in_room->sector_type == SECT_AIR &&
                (obj->wear_flags & ITEM_TAKE) &&
                obj->in_room->exit[5] &&
                obj->in_room->exit[5]->to_room)
               {
               ROOM_INDEX_DATA *new_room = obj->in_room->exit[5]->to_room;

               if (( rch = obj->in_room->people ) != NULL )
                  {
                  act( "$p falls away.", rch, obj, NULL, TO_ROOM );
                  act( "$p falls away.", rch, obj, NULL, TO_CHAR );
                  }

               obj_from_room(obj);
               obj_to_room(obj, new_room);

               if (( rch = obj->in_room->people ) != NULL )
                  {
                  act( "$p floats by.", rch, obj, NULL, TO_ROOM );
                  act( "$p floats by.", rch, obj, NULL, TO_CHAR );
                  }
               }
*/

Trophy bags

/*
   Ok, this is for making trophy bags.  Here is the idea.  Player kills some
major mobile, like Ares and can take the corpse and skin it and make a bag
out of it.  You can do this with player corpses as well, works great if you
have a PKILL mud.

   I originally set this up as a NPC only spell, like dragon breath.  The idea
was there would be a scroll of it, and also a taxidermist shopkeeper who would
do it for you.  On the MUD where it went in, it was made a player spell and
given to some kind of woodsman class.

   You can fiddle with the amount these bags will contain below.  Note that if
you decide to go with the taxidermist idea, and make him a shopkeeper, he will
buy corpses for 1-2gp and make 1000gp trophy bags.  Because of how Merc is set
up, you also need to change the "level" of corpses to level 1 instead of level
0, shopkeepers don't buy level 0 stuff.

   Add this to the spell table in CONST.C

        "make bag",	{ 37, 37, 37, 37 },
	spell_make_bag,	TAR_OBJ_INV,		POS_STANDING,
	NULL,			SLOT(50),	100,	24,
	"",			"!Make Bag!"*/

   Put this in MAGIC.C
*/

void spell_make_bag( int sn, int level, CHAR_DATA *ch, void *vo )
{
    static char *headers[] = { "corpse of the ", "corpse of The ",
                               "corpse of an ", "corpse of An ",
                               "corpse of a ", "corpse of A ",
                               "corpse of " }; // (This one must be last)
    OBJ_DATA *obj = (OBJ_DATA *) vo;
    char buf[MAX_STRING_LENGTH];
    int i;

    if ( obj->item_type != ITEM_CORPSE_NPC && obj->item_type != ITEM_CORPSE_PC)
	return;

    for (i = 0; i < 7; i++)
       {
       int len = strlen(headers[i]);
       if ( memcmp(obj->short_descr, headers[i], len) == 0 )
          {
          sprintf( buf, "bag %s", obj->short_descr+len );
          free_string( obj->name );
          obj->name = str_dup(buf);

          sprintf( buf, "A bag of fine %s hide catches your eye.  ",
             obj->short_descr+len );
          free_string( obj->description );
          obj->description = str_dup( buf );

          sprintf( buf, "bag made from %s hide", obj->short_descr+len );
          free_string( obj->short_descr );
          obj->short_descr = str_dup( buf );

          break;
          }
       }

    obj->item_type = ITEM_CONTAINER;
    obj->wear_flags = ITEM_HOLD|ITEM_TAKE;
    obj->timer = 0;
    obj->weight = 5;
    obj->level = level/3;
    obj->cost = level * 50;
    obj->value[0] = level * 10;                 /* Weight capacity */
    obj->value[1] = 1;                          /* Closeable */
    obj->value[2] = -1;                         /* No key needed */
    obj->pIndexData = get_obj_index( 5660 );    /* So it's not a corpse */

    act( "Your new $p looks pretty snazzy.", ch, obj, NULL, TO_CHAR );
    act( "$n's new $p looks pretty snazzy.", ch, obj, NULL, TO_ROOM );

    send_to_char( "Ok.\n\r", ch );
    return;
}

/*

   Put this in a zone.  If you change the number change it in the code
as well.

#5660
bag~
a generic bag~
A generic bag generates the VNUM for the 'make purse' spell.  ~
~
15 0 1
50 0 0 0 
5 50 0
*/

/* Put this in SPECIAL.C.  Remember to add this to spec_lookup() as well. */

bool spec_taxidermist( CHAR_DATA *ch )
{
    OBJ_DATA *inv;
    int sn;

    if ( ch->position != POS_STANDING )
	return FALSE;

    if ( ch->pIndexData->pShop == 0 ||
         time_info.hour < ch->pIndexData->pShop->open_hour ||
         time_info.hour > ch->pIndexData->pShop->close_hour )
       return FALSE;

    for ( inv = ch->carrying; inv != NULL; inv = inv->next_content )
    {
    if (inv->item_type == ITEM_CORPSE_NPC)
       {
       if ( ( sn = skill_lookup( "make bag" ) ) >= 0 )
          (*skill_table[sn].spell_fun) ( sn, ch->level, ch, inv );
       return TRUE;
       }
    else if (inv->wear_loc == WEAR_NONE && number_percent() < 5)
       {
       act( "$n suggests you buy $p.", ch, inv, NULL, TO_ROOM );
       return TRUE;
       }
    }

    return FALSE;
}

Snake-charmer mobile spec-fun

/* Ok, here is a spec-fun example for a nasty mobile known as the snake
   charmer.  I rather like this guy, but I never could find a MUD to
   put this code in.

   Here is what happens.  Player attacks snake charmer mobile.  Snake
   charmer then casts CHARM spell on player, and forces player to give
   charmer weapons, sing, and he can even order the player to CHAT that
   the snake charmer is this really great mob to attack.

   I figure this should liven up any egypt or india zone.
*/

bool spec_snake_charm( CHAR_DATA *ch )
{
    CHAR_DATA *victim;
    CHAR_DATA *v_next;
    char *spell;
    int sn;

    if ( ch->position != POS_FIGHTING )
       {
       switch ( number_bits( 3 ) ) {
       case 0:
          do_order( ch, "all sing charmer" ); /* a chance to get free here */
          break;
       case 1:
          do_order( ch,
             "all chat 'The snake charmer area is pretty cool.  "
             "I'm getting a lot of experience really fast!" );
          break;
       case 2:
          do_order( ch,
             "all chat 'YES!  I just got 327xp for killing the snake charmer!");
          break;
       case 3:
          do_order( ch, "all remove dagger" );
          do_order( ch, "all give dagger charmer" );
          break;
       case 4:
          do_order( ch, "all remove sword" );
          do_order( ch, "all give sword charmer" );
          break;
       case 5:
          do_order( ch, "all remove mace" );
          do_order( ch, "all give mace charmer" );
          break;
       case 6:
          do_order( ch, "all drop all" );
          break;
       case 7:
          do_order( ch, "all cast 'cure light' charmer" );
          break;
       };

       return TRUE;
       }

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
	v_next = victim->next_in_room;
	if ( victim->fighting == ch && number_bits( 2 ) == 0 )
	    break;
    }

    if ( victim == NULL )
	return FALSE;

	act( "$n begins playing a new, beautiful song.", ch, NULL, NULL, TO_ROOM );
    spell_charm_person(gsn_charm_person, ch->level, ch, victim );
    if (IS_AFFECTED(victim, AFF_CHARM))
       stop_fighting( victim, TRUE );

    return TRUE;
}

Random objects

/*
    This one is real nifty.  The idea comes from a Mr. Peckham whose
    address I have lost, this is my own reimplementation of it, as I
    never got to see his code.

    To get this to work you have to either modify your area file format
    to have resets for random objects and your DB.C loader.  I'll leave
    that as an exercise for you.

    If you want to see how this works without all the fuss, put something
    like this in your create_mobile() in DB.C.  This gives any mobile a
    random chance to have something special.

    if (number_percent() <= 10)
       wield_random_magic( mob );
    else if (number_percent() <= 10)
       wield_random_armor( mob );
*/


#define nelems(a) (sizeof (a)/sizeof (a)[0])

// Calculate a meaningful modifier and amount
void random_apply( OBJ_DATA *obj, CHAR_DATA *mob )
{
   static int attrib_types[] = { APPLY_STR, APPLY_DEX, APPLY_DEX, APPLY_INT,
      APPLY_INT, APPLY_WIS, APPLY_CON, APPLY_CON, APPLY_CON };
   static int power_types[] = { APPLY_MANA, APPLY_HIT, APPLY_MOVE, APPLY_AC };
   static int combat_types[] = { APPLY_HITROLL, APPLY_HITROLL, APPLY_DAMROLL,
      APPLY_SAVING_SPELL, APPLY_SAVING_SPELL, APPLY_SAVING_BREATH };

   AFFECT_DATA *paf = alloc_perm( sizeof(*paf) );
   paf->type	     = -1;
   paf->duration  = -1;
   paf->bitvector = 0;
   paf->next	     = obj->affected;
   obj->affected  = paf;
   switch (number_bits(2)) {
   case 0:
      paf->location  = attrib_types[number_range(0, nelems(attrib_types)-1)];
      paf->modifier  = 1;
      break;
   case 1:
      paf->location  = power_types[number_range(0, nelems(power_types)-1)];
      paf->modifier  = number_range(mob->level/2, mob->level);
      break;
   case 2:
   case 3:
      paf->location  = combat_types[number_range(0, nelems(combat_types)-1)];
      paf->modifier  = number_range(1, mob->level/6+1);
      break;
   }

   SET_BIT(obj->extra_flags, ITEM_MAGIC);

   // Is item cursed?
   if (number_percent() <= 5)
      {
      paf->modifier = -paf->modifier;
      SET_BIT(obj->extra_flags, ITEM_NODROP);
      if (number_percent() <= 15)
         SET_BIT(obj->extra_flags, ITEM_NOREMOVE);
      }
}

// Jewelry stuff
static char *adj1[] = { "splendid", "ancient", "dusty", "scratched",
   "flawed", "burnt", "heavy", "gilded", "spooky", "flaming", "plain",
   "ornate", "inscrutable", "obscene", "wrinkled" };
static char *adj2[] = { "diamond", "emerald", "topaz", "wooden", "jade",
   "white gold", "onyx", "tin", "glass", "marble", "black", "granite" };

#define MASK_IGNORE     (1<level ) );
   sh_int type;
   int    n_adj1 = number_range(0, nelems(adj1)-1);
   int    n_adj2 = number_range(0, nelems(adj2)-1);

   // Potion stuff
   static char *potions[] = { "bottle", "snifter", "jar", "bulb", "potion" };

   // Pill stuff
   static char *pills[] = { "bag of dust", "honeycomb", "root", "leaf",
      "crystal", "powder packet", "seed" };

   // Bag types (no wearable belts/backpacks... yet)
   static char *skins[] = { "lamia skin", "cloth", "leather", "tough leather",
      "zebra-skin", "gilded" };
   static char *bags[] = { "bag", "valise", "pail", "basket", "jar",
      "box", "sack" };

   char buffer[256];
   char buf[MAX_STRING_LENGTH];
   char name[MAX_STRING_LENGTH];

   switch (item_type) {
   case 48: /* scroll */
      sprintf(buffer, "%s %s scroll", adj1[n_adj1], adj2[n_adj2]);
      obj->value[0] = number_range(obj->level / 2+1, obj->level * 3 / 2+1); /* level */
      obj->value[0] = URANGE(1, obj->value[0], 36);
      obj->value[1] = random_spell(obj->value[0],
         MASK_IGNORE|MASK_OFFENSIVE|MASK_DEFENSIVE|MASK_SELF, &type);
      if (number_percent() < 50)
         obj->value[2] = random_spell(obj->value[0], 1<value[0] = number_range(obj->level / 3 + 1, obj->level * 3 / 2);  /* level */
      obj->value[0] = URANGE(1, obj->value[0], 36);
      obj->value[1] = number_fuzzy(obj->level / 2 + 3); /* max charges */
      obj->value[2] = number_range(1, obj->value[1]);   /* charges left */
      obj->value[3] = random_spell(obj->value[0], MASK_OFFENSIVE, &type);
      break;
   case 50: /* staff */
      sprintf(buffer, "%s %s staff", adj1[n_adj1], adj2[n_adj2]);
      obj->value[0] = number_range(obj->level / 4 + 1, obj->level * 3 / 2);  /* level */
      obj->value[0] = URANGE(1, obj->value[0], 36);
      obj->value[1] = number_fuzzy(obj->level / 2 + 3); /* max charges */
      obj->value[2] = number_range(1, obj->value[1]);   /* charges left */
      obj->value[3] = random_spell(obj->value[0],
         MASK_IGNORE|MASK_OFFENSIVE|MASK_DEFENSIVE|MASK_SELF, &type);
      break;
   case 51: /* potion */
      sprintf(buffer, "%s %s %s", adj1[n_adj1], adj2[n_adj2],
         potions[number_range(0, nelems(potions)-1)]);
      obj->value[0] = number_range(obj->level / 2+1, obj->level * 3 / 2+1); /* level */
      obj->value[0] = URANGE(1, obj->value[0], 36);
      obj->value[1] = random_spell(obj->value[0], MASK_DEFENSIVE, &type);
      if (number_percent() < 50)
         obj->value[2] = random_spell(obj->value[0], MASK_DEFENSIVE, &type);
      break;
   case 52: /* pill */
      sprintf(buffer, "%s %s %s", adj1[n_adj1], adj2[n_adj2],
         pills[number_range(0, nelems(pills)-1)]);
      obj->value[0] = number_range(obj->level / 2+1, obj->level * 3 / 2+1); /* level */
      obj->value[0] = URANGE(1, obj->value[0], 36);
      obj->value[1] = random_spell(obj->value[0], MASK_DEFENSIVE, &type);
      if (number_percent() < 50)
         obj->value[2] = random_spell(obj->value[0], MASK_DEFENSIVE, &type);
      break;
   case 53: /* bag */
      sprintf(buffer, "%s %s %s", adj1[n_adj1],
         skins[number_range(0, nelems(skins)-1)],
         bags[number_range(0, nelems(bags)-1)]);
      obj->value[0] = number_range(mob->level, mob->level * 25); /* weight */
      obj->value[1] = number_range(0, 1);
      obj->value[2] = -1;
      break;
   };

   // Generate the description strings
   free_string( obj->name );
   obj->name = str_dup( buffer );

   sprintf( buf, "a%s %s",
      (buffer[0] == 'a' || buffer[0] == 'e' || buffer[0] == 'i' ||
       buffer[0] == 'o' || buffer[0] == 'u') ? "n" : "", buffer );
   free_string( obj->short_descr );
   obj->short_descr = str_dup( buf );

   free_string( obj->description );
   sprintf( buf, "%s lies here.", obj->short_descr );
   obj->description = str_dup( buf );

   // Put the item in the mob's inventory
   obj_to_char( obj, mob );
}

// Anything wearable, and trinkets
void wield_random_armor( CHAR_DATA *mob )
{
   int item_type = number_range(0, MAX_WEAR - 1); /* template from LIMBO.ARE */
   OBJ_INDEX_DATA *pObjIndex = get_obj_index( item_type + 30 );
   OBJ_DATA *obj = create_object( pObjIndex, number_fuzzy( mob->level ) );
   int    n_adj1 = number_range(0, nelems(adj1)-1);
   int    n_adj2 = number_range(0, nelems(adj2)-1);
   char *name = "[random]";

   // Armor stuff
   static char *armor_types[] = { "leather", "studded leather", "bronze",
      "chain", "plate", "mithral" };
   static int armor_mul[] = { 1, 3, 2, 5, 10, 10 };
   static int armor_div[] = { 1, 2, 1, 1,  1,  3 };

   // Weapon stuff
   static char *weapon_types[] = { "sword", "sword", "sword", "sword", "sword",
      "short sword", "dagger", "dagger", "hammer", "mace", "mace", "whip" };
   static int weapon_dam[] = { 3, 3, 3, 3, 3, 11, 11, 11, 0, 7, 7, 4 };

   // Trinket stuff
   static char *noun[] = { "pebble", "bauble", "stone", "charm", "fetish",
      "bone", "trinket" };
   char buffer[64];

   char buf[MAX_STRING_LENGTH];

   if (obj->item_type == ITEM_ARMOR)
      {
      int ac_type = URANGE(0, mob->level/5, nelems(armor_types)-1);
      name = armor_types[ac_type];
      obj->weight *= armor_mul[ac_type];
      obj->weight /= armor_div[ac_type];

      if (number_percent() < mob->level / 3)
         random_apply(obj, mob);
      }
   else if (obj->item_type == ITEM_WEAPON)
      {
      int wea_type = number_range(0, nelems(weapon_types)-1);
      name = weapon_types[wea_type];
      obj->value[3] = weapon_dam[wea_type];
      }
   else if (obj->item_type == ITEM_TREASURE)
      {
      if (number_percent() < mob->level)
         {
         random_apply(obj, mob);

         if (number_percent() < mob->level / 3)
            random_apply(obj, mob);
         }

      if (obj->wear_flags & ITEM_HOLD) /* trinket? */
         sprintf(buffer, "%s %s %s", adj1[n_adj1],
            adj2[n_adj2],
            noun[number_range(0, nelems(noun)-1)]);
      else /* no, necklace or something */
         sprintf(buffer, "%s %s", adj1[n_adj1],
            adj2[n_adj2]);
      name = buffer;
      }

   sprintf( buf, obj->short_descr, name );
   free_string( obj->short_descr );
   obj->short_descr = str_dup( buf );

   obj_to_char( obj, mob );
   equip_char( mob, obj, item_type );
}
You also need to add the following to LIMBO.ARE
#30
light~
a%s %s light source~
A random light source makes the 'VNUM' for a random light.~
~
1 0 1
0 0 48 0
1 0 0
#31
ring~
a%s %s ring~
A random ring makes the 'VNUM' for a random ring.~
~
8 0 1|2
0 0 0 0
1 0 0
#32
ring~
a%s %s ring~
A random ring makes the 'VNUM' for a random ring.~
~
8 0 1|2
0 0 0 0
1 0 0
#33
necklace~
a%s %s necklace~
A random necklace makes the 'VNUM' for a random necklace.~
~
8 0 1|4
0 0 0 0
1 0 0
#34
cape~
a%s  %s cape~
A random cape makes the 'VNUM' for a random cape.~
~
9 0 1|4
0 0 0 0
5 0 0
#35
body armor~
%s body armor~
Random body armor makes the 'VNUM' for random body armor.~
~
9 0 1|8
0 0 0 0
10 0 0
#36
helmet~
a%s %s helmet~
A random helmet makes the 'VNUM' for a random helmet.~
~
9 0 1|16
0 0 0 0
5 0 0
#37
leggings~
%s leggings~
Random leggings make the 'VNUM' for random leggings.~
~
9 0 1|32
0 0 0 0
5 0 0
#38
boots~
%s boots~
Random boots make the 'VNUM' for random boots.~
~
9 0 1|64
0 0 0 0
5 0 0
#39
gauntlets~
%s gauntlets~
Random gauntlets make the 'VNUM' for random gauntlets.~
~
9 0 1|128
0 0 0 0
5 0 0
#40
sleeves~
%s sleeves~
Random sleeves make the 'VNUM' for random sleeves.~
~
9 0 1|256
0 0 0 0
4 0 0
#41
shield~
a %s shield~
A random shield makes the 'VNUM' for a random shield.~
~
9 0 1|512
0 0 0 0
12 0 0
#42
robe~
a %s robe~
A random robe makes the 'VNUM' for a random robe.~
~
9 0 1|1024
0 0 0 0
2 0 0
#43
belt~
a%s %s belt~
A random belt makes the 'VNUM' for a random belt.~
~
9 0 1|2048
0 0 0 0
3 0 0
#44
bracelet~
a%s %s bracelet~
A random bracelet makes the 'VNUM' for a random bracelet.~
~
8 0 1|4096
0 0 0 0
1 0 0
#45
bracer~
a%s %s bracer~
A random bracer makes the 'VNUM' for a random bracer.~
~
9 0 1|4096
0 0 0 0
5 0 0
#46
weapon~
a%s %s~
A random weapon makes the 'VNUM' for a random weapon.~
~
5 0 1|8192
0 0 0 0
5 0 0
#47
trinket~
a%s %s~
A random trinket makes the 'VNUM' for a random trinket.~
~
8 0 1|16384
0 0 0 0
4 0 0
#48
scroll~
a%s %s scroll~
A random scroll makes the 'VNUM' for a random scroll.~
~
2 0 1|16384
0 0 0 0
1 0 0
#49
wand~
a%s wand~
A random wand makes the 'VNUM' for a random wand.~
~
3 0 1|16384
0 0 0 0
1 0 0
#50
staff~
a%s %s staff~
A random staff makes the 'VNUM' for a random staff.~
~
4 0 1|16384
0 0 0 0
5 0 0
#51
potion~
a%s~
A random potion makes the 'VNUM' for a random potion.~
~
10 0 1|16384
0 0 0 0
1 0 0
#52
pill~
a%s~
A random pill makes the 'VNUM' for a random pill.~
~
26 0 1
0 0 0 0
1 0 0
#53
bag~
a%s~
A random bag makes the 'VNUM' for a random bag.~
~
15 0 1|16384
0 0 0 0
1 0 0


Maintained by esnible@acm.org
MUD info page
Last updated March 9, 1996