/
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: misc.cc,v 1.26.2.21 2000/05/13 19:42:59 greear Exp $
// $Revision: 1.26.2.21 $  $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
//

#include "command4.h"
#include <time.h>
#include <iostream.h>
#include <fstream.h>
#include "commands.h"
#include "battle.h"
#include "misc.h"
#include "misc2.h"
#include <stdio.h>
#include "classes.h"
#include <sys/types.h>
#include <unistd.h>
#include "const.h"
#include "spells.h"
#include <PtrArray.h>
#include "vehicle.h"
#include "load_wld.h"
#include "command3.h"
#include "skills.h"
#include <time.h>
#include "Filters.h"



/** calculate the ratio between objects casting spell a spell and the
 * number of players online.  It will be fudged so that 1 will be
 * considered a 'fair' amount.  If > 1.0 then the spell should probably
 * be weakened somewhat.
 */
float spell_objs_ratio(int spell_num) {
   // Take potions & scrolls into affect, all 2 per person.
   float cnt = SSCollection::instance().getSS(spell_num).getSpellCastingCount();
   if (cnt == 0)
      cnt = 1.0;

   float pcnt = (((float)(MudStats::instance().getPlayerCount()) 
                 * 1.5 + 10.0) / cnt);
   if (pcnt < 1.0)
      pcnt = 1.0;
   return pcnt;
}//spell_objs_ratio


const char* cstr(CSentryE e, critter& c) {
   return CSHandler::getString(e, c.getLanguageChoice());
}


String& getCurTime() { //in the real world (tm) :)
   static String retval(100);

   long int cur_time;
   time(&cur_time);
   retval = ctime(&cur_time);

   retval.Strip();
   return retval;
}

int bound(int low, int high, int val) {
   if (val < low)
      val = low;
   if (val > high)
      val = high;
   return val;
}


void critter::save() {
   if (!pc) {
      mudlog.log(ERROR, "ERROR:  non PC sent to critter::save()\n");
      return;
   }

   if (LEVEL == 0) {
      mudlog << "ERROR:  level 0 player trying to save, name:  "
             << *(getName()) << endl;
      return;
   }

   String buf(*(getName()));
   buf.Tolower();
   buf.Prepend("./Pfiles/");
   ofstream ofile(buf);
   if (!ofile) {
      mudlog.log(ERROR, "ERROR:  file not opened in save_pc.\n");
      return;
   }//if

   Write(ofile);
}//save_pc


/**  msg defaults to NULL if not entered explicitly.. */
short check_l_range(long i, long l, long h, critter& pc, short disp,
                    const char* msg) {
   String buf(100);
   if (i < l) {
      if (disp) {
         if (msg) {
            Sprintf(buf, "%s:  The number is too low.\n", msg);
            pc.show(buf);
         }
         else 
            show("The number is too low.\n", pc);
         return FALSE;
      }//if
      return FALSE;
   }//if
   else if (i > h) {
      if (disp) {
         if (msg) {
            Sprintf(buf, "%s:  The number is too high.\n", msg);
            pc.show(buf);
         }
         else
            show("The number is too high.\n", pc);
         return FALSE;
      }//if
      return FALSE;
   }//else
   return TRUE;
}//check_l_range()


char* get_his_her(const critter& crit) {

   if (crit.SEX == 0) { 
      return "her";
   }//if
   else if (crit.SEX == 1) {
      return "his";
   }//else
   else {
      return "its";
   }//else
}//get_his_her

char* get_hisself_herself(const critter& crit) { //this is
//grammatically incorrect, the code is same as get_himself_herself.

   if (crit.SEX == 0) { 
      return "herself";
   }//if
   else if (crit.SEX == 1) {
      return "himself";
   }//else
   else {
      return "itself";
   }//else
}//get_hisself_herself


char* get_he_she(const critter& crit) {

   if (crit.SEX == 0) { 
      return "she";
   }//if
   else if (crit.SEX == 1) {
      return "he";
   }//else
   else {
      return "it";
   }//else
}//get_he_she


int d(const int num_rolls, const int dice_sides) {
   int rtvalue = 0;

   for (int i = 0; i<num_rolls; i++) {
      rtvalue += 1 +(int)((rand() * (float)(dice_sides)) / (RAND_MAX + 1.0));
   }//for
   return rtvalue;
}//d()

/* in file parse.cc */
short isnum(String& word) {
   int len = word.Strlen();
   int i = 0;
   
   if (len == 0)
      return FALSE;

   if ((word[0] == '-') || (word[0] == '+'))
      i++;
   for ( ; i<len; i++) {
      if (!isdigit(word[i])) {
         return FALSE;
      }//if
   }//for
   return TRUE;
}//isnum


void join_in_battle(critter& agg, critter& vict) {
  //log("In join_in_battle...\n");

   if (mudlog.ofLevel(DBG)) {
      mudlog << "join_in_battle: agg: " << &agg << " vict: " << &vict << endl;
   }

   if (agg.isMob()) {
      mudlog.log(ERROR, "ERROR: join_in_battle called with agg as mob\n");
      return;
   }//if
   if (vict.isMob()) {
      mudlog.log(ERROR, "ERROR: join_in_battle called with vict as mob\n");
      return;
   }//if
   
   if (&vict == &agg) {
      if (mudlog.ofLevel(ERROR)) {
         mudlog << "ERROR:  vict == agg in join_in_battle, name:  "
                << *(name_of_crit(agg, ~0)) << "  address:  " << &agg
                << endl;
      }      
      return;
   }//if

   if (vict.isImmort() && vict.isNoHassle()
       && vict.getImmLevel() >= agg.getImmLevel()) {
      if (mudlog.ofLevel(INF)) {
         mudlog << "INFO:  Not joining battle because of !hassle, agg: "
                << agg.getName() << "  vict: " << vict.getName() << endl;
      }//if
      return;
   }//if

   //ensure only one copy (that's what gainData does)
   if (agg.getCurRoomNum() == vict.getCurRoomNum()) {
      embattled_rooms.gainData(&(room_list[agg.getCurRoomNum()]));
   }
   else {
      embattled_rooms.gainData(&(room_list[agg.getCurRoomNum()]));
      embattled_rooms.gainData(&(room_list[vict.getCurRoomNum()]));
   }

                /* make sure agg hits first */
   if (room_list[agg.getCurRoomNum()].removeCritter(&agg)) {
      room_list[vict.getCurRoomNum()].gainCritter(&agg);
   }
   else {
      if (mudlog.ofLevel(DIS)) {
         mudlog << "ERROR:  could not 'LoseData' in join_in_battle, agg:  "
                << *(name_of_crit(agg, ~0)) << "  address:  " << &agg
                << "  getCurRoomNum():  " << agg.getCurRoomNum() << endl;
      }
   }

   Put(&vict, agg.IS_FIGHTING);
   Put(&agg, vict.IS_FIGHTING);

   //Some mobs will remember....
   if (vict.isNpc()) {
      critter* virt_attacker = NULL;
      if (agg.isPc()) {
         virt_attacker = &agg;
      }
      else {
         virt_attacker = agg.getFollowerOf();
      }

      if (virt_attacker) {
         if ((((vict.getLevel() > 20) && vict.mob->getBadAssedness() > 0) ||
              ((vict.mob->getBadAssedness() > 3))) ||
             (((vict.getLevel() > 20) && vict.getBenevolence() <= 0) ||
              ((vict.getBenevolence() < 0)))) {
            vict.remember(*virt_attacker);
            do_tell(vict, "I'll remember that!!", *virt_attacker, FALSE,
                    virt_attacker->getCurRoomNum());
         }//if
         if ((vict.getBenevolence() <= -4) ||
             (vict.mob->getBadAssedness() >= 5)) {
            vict.setTrackingTarget(*(virt_attacker->getShortName()));
         }
      }//if we found a PC responsible
   }//if

   agg.PAUSE += 1; //vict should get at least one hit in
}//join_in_battle


void do_tick() {
   //gossip("An hour has passed in our fine land.", mob_list[2]);

                   /* do all decrementing */
   decrease_timed_affecting_doors(); //takes care of affected_dors list
   decrease_timed_affecting_lds();  //takes care of alllink deads
   decrease_timed_affecting_pcs();  //takes care of all pc's
   decrease_timed_affecting_smobs(); //takes care of affected_mobs list
   decrease_timed_affecting_objects(); //takes care of affected_objects list
   decrease_timed_affecting_rooms(); //takes care of affected_rooms

                   /* do all incrementing */
   do_regeneration_pcs();  //takes care of all pc's
   do_regeneration_smobs();  //takes care of affected_mobs list
   do_regeneration_objects();  //takes care of affected_objects list
   ZoneCollection::instance().doRegeneration(); //update mobs, objs, rooms if needed

                  /* time */

   config.hour++;
   if (config.hour >= 24) {
      config.hour = 0;
      config.day++;
   }//if
   if (config.day >= 365) {
      config.year++;
      config.day = 1;
   }//if

   config.writeDynamic("dynamic.cfg");
}//do_tick


void do_mini_tick() { // decrement pause count ect
   Cell<critter*> cll;
   pc_list.head(cll);
   critter* crit_ptr;
   Cell<stat_spell_cell*> scell;
   stat_spell_cell* sc_ptr;

   do_vehicle_moves();  //in misc2.cc

   while ((crit_ptr = cll.next())) {

      crit_ptr->spam_cnt = 0;

      // Turn un-blocked, if not in battle.
      if (!crit_ptr->isInBattle()) {
         crit_ptr->crit_flags.turn_off(21);
      }

      /* Take care of round-by-round affects on other spells/skills. */
      if ((sc_ptr = is_affected_by(EARTHMELD_SKILL_NUM, *crit_ptr))) {
         if (crit_ptr->isFighting()) {
            crit_ptr->MANA -= EARTHMELD_BATTLE_MANA_COST;
         }
         else {
            crit_ptr->MANA -= EARTHMELD_NON_BATTLE_MANA_COST;
         }

         if (crit_ptr->MANA <= 0) {
            crit_ptr->breakEarthMeld();
         }//if
      }//if

      // Take care of other things...
      if (crit_ptr->PAUSE > 0) {
         crit_ptr->PAUSE--;
      }//
      crit_ptr->MINI_AFFECTED_BY.head(scell);
      sc_ptr = scell.next();
      while (sc_ptr) {
         sc_ptr->bonus_duration--;
         if (sc_ptr->bonus_duration == 0) {
            rem_effects_crit(sc_ptr->stat_spell, *crit_ptr, TRUE);
            delete sc_ptr;
            sc_ptr = crit_ptr->MINI_AFFECTED_BY.lose(scell);
         }//if
         else
            sc_ptr = scell.next();
      }//while
   }//while

   affected_mobs.head(cll);
   while ((crit_ptr = cll.next())) {

      crit_ptr->spam_cnt = 0;

      // Turn un-blocked, if not in battle.
      if (!crit_ptr->isInBattle()) {
         crit_ptr->crit_flags.turn_off(21);
      }

      if (crit_ptr->PAUSE > 0)
         crit_ptr->PAUSE--;

      crit_ptr->MINI_AFFECTED_BY.head(scell);
      sc_ptr = scell.next();
      while (sc_ptr) {
         sc_ptr->bonus_duration--;
         if (sc_ptr->bonus_duration == 0) {
            rem_effects_crit(sc_ptr->stat_spell, *crit_ptr, TRUE);
            delete sc_ptr;
            sc_ptr = crit_ptr->MINI_AFFECTED_BY.lose(scell);
         }//if
         else
            sc_ptr = scell.next();
      }//while
   }//while

   // Decrease pause count for rooms that are running scripts.
   room* rm_ptr;
   for (int cnt = 0; cnt < proc_action_rooms.getCurLen(); cnt++) {
      if (!(rm_ptr = proc_action_rooms.elementAt(cnt))) {
         continue;
      }//if
      else {
         rm_ptr->decrementPause();
      }
   }//for

   // Decrease pause count for objects that are running scripts.
   object* o_ptr;
   for (int cnt = 0; cnt < proc_action_objs.getCurLen(); cnt++) {
      if (!(o_ptr = proc_action_objs.elementAt(cnt))) {
         continue;
      }//if
      else {
         o_ptr->decrementPause();
      }
   }//for

}//do_mini_tick


void do_regeneration_pcs() {
   float adj = 1.0, posn_mod;
   Cell<critter*> crit_cell;
   pc_list.head(crit_cell);
   critter* crit_ptr;

   //log("In do_regeneration_pcs\n");
 
   while ((crit_ptr = crit_cell.next())) {
      if ((crit_ptr->THIRST == 0) || (crit_ptr->HUNGER == 0)) {
         adj = 0.2; // big penalty for being hungry
      }
      else {
         adj = 1.0;
      }

      if ((crit_ptr->POS > POS_SIT) &&
          room_list[crit_ptr->getCurRoomNum()].canCamp()) {
         adj *= ((float)(200 + get_percent_lrnd(CAMPING_SKILL_NUM,
                                               *crit_ptr))) / 200.0;
      }//if camping is an issue

      posn_mod = (2.0 + (float)(crit_ptr->POS)) / 4.0;

      crit_ptr->HP += (int)((((float)(crit_ptr->CON) + 5.0) / 15.0) * 
                            (((float)(crit_ptr->HP_MAX)) / 9.0) * 
                            posn_mod * (((float)(crit_ptr->HP_REGEN)) / 100.0)
                            * adj + 10.0);

      crit_ptr->MANA += (int)(((((float)(crit_ptr->INT)) + 5.0) / 16.0) *
                              posn_mod *
                              (((float)(crit_ptr->MA_MAX)) / 7.0) *
                              (((float)(crit_ptr->MA_REGEN)) / 100.0) *
                              adj + 4.0);

      int tmp_mov;
      tmp_mov = (int)(((((float)(crit_ptr->DEX)) + 5.0) / 16.0) *
                      posn_mod * adj *
                      (((float)(crit_ptr->MV_MAX)) / 3.0) * 
                      (((float)(crit_ptr->MV_REGEN)) / 100.0) + 3.0);

      crit_ptr->MOV += tmp_mov;

      //if (tmp_mov < 0) {
      //   if (mudlog.ofLevel(DBG)) {
      //      mudlog << "WARNING:  mov regen < 0 for PC:  "
      //             <<  *(name_of_crit(*crit_ptr, ~0)) << "  it was:  "
      //             << tmp_mov << endl;
      //   }
      //}

      if (crit_ptr->HP > crit_ptr->HP_MAX)
         crit_ptr->HP = crit_ptr->HP_MAX;
      if (crit_ptr->MANA > crit_ptr->MA_MAX)
         crit_ptr->MANA = crit_ptr->MA_MAX;
      if (crit_ptr->MOV > crit_ptr->MV_MAX)
         crit_ptr->MOV = crit_ptr->MV_MAX;
      if (crit_ptr->HP < 0)
         crit_ptr->HP = 0;
      if (crit_ptr->MANA < 0)
         crit_ptr->MANA = 0;
      if (crit_ptr->MOV < 0)
         crit_ptr->MOV = 0;

      if ((((float)(crit_ptr->HP)) / (float)(crit_ptr->HP_MAX)) > 0.80) {
         stat_spell_cell* ssptr = 
            is_affected_by(BIND_WOUND_SKILL_NUM, *crit_ptr);
         if (ssptr) {
            crit_ptr->affected_by.loseData(ssptr);
            rem_effects_crit(BIND_WOUND_SKILL_NUM, *crit_ptr, TRUE);
        }
      }//if
   }//while

}//do_regeneration_pcs


void do_regeneration_smobs() {
   float posn_mod, adj = 1.0;
   Cell<critter*> crit_cell;
   affected_mobs.head(crit_cell);
   critter* crit_ptr;

   //log("In do_regeneration_smobs\n");

   while ((crit_ptr = crit_cell.next())) {
      posn_mod = (2.0 + crit_ptr->POS) / 4.0;
      crit_ptr->HP +=
         (int)(((crit_ptr->CON + 5.0) / 15.0) * 
               (crit_ptr->HP_MAX / 9.0) * 
               posn_mod * (crit_ptr->HP_REGEN / 100.0) * adj + 10.0);
      crit_ptr->MANA +=
         (int)(((crit_ptr->INT + 5.0) / 16.0)  * posn_mod *
               (crit_ptr->MA_MAX / 7.0) * (crit_ptr->MA_REGEN / 100.0) * adj + 4.0);
      crit_ptr->MOV += 
         (int)(((crit_ptr->DEX + 5.0) / 16.0) * posn_mod * 
               adj *
               (crit_ptr->MV_MAX / 2.0) * (crit_ptr->MV_REGEN / 100.0) + 5.0);
      
      if (crit_ptr->HP > crit_ptr->HP_MAX)
         crit_ptr->HP = crit_ptr->HP_MAX;
      if (crit_ptr->MANA > crit_ptr->MA_MAX)
         crit_ptr->MANA = crit_ptr->MA_MAX;
      if (crit_ptr->MOV > crit_ptr->MV_MAX)
         crit_ptr->MOV = crit_ptr->MV_MAX;
      
      if (((float)crit_ptr->HP / (float)crit_ptr->HP_MAX) > 0.80) {
         stat_spell_cell* ssptr = 
            is_affected_by(BIND_WOUND_SKILL_NUM, *crit_ptr);
         if (ssptr) {
            crit_ptr->affected_by.loseData(ssptr);
            rem_effects_crit(BIND_WOUND_SKILL_NUM, *crit_ptr, TRUE);
         }
      }//if
   }//while
   
}//do_regeneration_smobs


void do_regeneration_objects() {
  return;
}//do_regeneration_objects


int update_critters(int zone_num, short read_all) {
   switch (config.useMySQL) {
#ifdef USEMYSQL
      case true:
         return db_update_critters(zone_num, read_all);
         break;
#endif
      case false:
         return file_update_critters(zone_num, read_all);
         break;
   }
   return -1;
}

#ifdef USEMYSQL
int db_update_critters(int zone_num, short read_all) {
   critter tmp_mob;
   MYSQL_RES* result;
   MYSQL_ROW row;
   String query="select * from Critters where FROM_ZONE=";
   query+=zone_num;

   if (mysql_real_query(database, query, strlen(query))==0) {
      if ((result=mysql_store_result(database))==NULL) {
         if (mudlog.ofLevel(WRN)) {
            mudlog << "In [function]:\n";
            mudlog << "Error retrieving query results: "
                   << mysql_error(database) << endl;
         }
         return -1;
      }
      while ((row=mysql_fetch_row(result))) {
         int k = atoi(row[CRITTBL_MOB_NUMBER]);
         tmp_mob.dbRead(k, 0, read_all);
         if (!(&mob_list[k])) {
            if (mudlog.ofLevel(WRN)) {
               mudlog << "WARNING: mob[" << k << "] is NULL.\n";
            }
         }
         else {
            if (mob_list[k].MOB_FLAGS.get(4)) {
               Cell<object*> ocell;
               object* obj_ptr;
               tmp_mob.inv.head(ocell);
               while ((obj_ptr = ocell.next())) {
                  if ((obj_count(tmp_mob.inv, *obj_ptr) >
                      obj_count(mob_list[k].inv, *obj_ptr))
                      && (obj_ptr->getCurInGame() < obj_ptr->getMaxInGame())) {
                     Put (&obj_list[obj_ptr->getIdNum()], mob_list[k].inv);
                     recursive_init_loads(obj_list[obj_ptr->getIdNum()], 0);
                  }
               }
               for (int i = 1; i<MAX_EQ; i++) {
                  if ((tmp_mob.EQ[i] && !mob_list[k].EQ[i])
                      && (tmp_mob.EQ[i]->getCurInGame()
                          < tmp_mob.EQ[i]->getMaxInGame())) {
                     mob_list[k].EQ[i] = &obj_list[tmp_mob.EQ[i]->getIdNum()];
                     recursive_init_loads(*(mob_list[k].EQ[i]), 0);
                  }
               }
            }
         }
      }
      tmp_mob.CRITTER_TYPE = 1;
      mysql_free_result(result);
   }
   else {
      if (mudlog.ofLevel(WRN)) {
         mudlog << "In [function]:\n";
         mudlog << "Error executing query: " << mysql_error(database) << endl;
      }
      return -1;
   }
   return 0;
}
#endif

int file_update_critters(int zone_num, short read_all) {
   Cell<object*> ocell;
   critter tmp_mob;
   int k, i;
   object* obj_ptr;
   char temp_str[81];
   String buf(100);

   //Sprintf(buf, "In update_critters, zone: %i.", zone_num);
   //log(buf);

   Sprintf(buf, "./World/critters_%i", zone_num);
   ifstream mobfile(buf);
   if (!mobfile) { 
      buf.Prepend("cp ./World/DEFAULT_CRITTERS ");
      system(buf);
      mudlog.log(ERROR, "WARNING:  created CRITTER_FILE in update_critters");
      tmp_mob.CRITTER_TYPE = 1; //make it a SMOB, so destructor fires correctly
      return 0;
   }//if

   mobfile >> k;
   mobfile.getline(temp_str, 80); //junk line
   while (k != -1) {

      Sprintf(buf, "About to read mob:  %i, zone %i.", k, zone_num);
      mudlog.log(DBG, buf);

      if (!mobfile) {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR: error reading critters for zone: " << zone_num
                   << " trying to read mob# " << k << endl;
         }//if
         return -1;
      }//if

      tmp_mob.fileRead(mobfile, read_all);

      if (!mob_list[k].mob) {
         mudlog << "WARNING:  mob[" << k 
                << "] is NULL in update critters." << endl;
      }//if
      else {
         if (mob_list[k].MOB_FLAGS.get(4)) {

            //log("About to check if need to load more inv.\n");
            tmp_mob.inv.head(ocell);
            while ((obj_ptr = ocell.next())) {
               if ((obj_count(tmp_mob.inv, *obj_ptr) >
                   obj_count(mob_list[k].inv, *obj_ptr))
                   && (obj_ptr->getCurInGame() < obj_ptr->getMaxInGame())) {
                  Put(&(obj_list[obj_ptr->getIdNum()]), mob_list[k].inv);
                  recursive_init_loads(obj_list[obj_ptr->getIdNum()], 0);
               }//if
            }//while      
            
            for (i = 1; i<MAX_EQ; i++) {
               if ((tmp_mob.EQ[i] && !mob_list[k].EQ[i]) 
                   && (tmp_mob.EQ[i]->getCurInGame() 
                       < tmp_mob.EQ[i]->getMaxInGame())) {
                  mob_list[k].EQ[i] = &(obj_list[tmp_mob.EQ[i]->getIdNum()]);
                  recursive_init_loads(*(mob_list[k].EQ[i]), 0);
               }//if
            }//for
         }//if should update
      }//else

      mobfile >> k;
      mobfile.getline(temp_str, 80);
   }//while, the big loop, reads in a whole mob list
   //log("Done w/update critters.\n");

   tmp_mob.CRITTER_TYPE = 1; //make it a SMOB, so destructor fires correctly
   return 0;
}//update_critters

int update_objects(int zone_num, short read_all) {
   #ifdef USEMYSQL
   if (config.useMySQL)
      return db_update_objects(zone_num, read_all);
   else
   #endif
      return file_update_objects(zone_num, read_all);
}

#ifdef USEMYSQL
int db_update_objects(int zone_num, short read_all) {
   Cell<object *> ocell;
   int k;
   object temp_obj;
   object* obj_ptr;
   MYSQL_RES* result;
   MYSQL_ROW row;
   String query = "select distinct OBJ_NUM from ";
   query += "RoomInv inner join Rooms on RoomInv.ROOM_NUM=Rooms.ROOM_NUM";
   query += "where IN_ZONE=";
   query += zone_num;

   if (mysql_real_query(database, query, strlen(query))==0) {
      if ((result=mysql_store_result(database))==NULL) {
         if (mudlog.ofLevel(WRN)) {
            mudlog << "In db_update_objects(int, short):\n";
            mudlog << "Eror retrieving query results: " << mysql_error(database)
                   << endl;
         }
         return -1;
      }
      while ((row=mysql_fetch_row(result))) {
         k = atol(row[0]);
         temp_obj.dbRead(k, -1, read_all);

         if (obj_list[k].OBJ_FLAGS.get(OBJFLAG_NEEDS_RESETTING)) {
            temp_obj.inv.head(ocell);
            while ((obj_ptr = ocell.next())) {
               if ((obj_count(temp_obj.inv, *obj_ptr) >
                    obj_count(obj_list[k].inv, *obj_ptr)) &&
                   (obj_ptr->getCurInGame() < obj_ptr->getMaxInGame())) {
                  Put(&(obj_list[obj_ptr->getIdNum()]), obj_list[k].inv);
                  recursive_init_loads(obj_list[obj_ptr->getIdNum()], 0);
               }
            }
         }
      }
      mysql_free_result(result);
      temp_obj.IN_LIST = room_list[0].getInv();
   }
   else {
      if (mudlog.ofLevel(WRN)) {
         mudlog << "In db_update_objects(int, short):\n";
         mudlog << "Error executing query: " << mysql_error(database) << endl;
      }
      return -1;
   }
   return 0;
}
#endif

int file_update_objects(int zone_num, short read_all) {
   Cell<object*> ocell;
   int k;
   object temp_obj;
   object* obj_ptr;
   char temp_str[81];
   String buf(100);

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

   Sprintf(buf, "./World/objects_%i", zone_num);
   ifstream objfile(buf);
   if (!objfile) { 
      buf.Prepend("cp ./World/DEFAULT_OBJECTS ");
      system(buf);
      mudlog.log(ERROR, "WARNING:  created new OBJECT_FILE in update_objects");
      temp_obj.IN_LIST = room_list[0].getInv(); //hack to let destructor fire
                                                //happily, turn it into a SOBJ
      return 0;
   }//if

   objfile >> k;
   objfile.getline(temp_str, 80); //junk line
   while (k != -1) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "About to read obj:  " << k
                << " zone:  " << zone_num << endl;
      }

      temp_obj.fileRead(objfile, read_all);

      if (!objfile) {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR: error reading objects for zone: " << zone_num
                   << " trying to read obj# " << k << endl;
         }//if
         return -1;
      }//if

      if (obj_list[k].OBJ_FLAGS.get(70)) {

         //log("About to check if need to load more inv.\n");
         temp_obj.inv.head(ocell);
         while ((obj_ptr = ocell.next())) {
            if ((obj_count(temp_obj.inv, *obj_ptr) >
                 obj_count(obj_list[k].inv, *obj_ptr))
                && (obj_ptr->getCurInGame() < obj_ptr->getMaxInGame())) {
               Put(&(obj_list[obj_ptr->getIdNum()]), obj_list[k].inv);
               recursive_init_loads(obj_list[obj_ptr->getIdNum()], 0);
            }//if
         }//while      
      }//if should update

      objfile >> k;
      objfile.getline(temp_str, 80);
   }//while, the big loop, reads in a whole mob list

   temp_obj.IN_LIST = room_list[0].getInv(); //hack to let destructor fire
                                             //happily, turn it into a SOBJ
   //log("Done w/update objects.\n");
   return 0;
}//update_objects

int update_zone(int zone_num, short read_all) {
   switch (config.useMySQL) {
#ifdef USEMYSQL
      case true:
         return db_update_zone(zone_num, read_all);
#endif
      case false:
         return file_update_zone(zone_num, read_all);
   }
   return -1;
}

#ifdef USEMYSQL
int db_update_zone(int zone_num, short read_all) {
   int k;
   room tmp_room;
   vehicle tmp_veh;
   object* obj_ptr;
   Cell<object*> ocell;
   critter* crit_ptr;
   Cell<critter*> ccell;

   MYSQL_RES* result;
   MYSQL_ROW row;
   String query="select ROOM_NUM from Rooms where IN_ZONE=";
   query+=zone_num;

   if (ZoneCollection::instance().elementAt(zone_num).isLocked() &&
         (read_all == FALSE)) {
      return 0;
   }

   if (mysql_real_query(database, query, strlen(query))==0) {
      if ((result=mysql_store_result(database))==NULL) {
         if (mudlog.ofLevel(WRN)) {
            mudlog << "In db_update_zone(int, short):\n";
            mudlog << "Error retrieving query results: "
                   << mysql_error(database) << endl;
         }
         return -1;
      }
      while ((row=mysql_fetch_row(result))) {
         room* room_ptr;
         k=atoi(row[0]);
         if (k & 0x01000000) {
            tmp_veh.dbRead(k, read_all);
            room_ptr = &tmp_veh;
            k = (k & ~(0x01000000));
         }
         else {
            tmp_room.dbRead(k, read_all);
            room_ptr=&tmp_room;
         }

         if (read_all) {
            room_list[k].setTotalLoaded(TRUE);
         }
         room_ptr->getInv()->head(ocell);
         while ((obj_ptr = ocell.next())) {
            if ((obj_count(*(room_ptr->getInv()), *obj_ptr) >
                 obj_count(*(room_list[k].getInv()), *obj_ptr)) &&
                (obj_ptr->getCurInGame() <
                 obj_list[obj_ptr->getIdNum()].getMaxInGame())) {
               
               if (obj_ptr->isPlayerOwned()) {
                  object* pobox = load_player_box(obj_ptr->getIdNum());
                  if (pobox) {
                     pobox->IN_LIST = room_list[k].getInv();
                     pobox->setCurRoomNum(k, 0);
                     affected_objects.gainData(pobox);
                     room_list[k].gainObject(pobox);
                  }
                  else {
                     room_list[k].gainObject(&(obj_list[obj_ptr->getIdNum()]));
                  }
               }
               else {
                  room_list[k].gainObject(&(obj_list[obj_ptr->getIdNum()]));
               }
               recursive_init_loads(obj_list[obj_ptr->getIdNum()], 0);
            }
         }

         List<critter*> tmp_lst(room_ptr->getCrits());
         tmp_lst.head(ccell);
         int tmp_cc;
         int ingame_cc;
         while ((crit_ptr = ccell.next())) {
            tmp_cc = room_ptr->getCritCount(*crit_ptr);
            ingame_cc = room_list[k].getCritCount(*crit_ptr);
            if ((tmp_cc > ingame_cc) && ((crit_ptr->getCurInGame() - tmp_cc) <
                                         crit_ptr->getMaxInGame())) {

               if (crit_ptr->isPlayerShopKeeper()) {
                  critter* shop_keeper =
                     load_player_shop_owner(crit_ptr->getIdNum());
                  if (shop_keeper) {
                     shop_keeper->setCurRoomNum(k);
                     room_list[k].gainCritter(shop_keeper);
                  }
               }
               else {
                  room_list[k].gainCritter(&(mob_list[crit_ptr->getIdNum()]));
               }
               recursive_init_loads(mob_list[crit_ptr->getIdNum()]);
            }
         }
      }
      tmp_room.Clear();
      tmp_veh.Clear();
      mysql_free_result(result);
   }
   else {
      if (mudlog.ofLevel(WRN)) {
         mudlog << "In db_update_zone(int, short):\n";
         mudlog << "Error executing query: " << mysql_error(database) << endl;
      }
      return -1;
   }
   return 0;
}
#endif

int file_update_zone(int zone_num, short read_all) {
   int k = 0;
   room tmp_room;
   vehicle tmp_veh;

   object* obj_ptr;
   Cell<object*> ocell;
   critter* crit_ptr;
   Cell<critter*> ccell;
   char temp_str[100];
   String buf(100);

   //Sprintf(buf, "In update_zone:  zone# = %i.\n", zone_num);
   //log(buf);

         //if it's locked, don't mess w/it
   if (ZoneCollection::instance().elementAt(zone_num).isLocked() &&
       (read_all == FALSE)) { //if locked and an automatic update
      //log("Locked, returning...\n");
      return 0; //don't do anything
   }//if
      
   Sprintf(buf, "./World/zone_%i", zone_num);
   ifstream rfile(buf);
   if (!rfile) { 
      buf.Prepend("cp ./World/DEFAULT_ZONE ");
      system(buf);
      mudlog.log(ERROR, "WARNING: zone file created in update_zone.");
      mudlog.log(ERROR, buf);
      return 0;
   }//if

   rfile >> k;
   rfile.getline(temp_str, 80); //junk line
   while (k != -1) {
      room* room_ptr;

      if (!rfile) {
         if (mudlog.ofLevel(ERROR)) {
            mudlog << "ERROR: error reading rooms for zone: " << zone_num
                   << " trying to read room# " << k << endl;
         }//if
         return -1;
      }//if

      if (k & 0x01000000) { //it's a vehicle
         k = (k & ~(0x01000000));

         if (mudlog.ofLevel(DBG)) {
            mudlog << "About to read vehicle:  " << k
                << " zone:  " << zone_num << endl;
         }
         tmp_veh.fileRead(rfile, read_all);
         room_ptr = &tmp_veh;
      }
      else {
         if (mudlog.ofLevel(DBG)) {
            mudlog << "About to read room:  " << k
                   << " zone:  " << zone_num << endl;
         }
         tmp_room.fileRead(rfile, read_all);
         room_ptr = &tmp_room;
      }

      if (read_all) {
         room_list[k].setTotalLoaded(TRUE); //total_loaded flag
      }//if

      //log("About to check if need to load more inv into the room.\n");
      room_ptr->getInv()->head(ocell);
      while ((obj_ptr = ocell.next())) {
         if ((obj_count(*(room_ptr->getInv()), *obj_ptr) >
              obj_count(*(room_list[k].getInv()), *obj_ptr)) &&
             (obj_ptr->getCurInGame()
              < obj_list[obj_ptr->getIdNum()].getMaxInGame())) {

            if (obj_ptr->isPlayerOwned()) {
               object* pobox = load_player_box(obj_ptr->getIdNum());
               if (pobox) {
                  // Denote it as 'modified', (SMOB)
                  pobox->IN_LIST = room_list[k].getInv();
                  pobox->setCurRoomNum(k, 0);
                  affected_objects.gainData(pobox);
                  room_list[k].gainObject(pobox);
               }
               else {
                  //put a virgin copy in the room
                  room_list[k].gainObject(&(obj_list[obj_ptr->getIdNum()]));
               }
            }
            else {
               //points into object array
               room_list[k].gainObject(&(obj_list[obj_ptr->getIdNum()]));
            }
            recursive_init_loads(obj_list[obj_ptr->getIdNum()], 0);
         }//if
      }//while      

      //log("Checking for load-more-critters?\n");
      List<critter*> tmp_lst(room_ptr->getCrits());
      tmp_lst.head(ccell);
      int tmp_cc;
      int ingame_cc;
      while ((crit_ptr = ccell.next())) {
         tmp_cc = room_ptr->getCritCount(*crit_ptr);
         ingame_cc = room_list[k].getCritCount(*crit_ptr);
         if ((tmp_cc > ingame_cc) &&
             ((crit_ptr->getCurInGame() - tmp_cc) <
              crit_ptr->getMaxInGame())) {

            //ptr into mob array
            if (mudlog.ofLevel(DBG)) {
               mudlog << "LOAD: Loading critter#: " << crit_ptr->getIdNum()
                      << " in room: " << k << " curInGame: "
                      << crit_ptr->getCurInGame() << " maxInGame: "
                      << crit_ptr->getMaxInGame() << " tmp_cc: "
                      << tmp_cc << " ingame_cc: " << ingame_cc << endl;
            }

            if (crit_ptr->isPlayerShopKeeper()) {
               critter* shop_keeper = load_player_shop_owner(crit_ptr->getIdNum());
               if (shop_keeper) {
                  shop_keeper->setCurRoomNum(k);
                  room_list[k].gainCritter(shop_keeper);
               }//if
            }//if
            else {
               room_list[k].gainCritter(&(mob_list[crit_ptr->getIdNum()]));
            }

            // Only does objects now.
            recursive_init_loads(mob_list[crit_ptr->getIdNum()]);
         }//if
      }//while      
      rfile >> k;
      rfile.getline(temp_str, 80);
   }//while, the big loop, reads in a whole zone
   tmp_room.Clear();
   tmp_veh.Clear();
   return 0;
}//update_zone


void decrease_timed_affecting_pcs() {  //will decrease all
            //timed affects by one time unit, if zero, it will  
            //delete them from the spells_affecting list, it
            //will output all neccessary messages
            //UNLESS THE SPELL IS PERMANENT (ie if time_left = -1)

   // log("In decrease_timed_affecting_pcs\n");

   Cell<critter*> crit_cell;
   pc_list.head(crit_cell);
   critter* crit_ptr;
   Cell<stat_spell_cell*> sp_cell;
   stat_spell_cell* sp_ptr;
   String buf(100);

   while ((crit_ptr = crit_cell.next())) {
      if ((++(crit_ptr->pc->idle_ticks) > 30) && 
          (!crit_ptr->pc->imm_data || (crit_ptr->IMM_LEVEL < 2))) {
         if (mudlog.ofLevel(DBG)) {
            mudlog << "Logging off player in decrease_timed_affecting_pcs,"
                   << " name:  " << *(name_of_crit(*crit_ptr, ~0)) 
                   << "  address:  " << crit_ptr << "  ticks:  "
                   << crit_ptr->pc->idle_ticks << endl;
         }
         log_out(*crit_ptr);
      }//if

      if (TRUE /*crit_ptr->pc->mode == MODE_NORMAL*/) {
         crit_ptr->affected_by.head(sp_cell);
         sp_ptr = sp_cell.next();
         while (sp_ptr) {
               if (sp_ptr->bonus_duration != -1)
               sp_ptr->bonus_duration--;
            if (sp_ptr->bonus_duration == 0) {
               rem_effects_crit(sp_ptr->stat_spell, *crit_ptr, TRUE);
               delete sp_ptr;
               sp_ptr = crit_ptr->affected_by.lose(sp_cell);
            }//if
            else
               sp_ptr = sp_cell.next();
         }//while

         if (crit_ptr->HUNGER > 0)
            crit_ptr->HUNGER--;  //food
         if (crit_ptr->THIRST > 0)
            crit_ptr->THIRST--;  //drink
         if (crit_ptr->DRUGGED > 0)
            crit_ptr->DRUGGED--;  //drugged

         if (crit_ptr->MODE == MODE_NORMAL) {
            if (crit_ptr->HUNGER == 0)
                     show("You are famished.\n", *crit_ptr);
            if (crit_ptr->THIRST == 0)
               show("You are thirsty.\n", *crit_ptr);
            if (crit_ptr->DRUGGED == 0) {
               show("You no longer feel drugged.\n", *crit_ptr);
               crit_ptr->DRUGGED = -1;
            }//if
         }//if

                /* check for lights about to go out */
         if (crit_ptr->EQ[11]) {
            if (!crit_ptr->EQ[11]->IN_LIST) {
               crit_ptr->EQ[11] = obj_to_sobj(*(crit_ptr->EQ[11]), 
                                              &(crit_ptr->inv), crit_ptr->getCurRoomNum());
            }//if
            if (crit_ptr->EQ[11]->extras[0] == 1) {
               Sprintf(buf, "%S flickers.\n", 
                       long_name_of_obj(*(crit_ptr->EQ[11]), crit_ptr->SEE_BIT));
               buf.Cap();
               show(buf, *crit_ptr);
            }//if
         }//if

                /* check for lights gone out */
         if (crit_ptr->EQ[11]) {
            if (crit_ptr->EQ[11]->extras[0] == 0) {
               crit_ptr->EQ[11]->extras[0] = -2;
               Sprintf(buf, "%S dims and glows its last.\n", 
                       long_name_of_obj(*(crit_ptr->EQ[11]), crit_ptr->SEE_BIT));
               buf.Cap();
               show(buf, *crit_ptr);
               crit_ptr->crit_flags.turn_off(USING_LIGHT_SOURCE);
               room_list[crit_ptr->getCurRoomNum()].checkLight(FALSE);
               crit_ptr->EQ[11]->short_desc += "(OUT)";
               crit_ptr->EQ[11]->in_room_desc += "(OUT)";
            }//if
         }//if
      }//if
   }//while

}//decrease_timed_affecting_pcs


void decrease_timed_affecting_lds() {

   // log("In decrease_timed_affecting_pcs\n");

   Cell<critter*> crit_cell;
   linkdead_list.head(crit_cell);
   critter* crit_ptr;
   Cell<stat_spell_cell*> sp_cell;
   stat_spell_cell* sp_ptr;
   String buf(100);

   while ((crit_ptr = crit_cell.next())) {
      if (++(crit_ptr->pc->idle_ticks) > 30) {
         mudlog << "Logging off player in decrease_timed_affecting_lds,"
                << " name:  " << *(name_of_crit(*crit_ptr, ~0)) 
                << "  address:  " << crit_ptr << endl;
         log_out(*crit_ptr);
      }//if

      if (TRUE /*crit_ptr->pc->mode == MODE_NORMAL*/) {
         crit_ptr->affected_by.head(sp_cell);
         sp_ptr = sp_cell.next();
         while (sp_ptr) {
               if (sp_ptr->bonus_duration != -1)
               sp_ptr->bonus_duration--;
            if (sp_ptr->bonus_duration == 0) {
               rem_effects_crit(sp_ptr->stat_spell, *crit_ptr, TRUE);
               delete sp_ptr;
               sp_ptr = crit_ptr->affected_by.lose(sp_cell);
            }//if
            else
               sp_ptr = sp_cell.next();
         }//while

         if (crit_ptr->HUNGER > 0)
            crit_ptr->HUNGER--;  //food
         if (crit_ptr->THIRST > 0)
            crit_ptr->THIRST--;  //drink
         if (crit_ptr->DRUGGED == 0)
            crit_ptr->DRUGGED--;  //food

         if (crit_ptr->MODE == MODE_NORMAL) {
            if (crit_ptr->HUNGER == 0)
                     show("You are famished.\n", *crit_ptr);
            if (crit_ptr->THIRST == 0)
               show("You are thirsty.\n", *crit_ptr);
            if (crit_ptr->DRUGGED == 0) {
               show("You no longer feel drugged.\n", *crit_ptr);
               crit_ptr->DRUGGED = -1;
            }//if
         }//if

                /* check for lights about to go out */
         if (crit_ptr->EQ[11]) {
            if (!crit_ptr->EQ[11]->IN_LIST) {
               crit_ptr->EQ[11] = obj_to_sobj(*(crit_ptr->EQ[11]), 
                                              &(crit_ptr->inv), crit_ptr->getCurRoomNum());
            }//if
            if (crit_ptr->EQ[11]->extras[0] == 1) {
               Sprintf(buf, "%S flickers.\n", 
                    long_name_of_obj(*(crit_ptr->EQ[11]), crit_ptr->SEE_BIT));
               buf.Cap();
               show(buf, *crit_ptr);
            }//if
         }//if

                /* check for lights gone out */
         if (crit_ptr->EQ[11]) {
            if (crit_ptr->EQ[11]->extras[0] == 0) {
               crit_ptr->EQ[11]->extras[0] = -2;
               Sprintf(buf, "%S dims and glows its last.\n", 
                    long_name_of_obj(*(crit_ptr->EQ[11]), crit_ptr->SEE_BIT));
               buf.Cap();
               show(buf, *crit_ptr);
               crit_ptr->crit_flags.turn_off(USING_LIGHT_SOURCE);
               room_list[crit_ptr->getCurRoomNum()].checkLight(FALSE);
            }//if
         }//if
      }//if
   }//while

}//decrease_timed_affecting_lds


void decrease_timed_affecting_smobs() {  //will decrease all
            //timed affects by one time unit, if zero, it will  
            //delete them from the spells_affecting list, it
            //will output all neccessary messages
            //UNLESS THE SPELL IS PERMANENT (ie if time_left = -1)

   // log("In decrease_timed_affecting_mobs\n");

   Cell<critter*> crit_cell;
   affected_mobs.head(crit_cell);
   critter* crit_ptr;
   critter* tmp_crit;
   Cell<stat_spell_cell*> sp_cell;
   stat_spell_cell* sp_ptr;

   room* rm_ptr;
   object* tmp_obj;
   object* obj_ptr;

   crit_ptr = crit_cell.next();
   while (crit_ptr) {
      if (!IsEmpty(crit_ptr->affected_by)) {
         crit_ptr->affected_by.head(sp_cell);
         sp_ptr = sp_cell.next();
         while (sp_ptr) {
            if (sp_ptr->bonus_duration != -1)
               sp_ptr->bonus_duration--;
               if (sp_ptr->bonus_duration == 0) {
               rem_effects_crit(sp_ptr->stat_spell, *crit_ptr, TRUE);
               delete sp_ptr;
               sp_ptr = crit_ptr->affected_by.lose(sp_cell);
            }//if
            else 
               sp_ptr = sp_cell.next();
         }//while
      }//else

      // Check to see if the should, and can 'disolve'.  This is used for
      // summoned and other 'created' mobs.
      crit_ptr->mob->incrementTicksOld();
      // check to see if we can disolve it...
      if (crit_ptr->mob->isDisolvable() && !crit_ptr->isFighting()) {
         if (crit_ptr->mob->getTicksOld() > 60) {
            if ((crit_ptr->follower_of == NULL) &&
                (crit_ptr->master == NULL)) {
               // check for hunting...
               if (!crit_ptr->isTracking()) {
                  tmp_crit = crit_ptr;
                  crit_ptr = affected_mobs.lose(crit_cell);
                  tmp_crit->emote("wanders away...\n");
                  rm_ptr = tmp_crit->getCurRoom();
                  tmp_obj = rm_ptr->getInv()->peekFront();
                  agg_kills_vict(NULL, *tmp_crit, FALSE); //silently kill

                  // Now, clean up the corpse.
                  obj_ptr = rm_ptr->getInv()->peekFront();
                  if (obj_ptr && (obj_ptr != tmp_obj)) {
                     //ie if a corpse was placed in the room
                     // obj_ptr is it, and should be deleted.
                     rm_ptr->purgeObj(obj_ptr, NULL, FALSE);
                  }  
                  continue;
               }
            }//if
         }//if
      }//if

      crit_ptr->mob->decrementTicksTillFreedom();
      if (crit_ptr->master) {
         if (crit_ptr->mob->getTicksTillFreedom() <= 0) {
            crit_ptr->doFollow(*crit_ptr); //follow self
         }//if
      }//if

      crit_ptr = crit_cell.next();
   }//while

}//decrease_timed_affecting_smobs


void decrease_timed_affecting_objects() {  
   Cell<stat_spell_cell*> sp_cell;
   stat_spell_cell* sp_ptr;
   Cell<object*> cell;
   affected_objects.head(cell);
   object* obj_ptr;
   String buf(100);

   //log("In decrease_timed_affecting_objects\n");

   obj_ptr = cell.next();
   while (obj_ptr) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "decrease_timed_affecting_objects, obj_number:  "
                << obj_ptr->OBJ_NUM << " obj_ptr: " << obj_ptr << endl;
      }

      obj_ptr->affected_by.head(sp_cell);
      sp_ptr = sp_cell.next();
      while (sp_ptr) {
         if (sp_ptr->bonus_duration == 0) {
            rem_effects_obj(sp_ptr->stat_spell, *obj_ptr);
            delete sp_ptr;
            sp_ptr = obj_ptr->affected_by.lose(sp_cell);
         }//if
         else { 
           if (sp_ptr->bonus_duration != -1)
             sp_ptr->bonus_duration--;
           sp_ptr = sp_cell.next();
         }//else
      }//while

      if (mudlog.ofLevel(DBG)) {
         mudlog << "About to look after lightts.." << endl;
      }
      
                    /* take care of lights */

      if (obj_ptr->OBJ_FLAGS.get(OBJ_LIGHT_SOURCE)) {
         if (obj_ptr->extras[0] > -1) { //is not perm
           obj_ptr->extras[0]--;
         }//if
      }//if

                            /* corpses */
      if (mudlog.ofLevel(DBG)) {
         mudlog << "About to look after corpses.." << endl;
      }

      // If we ever let more than this object disolve, see the normalize_obj
      // method for notes on how not to crash the game!!
      if (obj_ptr->OBJ_NUM == config.corpseObject) { //if is corpse
         if (obj_ptr->bag && 
             obj_ptr->bag->time_till_disolve == 0) { //disolve it!!
            disolve_object(*obj_ptr);
            obj_ptr = affected_objects.lose(cell);
         }//if
         else {
           if (obj_ptr->bag->time_till_disolve != -1) {
             obj_ptr->bag->time_till_disolve--;
             obj_ptr = cell.next();
           }//if
           else {
              obj_ptr = cell.next();
           }
         }//else
      }//if is a corpse
      else {
         obj_ptr = cell.next(); //couldn't have disolved
      }//else

      if (mudlog.ofLevel(DBG)) {
         mudlog << "At end of while loop." << endl;
      }

   }//while

   while (!obj_to_be_disolved_list.isEmpty()) {
      obj_ptr = obj_to_be_disolved_list.popFront();
      if (mudlog.ofLevel(DBG)) {
         mudlog << "About to disolve object: " << obj_ptr << endl;
      }
      do_disolve_object(*obj_ptr);
   }//while

   if (mudlog.ofLevel(DBG)) {
      mudlog << "At end of decrease_timed_affecting_objects." << endl;
   }
}//decrease_timed_affecting_objects


void decrease_timed_affecting_doors() {  
   Cell<stat_spell_cell*> sp_cell;
   stat_spell_cell* sp_ptr;
   Cell<door*> cell;
   affected_doors.head(cell);
   door* dr_ptr;
   String buf(100);

   //log("In decrease_timed_affecting_doors\n");

   int take_me_off;
   dr_ptr = cell.next();
   while (dr_ptr) {
      if (dr_ptr->ticks_till_disolve > 0) {
         mudlog.log(DBG, "About to decrement a door's ticks_till_disolve.\n");
         if (--(dr_ptr->ticks_till_disolve) <= 0) {
            mudlog.log(DBG, "Gonna delete a door.\n");
            room_list[dr_ptr->in_room].DOORS.loseData(dr_ptr);
            Sprintf(buf, "%S closes up and vanishes.\n", 
                    name_of_door(*dr_ptr, ~0));
            buf.Cap();
            room_list[dr_ptr->in_room].showAllCept(buf);
            delete dr_ptr;
            dr_ptr = affected_doors.lose(cell);
            continue;
         }//if
      }//if
      
      take_me_off = TRUE;
      dr_ptr->affected_by.head(sp_cell);
      sp_ptr = sp_cell.next();
      while (sp_ptr) {
         take_me_off = FALSE;
         if (sp_ptr->bonus_duration != -1)
            sp_ptr->bonus_duration--;
         if ((sp_ptr->bonus_duration == 0) || (sp_ptr->bonus_duration < -1)) {
            rem_effects_door(sp_ptr->stat_spell, *dr_ptr, 
                             room_list[abs(dr_ptr->destination)],
                             room_list[dr_ptr->in_room], TRUE);
            delete sp_ptr;
            sp_ptr = dr_ptr->affected_by.lose(sp_cell);
         }//if
         else {
            sp_ptr = sp_cell.next();
         }
      }//while
      if (take_me_off)
         dr_ptr = affected_doors.lose(cell);
      else
         dr_ptr = cell.next();
   }//while
}//decrease_timed_affecting_doors


void disolve_object(object& obj) {
   obj_to_be_disolved_list.append(&obj);
}//disolve_object


void do_disolve_object(object& obj) {
   Cell<object*> cell(obj.inv);
   object *obj_ptr;
   String buf(100);

   if (mudlog.ofLevel(DBG)) {
      mudlog << "do_disolve_obj, addr: " << &obj << endl;
   }

   if (!obj.in_list) {
      core_dump("ERROR:  disolve_object called on non-sobj.\n");
   }//if

   //log("Before while\n");
   while ((obj_ptr = cell.next())) {
      if (obj_ptr->in_list)
         obj_ptr->in_list = obj.in_list;
      obj.in_list->append(obj_ptr);
   }//while
   //log("Before Clear()\n");
   obj.inv.clear();

   if (obj.in_list->loseData(&obj)) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "Successfully removed it from it's list.\n";
      }
   }
   else {
      Sprintf(buf, "Failed to remove object ptr: %i from it's list.",
              (int)(&obj));
      mudlog << "Failed to remove object ptr: " << &obj << " from it's list:"
             << obj.in_list << endl;
      core_dump(buf);
   }
                                       //obj to be disolved
   //log("Lost data.\n");
   delete &obj; //should fire deconstructors
   //log("Done.\n");
}//do_disolve_obj


void decrease_timed_affecting_rooms() {  //will decrease all
            //timed affects by one time unit, if zero, it will  
            //delete them from the spells_affecting list, it
            //will output all neccessary messages
            //UNLESS THE SPELL IS PERMANENT (ie if time_left = -1)

   Cell<room*> cell;
   affected_rooms.head(cell);
   room* rm_ptr = NULL;
   Cell<stat_spell_cell*> sp_cell;
   stat_spell_cell* sp_ptr = NULL;
   short take_it_off = FALSE;

   if (mudlog.ofLevel(DBG)) {
      mudlog << "In dta_rooms: affected_rooms.size() == " 
             << affected_rooms.size() << endl;
   }

   rm_ptr = cell.next();
   while (rm_ptr) {
      take_it_off = TRUE;

      if (mudlog.ofLevel(DBG)) {
         mudlog << "dta_rooms, rm_ptr: " << rm_ptr << endl;
      }

      Cell<door*> dcll(rm_ptr->DOORS);
      door* dptr, *tmp_dptr;
      dptr = dcll.next();
      while (dptr) {
         if (dptr->ticks_till_disolve != -1) {
            if ((--dptr->ticks_till_disolve) == 0) {
               tmp_dptr = dptr;
               dptr = rm_ptr->DOORS.lose(dcll);
               delete tmp_dptr;
            }//if
            else {
               take_it_off = FALSE;
               dptr = dcll.next();
            }//else
         }//if
         else
            dptr = dcll.next();
      }//while
      
      rm_ptr->affected_by.head(sp_cell);
      sp_ptr = sp_cell.next();
      while (sp_ptr) {
         if (sp_ptr->bonus_duration != -1) { //if not permanent
            take_it_off = FALSE;
            sp_ptr->bonus_duration--;
         }//if
         if (sp_ptr->bonus_duration == 0) {
            rem_effects_room(sp_ptr->stat_spell, *rm_ptr, TRUE);
            delete sp_ptr;
            sp_ptr = rm_ptr->affected_by.lose(sp_cell);
         }//if
         else
            sp_ptr = sp_cell.next();
      }//while
      
      if (take_it_off) {
         if (mudlog.ofLevel(DBG)) {
            mudlog << "Taking it off...\n";
         }
         rm_ptr = affected_rooms.lose(cell);
      }
      else {
         if (mudlog.ofLevel(DBG)) {
            mudlog << "NOT Taking it off...\n";
         }
         rm_ptr = cell.next();
      }
   }//while
}//decrease_timed_affecting_rooms


void add_spell_affecting_critter(int spell, int duration, critter& vict) {
   stat_spell_cell* spcell = new stat_spell_cell;

   //log("In add_spell_affecting_critter");

   spcell->stat_spell = spell;
   spcell->bonus_duration = duration; 
   Put(spcell, vict.affected_by);
}//gain spell_affected_by    


void add_spell_affecting_obj(int spell, int duration, object& vict) {
   stat_spell_cell* spcell = new stat_spell_cell;

   //log("In add_spell_affecting_obj\n");

   spcell->stat_spell = spell;
   spcell->bonus_duration = duration; 
   Put(spcell, vict.affected_by);
}//gain spell_affected_by    


void show(const char* message, critter& pc) {
   //log(message);

   if (mudlog.ofLevel(XMT)) {
      mudlog << "OUTPUT from -:" << *(pc.getName());
         mudlog << ":-  -:" << message << ":-\n" << endl;
   }

   if (pc.possessed_by) {
      pc.possessed_by->show("[POSSESSED]: ");
      pc.possessed_by->show(message);
   }

   if (pc.pc) {

      critter* snooper;
      if ((snooper = pc.SNOOPED_BY)) {
         mudlog.log(TRC, "Within snoop if\n");
         String buf2(100);
         Sprintf(buf2, "SNOOP_OUT:  -:%s:-\n", message);
         snooper->show(buf2);
      }//if snoop

      pc.setDoPrompt(TRUE);
      if (!message)
         return;

      if (pc.pc->output.Strlen() < OUTPUT_MAX_LEN) {
         pc.pc->output.Append(message);
      }//if
   }//if
}//show 


void show_all(const char* msg, const room& rm) {
   rm.showAllCept(msg, NULL);
}


void show_all_but_2(critter& A, critter& B, const char* msg,
                    room& rm) {
   Cell<critter*> cell(rm.getCrits());
   critter* crit_ptr;

   // log("In show_all_but_2\n");
   while ((crit_ptr = cell.next()))  { 
      if ((crit_ptr != &A) && (crit_ptr != &B)) 
         if (crit_ptr->POS < POS_SLEEP)  
            show(msg, *crit_ptr);
   }//while

   Cell<object*> cll(*(rm.getInv()));
   object* obj;

   while ((obj = cll.next())) {
     if (obj->obj_proc && (crit_ptr = obj->obj_proc->w_eye_owner)) {
       if (crit_ptr->POS < POS_SLEEP) {
         show("#####", *crit_ptr);
         show(msg, *crit_ptr);
       }//if
     }//if
   }//while

}//show_all_but_2


int doShowList(critter* pc, CSelectorColl& includes, CSelectorColl& denies,
               List<critter*>& lst, CSentryE cs_entry, ...) {
   va_list argp;
   va_start(argp, cs_entry);
   int retval = 0;
   retval = vDoShowList(pc, includes, denies, lst, cs_entry, argp);
   va_end(argp);
   return retval;
}


int vDoShowList(critter* pc, CSelectorColl& includes, CSelectorColl& denies,
                List<critter*>& lst, CSentryE cs_entry, va_list argp) {
   Cell<critter*> cll(lst);
   critter* ptr;
   String buf(100);
   String buf2(100);

   while ((ptr = cll.next())) {
      if (!(denies.matches(ptr, pc))) {
         if (includes.matches(ptr, pc)) {
            if (mudlog.ofLevel(DBG)) {
               mudlog << "vDoShowList, includes matched." << endl;
               mudlog << "cstr of " << (int)(cs_entry) << "-:"
                      << cstr(cs_entry, *ptr) << ":-" <<  endl;
            }

            vSprintf(buf, cstr(cs_entry, *ptr), argp);
            if (mudlog.ofLevel(DBG)) {
               mudlog << endl << "buf -:" << buf << ":-" << endl << endl;
            }
            ptr->show(buf);
         }//if
      }//if
   }//while
   return 0;
}//voDoShowList


void show_all(const char* msg) {
   Cell<critter*> cell(pc_list);
   critter* crit_ptr;

   while ((crit_ptr = cell.next()))  { 
      if (crit_ptr->pc && crit_ptr->PC_FLAGS.get(14)) {
         show(msg, *crit_ptr);
      }//if
   }//while
}//show_all_info


void out_str(const List<String*>& lst, critter& pc) { 
   Cell<String*> cell(lst);
   String *string;
   
   // log("In out_str.\n");

   if (pc.pc) {
      if (IsEmpty(lst)) {
         show("You see nothing special.\n", pc);
         return;
      }//if
      while ((string = cell.next())) {
         show(*string, pc);
         show("\n", pc);
      }//while
   }//if
}//out_str


/** Can over-ride the VIS/SEE bit stuff if you set see_all to true. */
void out_crit(const List<critter*>& lst, critter& pc, int see_all = FALSE) {
   Cell<critter*> cell(lst);
   Cell<stat_spell_cell*> cell2;
   stat_spell_cell* sp;
   critter* crit_ptr;
   String buf(100);

   // log("In out_crit\n");

   int see_bits = pc.getSeeBit();
   if (see_all) {
      see_bits = ~0;
   }

   if (pc.isUsingClient())
      show("<MOB_LIST>", pc);
   else if (pc.isUsingColor()) {
      pc.show(*(pc.getMobListColor()));
   }

   while ((crit_ptr = cell.next())) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "out_crit: got critter:  " << *(crit_ptr->getName())
                << endl;
      }
      if (detect(see_bits, crit_ptr->VIS_BIT) &&
          (crit_ptr != &pc)) { //can see it, not looker
         if ((crit_ptr->isHiding()) && //if is hiding
             (d(1, pc.LEVEL + 30) < 
              d(1, max(get_percent_lrnd(HIDE_SKILL_NUM, *crit_ptr) * 6,
                       get_percent_lrnd(BLEND_SKILL_NUM, *crit_ptr) * 6)))) {
            continue; //successful hide
         }//if

         if (crit_ptr->pc) { //is a pc

            if (mudlog.ofLevel(DBG)) {
               mudlog << "Doing pc..." << endl;
            }

            if (crit_ptr->isParalyzed()) {
               Sprintf(buf, "     %S %S %s\n",
                       name_of_crit(*crit_ptr, see_bits),
                       &(crit_ptr->short_desc), cstr(CS_PARALYZED, pc));
            }
            else {
               Sprintf(buf, "     %S %S %s\n", 
                       name_of_crit(*crit_ptr, see_bits), 
                       &(crit_ptr->short_desc), crit_ptr->getPosnStr(pc));
            }

            buf.Cap();
            if (crit_ptr->VIS_BIT & 2) {
               buf.setCharAt(1, '*');
            }//if
            
            show(buf, pc);
            crit_ptr->affected_by.head(cell2);
            while ((sp = cell2.next())) {
               if (sp->stat_spell == SANCTUARY_SKILL_NUM) {
                  Sprintf(buf, "\t\t%s glows brightly.\n", 
                          get_he_she(*crit_ptr));
                  buf.Cap();
                  show(buf, pc);
                  break; //cause there are no others to display now
               }//if
            }//while
         }//if
         else if (crit_ptr->isSmob() &&
                  (crit_ptr->POS != mob_list[crit_ptr->getIdNum()].POS)) {
            //is a SMOB with a different posn than corresponding MOB
            if (mudlog.ofLevel(DBG)) {
               mudlog << "Doing SMOB" << endl;
            }

            if (pc.shouldShowVnums()) {
               if (crit_ptr->isParalyzed()) {
                  Sprintf(buf, "     [%i]%P11 %S %s\n", crit_ptr->MOB_NUM,
                          name_of_crit(*crit_ptr, see_bits), cstr(CS_PARALYZED, pc));
               }
               else {
                  Sprintf(buf, "     [%i]%P11 %S %s\n", crit_ptr->MOB_NUM,
                          name_of_crit(*crit_ptr, see_bits), 
                          crit_ptr->getPosnStr(pc));
               }
            }
            else {
               if (crit_ptr->isParalyzed()) {
                  Sprintf(buf, "     %S %s\n",
                          name_of_crit(*crit_ptr, see_bits),
                          cstr(CS_PARALYZED, pc));
               }
               else {
                  Sprintf(buf, "     %S %s\n",
                          name_of_crit(*crit_ptr, see_bits), 
                          crit_ptr->getPosnStr(pc));
               }
            }
            buf.Cap();

            if (crit_ptr->VIS_BIT & 2) {
               buf.setCharAt(1, '*');
            }//if
            show(buf, pc);
            
            crit_ptr->affected_by.head(cell2);
            while ((sp = cell2.next())) {
               if (sp->stat_spell == SANCTUARY_SKILL_NUM) {
                  Sprintf(buf, "\t\t%s glows brightly.\n", 
                          get_he_she(*crit_ptr));
                  buf.Cap();
                  show(buf, pc);
                  break;
               }//if
            }//while
         }//if
         else if (crit_ptr->mob) { //then it is a mob, or SMOB w/same posn as MOB

            if (mudlog.ofLevel(DBG)) {
               mudlog << "Doing MOB" << endl;
            }

            if (pc.shouldShowVnums()) {
               if (crit_ptr->isParalyzed()) {
                  Sprintf(buf, "     [%i]%P11 %S %s\n", crit_ptr->MOB_NUM,
                          name_of_crit(*crit_ptr, see_bits), cstr(CS_PARALYZED, pc));
               }
               else {
                  Sprintf(buf, "     [%i]%P11 %S\n", crit_ptr->MOB_NUM,
                          &(crit_ptr->in_room_desc));
               }
            }//if
            else {
               if (crit_ptr->isParalyzed()) {
                  Sprintf(buf, "       %S %s\n",
                          name_of_crit(*crit_ptr, see_bits), cstr(CS_PARALYZED, pc));
               }
               else {
                  Sprintf(buf, "       %S\n", &(crit_ptr->in_room_desc));
               }
            }

            if (crit_ptr->VIS_BIT & 2) {
               buf.setCharAt(1, '*');
            }//if
            buf.Cap();
            show(buf, pc);
            
            crit_ptr->affected_by.head(cell2);
            while ((sp = cell2.next())) {
               if (sp->stat_spell == SANCTUARY_SKILL_NUM) {
                  Sprintf(buf, "\t\t%s glows brightly.\n", 
                          get_he_she(*crit_ptr));
                  buf.Cap();
                  show(buf, pc);
                  break;
               }//if
            }//while
         }//else
         else {
            mudlog << __PRETTY_FUNCTION__ << "ERROR: crit_ptr is not sane: "
                   << crit_ptr << endl;
         }
      }//if
   }//while
   if (pc.USING_CLIENT) {
      show("</MOB_LIST>", pc);
   }
   else if (pc.isUsingColor()) {
      pc.show(*(pc.getDefaultColor()));
   }
}//out_crit



void out_inv(const List<object*>& lst, critter& pc, 
             const short type_of_list) {
                               //outs the names object*
   Cell<object*> cell(lst);
   object*  obj_ptr;
   String buf(100);

   mudlog.log(TRC, "In out_inv.\n");

   if (pc.isUsingClient()) {
      show("<ITEM_LIST>", pc);
   }
   else if (pc.isUsingColor()) {
      pc.show(*(pc.getObjListColor()));
   }
   
   if (IsEmpty(lst) && type_of_list == OBJ_INV) {
      show("        [empty]        \n", pc);
      if (pc.isUsingClient()) {
         show("</ITEM_LIST>", pc);
      }
      else if (pc.isUsingColor()) {
         pc.show(*(pc.getDefaultColor()));
      }

      mudlog.log(DBG, "Done with out_inv (empty).\n");
      return;
   }//if

   switch (type_of_list)
      {
      case ROOM_INV:
         while ((obj_ptr = cell.next())) {

            if (detect(pc.SEE_BIT, obj_ptr->OBJ_VIS_BIT)) {
               if (pc.shouldShowVnums()) {
                  char tmp[50];
                  sprintf(tmp, "%p: ", obj_ptr);
                  Sprintf(buf, "   %s [%i] %P11 %S", tmp, obj_ptr->OBJ_NUM,
                          &(obj_ptr->in_room_desc));
               }
               else {
                  Sprintf(buf, "\t%S", &(obj_ptr->in_room_desc));
               }
               buf.Cap();

               if (obj_ptr->isHerb()) {
                  if (d(1, 100) <= 
                      d(1, 2 * get_percent_lrnd(HERBALISM_SKILL_NUM, pc))) {
                     buf.Append("(herb)");
                  }//if
               }//if

               if (pc.canDetectMagic() &&
                   (!IsEmpty(obj_ptr->affected_by) ||
                    !IsEmpty(obj_ptr->stat_affects))) {
                  buf.Append(" {Blue Glow}\n");
               }//if
               else {
                  buf.Append("\n");
               }

               if (obj_ptr->OBJ_VIS_BIT & 2) {
                  buf.Prepend("*");
               }//if

               show(buf, pc);
            }//if
         }//while
         break;
      case OBJ_INV: case CRIT_INV:
         while ((obj_ptr = cell.next())) {
            if (detect(pc.SEE_BIT, obj_ptr->OBJ_VIS_BIT)) {
               if (pc.shouldShowVnums()) {
                  char tmp[50];
                  sprintf(tmp, "%p: ", obj_ptr);
                  Sprintf(buf, "   %s [%i] %P11 %S", tmp, obj_ptr->OBJ_NUM,
                          long_name_of_obj(*obj_ptr, ~0));
               }
               else {
                  Sprintf(buf, "\t%S", long_name_of_obj(*obj_ptr, ~0));
               }

               buf.Cap();

               if (obj_ptr->isHerb()) {
                  if (d(1, 100) <= 
                      d(1, 2 * get_percent_lrnd(HERBALISM_SKILL_NUM, pc))) {
                     buf.Append("(herb)");
                  }//if
               }//if

               if  (pc.canDetectMagic() &&
                    (!IsEmpty(obj_ptr->affected_by) ||
                     !IsEmpty(obj_ptr->stat_affects))) {
                  buf.Append(" {Blue Glow}\n");
               }//if
               else {
                  buf.Append("\n");
               }

               if (obj_ptr->OBJ_VIS_BIT & 2) {
                  buf.Prepend("*");
               }//if
               show(buf, pc);
            }//if
         }//while
         break;
      default:
         mudlog.log(ERROR, "ERROR:  default called in out_inv.\n");
         break;
      }//switch type_of_list

   if (pc.isUsingClient()) {
      show("</ITEM_LIST>", pc);
   }
   else if (pc.isUsingColor()) {
      pc.show(*(pc.getDefaultColor()));
   }

   mudlog.log(DBG, "Done with out_inv.\n");

}//out_inv

critter* have_crit_named(List<critter*>& lst, const int i_th,
                         const String* name, const int see_bit,
                         const room& rm, int do_exact = FALSE) {
   int foo = 0;
   return have_crit_named(lst, i_th, name, see_bit, foo, rm, do_exact);
}

critter* have_crit_named(List<critter*>& lst, const int i_th,
                         const String* name, const int see_bit,
                         int& count_sofar, const room& rm, int do_exact = FALSE) {
   Cell<String*> char_cell;
   Cell<critter*> cell(lst);
   critter* crit_ptr;
   int count = 0;
   int ptr_v_bit, len;
   String *string;

   if (mudlog.ofLevel(DBG)) {
      mudlog << "in have_crit_named, i_th: " << i_th << " name -:"
             << *name << ":- see_bit: " << see_bit << " rm# "
             << rm.getIdNum() << " do_exact: " << do_exact << endl;
   }

   if (!name) {
      mudlog.log(ERROR, "ERROR:  NULL name sent to have_crit_named.\n");
      return NULL;
   }//if

   if ((len = name->Strlen()) == 0) 
      return NULL;

   if (i_th <= 0)
      return NULL;

   int matched;
   while ((crit_ptr = cell.next())) {
      ptr_v_bit = (crit_ptr->VIS_BIT | rm.getVisBit()); 
      if (detect(see_bit, ptr_v_bit)) {
         crit_ptr->names.head(char_cell);
         matched = FALSE;
         while ((string = char_cell.next()) && !matched) {
            if (do_exact) {
                  if (strcasecmp(*string, *name) == 0) { 
                  matched = TRUE;
                  count++;
                  count_sofar++;
               }//if
            }//if exact
            else {
                  if (strncasecmp(*string, *name, len) == 0) { 
                  matched = TRUE;
                  count++;
                  count_sofar++;
               }//if
            }//else
            if (count == i_th) {
               return crit_ptr;
            }//if
         }//while
      }//if
   }//while
   return NULL;
}//have_crit_named 


int crit_sub_a_4_b(critter* a, List<critter*>& lst, 
                   const int i_th, const String* name,
                   const int see_bit, room& rm) {
   Cell<String*> char_cell;
   Cell<critter*> cell(lst);
   critter* crit_ptr;
   int count = 0, ptr_v_bit;
   String *string;

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

   if (!name || !a) {
      mudlog.log(ERROR, "ERROR:  NULL(s) sent to crit_sub_a_4_b.\n");
      return FALSE;
   }//if

   if (name->Strlen() == 0) {
      mudlog.log(ERROR, "ERROR:  name of zero length sent to crit_sub_a_4_b.\n"); 
      return FALSE;
   }//if

   int matched;
   while ((crit_ptr = cell.next())) {
      ptr_v_bit = (crit_ptr->VIS_BIT | rm.getVisBit()); 
      if (detect(see_bit, ptr_v_bit)) {
         crit_ptr->names.head(char_cell);
         matched = FALSE;
         while ((string = char_cell.next()) && !matched) {
            if (strncasecmp(*string, *name, name->Strlen()) == 0){ 
               matched = TRUE;
               count++;
               if (count == i_th) {  //found right one
                  lst.assign(cell, a);
                  return TRUE;
               }//if
            }//if
         }//while
      }//if
   }//while
   return FALSE;
}//crit_sub_a_4_b 


int obj_sub_a_4_b(object* a, List<object*>& lst, const int i_th,
                  const String* name, const int see_bit, room& rm) {
   Cell<String*> char_cell;
   Cell<object*> cell(lst);
   object* obj_ptr;
   int count = 0, ptr_v_bit;
   String *string;

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

   if (!name || !a) {
      mudlog.log(ERROR, "ERROR:  NULL(s) sent to obj_sub_a_4_b.\n");
      return FALSE;
   }//if

   if (name->Strlen() == 0) {
      mudlog.log(ERROR, "ERROR:  name of zero length sent to obj_sub_a_4_b.\n"); 
      return FALSE;
   }//if

   int matched;
   while ((obj_ptr = cell.next())) {
      ptr_v_bit = (obj_ptr->OBJ_VIS_BIT | rm.getVisBit()); 
      if (detect(see_bit, ptr_v_bit)) {
         obj_ptr->names.head(char_cell);
         matched = FALSE;
         while ((string = char_cell.next()) && !matched) {
            if (strncasecmp(*string, *name, name->Strlen()) == 0){ 
               matched = TRUE;
               count++;
               if (count == i_th) {  //found right one
                  //obj_ptr = Prev(cell);  //back cell up one
                  lst.assign(cell, a);
                  return TRUE;
               }//if
            }//if
         }//while
      }//if
   }//while
   return FALSE;
}//obj_sub_a_4_b 


object* have_obj_named(const List<object*>& lst, const int i_th,
                       const String* name, const int see_bit,
                       const room& rm) {
   int foo = 0;
   return have_obj_named(lst, i_th, name, see_bit, rm, foo);
}

object* have_obj_named(const List<object*>& lst, const int i_th,
                       const String* name, const int see_bit,
                       const room& rm, int& count_sofar) {
   Cell<String*> char_cell;
   Cell<object*> cell(lst);
   object* obj_ptr;
   int count = 0, ptr_v_bit;
   String *string;

   if (!name) {
      mudlog.log(ERROR, "ERROR:  Null sent to have_obj_named.k\n");
      return NULL;
   }//if

   if (name->Strlen() == 0) 
      return NULL;

   int matched;
   while ((obj_ptr = cell.next())) {
      ptr_v_bit = (obj_ptr->OBJ_VIS_BIT | rm.getVisBit());
      if (detect(see_bit, ptr_v_bit)) {
         obj_ptr->names.head(char_cell);
         matched = FALSE;
         while ((string = char_cell.next()) && !matched) {
            if (strncasecmp(*string, *name, name->Strlen()) == 0){ 
               matched = TRUE;
               count++;
               count_sofar++;
               if (count == i_th) {
                  return obj_ptr;
               }//if
            }//if
         }//while
      }//if
   }//while
   return NULL;
}//have_obj_named 


int obj_named_count(const List<object*>& lst, const String* name,
                    const int see_bit, const room& rm) {
   Cell<String*> char_cell;
   Cell<object*> cell(lst);
   object* obj_ptr;
   int count = 0, ptr_v_bit;
   String *string;

   if (name->Strlen() == 0) 
      return 0;

   while ((obj_ptr = cell.next())) {
      ptr_v_bit = (obj_ptr->OBJ_VIS_BIT | rm.getVisBit());
      if (detect(see_bit, ptr_v_bit)) {
         obj_ptr->names.head(char_cell);
         while ((string = char_cell.next())) {
            if (strncasecmp(*string, *name, name->Strlen()) == 0){ 
               count++;
               break; //out of the internal while loop
            }//if
         }//while
      }//if
   }//while
   return count;
}//have_obj_named 


int  obj_is_named(const object& obj, const String& name) {
   Cell<String*> char_cell(obj.names);
   String *string;
   int len;

   //log("In obj_is_named\n");

   if ((len = name.Strlen()) == 0)
      return FALSE;

   while ((string = char_cell.next())) {
      if (strncasecmp(*string, name, len) == 0) {
         return TRUE;
      }//if
   }//while
   return FALSE;
}//obj_is_named 


int  door_is_named(const door_data& dr, const String& name) {
   Cell<String*> char_cell(dr.names);
   String *string;
   int len;

   //log("In obj_is_named\n");

   if ((len = name.Strlen()) == 0)
      return FALSE;

   while ((string = char_cell.next())) {
      if (strncasecmp(*string, name, len) == 0) {
         return TRUE;
      }//if
   }//while
   return FALSE;
}//door_is_named


//boolean functionality here...
int  mob_is_named(const critter& pc, const String& name) {
   return pc.isNamed(name);
}


const String* name_of_crit(critter& pc, int see_bit) {
   return pc.getName(see_bit);
}


String* name_of_obj(const object& obj, int see_bit) {
   if (!detect(see_bit, obj.OBJ_VIS_BIT)) {
      return &SOMETHING;
   }//if
   else {
      String* tmp = Top(obj.names);
      if (tmp)
         return tmp;
      else
         return &SOMETHING;
   }//else
}//name_of_obj


String* long_name_of_obj(object& obj, int see_bit) {
   if (obj.short_desc.Strlen() == 0 ||
        !detect(see_bit, obj.OBJ_VIS_BIT)) {
      return &SOMETHING;
   }//if
   else {
      return &(obj.short_desc);
   }//else
}//long name_of_obj

String* name_of_room(const room& rm, int see_bit) {
   if ((IsEmpty(rm.names)) || !detect(see_bit, rm.getVisBit())) {
      return &SOMEWHERE;
   }//if
   else {
      return Top(rm.names);
   }//else
}//name_of_room


String* name_of_door(const door& dr, int see_bit) {
   return name_of_dr_data(*(dr.dr_data), see_bit, dr.destination);
}//name_of_door


String* name_of_dr_data(const door_data& dr, int see_bit, int dest) {
   Cell<String*> cell(dr.names);
   String *str, *str2;

   if (!detect(see_bit, dr.vis_bit)) {
      return &SOMETHING;
   }//if

   if (IsEmpty(dr.names))
      return &UNKNOWN;

   if (dest >= 0) {
      str = cell.next();
      str2 = cell.next();

      if (str2)
         if (*str2 != "#")
            return str2;
         else
            return str;
      else  // no specific name, just the direction
         return str;
    }//if
    else {
      str = cell.prev();
      str2 = cell.prev();

      if (str2)
         if (*(str2) != "#")
            return str2;
         else
            return str;
      else  // no specific name, just the direction
         return str;
   }//else
}//name_of_dr_data
       

// Will be sent an IP with an optional N on the beginning,
// all, not just newbies
short is_banned(const String& ip) {
   Cell<String*> cll(banned_hosts);
   String* ptr;

   while ((ptr = cll.next())) {
      if (toupper(ptr->charAt(0)) == 'N') {
         continue; //not interested in newbie bans here.
      }//if
      else {
         if (strncmp(ip, *(ptr), min(ip.Strlen(), ptr->Strlen())) == 0)
            return TRUE;
      }
   }//while
   return FALSE;
}//is_banned


// Will be sent an IP with an optional N on the beginning,
// all, not just newbies
short is_newbie_banned(const String& ip) {
   Cell<String*> cll(banned_hosts);
   String* ptr;

   while ((ptr = cll.next())) {
      if (toupper(ptr->charAt(0)) == 'N') {
         const char* str = *ptr; //convert to const char*
         if (strncmp(ip, str + 1, max(ip.Strlen(), ptr->Strlen() - 1)) == 0)
            return TRUE;
      }//if
   }//while
   return FALSE;
}//is_newbie_banned


int detect(int see_bit, int vis_bit) {
   return ((see_bit & vis_bit) == vis_bit);
}//detect