/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
/* Crimson2 Mud Server
 * All source written/copyright Ryan Haksi 1995 *
 * This source code is proprietary. Use in whole or in part without
 * explicity permission by the author is strictly prohibited
 *
 * Current email address(es): cryogen@infoserve.net
 * Phone number: (604) 591-5295
 *
 * C4 Script Language written/copyright Cam Lesiuk 1995
 * Email: clesiuk@engr.uvic.ca
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef WIN32
  #include <unistd.h> /* for unlink */
#endif

#include "crimson2.h"
#include "macro.h"
#include "log.h"
#include "queue.h"
#include "mem.h"
#include "str.h"
#include "ini.h"
#include "extra.h"
#include "property.h"
#include "code.h"
#include "file.h"
#include "thing.h"
#include "exit.h"
#include "world.h"
#include "send.h"
#include "base.h"
#include "index.h"
#include "object.h"
#include "char.h"
#include "group.h"
#include "fight.h"
#include "player.h"
#include "affect.h"
#include "reset.h"
#include "area.h"
#include "cmd_move.h"
#include "cmd_cbt.h"
#include "mobile.h"

#define MOBILE_INDEX_SIZE 8192

INDEX        mobileIndex;
MOBTEMPLATE *spiritTemplate = NULL;

MTYPE mTypeList[] = {
  { "NONE",             0 },
  { "ROBOT",            MT_ROBOT },
  { "ANIMAL",           MT_ORGANIC|MT_ANIMAL },
  { "PLANT",            MT_ORGANIC|MT_PLANT },
  { "GALCIV-CITIZEN",   MT_ORGANIC|MT_SENTIENT|MT_HASMONEY },
  { "SENTIENT-PLANT",   MT_ORGANIC|MT_PLANT|MT_SENTIENT },
  { "SENTIENT-ROBOT",   MT_ROBOT|MT_SENTIENT },
  { "", 0 }
};

BYTE *mActList[] = {
  "TRACKER",
  "SENTINEL",
  "SCAVENGER",
  "PICKPOCKET",
  "FORGIVING",
  "AGGRESSIVE",
  "STAYAREA",
  "WIMPY",
  "HYPERAGGR",
  "RPUNCTURE",
  "RSLASH",
  "RCONCUSSIVE",
  "RHEAT",
  "REMR",
  "RLASER",
  "RPSYCHIC",
  "RACID",
  "RPOISON",
  "RESCUE",
  ""
};

BYTE     mobileOfLog;    /* log if a virtual # cant be found */
BYTE     mobileReadLog;  /* log each mob structure as its read */
LWORD    mobileNum = 0;  /* number of mob structures in use - just curious really */

void MobileInit(void) {
  BYTE buf[256];

  mobileOfLog   = INILWordRead("crimson2.ini", "mobileOfLog", 0);
  mobileReadLog = INILWordRead("crimson2.ini", "mobileReadLog", 0);
  sprintf(buf, "Reading Mobile logging defaults\n");
  Log(LOG_BOOT, buf);
  sprintf(buf, "MobileTemplate structure size is %d bytes\n", sizeof(MOBTEMPLATE));
  Log(LOG_BOOT, buf);

  IndexInit(&mobileIndex, MOBILE_INDEX_SIZE, "mobileIndex", 0);

  MEMALLOC(spiritTemplate, MOBTEMPLATE, MOBILE_ALLOC_SIZE);
  memset( (void*)spiritTemplate, 0, sizeof(MOBTEMPLATE)); /* init to zeros */
  spiritTemplate->mVirtual = MVIRTUAL_SPIRIT;
  spiritTemplate->mPos = POS_STANDING;
  spiritTemplate->mKey = STRCREATE("dissembodied spirit");
  spiritTemplate->mSDesc = STRCREATE("a spirit");
  spiritTemplate->mLDesc = STRCREATE("a dissembodied spirit floats here");
  spiritTemplate->mDesc = STRCREATE("its a ghost!\n");
  BITSET(spiritTemplate->mAffect, AF_INVIS|AF_IMPROVED|AF_VACUUMWALK|AF_BREATHWATER|AF_DARKVISION|AF_PHASEWALK|AF_WATERWALK|AF_PHASEDOOR);
  BITSET(spiritTemplate->mAct, MACT_SENTINEL);
  spiritTemplate->mProperty = PropertyCreate(spiritTemplate->mProperty, STRCREATE("%CarryMax"), STRCREATE("0"));
}


void MobileRead(WORD area) {
  FILE        *mobileFile;
  BYTE         mobFileBuf[256];
  BYTE         buf[256];
  BYTE         tmp[256];
  STR         *sKey;
  STR         *sDesc;
  MOBTEMPLATE *mobile;
  LWORD last = -1;

  sprintf(mobFileBuf, "area/%s.mob", areaList[area].aFileName->sText);
  mobileFile = fopen(mobFileBuf, "rb");
  if (!mobileFile) {
    sprintf(buf, "Unable to read %s, killing server\n", mobFileBuf);
    Log(LOG_BOOT, buf);
    PERROR("MobileRead");
    exit(ERROR_BADFILE);
  }
  if (areaList[area].aOffset) {
    sprintf(buf, "Relocating Area/Mobs [%s]\n", areaList[area].aFileName->sText);
    Log(LOG_BOOT, buf);
  }

  /* okay we opened it up so read it.... */
  fscanf(mobileFile, " %s ", tmp); /* get virtual number */
  while (!feof(mobileFile)) {
    if (tmp[0] == '$' || feof(mobileFile))
      break; /* Dikumud file format EOF character */
    if (tmp[0] != '#') { /* whoa... whadda we got here */
      sprintf(buf, "MobileRead: Unknown virtual %s, aborting\n", tmp);
      Log(LOG_BOOT, buf);
      break;
    }
    MEMALLOC(mobile, MOBTEMPLATE, MOBILE_ALLOC_SIZE);
    memset( (void*)mobile, 0, sizeof(MOBTEMPLATE)); /* init to zeros */
    mobile->mVirtual = atoi(tmp+1);
    mobile->mVirtual += areaList[area].aOffset;
    if (mobileReadLog) {
      sprintf(buf, "Reading Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    /* confirm that virtual number is valid */
    if (mobile->mVirtual < last) {
      sprintf(buf, "%s - mob%s < than previous\n", mobFileBuf, tmp);
      Log(LOG_BOOT, buf);
    }
    last = MAXV(last, mobile->mVirtual);
    if (mobile->mVirtual < areaList[area].aVirtualMin) {
      sprintf(buf, "%s - mob%s < than %ld\n", mobFileBuf, tmp, areaList[area].aVirtualMin);
      Log(LOG_BOOT, buf);
      break;
    }
    if (mobile->mVirtual > areaList[area].aVirtualMax) {
      sprintf(buf, "%s - mob%s > than %ld\n", mobFileBuf, tmp, areaList[area].aVirtualMax);
      Log(LOG_BOOT, buf);
      break;
    }

    /* NOTE that Private Strings are not designated as such, until just prior to editing */
    mobileNum++;
    mobile->mKey   = FileStrRead(mobileFile);
    if (fileError) {
      sprintf(buf, "Error reading Key for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    mobile->mSDesc = FileStrRead(mobileFile);
    if (fileError) {
      sprintf(buf, "Error reading Desc for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    /* in crimson2 no lDesc should have \n on the end if there
     * is one lose it (in vanilla diku mob ldescs have a trailing \n)
     */
    mobile->mLDesc = FileStrRead(mobileFile);
    if (fileError) {
      sprintf(buf, "Error reading LDesc for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    if (mobile->mLDesc->sText[mobile->mLDesc->sLen-1] == '\n') {
      strcpy(buf, mobile->mLDesc->sText);
      buf[mobile->mLDesc->sLen-1] = '\0';
      STRFREE(mobile->mLDesc);
      mobile->mLDesc = STRCREATE(buf);
    }
    mobile->mDesc  = FileStrRead(mobileFile);
    if (fileError) {
      sprintf(buf, "Error reading Desc for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    mobile->mAct   = FileFlagRead(mobileFile, mActList);    
    mobile->mAffect= FileFlagRead(mobileFile, affectList); 
    fscanf(mobileFile, " %hd ", &mobile->mAura);
    fscanf(mobileFile, "%*s\n"); /* throwaway old S/M param */
    fscanf(mobileFile, " %hd ", &mobile->mLevel);
    fscanf(mobileFile, " %hd ", &mobile->mHitBonus);
    fscanf(mobileFile, " %hd ", &mobile->mArmor);
    fscanf(mobileFile, " %hd ", &mobile->mHPDiceNum);
    fscanf(mobileFile, "d");
    fscanf(mobileFile, "D");
    fscanf(mobileFile, " %hd ", &mobile->mHPDiceSize);
    fscanf(mobileFile, "+%hd ", &mobile->mHPBonus);
    fscanf(mobileFile, " %hd ", &mobile->mDamDiceNum);
    fscanf(mobileFile, "d");
    fscanf(mobileFile, "D");
    fscanf(mobileFile, " %hd ", &mobile->mDamDiceSize);
    fscanf(mobileFile, "+%hd ", &mobile->mDamBonus);
    fscanf(mobileFile, " %ld ", &mobile->mMoney);
    fscanf(mobileFile, " %ld ", &mobile->mExp);
    mobile->mPos  = FILETYPEREAD(mobileFile, posList);
    if (fileError) {
      sprintf(buf, "Error reading Pos for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    TYPECHECK(mobile->mPos, posList);
    mobile->mType = FILETYPEREAD(mobileFile, mTypeList);
    if (fileError) {
      sprintf(buf, "Error reading Type for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    TYPECHECK(mobile->mType, mTypeList);
    mobile->mSex = FILETYPEREAD(mobileFile, sexList);
    if (fileError) {
      sprintf(buf, "Error reading Sex for Mobile#%ld\n", mobile->mVirtual);
      Log(LOG_BOOT, buf);
    }
    TYPECHECK(mobile->mSex, sexList);

    /* read attachments ie exits, keywords etc */
    while (!feof(mobileFile)) {
      fscanf(mobileFile, " %s ", tmp);
      if (tmp[0] == '#') /* well thats it for this chunk of the world */
        break;

      if (tmp[0] == '$') /* well thats it for this chunk of the world */
        break;

      else if (tmp[0] == 'E') { /* haha an extra */
        sKey  = FileStrRead(mobileFile);
        if (fileError) {
          sprintf(buf, "Error reading ExtraKey for Mobile#%ld\n", mobile->mVirtual);
          Log(LOG_BOOT, buf);
        }
        sDesc = FileStrRead(mobileFile);
        if (fileError) {
          sprintf(buf, "Error reading ExtraDesc for Mobile#%ld\n", mobile->mVirtual);
          Log(LOG_BOOT, buf);
        }
        mobile->mExtra = ExtraAlloc(mobile->mExtra, sKey, sDesc);
      }

      else if (tmp[0] == 'P') { /* a property of some kind */
        sKey = FileStrRead(mobileFile); /* property name */
        if (fileError) {
          sprintf(buf, "Error reading PropertyKey for Mobile#%ld\n", mobile->mVirtual);
          Log(LOG_BOOT, buf);
        }
        sDesc = FileStrRead(mobileFile);/* property value */
        if (fileError) {
          sprintf(buf, "Error reading PropertyDesc for Mobile#%ld\n", mobile->mVirtual);
          Log(LOG_BOOT, buf);
        }
        mobile->mProperty = PropertyCreate(mobile->mProperty, sKey, sDesc);
      }

      else { /* what the hell is this anyways? */
        sprintf(buf, "MobileRead: Unknown character in Mobile Entry #%ld\n", mobile->mVirtual);
        Log(LOG_ERROR, buf);
        do {
          fscanf(mobileFile, " %s ", tmp);
        } while (tmp[0]!='#' && tmp[0]!='$' && !feof(mobileFile));
      }

    }

    /* guess this ones a keeper, update mins and maxes etc... */
    IndexInsert(&areaList[area].aMobIndex, mobile, MobileCompareProc);
    if (indexError) {
      sprintf(buf, "%s - mob%s duplicated\n", mobFileBuf, tmp);
      Log(LOG_BOOT, buf);
    }
  }
  /* all done close up shop */
  fclose(mobileFile);
}

MOBTEMPLATE *MobileOf(LWORD virtual) {
  THING *search;
  BYTE   buf[256];
  LWORD  area;

  area = AreaOf(virtual);
  if (area == -1) return NULL;

  search = IndexFind(&areaList[area].aMobIndex, (void*)virtual, MobileFindProc);
  if (search) {
    return MobTemplate(search);
  } else {
    if (mobileOfLog) {
      sprintf(buf, "MobileOf: virtual %ld doesnt exist\n", virtual);
      Log(LOG_ERROR, buf);
    }
  }
  return NULL;
}


void MobileWrite(WORD area) {
  FILE        *mobileFile;
  BYTE         mobFileBuf[256];
  BYTE         buf[256];
  MOBTEMPLATE *mobile;
  LWORD        i;
  EXTRA       *extra;
  PROPERTY    *property;
  
  /* take a backup case we crash halfways through this */
  sprintf(buf,
    "mv area/mob/%s.mob area/mob/%s.mob.bak",
    areaList[area].aFileName->sText,
    areaList[area].aFileName->sText);
  system(buf);
  
  sprintf(mobFileBuf, "area/%s.mob", areaList[area].aFileName->sText);
  mobileFile = fopen(mobFileBuf, "wb");
  if (!mobileFile) {
    sprintf(buf, "Unable to write %s, killing server\n", mobFileBuf);
    Log(LOG_ERROR, buf);
    PERROR("MobileWrite");
    return;
  }
  
  /* okay we opened it up so write it.... */
  for (i=0; i<areaList[area].aMobIndex.iNum; i++) {
    mobile = MobTemplate(areaList[area].aMobIndex.iThing[i]);
    fprintf(mobileFile, "#%ld\n", mobile->mVirtual); /* get virtual number */
    FileStrWrite(mobileFile, mobile->mKey);
    FileStrWrite(mobileFile, mobile->mSDesc);
    FileStrWrite(mobileFile, mobile->mLDesc);
    FileStrWrite(mobileFile, mobile->mDesc);
    FileFlagWrite(mobileFile, mobile->mAct, mActList, ' ');
    FileFlagWrite(mobileFile, mobile->mAffect, affectList, ' ');
    fprintf(mobileFile, "%hd S\n", mobile->mAura);
    fprintf(mobileFile, "%hd %hd %hd", mobile->mLevel, mobile->mHitBonus, mobile->mArmor);
    fprintf(mobileFile, " %hdd%hd+%hd",   mobile->mHPDiceNum,  mobile->mHPDiceSize,  mobile->mHPBonus);
    fprintf(mobileFile, " %hdd%hd+%hd\n", mobile->mDamDiceNum, mobile->mDamDiceSize, mobile->mDamBonus);
    fprintf(mobileFile, "%ld %ld\n", mobile->mMoney, mobile->mExp);
    FILETYPEWRITE(mobileFile, mobile->mPos,  posList,   ' ');
    FILETYPEWRITE(mobileFile, mobile->mType, mTypeList, ' ');
    FILETYPEWRITE(mobileFile, mobile->mSex,  sexList,   '\n');
    
    /* extra descriptions */
    for (extra=mobile->mExtra; extra; extra=extra->eNext) {
      fprintf(mobileFile, "E\n");
      FileStrWrite(mobileFile, extra->eKey);
      FileStrWrite(mobileFile, extra->eDesc);
    }
    
    /* property's */
    for (property=mobile->mProperty; property; property=property->pNext) {
      fprintf(mobileFile, "P\n");
      FileStrWrite(mobileFile, property->pKey);
      if ( CodeIsCompiled(property) ) {
        if (!areaWriteBinary) {
          /* decompile the property */
          CodeDecompProperty(property, NULL);
          mobile->mCompile=1; /* compile on demand enabled */
          /* Warn if it didnt decompile */
          if (CodeIsCompiled(property)) {
            sprintf(buf, "MobileWrite: Property %s failed to decompile for mobile#%ld!\n",
              property->pKey->sText, 
              mobile->mVirtual);
            Log(LOG_AREA, buf);
          } else {
            FileStrWrite(mobileFile, property->pDesc);
          }
        } else {
          FileBinaryWrite(mobileFile, property->pDesc);
        }
      } else {
        FileStrWrite(mobileFile, property->pDesc);
      }
    }
    
  }
  /* all done close up shop */
  fprintf(mobileFile, "$");
  fclose(mobileFile);

  /* turf backup we didnt crash */
  sprintf(buf,
    "area/%s.mob.bak",
    areaList[area].aFileName->sText);
  unlink(buf);

}


INDEXPROC(MobileCompareProc) { /* BYTE IndexProc(void *index1, void *index2) */
  if ( MobTemplate(index1)->mVirtual == MobTemplate(index2)->mVirtual )
    return 0;
  else if ( MobTemplate(index1)->mVirtual < MobTemplate(index2)->mVirtual )
    return -1;
  else
    return 1;
}

INDEXFINDPROC(MobileFindProc) { /* BYTE IFindProc(void *key, void *index) */
  if ( (LWORD)key == MobTemplate(index)->mVirtual )
    return 0;
  else if ( (LWORD)key < MobTemplate(index)->mVirtual )
    return -1;
  else
    return 1;
}


THING *MobileAlloc(void) {
  MOB *mobile;

  MEMALLOC(mobile, MOB, MOBILE_ALLOC_SIZE);
  memset(mobile, 0, sizeof(MOB));
  IndexAppend(&mobileIndex, Thing(mobile));

  Thing(mobile)->tType = TTYPE_MOB;
  return Thing(mobile);
}

THING *MobileCreate(MOBTEMPLATE *template, THING *within) {
  THING    *mobile;
  THING    *leader = NULL;
  THING    *i;
  PROPERTY *p;

  if (!template) return NULL;
  mobile = MobileAlloc();

  Thing(mobile)->tSDesc          = StrAlloc(template->mSDesc);
  Thing(mobile)->tDesc           = StrAlloc(template->mDesc);
  Thing(mobile)->tExtra          = ExtraCopy(template->mExtra);
  if (template->mCompile) {
    template->mCompile = 0;
    for (p=template->mProperty; p; p=p->pNext) {
      CodeCompileProperty(p, NULL);
    }
  }
  Thing(mobile)->tProperty       = PropertyCopy(template->mProperty);
  Base(mobile)->bKey             = StrAlloc(template->mKey);
  Base(mobile)->bLDesc           = StrAlloc(template->mLDesc);
  Base(mobile)->bWeight          = template->mWeight;
  Character(mobile)->cAura       = template->mAura;
  Character(mobile)->cLevel      = template->mLevel;
  Character(mobile)->cHitBonus   = template->mHitBonus;
  Character(mobile)->cArmor      = template->mArmor;
  Character(mobile)->cHitP       = Dice(template->mHPDiceNum,
                                        template->mHPDiceSize)
                                  +template->mHPBonus;
  Character(mobile)->cHitPMax    = Character(mobile)->cHitP;

  Character(mobile)->cExp        = template->mExp;
  Character(mobile)->cPos        = template->mPos;
  Character(mobile)->cSex        = template->mSex;
  Mob(mobile)->mTemplate         = template;

  /* correct for affect flags */
  Character(mobile)->cAffectFlag = template->mAffect;
  BITCLR(Character(mobile)->cAffectFlag, AF_GROUP);
  if (BIT(template->mAffect, AF_IMPROVED))
    BITSET(Character(mobile)->cAffectFlag, AF_INVIS);

  /* if they carry money then give them cash */
  if (BIT(mTypeList[template->mType].mTFlag, MT_HASMONEY))
    Character(mobile)->cMoney      = template->mMoney;

  template->mOnline++;
  CodeCheckFlag(mobile);

  if (within) {
    /* put mobile in room */
    ThingTo(mobile, within);

    /* try to group with somebody */
    if (BIT(template->mAffect, AF_GROUP)) {
      for (i=within->tContain; i; i=i->tNext) {
        /* not a mob cant be a leader */
        if (i->tType != TTYPE_MOB) continue;
        /* not a mob that groups */
        if (!BIT(Mob(i)->mTemplate->mAffect, AF_GROUP)) continue;
        if (Character(i)->cLead) {
          /* not the leader of its group */
          if (Character(i)->cLead != i) continue;
          /* if following a player */
          if (Character(i)->cLead->tType == TTYPE_PLR) continue;
        }
        leader = i; /* a potential leader */
      }
      /* if we found a leader group with it */
      if (leader && leader != mobile) {
        CharAddFollow(mobile, leader);
        BITSET(Character(mobile)->cAffectFlag, AF_GROUP);
        /*BITSET(Character(leader)->cAffectFlag, AF_GROUP);*/
      }
    }
  }

  /* once struct is fully inited give 'em some pts */
  Character(mobile)->cMoveP      = CharGetMovePMax(mobile);
  Character(mobile)->cPowerP     = CharGetPowerPMax(mobile);

  return mobile;
}

void MobileFree(THING *thing) {
  Mob(thing)->mTemplate->mOnline--;
  IndexDelete(&mobileIndex, thing, NULL);
  MEMFREE(Mob(thing), MOB);
}

LWORD MobilePresent(MOBTEMPLATE *template, THING *within) {
  LWORD  present = 0;
  THING *i;

  for (i = within->tContain; i; i=i->tNext) {
    if ( (i->tType == TTYPE_MOB) && (Mob(i)->mTemplate == template) )
      present++;
  }
  return present;
}

void MobileTick() {
  LWORD  i=0;
  LWORD  gain;
  THING *thing=NULL;

  if (mobileIndex.iNum==0) return;
  while(1) {
    /* Corbin's footnote: you did this really lame Cryogen - 
     * why not just use a for() loop like everyone else? CONFUSING! */
    if (thing == mobileIndex.iThing[i]) i++;
    if (i>=mobileIndex.iNum) break;
    thing = mobileIndex.iThing[i];
    
    /* do a tick */
    if (CharTick(thing)) continue;
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }

    /* gain hit points back */
    gain = PropertyGetLWord(thing, "%GainHitP", 10+Character(thing)->cLevel/3);
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
    gain = CharGainAdjust(thing, GAIN_HITP, gain);
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
    Character(thing)->cHitP   += gain;
    MAXSET(Character(thing)->cHitP,   CharGetHitPMax(thing));
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }

    /* gain move points back */
    gain = PropertyGetLWord(thing, "%GainMoveP", 10+Character(thing)->cLevel/3);
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
    gain = CharGainAdjust(thing, GAIN_MOVEP, gain);
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
    Character(thing)->cMoveP  += gain;
    MAXSET(Character(thing)->cMoveP,  CharGetMovePMax(thing));
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }

    /* gain mana points back */
    gain = PropertyGetLWord(thing, "%GainPowerP", 10+Character(thing)->cLevel/3);
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
    gain = CharGainAdjust(thing, GAIN_POWERP, gain);
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
    Character(thing)->cPowerP += gain;
    MAXSET(Character(thing)->cPowerP, CharGetPowerPMax(thing));
    /* crash test - debug purposes only -- I was trying to track a bug */
      if (thing!=mobileIndex.iThing[i]) {int *c; c=NULL; *c=1; }
 
    /* gain money back */
    if (Character(thing)->cMoney < Mob(thing)->mTemplate->mMoney) {
      gain = PropertyGetLWord(thing, "%GainMoney", 0);
      Character(thing)->cMoney += gain;
      MAXSET(Character(thing)->cMoney, Mob(thing)->mTemplate->mMoney);
    } 
  } 
}

LWORD MobileCheckExit(THING *mobile, EXIT *exit) {
  /* basic stuff */
  if (!mobile) return FALSE;
  if (!exit) return FALSE;

  /* must be in a world */
  if (!Base(mobile)->bInside->tType == TTYPE_WLD) return FALSE;
  /* area cant have MOBFREEZE set */
  if (BIT(areaList[Wld(Base(mobile)->bInside)->wArea].aResetFlag, RF_MOBFREEZE)) return FALSE;
  /* exit must lead somewhere */
  if (!exit->eWorld) return FALSE;
  /* cant be a room with no exits */
  if (!Wld(exit->eWorld)->wExit) return FALSE;
  /* check if we cant leave our area */
  if (BIT(Mob(mobile)->mTemplate->mAct, MACT_STAY_AREA) 
  && Wld(Base(mobile)->bInside)->wArea != Wld(exit->eWorld)->wArea)
    return FALSE;
  /* avoid deathtraps */
  if (BITANY(Wld(exit->eWorld)->wFlag, WF_DEATHTRAP|WF_NOMOB)) return FALSE;

  /* MoveExitParse will block the move if they are fighting,
     dont do it here */
  
  /* Check for SENTINEL is elsewhere, that way SENTINELS, will
     still track down enemies, flee etc */

  return TRUE;
}


/* 
 * NOTE: This is probably the most frequently called function in the mud
 *    keep that in mind when you are twiddling with it.
 */
void MobileIdlePrimitive(THING *mobile) {  
  /* Should probably put an explicit check for cFightExit here and move
     towards it */

  /* Tracker mobs will hunt down their assailants */
  if (  (BIT(Mob(mobile)->mTemplate->mAct, MACT_TRACKER)) 
      &&(Mob(mobile)->mTrack)
      &&(Character(mobile)->cMoveP >= CharGetMovePMax(mobile)>>2)
      &&(  (!BIT(Mob(mobile)->mTemplate->mAct, MACT_WIMPY))/* will flee if chasee enters room */
         ||(Character(mobile)->cHitP >= CharGetHitPMax(mobile)/5) )
      ) {  /* Will chase people who flee */
    EXIT  *exit;
    
    if (Base(mobile)->bInside != Base(Mob(mobile)->mTrack)->bInside) {
      exit = ThingTrack(Base(mobile)->bInside, Mob(mobile)->mTrack, PropertyGetLWord(mobile, "%Track", Mob(mobile)->mTemplate->mLevel+10)*3);
      if (MobileCheckExit(mobile, exit)) {
        MoveExitParse(mobile, exit);
        /* should pick a fight here - but give the player time to react */
        return;
      } else {
        if (!Number(0,30)) Mob(mobile)->mTrack = NULL; /* give up */
      }
    }
  }

  /* Scavenger mobs will take stuff lying on the ground */  
  if(BIT(Mob(mobile)->mTemplate->mAct, MACT_SCAVENGER)) {/* will pick stuff up */
    THING *i, *next;

    for(i=Base(mobile)->bInside->tContain; i; i=next) {
      next=i->tNext;
      if (i->tType==TTYPE_OBJ 
       && !Number(0,10) 
       && Obj(i)->oTemplate->oValue>5
       && CharCanCarry(mobile, i)) {
        /* guess we can take it */
        SendAction("^bYou get ^g$N\n", 
                   mobile, i, SEND_SRC |SEND_AUDIBLE|SEND_CAPFIRST);
        SendAction("^b$n gets ^g$N\n",   
                   mobile, i, SEND_ROOM|SEND_AUDIBLE|SEND_CAPFIRST);
        ThingTo(i, mobile);
        return;
      }
    }
  }

  /* Wimpy mobs will keep running away if their hit points are low */  
  if(BIT(Mob(mobile)->mTemplate->mAct, MACT_WIMPY)
  && (Character(mobile)->cHitP < CharGetHitPMax(mobile)/5)) {/* will flee if chasee enters room */
    THING *i;
    EXIT  *exit;

    if (Base(mobile)->bInside->tType == TTYPE_WLD) {
      for(i=Base(mobile)->bInside; i && i!=Mob(mobile)->mTrack; i=i->tNext);
      if(i && ThingCanSee(mobile, i)==TCS_SEENORMAL) {  
        /* Effectively a MoveRandom */
        exit = ExitDir(Wld(Base(mobile)->bInside)->wExit, Number(0,6));
        if((exit)
         &&(exit->eWorld)
         &&((!BIT(Mob(mobile)->mTemplate->mAct, MACT_STAY_AREA)
           ||Wld(Base(mobile)->bInside)->wArea == Wld(exit->eWorld)->wArea))) {
          MoveExitParse(mobile, exit);
          return;
        }
      }
    }
  }
  
  /* Will steal items from players */
  if(BIT(Mob(mobile)->mTemplate->mAct, MACT_PICKPOCKET)) {/* steals items */
  }
  
  /* Hiding mobs tend towards lurking more */
  if (BIT(Character(mobile)->cAffectFlag, AF_HIDDEN)) {
    if (!Number(0,2)) return;
  } else if (BIT(Mob(mobile)->mTemplate->mAffect, AF_HIDDEN)) {
    CmdHide(mobile, "");
    /* if (BIT(Character(mobile)->cAffectFlag, AF_HIDDEN)) */ return;
  }

  /* Attack stuff sometimes */
  if (BIT(Mob(mobile)->mTemplate->mAct, MACT_AGGRESSIVE)
    ||BIT(Mob(mobile)->mTemplate->mAct, MACT_HYPERAGGR)) {/* Will initiate fights */
    THING *i;

    /* see if the person we are after is here first off*/
    if (Mob(mobile)->mTrack) {
      for(i=Base(mobile)->bInside->tContain; i && i!=Mob(mobile)->mTrack; i=i->tNext);
      if (i && ThingCanSee(mobile, i)==TCS_SEENORMAL) {
        FightStart(mobile, i, 1, NULL);
        return;
      }
    }
    /* oh well kill someone at random */
    for(i=Base(mobile)->bInside->tContain; i; i=i->tNext) {
      /* Aggr mobs will attack players */
      if( i->tType==TTYPE_PLR 
        && !Number(0,10) 
        && !BIT(Plr(i)->pSystem, PS_NOHASSLE) 
        && ThingCanSee(mobile, i)>TCS_CANTSEE) {  
        /* make sure we dont wack a group member */
        /*if  (!Character(mobile)->cLead
          || Character(mobile)->cLead!=Character(i)->cLead) {*/
        if (!GroupIsGroupedMember(i,mobile)) {
          FightStart(mobile, i, 1, NULL);
          return;
        }
      /* HyperAggr Mobs will also attack other mobs */
      } else if ( i->tType==TTYPE_MOB 
               && BIT(Mob(mobile)->mTemplate->mAct, MACT_HYPERAGGR) 
               && !Number(0,20) 
               && ThingCanSee(mobile, i)>TCS_CANTSEE) {
        /* make sure we dont wack a group member or should we? */
        /*if  (!BIT(Character(mobile)->cAffectFlag, AF_GROUP) 
          || !Character(mobile)->cLead
          || Character(mobile)->cLead!=Character(i)->cLead) {*/
        if (!GroupIsGroupedMember(i,mobile)) {
          FightStart(mobile, i, 1, NULL);
          return;
        }
      }
    }
  }

  /* Move around randomly, 
     Note: hurt mobs should look for nearby regen rooms and go there */  
  if(!BIT(Mob(mobile)->mTemplate->mAct, MACT_SENTINEL) /* sentinels dont move */
    &&(!BIT(areaList[Wld(Base(mobile)->bInside)->wArea].aResetFlag, RF_MOBFREEZE))
    &&(Character(mobile)->cMoveP >= CharGetMovePMax(mobile)>>1)
    &&(!Character(mobile)->cLead || Character(mobile)->cLead==mobile)) {   /* if we are following someone dont leave */
    EXIT *exit;
    LWORD exitNum;
    
    if (Base(mobile)->bInside 
     && Base(mobile)->bInside->tType == TTYPE_WLD 
     && !Number(0,10)) {

      /* count exits */
      for (exitNum=0,exit=Wld(Base(mobile)->bInside)->wExit; exit; exit=exit->eNext) exitNum++;
      /* pick a random direction */
      exitNum = Number(1,exitNum);
      for (exit=Wld(Base(mobile)->bInside)->wExit; exit&&exitNum!=1; exit=exit->eNext,exitNum--);

      if(MobileCheckExit(mobile, exit)) {
        MoveExitParse(mobile, exit);
        return;
      }
    }
  }

/* End of MobileIdlePrimitive() */
}


void MobileIdle(THING *mobile) {
  /* if they are fighting then they are not idling */
  if (Character(mobile)->cFight) return;

  /* let code handler get first crack at it */
  if ((CodeParseIdle(mobile))) return;

  MobileIdlePrimitive(mobile);
}