/
ScryMUD/mud/
ScryMUD/mud/grrmud/Boards/
ScryMUD/mud/grrmud/Help/
ScryMUD/mud/grrmud/Pfiles/
ScryMUD/mud/grrmud/PlayerSacks/
ScryMUD/mud/grrmud/PlayerShops/
ScryMUD/mud/grrmud/help_filter/
ScryMUD/mud/hegemon/
ScryMUD/mud/hegemon/data/
ScryMUD/mud/hegemon/data/help/battle/
ScryMUD/mud/hegemon/data/help/client/
ScryMUD/mud/hegemon/data/help/communications/
ScryMUD/mud/hegemon/data/help/skills/
ScryMUD/mud/hegemon/data/help/spells/
ScryMUD/mud/include/
ScryMUD/mud/lib/
ScryMUD/mud/lib/bitfield/
ScryMUD/mud/lib/log/
ScryMUD/mud/lib/string2/
// $Id: misc2.cc,v 1.24.2.19 2000/05/13 19:42:59 greear Exp $
// $Revision: 1.24.2.19 $  $Author: greear $ $Date: 2000/05/13 19:42:59 $

//
//ScryMUD Server Code
//Copyright (C) 1998  Ben Greear
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// To contact the Author, Ben Greear:  greear@cyberhighway.net, (preferred)
//                                     greearb@agcs.com
//

///*************************** misc2.cc ****************************///
///********  Headers for this file are found in misc2.h   ************///
///********  Bitfield masks defined here as well.         ************///

#include "misc.h"
#include "misc2.h"
#include <stdio.h>
#include <string.h>
#include "classes.h"
#include "commands.h"
#include "command2.h"
#include "command3.h"
#include "spec_prc.h"
#include "batl_prc.h"
#include "spells.h"
#include "skills.h"
#include <PtrArray.h>
#include "vehicle.h"
#include <sys/types.h>
#include <signal.h>
#include <LogStream.h>
#include "SkillSpell.h"


// Attempt to load the player-run shop owner of that number.
// Returns a newly allocated SMOB, or NULL if a problem is
// encountered.
critter* load_player_shop_owner(int mob_num) {
   switch (config.useMySQL) {
#ifdef USEMYSQL
      case true:
         return db_load_player_shop_owner(mob_num);
         break;
#endif
      case false:
         return file_load_player_shop_owner(mob_num);
         break;
   }
   return NULL;
}
#ifdef USEMYSQL
critter* db_load_player_shop_owner(int mob_num) {
   critter* sk = new critter();
   sk->dbRead(mob_num, 0, true);
   sk->short_cur_stats[26] = 1;
   affected_mobs.append(sk);
   return sk;
}
#endif
critter* file_load_player_shop_owner(int mob_num) {
   String buf(100);

   if (mob_list[mob_num].isInUse()) {
      // Find the file name of our shopkeeper.
      Sprintf(buf, "./PlayerShops/%S_%i", mob_list[mob_num].getShortName(),
              mob_num);

      ifstream dafile(buf);
      
      if (dafile) {
         critter* sk = new critter();
         sk->fileRead(dafile, TRUE); //read all inventory.

         // Make sure it's labeled as a SMOB
         sk->short_cur_stats[26] = 1; /* make it a SMOB */
         affected_mobs.append(sk);

         return sk;
      }//if
      else {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR:  Could not open Player Shop Keeper -:"
                   << buf << ":- for reading." << endl;
         }//if
         return NULL;
      }//else
   }//if
   else {
      if (mudlog.ofLevel(ERROR)) {
         mudlog << "ERROR:  load_player_shop_owner, mob_num: "
                << mob_num << " is not in use." << endl;
      }//if
      return NULL;
   }//else
}//load_player_shop_owner


int save_player_shop_owner(critter& pc) {
   String buf(100);

   if (!pc.isPlayerShopKeeper()) {
      mudlog << "ERROR:  save_player_shop_owner, mob: " 
             << pc.getName() << " id_num: " << pc.getIdNum()
             << " is not a shop keeper." << endl;
      return FALSE;
   }

   if (mob_list[pc.getIdNum()].isInUse()) {
      // Find the file name of our shopkeeper.
      Sprintf(buf, "./PlayerShops/%S_%i", mob_list[pc.getIdNum()].getShortName(),
              pc.getIdNum());

      ofstream dafile(buf);
      
      if (dafile) {
         pc.Write(dafile);
         return TRUE;
      }//if
      else {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR:  Could not open Player Shop Keeper -:"
                   << buf << ":- for writing." << endl;
         }//if
         return FALSE;
      }//else
   }//if
   else {
      if (mudlog.ofLevel(ERROR)) {
         mudlog << "ERROR:  save_player_shop_owner, mob_num: "
                << pc.getIdNum() << " is not in use." << endl;
      }//if
      return FALSE;
   }//else
}//save_player_shop_owner

object* load_player_box(int box_num) {
#ifdef USEMYSQL
   if (config.useMySQL)
      return db_load_player_box(box_num);
   else
#endif
      return file_load_player_box(box_num);
}

#ifdef USEMYSQL
object* db_load_player_box(int box_num) {
   object* box = new object();
   box->dbRead(box_num, -1, true);
   return box;
}
#endif

object* file_load_player_box(int box_num) {
   String buf(100);

   if (obj_list[box_num].isInUse()) {
      Sprintf(buf, "./PlayerSacks/%i", box_num);

      ifstream dafile(buf);
      
      if (dafile) {
         object* box = new object();
         box->fileRead(dafile, TRUE);

         return box;
      }
      else {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR:  Could not open Player Box -:"
                   << buf << ":- for reading." << endl;
         }
         return NULL;
      }
   }
   else {
      if (mudlog.ofLevel(ERROR)) {
         mudlog << "ERROR:  load_player_box, box_num: "
                << box_num << " is not in use." << endl;
      }
      return NULL;
   }
}

int save_player_box(object& box) {
   String buf(100);

   if(obj_list[box.getIdNum()].isInUse()) {
      Sprintf(buf, "./PlayerSacks/%i", box.getIdNum());

      ofstream dafile(buf);

      if(dafile) {
         box.Write(dafile);
         return TRUE;
      }
      else {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR:  Could not open Player Owned Box -:"
                   << box.getIdNum() << ":- for writing." << endl;
         }
         return FALSE;
      }
   }
   else {
      if (mudlog.ofLevel(ERROR)) {
         mudlog << "ERROR:  save_player_box, obj_num: "
                << box.getIdNum() << " is not in use." << endl;
      }
      return FALSE;
   }
}

int core_dump(const char* msg) {
   cerr << "In my_assert, msg:  " << msg << endl;
   mudlog << "ERROR:  Dumping core on purpose..." << endl
          << flush;
   //From the great Katrina McClelan!
   //TODO:  Add waidpid etc...
   kill(getpid(),SIGSEGV); /* this'll dump core */
   sleep(10); /* make sure it stops here */

   return TRUE;

//    if (!fork()) {
//       /* child copy starts here */
//       /* dead by here */
//       kill(getpid(),SIGSEGV); /* this'll dump core */
//       sleep(10); /* make sure it stops here */
//    }
//    else {
//       cerr << "fork failed:  " << strerror(errno) << endl;
//    }
//    return TRUE;
}//core_dump
        /* parent continues unaware */


short a_will_help_b_against_c(critter& a, critter& b, critter& c) {
  if (!a.mob || !a.mob->proc_data || a.INT1)
    return FALSE;

  if (a.mob->getSocialAwareness() < 2)
    return FALSE;

  int should = a.mob->getSocialAwareness() * 10 + 5;
  /* should a like b? */
  if (a.CLASS == b.CLASS)
    should += 50;

  if (a.RACE == b.RACE)
    should += 75;

  if (abs(abs(a.ALIGN) - (abs(b.ALIGN))) < 300) 
    should += 90;

  if (a.ALIGN > 300)
    should += 35;

  /* now figure out why a should like c */
  if (a.CLASS == c.CLASS)
    should -= 50;

  if (a.RACE == c.RACE)
    should -= 75;

  if (abs(abs(a.ALIGN) - (abs(b.ALIGN))) < 300) 
    should -= 90;

  should -= d(1, (40 - a.LEVEL) * 4); //generally don't wanna help out..

  if (should > 0)
    return TRUE;
  else
    return FALSE;
}//a_helps_b_against_c

  
void lose_fly(critter& pc, short do_msg = FALSE) {
  int is_dead;
  if (do_msg) {
    show("You glance down just in time to see the ground rushing up to meet you.\n", pc);
    pc.emote("suddenly falls out of the sky.\n");
  }

  if (ROOM.needsFly() && (ROOM.getFallTo() != 0)) {
    pc.doGoToRoom(ROOM.getFallTo(), "from the sky", NULL, is_dead, 
                  pc.getCurRoomNum(), 1, TRUE);
  }

  if (!is_dead)
    look(1, &NULL_STRING, pc, TRUE);
}//lose_fly

void unpetrify(critter& pc, short do_msg = FALSE) {
  if (do_msg)
    show("Your skin returns to its origional rubbery texture.\n", pc);
  pc.CRIT_FLAGS.turn_off(14);
}//unpetrify

void show_stat_affects(object& obj, critter& pc) {
   Cell<stat_spell_cell*> cll(obj.stat_affects);
   stat_spell_cell* ptr;
   String buf(100);

   if (obj.stat_affects.isEmpty()) {
      pc.show("NONE\n");
   }
   
   while ((ptr = cll.next())) {
      if (ptr->stat_spell == 1) {
         Sprintf(buf, "Strength[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 2) {
         Sprintf(buf, "Inteligence[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 3) {
         Sprintf(buf, "Constitution[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 4) {
         Sprintf(buf, "Charisma[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 5) {
         Sprintf(buf, "Wisdom[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 6) {
         Sprintf(buf, "Dexterity[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 7) {
         Sprintf(buf, "Hit (chance)[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 8) {
         Sprintf(buf, "Damage[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 9) {
         Sprintf(buf, "AC (armor)[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 10) {
         Sprintf(buf, "Attacks[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 15) {
         Sprintf(buf, "HP (current)[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 16) {
         Sprintf(buf, "Mana (current)[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 17) {
         Sprintf(buf, "Movement (current)[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 18) {
         Sprintf(buf, "Alignment[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 22) {
         Sprintf(buf, "Practices[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 23) {
         Sprintf(buf, "HP (max)[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 24) {
         Sprintf(buf, "Mana (max)[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 25) {
         Sprintf(buf, "Movement (max)[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 27) {
         Sprintf(buf, "Damage Recieved Modifier[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 28) {
         Sprintf(buf, "Damage Given Modifier[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 29) {
         Sprintf(buf, "Heat Resistance[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 30) {
         Sprintf(buf, "Cold Resistance[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 31) {
         Sprintf(buf, "Electrical Resistance[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 32) {
         Sprintf(buf, "Spell Resistance[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 35) {
         Sprintf(buf, "Bare Hand Dice Count[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 36) {
         Sprintf(buf, "Bare Hand Dice Sides[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 37) {
         Sprintf(buf, "HP Regeneration[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 38) {
         Sprintf(buf, "Mana Regeneration[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 39) {
         Sprintf(buf, "Movement Regeneration[%i]:%P25 %i\n",
                 ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 100) {
         Sprintf(buf, "Hunger[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 101) {
         Sprintf(buf, "Thirst[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else if (ptr->stat_spell == 102) {
         Sprintf(buf, "Drugged[%i]:%P25 %i\n", ptr->stat_spell, ptr->bonus_duration);
         show(buf, pc);
      }//if
      else {
         Sprintf(buf, "Unknown [%i]:, value: [%i]\n", ptr->stat_spell,
                 ptr->bonus_duration);
         show(buf, pc);
      }//if

  }//while
}//show_stat_affects


/** Tests this performs:  A(!animal)
 *                        B(!pc in_battle)  b (! in mob room)
 *                        C(owns aux_crit) F(frozen)
 *                        G(!gagged) I(is immort) K(know spell), M(has mana), 
 *                        m(!mob, smob ok), N(!magic),
 *                        P(paralyzed), R(owns aux_rm), r(resting or standing)
 *                        S(is_standing), V(!violence), Z(room is zlocked)
 *  Syntax is:  If test is TRUE, then action can be done.  Note that some
 *     of the conditions are negative logic.  Thus, 'A' will be TRUE if
 *     the critter is NOT an ANIMAL.
 *
 *  Case matters.
 */
int ok_to_do_action(critter* vict, const char* flags, int spell_num,
                    critter& pc, room* aux_rm = NULL, critter* aux_crit = NULL,
                    int do_msg = TRUE) {
  String buf(100);
  int len = strlen(flags);  
  char chr;
  int mana_cost = 0;

  // mana_cost = SSCollection::instance().getSS(spell_num).getManaCost();
  mana_cost = get_mana_cost(spell_num, pc);

  if (pc.isMob()) {
     Sprintf(buf, "ERROR:  mob casting spell# %i.\n", spell_num);
     mudlog.log(ERROR, buf);
     return FALSE;
  }//if

  for (int i = 0; i<len; i++) {
     chr = flags[i];
     if (chr == 'M') {
        if (mana_cost > pc.MANA) {
           if (do_msg) {
              show("You don't have enough energy to cast this spell!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'A') {
        if (pc.getClass() == ANIMAL) {
           if (do_msg) {
              show("Animals can't do that!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if    
     else if (chr == 'K') {
        if (get_percent_lrnd(spell_num, pc) <= 0) {
           if (do_msg) {
              show("You don't know where to begin!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if    
     else if (chr == 'm') {
        if (pc.isMob()) {
           if (do_msg) {
              mudlog << "ERROR: ok_to_do_action, got a MOB (not SMOB).\n";
              pc.show("ERROR:  mob trying to do an action.\n");
           }//if
           return FALSE;
        }//if
     }//if    
     else if (chr == 'G') {
        if (pc.isGagged()) {
           if (do_msg) {
              show("You have been gagged.\n", pc);
              return FALSE;
           }//if
        }
     }
     else if (chr == 'P') {
        if (pc.CRIT_FLAGS.get(14)) {
           if (do_msg) {
              show("You are paralyzed!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if    
     else if (chr == 'F') {
        if (pc.pc && pc.PC_FLAGS.get(0)) {
           if (do_msg) {
              show("You are frozen!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'r') {
        if (pc.POS > POS_REST) {
           if (do_msg) {
              show("You must be in a more lively position!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'B') {
        if (!pc.IS_FIGHTING.isEmpty()) {
           if (do_msg) {
              show("You can't concentrate enough in battle!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'b') {
        if (ROOM.isNoMob()) {
           if (do_msg) {
              show("NPC's cannot be here.\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'N') {
        if (ROOM.isNoMagic()) {
           if (do_msg) {
              show("Magic cannot function here!\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'S') {
        if (pc.POS != POS_STAND) {
           if (do_msg) {
              show("You must be standing in order to do this.\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'R') {
        if (!aux_rm || !pc.doesOwnRoom(*aux_rm)) {
           if (do_msg) {
              pc.show("You do not own that room.\n");
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'Z') {
        if (!aux_rm || !aux_rm->isZlocked()) {
           if (do_msg) {
              show("You cannot edit a room that is not locked.\n", pc);
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'C') {
        if (!pc.doesOwnCritter(*aux_crit)) {
           if (do_msg) {
              pc.show("You do not own that critter.\n");
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'I') {
        if (!pc.isImmort()) {
           if (do_msg) {
              pc.show("Ehh??\n");
           }//if
           return FALSE;
        }//if
     }//if
     else if (chr == 'V') {
        if (ROOM.isZlocked() && !(pc.doesOwnRoom(ROOM) || pc.getImmLevel() > 8)) {
           if (do_msg) {
              pc.show("You don't own this room, and the zone is locked.\n");
           }
           return FALSE;
        }
        if (!pc.isImmort()) {
           if (ROOM.isHaven() && (&pc != vict)) {
              if (do_msg) {
                 show("Violence is not permitted here.\n", pc);
              }//if
              return FALSE;
           }//if
           else if (ROOM.isNoPK() && vict && vict->pc && (&pc != vict)) {
              if (do_msg) {
                 show("Player killing is not allowed here.\n", pc);
              }//if
              return FALSE;
           }//if
        }//if
     }//if
  }//for
  return TRUE;
}//ok_to_do_action


critter* check_for_diversions(critter& pc, char* tests, critter& agg) {
  char chr;
  int i;
  String buf(100);
   
  if (!tests)
    return &pc;

  for (i = 0; (chr = tests[i]); i++) {
    if (chr == 'S') {
      if (pc.temp_crit && pc.SHIELDED_BY) {
        if (d(1, 175) > pc.SHIELDED_BY->DEX + 
            get_percent_lrnd(SHIELD_SKILL_NUM, *(pc.SHIELDED_BY))) {
          if (ROOM.haveCritter(pc.SHIELDED_BY)) {
             return pc.SHIELDED_BY;
          }//if
        }//if
      }//if
    }//if
    else if (chr == 'G') {
      if (pc.temp_crit) {
        if (pc.GUARDED_BY) {
          if (d(1, 175) > pc.GUARDED_BY->DEX +
              get_percent_lrnd(GUARD_SKILL_NUM, *(pc.GUARDED_BY))) {
            if (ROOM.haveCritter(pc.GUARDED_BY)) {
               return pc.GUARDED_BY;
            }
          }//if
        }//if
      }//if
    }//if Guard
    else if (chr == 'M') {
      if (pc.mirrors > 0) {
        if (d(1, pc.mirrors) > 1) {
          pc.mirrors--;
          Sprintf(buf, "%S shatters a mirror image of you.\n",
                  name_of_crit(agg, pc.SEE_BIT));
          buf.Cap();
          show(buf, pc);
          Sprintf(buf, "breaks a mirror image of %S.",
                  name_of_crit(pc, ~0));
          emote(buf, agg, room_list[agg.getCurRoomNum()], TRUE, &pc);
          return NULL;
        }//if hit a mirror
      }//if has mirrors
    }//if mirror check
  }//for 

  return &pc;
}//check_for_diversions


stat_spell_cell* is_affected_by(int spell_num, critter& pc) {
  Cell<stat_spell_cell*> cll(pc.affected_by);
  stat_spell_cell* ptr;

  while ((ptr = cll.next())) {
    if (ptr->stat_spell == spell_num)
      return ptr;
  }//while

  return NULL;
}//is_affected_by


stat_spell_cell* is_affected_by(int spell_num, object& obj) {
  Cell<stat_spell_cell*> cll(obj.affected_by);
  stat_spell_cell* ptr;

  while ((ptr = cll.next())) {
    if (ptr->stat_spell == spell_num)
      return ptr;
  }//while

  return NULL;
}//is_affected_by (obj)


stat_spell_cell* has_stat_affect(int stat_num, object& obj) {
  Cell<stat_spell_cell*> cll(obj.stat_affects);
  stat_spell_cell* ptr;

  while ((ptr = cll.next())) {
    if (ptr->stat_spell == stat_num)
      return ptr;
  }//while

  return NULL;
}//has_stat_affect


const char* get_opposite_dir(const char* dir) {
   int i = strlen(dir);

   if (strncasecmp(dir, "north", i) == 0)
      return "south";
   else if (strncasecmp(dir, "east", i) == 0)
      return "west";
   else if (strncasecmp(dir, "south", i) == 0)
      return "north";
   else if (strncasecmp(dir, "west", i) == 0)
      return "east";
   else if (strncasecmp(dir, "northeast", i) == 0)
      return "southwest";
   else if (strncasecmp(dir, "southeast", i) == 0)
      return "northwest";
   else if (strncasecmp(dir, "southwest", i) == 0)
      return "northeast";
   else if (strncasecmp(dir, "northwest", i) == 0)
      return "southeast";
   else if (strncasecmp(dir, "up", i) == 0)
      return "down";
   else if (strncasecmp(dir, "down", i) == 0)
      return "up";
   else 
      return "UNKNOWN";
}//get_opposite_dir


void leave_room_effects(room& rm, critter& pc) {
   Cell<door*> cll(rm.DOORS);
   door* ptr;

   while ((ptr = cll.next())) {
      if (ptr->crit_blocking == &pc) {
         ptr->dr_data->door_data_flags.turn_off(14); //no longer blocked
         ptr->crit_blocking = NULL;
      }//if
   }//while

   pc.CRIT_FLAGS.turn_off(22); //no longer hiding or melding

   if (pc.pc) {
      pc.PC_FLAGS.turn_off(17);
   }//if
   rm.checkLight(FALSE);

   String cmd = "exit";
   rm.checkForProc(cmd, NULL_STRING, pc, -1);

}//leave_room_effects


void leave_room_effects(room& rm, object& obj) {

   rm.checkLight(FALSE);

   //String cmd = "exit";
   //rm.checkForProc(cmd, NULL_STRING, pc, -1);

}//leave_room_effects (Objects)



const String* single_obj_name(object& obj, int see_bit) {
   if (detect(see_bit, obj.OBJ_VIS_BIT))
      return Top(obj.names);
   else
      return &SOMETHING; //global 'someone' String
}//single_obj_name


const char* get_himself_herself(critter& pc) {
   if (pc.SEX == 0)
      return "herself";
   else if (pc.SEX == 1) 
      return "himself";
   else return "itself";
}//

const char* get_dude_chic(critter& pc) {
   if (pc.SEX == 0)
      return "chic";
   else if (pc.SEX == 1) 
      return "dude";
   else return "being";
}//

const char* get_fellow_lady(critter& pc) {
   if (pc.SEX == 0)
      return "lady";
   else if (pc.SEX == 1) 
      return "fellow";
   else return "being";
}//

const char* get_him_her(critter& pc) {
   if (pc.SEX == 0)
      return "her";
   else if (pc.SEX == 1) 
      return "him";
   else return "it";
}//


int is_grouped(critter& crit, critter& pc) {
   Cell<critter*> cll(pc.GROUPEES);
   critter* ptr;

   while ((ptr = cll.next())) {
      if (ptr == &crit)
         return TRUE;
   }//while

   crit.GROUPEES.head(cll);
   while ((ptr = cll.next())) {
      if (ptr == &pc)
         return TRUE;
   }//while
   return FALSE;
}//is_grouped



int get_race_num(const char* name) {
   if (strcasecmp(name, "undead") == 0)
      return UNDEAD;
   if (strcasecmp(name, "animal") == 0)
      return ANIMAL;
   if (strcasecmp(name, "monster") == 0)
      return MONSTER;
   if (strcasecmp(name, "human") == 0)
      return HUMAN;
   if (strcasecmp(name, "anitre") == 0)
      return ANITRE;
   if (strcasecmp(name, "avintre") == 0)
      return AVINTRE;
   if (strcasecmp(name, "darkling") == 0)
      return DARKLING;
   if (strcasecmp(name, "drow") == 0)
      return DROW;
   if (strcasecmp(name, "dragon") == 0)
      return DRAGON;
   if (strcasecmp(name, "dwarf") == 0)
      return DWARF;
   if (strcasecmp(name, "ironclad") == 0)
      return IRONCLAD;
   if (strcasecmp(name, "orgue") == 0)
      return OGRUE;
   if (strcasecmp(name, "rocktroll") == 0)
      return ROCKTROLL;
   if (strcasecmp(name, "elf") == 0)
      return ELF;
   if (strcasecmp(name, "faerie") == 0)
      return FAERIE;
   if (strcasecmp(name, "entity") == 0)
      return ENTITY;
   if (strcasecmp(name, "sombrian") == 0)
      return SOMBRIAN;
   if (strcasecmp(name, "avian") == 0)
      return AVIAN;
   return OTHER_RACE;
}//get race_num



int get_class_num(const char* name) {
   if (strcasecmp(name, "warrior") == 0) 
      return WARRIOR;
   if (strcasecmp(name, "sage") == 0) 
      return SAGE;
   if (strcasecmp(name, "wizard") == 0) 
      return WIZARD;
   if (strcasecmp(name, "ranger") == 0) 
      return RANGER;
   if (strcasecmp(name, "thief") == 0) 
      return THIEF;
   if (strcasecmp(name, "alchemist") == 0) 
      return ALCHEMIST;
   if (strcasecmp(name, "cleric") == 0) 
      return CLERIC;
   if (strcasecmp(name, "bard") == 0) 
      return BARD;
   return OTHER_CLASS;
}//get class num



const char* get_race_name(int num) {
   if (num == OTHER_RACE)
      return "other";
   if (num == UNDEAD)
      return "undead";
   if (num == ANIMAL)
      return "animal";
   if (num == MONSTER)
      return "monster";
   if (num == HUMAN)
      return "human";
   if (num == ANITRE)
      return "anitre";
   if (num == AVINTRE)
      return "avintre";
   if (num == DARKLING)
      return "darkling";
   if (num == DROW)
      return "drow";
   if (num == DRAGON)
      return "dragon";
   if (num == DWARF)
      return "dwarf";
   if (num == IRONCLAD)
      return "ironclad";
   if (num == OGRUE)
      return "ogrue";
   if (num == ROCKTROLL)
      return "rocktroll";
   if (num == ELF)
      return "elf";
   if (num == FAERIE)
      return "faerie";
   if (num == ENTITY)
      return "entity";
   if (num == SOMBRIAN)
      return "sombrian";
   if (num == AVIAN)
      return "avian";
   return "UNKNOWN";
}//get race name

const char* get_class_name(int num) {
   if (num == OTHER_CLASS) 
      return "other";
   if (num == WARRIOR)
      return "warrior";
   if (num == SAGE)
      return "sage";
   if (num == WIZARD)
      return "wizard";
   if (num == RANGER)
      return "ranger";
   if (num == THIEF)
      return "thief";
   if (num == ALCHEMIST)
      return "alchemist";
   if (num == CLERIC)
      return "cleric";
   if (num == BARD)
      return "bard";
   return "UNKNOWN";
}//get class name


int max(int i, int j) {
  if (i > j)
    return i;
  return j;
}

int min(int i, int j) {
   if (i < j)
      return i;
   return j;
}

void save_all() {
  Cell<critter*> cll(pc_list);
  critter* ptr;

  //log("In save_all.\n");

  while ((ptr = cll.next())) {
    if (ptr->MODE == MODE_NORMAL) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "save_all:  Name of crit -:" << *(name_of_crit(*ptr, ~0))
                << ":-" << endl;
      }
      ptr->save();
    }//if
  }//while

}//save all


say_proc_cell* have_topic_named(List<say_proc_cell*> lst, const String& msg) {
  Cell<say_proc_cell*> cll(lst);
  say_proc_cell* ptr;

  while ((ptr = cll.next())) {
    if (strcasecmp(ptr->topic, msg) == 0) {
      return ptr;
    }//if
  }//while

  return NULL;
}//have_topic_named


void strip_hegemon_tags(String& str) {
   // If we find a tag, delete it.  Maybe do more interesting things
   // later.
   int len = str.Strlen();
   String retval(len);
   char ch;
   int in_tag = false;
   int prev_was_lt = false;

   for (int i = 0; i<len; i++) {
      ch = str[i];
      if (ch == '<') {
         if (in_tag) {
            continue;
         }
         else {
            if (prev_was_lt) {
               prev_was_lt = FALSE;
               retval += ch;
            }
            else {
               prev_was_lt = TRUE;
            }
         }//else
      }//if
      else {
         if (prev_was_lt) {
            in_tag = TRUE;
         }

         if (ch == '>') {
            prev_was_lt = FALSE;
            if (in_tag) {
               in_tag = FALSE;
            }
            else {
               retval += ch;
            }
         }
         else {
            prev_was_lt = FALSE;
            if (in_tag) {
               continue;
            }
            else {
               retval += ch;
            }
         }//else
      }//else
   }//for
   str = retval;
}//strip_hegemon_tags


void parse_communication(String& str) {
   int sofar = 0;
   int len = str.Strlen();
   char ch;
   int max_len = 380;
   
   // First, check for censored strings.  This is a lot of work
   // for the computer, and I hate censoring, but experience has
   // shown it to be needed.  Edit the CensoredStrings in const.cc
   // to modify this...

   String tmp(str);
   tmp.Tolower(); //make it all lower-case

   //const char* incoming = tmp; //will cast to char*

   // Skip censorship for now. --BEN
   //const char* censored;
   //for (int i = 0; (censored = CensoredStrings[i]); i++) {
   //   if (strstr(incoming, censored)) {
   //      str = "CENSORED";
   //      return;
   //   }
   //}

   String retval(len + 5);

   for (int i = 0; i<len; i++) {
      ch = str[i];
      if (++sofar > max_len) {
         // retval needs to have a newline inserted before the current
         // spot. (retval.Strlen())
         int retval_len = retval.Strlen();
         for (int j = retval_len; j > (retval_len - max_len); j--) {
            if (isspace(retval[j])) {
               retval.setCharAt(j, '\n');
               max_len = 380;
               sofar = 0;
               break;
            }//if
         }//for
         if (sofar > 0) { //didn't find a space
            retval += '\n';
            max_len = 380;
            sofar = 0;
         }
      }//if
      if (ch == '<') {
         retval += "<<";
      }
      else {
         if (ch == '\n') {
            max_len = 380;
            sofar = 0;
         }
         retval += ch;
      }
   }//for
   str = retval;
}//parse_communication


void parse_for_max_80(String& str) {
   int start = 0;
   int sofar = 0;
   int len = str.Strlen();
   int i;
   short found_it;

   while (TRUE) {
      found_it = FALSE;
      i = start;
      if ((i + 79) >= len) {
         break;//done
      }//if
      while ((i < len) && (str[i] != '\n') && (i < (start + 80))) {
         i++;
      }//while
      sofar = i;
      if ((sofar - start) > 79) { //gotta fix it
         while (i > start) {
            i--;
            if (str[i] == ' ') {
               str.setCharAt(i, '\n');
               i++; //move past newline
               start = i;
               found_it = TRUE;
               break;
            }//if
         }//while
         if (!found_it) { //there wasn't a space
            str.setCharAt((i + 78), '\n'); 
            start = i + 79;
         }//if
      }//if need fixing
      else {
         start = sofar + 1;
      }//else
   }//while true

   if (len == 0)
      return; //hack

   for (i = 0; i < (len - 1); i++) {
      if ((str[i] == '~') && (isspace(str[i+1]))) {
         str.setCharAt(i, ' ');
      }//if
   }//for

   if (str[len - 1] == '~')
       str.setCharAt((len - 1), ' ');

}//parse_for_max_80()


short name_is_secret(const String* name, door& dr) {
   Cell<String*> cll(dr.dr_data->names);
   String* ptr;
   int len = name->Strlen();

   if (len == 0)
      return FALSE;

   if (dr.destination >= 0) { //if positive, go from top
      ptr = cll.next();
      while ((ptr = cll.next())) {
         if (*ptr == "#")
            break; // didn't find it
         if (strncasecmp(*name, *ptr, len) == 0)
            return TRUE;
      }//while
      return FALSE;
   }//if
   else {
      ptr = cll.prev();
      while ((ptr = cll.prev())) {
         if (*ptr == "#")
            break; // didn't find it
         if (strncasecmp(*name, *ptr, len) == 0)
            return TRUE;
      }//while
      return FALSE;
   }//else
}//name_is_secret

// May return a NULL!
String* dir_of_room(room& rm, int dest_rm_num) {
   Cell<door*> cll(rm.DOORS);
   door* ptr;

   while ((ptr = cll.next())) {
      if (abs(ptr->destination) == abs(dest_rm_num)) {
         return direction_of_door(*ptr);
      }//if
   }//while

   return NULL;
}//dir_of_room


int get_next_msg_num(object& board) {
   Cell<object*> cll(board.inv);
   object* ptr;
   int sofar = 0;
   int i;

   while ((ptr = cll.next())) {
      i = atoi(*(ptr->names.peekFront()));
      if (i > sofar)
         sofar = i;
   }//while

   return sofar + 1;
}//get_next_msg_num


String owner_of_zone(int rm_num) {
   int znum = room_list[rm_num].getZoneNum();

   return ZoneCollection::instance().elementAt(znum).getFirstOwner();

}//owner_of_zone


void do_vehicle_moves() {
   Cell<room*> cll;
   pulsed_proc_rooms.head(cll); //declared in grrmud.cc 
   room* rm_ptr;
   if (mudlog.ofLevel(DBG)) {
      mudlog << "In do_vehicle_moves, pulsed_proc_rooms.Size():  "
             << pulsed_proc_rooms.size() << endl;
   }

   while ((rm_ptr = cll.next())) {
      //if (mudlog.ofLevel()) {
      //   mudlog << "Checking on room/vehicle number:  " 
      //          << rm_ptr->getRoomNum() << endl;
      //}
      if (rm_ptr->isVehicle() && rm_ptr->isInUse() && !rm_ptr->isZlocked()) {
         vehicle* veh_ptr = (vehicle*)(rm_ptr);
         if (veh_ptr->isSelfGuided()) { //its not pc-guided

            veh_ptr->decrementTicksTillNextStop();
            if (veh_ptr->getTicksTillNextStop() <= 0) {

               veh_ptr->move();
            }//if
         }//if
      }//if
   }//while
}//do_vehicle_moves()


int mob_can_enter(critter& pc, room& rm, short do_msg, int check_no_wander = FALSE) {
   int retval = FALSE;

   if (!rm.isInUse()) { //if not used
      if (do_msg)
         show("That room doesn't really exist!!\n", pc);
   }//if
   else if ((rm.isNoMortal() && !pc.isImmort())) {
      if (do_msg) {
         pc.show("Mortals are not allowed there!!\n");
      }
   }
   else if ((rm.needsBoat()) && //need boat
            (!(pc.CRIT_FLAGS.get(4) || pc.CRIT_FLAGS.get(3)))) {
      if (do_msg)
         show("You need a boat to go there.\n", pc);
   }//if
   else if ((rm.needsDive()) && //need underwater ability
            (!pc.canDive())) {
      if (do_msg)
         show("You must be able to dive to go there.\n", pc);
   }//if
   else if ((rm.needsClimb()) && //need climb
            (!(pc.isFlying() || pc.canClimb()))) {
      if (do_msg) {
         show("You lose your footing and almost fall!!\n", pc);
         emote("slips and almost falls!!\n", pc, ROOM, TRUE);
         pc.PAUSE++; //punish them a bit
      }//if
   }//if
   else if ((rm.needsFly()) && //need fly
            (!(pc.CRIT_FLAGS.get(3)))) {
      if (do_msg)
         show("You have to be flying to go there.\n", pc);
   }//if
   else if ((rm.isNoMob()) && // !mob?
               (!pc.isPc())) {
      if (do_msg)
         show("Mobs are not allowed to move there.\n", pc); //never see prob.
   }//if !mob
   else if (!pc.isPc() && check_no_wander && 
            (rm.isNoWanderMob() || 
             (rm.isNoWanderForeignMob() && (pc.getHomeTown() != rm.getZoneNum())))) {
      if (do_msg)
         show("Mobs are not allowed to wander there.\n", pc); //never see prob.
   }//if !mob
   else if (rm.isZlocked()) {
      if (pc.isImmort()) {
         if (rm.getZoneNum() != 0) {
            if ((pc.getImmLevel() <= 2) &&
                (!ZoneCollection::instance().elementAt(rm.getZoneNum()).isOwnedBy(pc))) {
               if (do_msg) {
                  pc.show("Immorts may no longer visit other areas that are\n");
                  pc.show("locked.  If you have a legit reason, ask a higher\n");
                  pc.show("builder or coder for permission.\n");
               }//if
               return FALSE;
            }//if
            else {
               return TRUE;
            }
         }//if
         else {
            return TRUE;
         }
      }//if is Immort
      else {
         if (do_msg)
            show("That direction has been locked to your kind.\n", pc);
         return FALSE;
      }
   }//if
   else
      retval = TRUE;
   return retval;
}//mob_can_enter



void out_stat_list(const List<stat_spell_cell*>& lst, critter& pc,
                   const BitfieldNames& names) {
   Cell<stat_spell_cell*> cll(lst);
   stat_spell_cell* ptr;
   String buf(100);
   String buf2(100);

   if (lst.isEmpty()) {
      pc.show("NONE\n");
      return;
   }

   while ((ptr = cll.next())) {
      Sprintf(buf, "(%s %i) ", names.getName(ptr->stat_spell),
              ptr->bonus_duration);
      buf2.Append(buf);
   }//while
   buf2.Append("\n");
   pc.show(buf2);
}//out_stat_list


void out_spell_list(const List<stat_spell_cell*>& lst, critter& pc) {
   Cell<stat_spell_cell*> cll(lst);
   stat_spell_cell* ptr;
   String buf(100);
   String buf2(100);

   if (lst.isEmpty()) {
      pc.show("NONE\n");
      return;
   }

   while ((ptr = cll.next())) {
      Sprintf(buf, "(%s %i) ",
              SSCollection::instance().getNameForNum(ptr->stat_spell),
              ptr->bonus_duration);
      buf2.Append(buf);
   }//while
   buf2.Append("\n");
   pc.show(buf2);
}//out_spell_list


object* have_obj_numbered(const List<object*>& lst, const int i_th,
                          const int obj_num, const int see_bit,
                          const room& rm) {
   Cell<object*> cll(lst);
   object* ptr;
   int count = 0;

   while ((ptr = cll.next())) {
      if (ptr->OBJ_NUM == obj_num) {
         if (detect(see_bit, (ptr->OBJ_VIS_BIT | rm.getVisBit()))) { 
            count++;
            if (count == i_th) {
               return ptr;
            }//if
         }//if detect
      }//if obj nums agree
   }//while
   return NULL;
}//have_obj_numbered


int get_game_time() {
   return config.hour;
}//get_game_time


const char* get_month(int day) {
   if (day <= 31) 
      return "January";
   else if (day <= 60)
      return "February";
   else if (day <= 91)
      return "March";
   else if (day <= 120)
      return "April";
   else if (day <= 150)
      return "May";
   else if (day <= 181)
      return "June";
   else if (day <= 211)
      return "July";
   else if (day <= 242)
      return "August";
   else if (day <= 273)
      return "September";
   else if (day <= 303)
      return "October";
   else if (day <= 334)
      return "November";
   else if (day <= 365)
      return "December";
   else {
      mudlog.log(ERROR, "ERROR:  day is > 365, in get_month.\n");
      return "Leap Month";
   }//else
}//get_month
      

const int get_day_of_month(int day) { //day of the year that is
   if (day <= 31) 
      return day;
   else if (day <= 60)
      return day - 31;
   else if (day <= 91)
      return day - 60;
   else if (day <= 120)
      return day - 91;
   else if (day <= 150)
      return day - 120;
   else if (day <= 181)
      return day - 150;
   else if (day <= 211)
      return day - 181;
   else if (day <= 242)
      return day - 211;
   else if (day <= 273)
      return day - 242;
   else if (day <= 303)
      return day - 273;
   else if (day <= 334)
      return day - 303;
   else if (day <= 365)
      return day - 334;
   else {
      mudlog.log(ERROR, "ERROR:  day is > 365, get_day_of_month.\n");
      return 32;
   }//else
}//get_day_of_month

const char* military_to_am(int m_time) {
   if (m_time == 0)
      return "1 am";
   else if (m_time == 1)
      return "2 am";
   else if (m_time == 2)
      return "3 am";
   else if (m_time == 3)
      return "4 am";
   else if (m_time == 4)
      return "5 am";
   else if (m_time == 5)
      return "6 am";
   else if (m_time == 6)
      return "7 am";
   else if (m_time == 7)
      return "8 am";
   else if (m_time == 8)
      return "9 am";
   else if (m_time == 9)
      return "10 am";
   else if (m_time == 10)
      return "11 am";
   else if (m_time == 11)
      return "12 pm";
   else if (m_time == 12)
      return "1 pm";
   else if (m_time == 13)
      return "2 pm";
   else if (m_time == 14)
      return "3 pm";
   else if (m_time == 15)
      return "4 pm";
   else if (m_time == 16)
      return "5 pm";
   else if (m_time == 17)
      return "6 pm";
   else if (m_time == 18)
      return "7 pm";
   else if (m_time == 19)
      return "8 pm";
   else if (m_time == 20)
      return "9 pm";
   else if (m_time == 21)
      return "10 pm";
   else if (m_time == 22)
      return "11 pm";
   else if (m_time == 23)
      return "12 am";
   else {
      mudlog.log(ERROR, "ERROR:  m_time out of range, military_to_am.\n");
      return "0 am";
   }//else
}//military_to_am


room* get_next_room(int zone_num) {

   for (int i = ZoneCollection::instance().elementAt(zone_num).getBeginRoomNum();
        i <= ZoneCollection::instance().elementAt(zone_num).getEndRoomNum();
        i++) {
      if (!room_list[i].isInUse()) { //if room is not used
         room_list[i].setRoomNum(i); //This really shouldn't be needed!
         return &(room_list[i]); //ptr to that room
      }//if
   }//for
   return NULL; //there was no free room
}//get_next_room()


int get_next_obj() { 
   for (int i = 10; i<NUMBER_OF_ITEMS; i++) { 
      if (!(obj_list[i].OBJ_FLAGS.get(10))) {
         return i;
      }//if
   }//for
   return -1; //there was no free room
}//get_next_obj()


int get_next_door() { 
   for (int i = 10; i<NUMBER_OF_DOORS; i++) { 
      if (!(door_list[i].door_data_flags.get(10))) {
         return i;
      }//if
   }//for
   return -1; //there was no free door
}//get_next_door()


int get_next_mob() { 
   for (int i = 10; i<NUMBER_OF_MOBS; i++) { 
      if (!(mob_list[i].CRIT_FLAGS.get(18))) {
         return i;
      }//if
   }//for
   return -1; //there was no free mob
}//get_next_obj()


int get_percent_lrnd(int skill_num, const critter& pc, short automatic = FALSE) {
   if (!pc.pc) {
      if (automatic && pc.mob && pc.mob->proc_data) {
         if (pc.FLAG1.get(12)) { //evasive maneuvers flag
            if (skill_num == DODGE_SKILL_NUM)
               return (70 + pc.LEVEL);
            if (skill_num == PARRY_SKILL_NUM)
               return (70 + pc.LEVEL);
         }//if
      }//if automatic (ie should search)
      return (70 + pc.LEVEL);
   }//if not a pc
   
   int retval;
   if (pc.SKILLS_KNOWN.Find(skill_num, retval)) {
      return retval;
   }//if
   
   return -1;
}//%lrnd_skill


void increment_percent_lrnd(int skill_num, critter& pc) {

   if (!pc.pc)
     return;

   float inc;
   int dif = SSCollection::instance().getSS(skill_num).getDifficulty();
   
   inc = (33.0/(float)dif) * (((float)(pc.INT)/4.0) * ((float)(pc.INT)/4.0) +
                              (float)(pc.INT)/2.0);
   inc = max((int)(inc), 1);

   int p_lrnt;
   if (pc.SKILLS_KNOWN.Find(skill_num, p_lrnt)) {
      p_lrnt += (int)inc;
      if (p_lrnt > 100)
        p_lrnt = 100;
      pc.SKILLS_KNOWN.Insert(skill_num, p_lrnt); //update value
      return;
   }//if
   mudlog.log(ERROR,
              "ERROR:  tried to increment_%_lrnd on a skill unknown by pc.\n");
}//inc_skill


void init_masks() {
            /*  OBJ_WEAR_FLAGS  */
   int i;
   for (i = 22; i < (21 + MAX_EQ); i++) { 
      Obj_Wear_Flags_Mask.turn_on(i);
   }

            /*  OBJ_CONSUME_PROCS_FLAGS  */
   Obj_Consume_Procs_Mask.turn_on(0); //teleport
   Obj_Consume_Procs_Mask.turn_on(3); //poison

            /*  OBJ_WEAR_PROCS  */
   //Obj_Wear_Procs_Mask.turn_on(??); //no special ones coded yet

            /*  OBJ_REMOVE_PROCS  */
   //Obj_Remove_Procs_Mask.turn_on(??); //no special ones coded yet

   //Shop_Data_Buy_Procs_Mask.turn_on(0);   //buy_proc_0

   //Shop_Data_Sell_Procs_Mask.turn_on(1);   //sell_proc_0

   //Shop_Data_Offer_Procs_Mask.turn_on(2);   //offer_proc_0

}//init masks


String* direction_of_door(const door& drr) {
   String* ptr = NULL;
   if (drr.destination < 0)
      ptr = drr.dr_data->names.peekRear();
   else
      ptr = drr.dr_data->names.peekFront();

   if (ptr)
      return ptr;
   else
      return &UNKNOWN;
}//direction_of_door


const char* abbrev_dir_of_door(const door& drr) {
   String* dir;

   if (drr.destination < 0) 
      dir = drr.dr_data->names.peekRear();
   else
      dir = drr.dr_data->names.peekFront();

   if (dir == NULL) {
      return "??";
   }//if

   if (strcasecmp(*dir, "north") == 0) 
      return "N";
   else if (strcasecmp(*dir, "northwest") == 0)
      return "NW";
   else if (strcasecmp(*dir, "northeast") == 0)
      return "NE";
   else if (strcasecmp(*dir, "east") == 0)
      return "E";
   else if (strcasecmp(*dir, "south") == 0)
      return "S";
   else if (strcasecmp(*dir, "southeast") == 0)
      return "SE";
   else if (strcasecmp(*dir, "southwest") == 0)
      return "SW";
   else if (strcasecmp(*dir, "west") == 0)
      return "W";
   else if (strcasecmp(*dir, "up") == 0)
      return "U";
   else if (strcasecmp(*dir, "down") == 0)
      return "D";
   else return "??";
}//abbrev_dir_of_door


int obj_count(List<object*>& lst, object& src) {
   int retval = 0;
   Cell<object*> cll(lst);
   object* ptr;

   while ((ptr = cll.next())) {
      if (ptr->OBJ_NUM == src.OBJ_NUM) {
         retval++;
      }//if
   }//while
   return retval;
}//obj_count

int crit_count(List<critter*>& lst, critter& src) {
   int retval = 0;
   Cell<critter*> cll(lst);
   critter* ptr;

   if (!src.mob) {
      return retval;
   }//if

   while ((ptr = cll.next())) {
      if (ptr->mob) {
         if (ptr->MOB_NUM == src.MOB_NUM) {
            retval++;
         }//if
      }//if
   }//while
   return retval;
}//crit_count


void clear_crit_list(List<critter*>& lst) {
   Cell<critter*> cll(lst);
   critter* ptr;
   //log("In clear_crit_list.\n");

   ptr = cll.next();
   while (ptr) {
      if ((ptr->pc) || (ptr->isSmob())) { //if smob/pc 
         delete ptr;   //delete it for sure
      }//if
      ptr = lst.lose(cll);
   }//while
}//clear_crit_list


void clear_obj_list(List<object*>& lst) {
   Cell<object*> cll(lst);
   object* ptr;

   //log("In clear_obj_list.\n");

   ptr = cll.next();
   while (ptr) {
      //log("In while loop.\n");
      if (ptr->in_list) { //if its a SOBJ
         delete ptr;   //delete it for sure
      }//if
      
      ptr = lst.lose(cll);
   }//while
}//clear_obj_list


void out_field(const bitfield& field, critter& pc, const BitfieldNames& names) {
   int k = field.max_bit();
   String buf(100);
   String tmp(50);
   int sofar = 0;

   Sprintf(buf, "%S (SET)\n\t", &(names.getHeader()));
   pc.show(buf);
   buf = "";

   for (int i = 0; i <= k; i++) {
      if (field.get(i)) {
         Sprintf(tmp, "[%i] %s,  ", i, names.getName(i));
         if ((sofar + tmp.Strlen()) > 80) {
            buf += "\n\t";
            sofar = tmp.Strlen();
         }
         else {
            sofar += tmp.Strlen();
         }
         buf += tmp;
      }//if
   }//for
   buf.Append("\n");
   show(buf, pc);
}//out_field

   
int critter::doBecomeNonPet() {
   String buf(100);

   if (isMob()) {
      mudlog.log(ERROR, "ERROR:  mob sent to unpet.\n");
      return -1;
   }//if

   if (!MASTER) {
      return -1;
   }//if

   MASTER->PETS.loseData(this); //master no longer has pc as pet...
   MASTER = NULL;   // slave of none

   return doUngroup(1, &NULL_STRING);
}//unpet


const char* class_of_crit(critter& pc) {
   return get_class_name(pc.CLASS);
}//class_of_crit

int find_and_delete_obj(object* obj_to_find, int room_num) {
   // Find an object in the rooms current inventory, also look in containers' and
   // critters' inventories.
   // Returns FALSE on error (room doesn't exist, removed wasn't found)
   //         TRUE  when an object has been removed
   object* obj = NULL;
   critter* crit = NULL;

   if (mudlog.ofLevel(DBG)) {
      mudlog << "DEBUG:  in find_and_remove_obj(object* obj_to_find = " << obj_to_find
             << ", int rm_num = " << room_num << ")\n";
   }

   // Sanity checks, make sure this is a valid room
   if ((0 > room_num > NUMBER_OF_ROOMS) || !room_list[room_num].isInUse()) {
      mudlog.log(DBG, "DEBUG:  Room is invalid.\n");
      return FALSE;
   }

   Cell<object *> obj_cll(*(room_list[room_num].getInv()));
   Cell<critter*> crit_cll(room_list[room_num].getCrits());

   // Try to find obj_to_find in the room's inventory
   mudlog.log(DBG, "DEBUG:  checking objects in the room.\n");
   while((obj = obj_cll.next())) {
      if (obj == obj_to_find) {
         // Found it, so delete it
         mudlog.log(DBG, "DEBUG:  Found object in the room.\n");
         room_list[room_num].loseInv(obj_to_find);
         if (obj_to_find->isModified()) {
            delete obj_to_find;
         }
         return TRUE;
      }
      else if (find_and_delete_obj(obj_to_find, obj)) {
         mudlog.log(DBG, "DEBUG:  Found object in a container.\n");
         return TRUE;
      }
   }

   // Check the critters in the room
   mudlog.log(DBG, "DEBUG:  checking on critters in the room.\n");
   while((crit = crit_cll.next())) {
      if (find_and_delete_obj(obj_to_find, crit)) {
         mudlog.log(DBG, "DEBUG:  Found object on a critter.\n");
         return TRUE;
      }
   }

   // Guess we didn't find it
   mudlog.log(DBG, "DEBUG:  Didn't find object in room.\n");
   return FALSE;
}

int find_and_delete_obj(object* obj_to_find, critter* crit_ptr) {
   // Find an object in a critter's current inventory, also look in containers the
   // critter owns, and at the critter's equipment list
   // Returns FALSE on error (crit_ptr is bad, didn't find obj_to_find)
   //         TRUE  when an object has been removed
   object* obj = NULL;

   if (mudlog.ofLevel(DBG)) {
      mudlog << "DEBUG:  in find_and_remove_obj(object* obj_to_find = " << obj_to_find
             << ", crit_ptr = " << crit_ptr << ")\n";
   }

   if (!crit_ptr) {
      mudlog.log(DBG, "DEBUG:  bad crit_ptr.\n");
      return FALSE;
   }

   Cell<object*> obj_cll(crit_ptr->inv);

   // Try to find obj_to_find in the critter's inventory
   mudlog.log(DBG, "DEBUG:  checking critter's inventory.\n");
   obj = obj_cll.next();
   while (obj) {
      if (obj == obj_to_find) {
         // Found it, so delete it
         mudlog.log(DBG, "DEBUG:  found object in critter's inventory.\n");
         crit_ptr->loseInv(obj_to_find);
         if (obj_to_find->isModified()) {
            delete obj_to_find;
         }
         return TRUE;
      }
      else {
         if (find_and_delete_obj(obj_to_find, obj)) {
            mudlog.log(DBG, "DEBUG:  Found object in a container (2).");
            return TRUE;
         }
      }//else
      obj = obj_cll.next();
   }

   // Check the critter's equipped items
   mudlog.log(DBG, "DEBUG:  checking critter's equipment.");
   for (int i=1; i < MAX_EQ; i++) {
      if (crit_ptr->EQ[i] == obj_to_find) {
         // Found it, so delete it
         mudlog.log(DBG, "DEBUG:  found object in critter's EQ[].");
         remove_eq_effects(*crit_ptr->EQ[i], *crit_ptr, FALSE, FALSE, i);
         crit_ptr->EQ[i] = NULL;
         if (obj_to_find->isModified()) {
            delete obj_to_find;
         }
         return TRUE;
      }
      else {
         if (crit_ptr->EQ[i] && find_and_delete_obj(obj_to_find, crit_ptr->EQ[i])) {
            mudlog.log(DBG, "DEBUG:  Found object in a container.\n");
            return TRUE;
         }
      }
   }

   // Guess we didn't find it
   mudlog.log(DBG, "DEBUG:  didn't find object on critter.\n");
   return FALSE;
}

int find_and_delete_obj(object* obj_to_find, object* find_in) {
   // Find an object in another object's inventory - infinately recursive
   // Returns FALSE on error (find_in isn't a container, obj_to_find isn't in find_in)
   //         TRUE  when an object has been removed
   object* obj = NULL;

   if (mudlog.ofLevel(DBG)) {
      mudlog << "DEBUG:  in find_and_remove_obj(object* obj_to_find = " << obj_to_find
             << ", object* find_in = " << find_in << ")\n";
   }

   Cell<object *> cll(find_in->inv);

   // Try to find obj_to_find in find_in's inventory
   mudlog.log(DBG, "DEBUG:  checking object's inventory.\n");
   obj = cll.next();
   while (obj) {
      if (obj == obj_to_find) {
         // Found it, so, delete it
         mudlog.log(DBG, "DEBUG:  Found object in container.\n");
         find_in->loseInv(obj_to_find);
         if (obj_to_find->isModified()) {
            delete obj_to_find;
         }
         return TRUE;
      }
      else {
         if (find_and_delete_obj(obj_to_find, obj)) {
            mudlog.log(DBG, "DEBUG:  Found object in container in container.\n");
            return TRUE;
         }
      }
      obj = cll.next();
   }

   // Guess we didn't find it
   mudlog.log(DBG, "DEBUG:  Couldn't find object in container.\n");
   return FALSE;
}