areaeditor/
/*
** AreaEditor - a program for editing SMAUG and ROM area files.
** Author: Nick Gammon
** http://www.gammon.com.au/
** See Copyright Notice at the end of AreaEditor.h
*/

// AreaEditorDoc.cpp : implementation of the CAreaEditorDoc class
//

#include "stdafx.h"
#include "AreaEditor.h"
#include "MainFrm.h"

#include "AreaEditorDoc.h"
#include "AreaLoadingProblems.h"
#include "AreaEditorView.h"

#include "MobileView.h"
#include "MUDProgramView.h"
#include "ObjectView.h"
#include "ObjectAffectView.h"
#include "ObjectExtraDescriptionView.h"
#include "AreaView.h"
#include "RoomView.h"
#include "RoomExitView.h"
#include "RoomMapView.h"
#include "ResetView.h"
#include "ShopView.h"
#include "RepairView.h"
#include "HelpView.h"

#include "AreaSummary.h"
#include "WalkthroughDlg.h"
#include "defaults.h"
#include "AreaTypeDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CMUDitem, CObject)

IMPLEMENT_DYNAMIC(CArea, CMUDitem)
IMPLEMENT_DYNAMIC(CMobile, CMUDitem)
IMPLEMENT_DYNAMIC(CMUDprogram, CMUDitem)
IMPLEMENT_DYNAMIC(CMUDObject, CMUDitem)
IMPLEMENT_DYNAMIC(CAffect, CMUDitem)
IMPLEMENT_DYNAMIC(CExtraDescription, CMUDitem)
IMPLEMENT_DYNAMIC(CExit, CMUDitem)
IMPLEMENT_DYNAMIC(CRoomMap, CMUDitem)
IMPLEMENT_DYNAMIC(CRoom, CMUDitem)
IMPLEMENT_DYNAMIC(CReset, CMUDitem)
IMPLEMENT_DYNAMIC(CShop, CMUDitem)
IMPLEMENT_DYNAMIC(CRepair, CMUDitem)
IMPLEMENT_DYNAMIC(CHelp, CMUDitem)

#define ROM (m_AreaType == eROM)
#define SMAUG (m_AreaType == eSMAUG)

/////////////////////////////////////////////////////////////////////////////
// CAreaEditorDoc

IMPLEMENT_DYNCREATE(CAreaEditorDoc, CDocument)

BEGIN_MESSAGE_MAP(CAreaEditorDoc, CDocument)
	//{{AFX_MSG_MAP(CAreaEditorDoc)
	ON_COMMAND(ID_FILE_AREA_SUMMARY, OnFileAreaSummary)
	ON_UPDATE_COMMAND_UI(ID_STATUS_MODIFIED, OnUpdateStatusModified)
	ON_COMMAND(ID_AREA_CHECK, OnAreaCheck)
	ON_COMMAND(ID_AREA_RENUMBER, OnAreaRenumber)
	ON_UPDATE_COMMAND_UI(ID_AREA_RENUMBER, OnUpdateAreaRenumber)
	ON_COMMAND(ID_AREA_WALKTHROUGH, OnAreaWalkthrough)
	ON_UPDATE_COMMAND_UI(ID_AREA_WALKTHROUGH, OnUpdateAreaWalkthrough)
	ON_COMMAND(ID_FILE_EXPORT_MUSH, OnFileExportMush)
	ON_UPDATE_COMMAND_UI(ID_FILE_EXPORT_MUSH, OnUpdateFileExportMush)
	ON_COMMAND(ID_VIEW_WARNINGS, OnViewWarnings)
	ON_UPDATE_COMMAND_UI(ID_VIEW_WARNINGS, OnUpdateViewWarnings)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CAreaEditorDoc construction/destruction

CAreaEditorDoc::CAreaEditorDoc()
{
  m_active_view = NULL;
  m_Area = NULL;
  m_leftview = NULL;
  m_iCurrentMobileTab = 0;
  m_iCurrentRoomTab = 0;
  m_iCurrentObjectTab = 0;
  m_iCurrentExitTab = 0;
  m_iCurrentAreaTab = 0;
  m_AreaType = (t_areatype) App.GetProfileInt (sProfilePreferences, sProfileDefaultAreaType, eSMAUG);
  m_Original_AreaType = m_AreaType;
  m_bDoingDelete = false;

}

CAreaEditorDoc::~CAreaEditorDoc()
{

// ========================================================================
// delete all mobs

  for (POSITION mobPos = m_MobList.GetHeadPosition (); mobPos; )
    delete m_MobList.GetNext (mobPos);

  m_MobList.RemoveAll ();   // pointers are deleted, remove list items


// ========================================================================
// delete all objects

  for (POSITION objPos = m_ObjectList.GetHeadPosition (); objPos; )
    delete m_ObjectList.GetNext (objPos);

  m_ObjectList.RemoveAll ();   // pointers are deleted, remove list items

// ========================================================================

// delete all rooms

  for (POSITION roomPos = m_RoomList.GetHeadPosition (); roomPos; )
    delete m_RoomList.GetNext (roomPos);

  m_RoomList.RemoveAll ();   // pointers are deleted, remove list items

// ========================================================================
// delete all resets


  for (POSITION resetPos = m_ResetList.GetHeadPosition (); resetPos; )
    delete m_ResetList.GetNext (resetPos);

  m_ResetList.RemoveAll ();   // pointers are deleted, remove list items

// ========================================================================
// delete all shops

  for (POSITION shopPos = m_ShopList.GetHeadPosition (); shopPos; )
    delete m_ShopList.GetNext (shopPos);

  m_ShopList.RemoveAll ();   // pointers are deleted, remove list items

// ========================================================================
// delete all repairs

  for (POSITION repairPos = m_RepairList.GetHeadPosition (); repairPos; )
    delete m_RepairList.GetNext (repairPos);

  m_RepairList.RemoveAll ();   // pointers are deleted, remove list items

// ========================================================================
// delete all helps

  for (POSITION helpPos = m_HelpList.GetHeadPosition (); helpPos; )
    delete m_HelpList.GetNext (helpPos);

  m_HelpList.RemoveAll ();   // pointers are deleted, remove list items

// ========================================================================
// delete the area object

  delete m_Area;

}   // end of CAreaEditorDoc::~CAreaEditorDoc  (destructor)


BOOL CAreaEditorDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;

	// TODO: add reinitialization code here
	// (SDI documents will reuse this document)

	return TRUE;
}



char CAreaEditorDoc::fread_letter (void)
  {
  return m_FileRead.fread_letter ();
  }   // end of fread_letter

CString CAreaEditorDoc::fread_to_eol (void)
  {
  return m_FileRead.fread_to_eol ();
  }   // end of fread_to_eol

CString CAreaEditorDoc::fread_word (void)
  {
  return m_FileRead.fread_word ();
  } // end of fread_word

CString CAreaEditorDoc::fread_string (const bool bLeftJustify)
  {
  return m_FileRead.fread_string (bLeftJustify);
  } // end of fread_string

CString CAreaEditorDoc::fread_line (void)
  {
  return m_FileRead.fread_line ();
  } // end of fread_line

long CAreaEditorDoc::fread_number (const bool bFlag)
  {
  return m_FileRead.fread_number (bFlag);
  } // end of fread_number

EXT_BV CAreaEditorDoc::fread_bitvector (const bool bFlag)
  {
  return m_FileRead.fread_bitvector (bFlag);
  }

// ========================================================================

void CAreaEditorDoc::load_SMAUG_area (void)
  {

  m_Area->strAreaName = fread_string ();

  } // end of CAreaEditorDoc::load_SMAUG_area

// ========================================================================

void CAreaEditorDoc::load_ROM_area (void)
  {

  m_Area->strFileName	= fread_string ();
  m_Area->strAreaName		= fread_string ();
  m_Area->strAuthor	= fread_string ();
  m_Area->min_vnum	= fread_number ();
  m_Area->max_vnum	= fread_number ();

  } // end of CAreaEditorDoc::load_ROM_area

// ========================================================================

void CAreaEditorDoc::load_area (void)
  {

  if (m_Area)
    ThrowErrorException ("Already have a #AREA declared");

  m_Area = new CArea (RUNTIME_CLASS(CAreaView), this);

  if (SMAUG)
    load_SMAUG_area ();
  else if (ROM)
    load_ROM_area ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  } // end of CAreaEditorDoc::load_area

// ========================================================================

void CAreaEditorDoc::load_author (void)
  {

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  m_Area->strAuthor = fread_string ();
  } // end of CAreaEditorDoc::load_author

// ========================================================================

void CAreaEditorDoc::load_version(void)
{

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  m_Area->area_version   = fread_number ();
} // end of CAreaEditorDoc::load_version

// ========================================================================

void CAreaEditorDoc::load_climate(void)
{

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");
	
	m_Area->climate_temp    = fread_number ();
	m_Area->climate_precip  = fread_number ();
	m_Area->climate_wind    = fread_number ();
	
} // end of CAreaEditorDoc::load_climate


// ========================================================================

void CAreaEditorDoc::load_neighbour(void)
{

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");
	
	m_Area->neighboursList.AddTail (fread_string ());
	
} // end of CAreaEditorDoc::load_neighbour

// ========================================================================

void CAreaEditorDoc::load_ranges (void)
  {

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

    int x1, x2, x3, x4;
    CString strLine;

    while (true)
    {
	  strLine = fread_line ();

	  if (strLine [0] == '$')
	    break;

	  x1=x2=x3=x4=0;
	  sscanf( strLine, "%i %i %i %i", &x1, &x2, &x3, &x4 );

	  m_Area->low_soft_range = x1;
	  m_Area->hi_soft_range = x2;
	  m_Area->low_hard_range = x3;
	  m_Area->hi_hard_range = x4;
    }
    return;

  } // end of CAreaEditorDoc::load_ranges

// ========================================================================

void CAreaEditorDoc::load_resetmsg (void)
  {
  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  m_Area->strResetMsg = fread_string ();
  } // end of CAreaEditorDoc::load_resetmsg

// ========================================================================

void CAreaEditorDoc::load_flags (void)
  {
  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

    int x1, x2;
    CString strLine;
	  strLine = fread_line ();

    x1=x2=0;
    sscanf( strLine, "%i %i", &x1, &x2 );
    m_Area->flags = x1;
    m_Area->reset_frequency = x2;

  } // end of CAreaEditorDoc::load_flags

// ========================================================================

void CAreaEditorDoc::load_economy (void)
  {
  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  m_Area->high_economy = fread_number ();
  m_Area->low_economy = fread_number ();
  } // end of CAreaEditorDoc::load_economy

// ========================================================================

void CAreaEditorDoc::mprog_read_programs (CMUDitem * item, 
                                          CMUDprogramList & programlist)
  {
CMUDprogram * mprg;
  char        letter;
  bool        done = false;
CString strMessage;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

// create first program item and add to mobile list

  mprg = new CMUDprogram (RUNTIME_CLASS(CMUDProgramView), this);
  programlist.AddTail (mprg);

    while ( !done )
  {

    CString strProgramType = fread_word();

    if (!ProgramNameList.FindValue (strProgramType, mprg->type, false))
        ThrowErrorException ("Unknown program type %s in MOB program",
                              (LPCTSTR) strProgramType);

    if (mprg->type == 0)    // IN_FILE_PROG
       ThrowErrorException("Have not implemented IN_FILE_PROG just now");

	  mprg->arglist        = fread_string();
	  fread_to_eol();
	  mprg->comlist        = fread_string(false);
	  fread_to_eol();

    if (CheckCommandSyntax (mprg->comlist, 
                            strMessage,
                            mprg->xref_rooms,
                            mprg->xref_objects,
                            mprg->xref_mobs))
       LoadWarning ("%s: %s",
                    (LPCTSTR) item->Summary (),
                    (LPCTSTR) strMessage);

	  switch ( letter = fread_letter() )
	  {
	    case '>':   // add new program to list
          mprg = new CMUDprogram (RUNTIME_CLASS(CMUDProgramView), this);
          programlist.AddTail (mprg);
	     break;
	    case '|':
	       fread_to_eol();
	       done = true;
	     break;
	    default:
        ThrowErrorException ("MOB program must be followed by \">\" or \"|\"");
	     break;
	  }
  }

  } // end of AreaEditorDoc::mprog_read_programs 

// ========================================================================

void CAreaEditorDoc::load_SMAUG_mobiles (void)
  {
  long vnum;
  CMobile * pMobIndex;
  char letter;
  CString ln;
  int x1, x2, x3, x4, x5, x6, x7;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  letter = fread_letter (); // priming read - end of loop reads another one

  while (true)
    {
    // mob must start with #<mob number>

    if (letter != '#')
        ThrowErrorException ("MOB details must start with a \"#\"");

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of mobiles list

    pMobIndex = new CMobile (RUNTIME_CLASS(CMobileView), this);    // allocate a new mob class thingummy
    m_MobList.AddTail (pMobIndex);    // and add it to our list

  	pMobIndex->vnum			= vnum;

    // update vnum ranges
    if (vnum > m_Area->mob_hi_vnum)
      m_Area->mob_hi_vnum = vnum;
    if (vnum < m_Area->mob_low_vnum)
      m_Area->mob_low_vnum = vnum;

	  pMobIndex->player_name		= fread_string();
	  pMobIndex->short_descr		= fread_string();
	  pMobIndex->long_descr		= fread_string();

    // remove trailing ENDLINE from long description
    if (pMobIndex->long_descr.Right (strlen (ENDLINE)) == ENDLINE)
      pMobIndex->long_descr = pMobIndex->long_descr.Left 
          (pMobIndex->long_descr.GetLength () - 2);

    // long description shouldn't exceed iMaxDescriptionLength characters
    if (pMobIndex->long_descr.GetLength () > iMaxDescriptionLength)
      LoadWarning ("Mob %i description \"%s\" more than %i characters",
                  vnum, 
                  (LPCTSTR) pMobIndex->long_descr,
                  iMaxDescriptionLength);

	  pMobIndex->description		= fread_string(false);

    if (!pMobIndex->long_descr.IsEmpty ())
	    pMobIndex->long_descr.SetAt (0, UPPER(pMobIndex->long_descr[0]));
    if (!pMobIndex->description.IsEmpty ())
  	  pMobIndex->description.SetAt (0, UPPER(pMobIndex->description[0]));

	  pMobIndex->act			= fread_bitvector();  
	  pMobIndex->affected_by		= fread_bitvector();  
	  pMobIndex->alignment		= fread_number();

	  letter				= fread_letter();

	  pMobIndex->level		= fread_number();

	  pMobIndex->mobthac0		= fread_number();
	  pMobIndex->ac			= fread_number();
	  pMobIndex->hitnodice		= fread_number();
	  /* 'd'		*/		  fread_letter();
	  pMobIndex->hitsizedice		= fread_number();
	  /* '+'		*/		  fread_letter();
	  pMobIndex->hitplus		= fread_number();
	  pMobIndex->damnodice		= fread_number();
	  /* 'd'		*/		  fread_letter();
	  pMobIndex->damsizedice		= fread_number();
	  /* '+'		*/		  fread_letter();
	  pMobIndex->damplus		= fread_number();
	  pMobIndex->gold			= fread_number();
	  pMobIndex->exp			= fread_number();
	  pMobIndex->position		= fread_number();
	  pMobIndex->defposition		= fread_number();

	  /*
	   * Back to meaningful values.
	   */
	  pMobIndex->sex			= fread_number();

	  if ( letter != 'S' && letter != 'C' )
      ThrowErrorException ("MOB type must be \"S\" or \"C\"");


	  if ( letter == 'C' ) /* Realms complex mob 	-Thoric */
	    {
	      pMobIndex->perm_str			= fread_number();
	      pMobIndex->perm_int			= fread_number();
	      pMobIndex->perm_wis			= fread_number();
	      pMobIndex->perm_dex			= fread_number();
	      pMobIndex->perm_con			= fread_number();
	      pMobIndex->perm_cha			= fread_number();
	      pMobIndex->perm_lck			= fread_number();
 	      pMobIndex->saving_poison_death	= fread_number();
	      pMobIndex->saving_wand		= fread_number();
	      pMobIndex->saving_para_petri	= fread_number();
	      pMobIndex->saving_breath		= fread_number();
	      pMobIndex->saving_spell_staff	= fread_number();
	      ln = fread_line();
	      x1=x2=x3=x4=x5=x6=x7=0;
	      sscanf( ln, "%i %i %i %i %i %i %i", &x1, &x2, &x3, &x4, &x5, &x6, &x7 );
	      pMobIndex->race		= x1;
	      pMobIndex->mobclass		= x2;
	      pMobIndex->height		= x3;
	      pMobIndex->weight		= x4;
	      pMobIndex->speaks		= x5;
	      pMobIndex->speaking		= x6;
	      pMobIndex->numattacks	= x7;
	      pMobIndex->hitroll		= fread_number();
	      pMobIndex->damroll		= fread_number();
	      pMobIndex->xflags		= fread_number();
	      pMobIndex->resistant	= fread_number();
	      pMobIndex->immune		= fread_number();
	      pMobIndex->susceptible	= fread_number();
	      pMobIndex->attacks		= fread_bitvector();
	      pMobIndex->defenses		= fread_bitvector();
	    }   // end of Realms complex mob
	  else
	    {   // not a Realms complex mob
	      pMobIndex->perm_str		= 13;
	      pMobIndex->perm_dex		= 13;
	      pMobIndex->perm_int		= 13;
	      pMobIndex->perm_wis		= 13;
	      pMobIndex->perm_cha		= 13;
	      pMobIndex->perm_con		= 13;
	      pMobIndex->perm_lck		= 13;
	      pMobIndex->race		= 0;
	      pMobIndex->mobclass		= 3;
	      pMobIndex->xflags		= 0;
	      pMobIndex->resistant	= 0;
	      pMobIndex->immune		= 0;
	      pMobIndex->susceptible	= 0;
	      pMobIndex->numattacks	= 0;
	      pMobIndex->attacks = 0;
	      pMobIndex->defenses = 0;
	      pMobIndex->hitroll		= 0;
	      pMobIndex->damroll		= 0;
        pMobIndex->saving_poison_death = 0;
        pMobIndex->saving_wand		= 0;
        pMobIndex->saving_para_petri= 0;
        pMobIndex->saving_breath		= 0;
        pMobIndex->saving_spell_staff	= 0;
	      pMobIndex->height		= 0;
	      pMobIndex->weight		= 0;
	      pMobIndex->speaks		= 0;
	      pMobIndex->speaking		= 0;

	    }   // end of not a Realms complex mob

    letter = fread_letter();
	  if ( letter == '>' )    // mob program?
      {
	     mprog_read_programs(pMobIndex, pMobIndex->programlist);
  	   letter = fread_letter(); // re-prime read for next time around loop
      }   // end of having a mob program
    
    } // end of processing each mob
  
  } // end of CAreaEditorDoc::load_SMAUG_mobiles

  // ========================================================================

  /*
  OK guys, ROM is so different from SMAUG I thought it would be less
  confusing to do its own section.
  */

void CAreaEditorDoc::load_ROM_mobiles (void)
  {
  long vnum;
  CMobile * pMobIndex;
  char letter;
  CString word;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  letter = fread_letter (); // priming read - end of loop reads another one

  while (true)
    {
    // mob must start with #<mob number>

    if (letter != '#')
        ThrowErrorException ("MOB details must start with a \"#\"");

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of mobiles list

    pMobIndex = new CMobile (RUNTIME_CLASS(CMobileView), this);    // allocate a new mob class thingummy
    m_MobList.AddTail (pMobIndex);    // and add it to our list

  	pMobIndex->vnum			= vnum;

    // update vnum ranges
    if (vnum > m_Area->mob_hi_vnum)
      m_Area->mob_hi_vnum = vnum;
    if (vnum < m_Area->mob_low_vnum)
      m_Area->mob_low_vnum = vnum;

	  pMobIndex->player_name		= fread_string();
	  pMobIndex->short_descr		= fread_string();
	  pMobIndex->long_descr		= fread_string();

    // remove trailing ENDLINE from long description
    if (pMobIndex->long_descr.Right (strlen (ENDLINE)) == ENDLINE)
      pMobIndex->long_descr = pMobIndex->long_descr.Left 
          (pMobIndex->long_descr.GetLength () - 2);

    // long description shouldn't exceed iMaxDescriptionLength characters
    if (pMobIndex->long_descr.GetLength () > iMaxDescriptionLength)
      LoadWarning ("Mob %i description \"%s\" more than %i characters",
                  vnum, 
                  (LPCTSTR) pMobIndex->long_descr,
                  iMaxDescriptionLength);

	  pMobIndex->description		= fread_string(false);

    if (!pMobIndex->long_descr.IsEmpty ())
	    pMobIndex->long_descr.SetAt (0, UPPER(pMobIndex->long_descr[0]));
    if (!pMobIndex->description.IsEmpty ())
  	  pMobIndex->description.SetAt (0, UPPER(pMobIndex->description[0]));

    // ROM race is alpha - lookup race table to convert it

    /* sex */
    word = fread_string();
    if (!MobRaceList.FindValue (word, pMobIndex->race, true))
      LoadWarning ("Mob %i race \"%s\" not in race table",
                  vnum, (LPCTSTR) word);

	  pMobIndex->act			= fread_bitvector(true);   // ROM flag
	  pMobIndex->affected_by		= fread_bitvector(true);   // ROM flag
	  pMobIndex->alignment		= fread_number();
    pMobIndex->group		= fread_number();
	  pMobIndex->level		= fread_number();

    pMobIndex->hitroll  = fread_number ();  

	  /* read hit dice */
    pMobIndex->hitnodice = fread_number();  
    /* 'd'          */                fread_letter(); 
    pMobIndex->hitsizedice 	= fread_number();
    /* '+'          */                fread_letter();   
    pMobIndex->hitplus  = fread_number(); 

 	  /* read mana dice */
	  pMobIndex->mananodice	= fread_number();
					    fread_letter();
	  pMobIndex->manasizedice	= fread_number();
					    fread_letter();
	  pMobIndex->manaplus= fread_number();

	  /* read damage dice */
	  pMobIndex->damnodice	= fread_number();
					    fread_letter();
	  pMobIndex->damsizedice	= fread_number();
					    fread_letter();
	  pMobIndex->damplus	= fread_number();


    /* damage type */
    word = fread_word();
    if (!ObjectAttackList.FindValue (word, pMobIndex->dam_type, true))
      LoadWarning ("Mob %i damage type \"%s\" not in attack table",
                  vnum, (LPCTSTR) word);

	  /* read armor class */
	  pMobIndex->ac_pierce	= fread_number() * 10;
	  pMobIndex->ac_bash		= fread_number() * 10;
	  pMobIndex->ac_slash		= fread_number() * 10;
	  pMobIndex->ac_exotic	= fread_number() * 10;

	  /* read flags and add in data from the race table */
	  pMobIndex->attacks		  = fread_bitvector(true);
	  pMobIndex->immune		    = fread_number(true);
	  pMobIndex->resistant		= fread_number(true);
	  pMobIndex->susceptible	= fread_number(true);

	  /* vital statistics */


    /* position */
    word = fread_word();
    if (!MobPositionList.FindValue (word, pMobIndex->position, true))
      LoadWarning ("Mob %i position \"%s\" not in position table",
                  vnum, (LPCTSTR) word);


    /* position */
    word = fread_word();
    if (!MobPositionList.FindValue (word, pMobIndex->defposition, true))
      LoadWarning ("Mob %i default position \"%s\" not in position table",
                  vnum, (LPCTSTR) word);

    /* sex */
    word = fread_word();
    if (!MobSexList.FindValue (word, pMobIndex->sex, true))
      LoadWarning ("Mob %i sex type \"%s\" not in sex table",
                  vnum, (LPCTSTR) word);

	  pMobIndex->gold		= fread_number();

	  pMobIndex->form			= fread_number(true);  
	  pMobIndex->xflags		= fread_number(true);

    /* size */
    word = fread_word();
    if (!MobSizeList.FindValue (word, pMobIndex->size, true))
      LoadWarning ("Mob %i size type \"%s\" not in size table",
                  vnum, (LPCTSTR) word);

	  pMobIndex->material		= fread_word();

    while (true)
      {
  	  letter = fread_letter();
	
      if ( letter == '>' )    // mob program?
	       mprog_read_programs(pMobIndex, pMobIndex->programlist);
      else
        if (letter == 'F')
          {
          CString strFlagName = fread_word ();
          long vector = fread_number (true);  // get flag

		      if (!str_prefix(strFlagName,"act"))
		          pMobIndex->remove_act |= vector;
          else if (!str_prefix(strFlagName,"aff"))
		          pMobIndex->remove_aff |= vector;
		      else if (!str_prefix(strFlagName,"off"))
		          pMobIndex->remove_off |= vector;
		      else if (!str_prefix(strFlagName,"imm"))
		          pMobIndex->remove_imm |= vector;
		      else if (!str_prefix(strFlagName,"res"))
		          pMobIndex->remove_res |= vector;
		      else if (!str_prefix(strFlagName,"vul"))
		          pMobIndex->remove_vul |= vector;
		      else if (!str_prefix(strFlagName,"for"))
		          pMobIndex->remove_for |= vector;
		      else if (!str_prefix(strFlagName,"par"))
		          pMobIndex->remove_par |= vector;
		      else
            ThrowErrorException ("Flag \"%s\" not found.", 
                (LPCTSTR) strFlagName);
         }
        else
          break;    // not a flag
    
      } // end of looking for flags and things

    } // end of processing each mob
  
  } // end of CAreaEditorDoc::load_ROM_mobiles

// ========================================================================

void CAreaEditorDoc::load_mobiles (void)
  {
  if (ROM)
    load_ROM_mobiles ();
  else if (SMAUG)
    load_SMAUG_mobiles ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  } // end of CAreaEditorDoc::load_mobiles

// ========================================================================

int CAreaEditorDoc::SMAUG_skill_lookup (const char *name)
    {

    if (App.m_SkillList.IsEmpty ())
      ThrowErrorException ("Skills table not loaded - cannot look up skill name");

    for (POSITION skillPos = App.m_SkillList.GetHeadPosition (); skillPos; )
      {
      CSkill * skill = App.m_SkillList.GetNext (skillPos);
			if ( LOWER(name[0]) == LOWER(skill->name[0])
			    &&  !str_prefix( name, skill->name ) )
          return skill->slot;
      }

    return -1;

    }   // end of CAreaEditorDoc::SMAUG_skill_lookup

// ========================================================================

CString CAreaEditorDoc::SMAUG_skill_name (const int iValue,
                                          const bool bNone)
  {
  CSkill * skill;

  if (iValue == -1)
    return bNone ? "NONE" : "";

  for (POSITION skillPos = App.m_SkillList.GetHeadPosition (); skillPos; )
    {
    skill =  App.m_SkillList.GetNext (skillPos);
    if (skill->slot == iValue)
      return skill->name;
    }

  return bNone ? "NONE" : "";

  } // end of SMAUG_skill_name

// ========================================================================

int CAreaEditorDoc::ROM_skill_lookup (const char *name)
    {
int iValue;

    if (ROMSkillNameList.FindValue (name, iValue, true))
      return iValue;

    return -1;

    }   // end of CAreaEditorDoc::ROM_skill_lookup

// ========================================================================

CString CAreaEditorDoc::ROM_skill_name (const int iValue,
                                        const bool bNone)
  {
  CString strName;

  if (iValue == -1)
    return bNone ? "NONE" : "";

  if (ROMSkillNameList.FindName (iValue, strName))
    return strName;

  return bNone ? "NONE" : "";

  } // end of ROM_skill_name

// ========================================================================

CString CAreaEditorDoc::skill_name (const int iValue,
                                    const bool bNone)
  {

  if (ROM)
    return ROM_skill_name (iValue, bNone);
  else if (SMAUG)
    return SMAUG_skill_name (iValue, bNone);
  else
    return bNone ? "NONE" : "";

  } // end of skill_name

// ========================================================================

  void CAreaEditorDoc::load_SMAUG_objects (void)
  {
  long vnum;
  CMUDObject * pObjIndex;
  char letter;
  CString ln;
  int x1, x2, x3, x4, x5, x6;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  letter = fread_letter (); // priming read - end of loop reads another one

  while (true)
    {
    // object must start with #<object number>

    if (letter != '#')
        ThrowErrorException ("Object details must start with a \"#\"");

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of objects list

    pObjIndex = new CMUDObject (RUNTIME_CLASS(CObjectView), this);    // allocate a new mob class thingummy
    m_ObjectList.AddTail (pObjIndex);    // and add it to our list

  	pObjIndex->vnum			= vnum;

    // update vnum ranges
    if (vnum > m_Area->obj_hi_vnum)
      m_Area->obj_hi_vnum = vnum;
    if (vnum < m_Area->obj_low_vnum)
      m_Area->obj_low_vnum = vnum;

	  pObjIndex->name		= fread_string();
	  pObjIndex->short_descr		= fread_string();
	  pObjIndex->description		= fread_string();
	  pObjIndex->action_desc		= fread_string(false);

    if (!pObjIndex->description.IsEmpty ())
  	  pObjIndex->description.SetAt (0, UPPER(pObjIndex->description[0]));


	  pObjIndex->item_type		= fread_number ();
	  pObjIndex->extra_flags		= fread_bitvector();
	  pObjIndex->wear_flags		= fread_bitvector();

    if (!m_FileRead.LineEmpty ())
       pObjIndex->layers = fread_number ();
    else
       pObjIndex->layers = 0;

#if 0
	  ln = fread_line ();
	  x1=0;
	  sscanf( ln, "%i", &x1 );
	  pObjIndex->layers		= x1;
#endif

	  ln = fread_line ();
	  x1=x2=x3=x4=x5=x6=0;
	  sscanf( ln, "%i %i %i %i %i %i",
		  &x1, &x2, &x3, &x4, &x5, &x6 );
	  pObjIndex->value[0]		= x1;
	  pObjIndex->value[1]		= x2;
	  pObjIndex->value[2]		= x3;
	  pObjIndex->value[3]		= x4;
	  pObjIndex->value[4]		= x5;
	  pObjIndex->value[5]		= x6;
	  pObjIndex->weight		= fread_number ();
	  pObjIndex->weight = UMAX( 1, pObjIndex->weight );
	  pObjIndex->cost			= fread_number ();
	  pObjIndex->rent		  	= fread_number (); /* unused */

    if (m_Area->area_version == 1 )
      {
	    switch ( pObjIndex->item_type )
	    {
	    case ITEM_PILL:
	    case ITEM_POTION:
	    case ITEM_SCROLL:
	        pObjIndex->value[1] = SMAUG_skill_lookup ( fread_word ()) ;
	        pObjIndex->value[2] = SMAUG_skill_lookup ( fread_word ()) ;
	        pObjIndex->value[3] = SMAUG_skill_lookup ( fread_word ()) ;
	      break;
	    case ITEM_STAFF:
	    case ITEM_WAND:
	        pObjIndex->value[3] = SMAUG_skill_lookup ( fread_word ()) ;
	      break;
	    case ITEM_SALVE:
	        pObjIndex->value[4] = SMAUG_skill_lookup ( fread_word ()) ;
	        pObjIndex->value[5] = SMAUG_skill_lookup ( fread_word ()) ;
	      break;
	    }   // end of switch on object type
    }   // end of area version not being zero
  
    // look for programs, extras, affects

	  while (true)
	    {
	    letter = fread_letter();

	    if ( letter == 'A' )    // affects
	      {
		    CAffect *paf = new CAffect (RUNTIME_CLASS(CObjectAffectView), this);
        pObjIndex->affectlist.AddTail (paf);
		    paf->location		= fread_number ();
        /*
		    if ( paf->location == APPLY_WEAPONSPELL
		    ||   paf->location == APPLY_WEARSPELL
		    ||   paf->location == APPLY_REMOVESPELL
		    ||   paf->location == APPLY_STRIPSN )
          {
          CSkill * skill = NULL;
          int i = 0;
          int slot =  fread_number ();
          for (skillPos = App.m_SkillList.GetHeadPosition (); skillPos; i++)
            {
            CSkill * skill = App.m_SkillList.GetNext (skillPos);
            if (skill->slot == slot)
              break;
            }
          if (skill)
		        paf->modifier		= i;
          else
            ThrowErrorException ("skill not in skill table");
          }
		    else
        */
		      paf->modifier		= fread_number ();
	      }

      else if ( letter == 'E' )   // extra description
	      {
		    CExtraDescription *ed = new CExtraDescription (RUNTIME_CLASS(CObjectExtraDescriptionView), this);
        pObjIndex->extralist.AddTail (ed);

		    ed->keyword		= fread_string ();
		    ed->description		= fread_string (false);
	      }

      else if ( letter == '>' )   // programs
	      {
	       mprog_read_programs(pObjIndex, pObjIndex->programlist);
	      }   // end of reading programs

      else
  		  break;
	  }   // end of loooking for programs, extras, affects
    
    } // end of processing each object
  
  } // end of CAreaEditorDoc::load_SMAUG_objects

  // ========================================================================

  void CAreaEditorDoc::load_ROM_objects (void)
  {
  long vnum;
  CMUDObject * pObjIndex;
  char letter;
  CString word;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  letter = fread_letter (); // priming read - end of loop reads another one

  while (true)
    {
    // object must start with #<object number>

    if (letter != '#')
        ThrowErrorException ("Object details must start with a \"#\"");

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of objects list

    pObjIndex = new CMUDObject (RUNTIME_CLASS(CObjectView), this);    // allocate a new mob class thingummy
    m_ObjectList.AddTail (pObjIndex);    // and add it to our list

  	pObjIndex->vnum			= vnum;

    // update vnum ranges
    if (vnum > m_Area->obj_hi_vnum)
      m_Area->obj_hi_vnum = vnum;
    if (vnum < m_Area->obj_low_vnum)
      m_Area->obj_low_vnum = vnum;

	  pObjIndex->name		= fread_string();
	  pObjIndex->short_descr		= fread_string();
	  pObjIndex->description		= fread_string();

    if (!pObjIndex->description.IsEmpty ())
  	  pObjIndex->description.SetAt (0, UPPER(pObjIndex->description[0]));

    pObjIndex->material		= fread_string();

    // item type
    word = fread_word();
    if (!ObjectItemList.FindValue (word, pObjIndex->item_type, true))
      LoadWarning ("Object %i item type \"%s\" not in item table",
                  vnum, (LPCTSTR) word);

    pObjIndex->extra_flags          = fread_bitvector (true);
    pObjIndex->wear_flags           = fread_number (true);

  CString word;
  CString strAttackType;

	switch(pObjIndex->item_type)
	{
	case ITEM_WEAPON:

      // weapon lookup
      word = fread_word();
      if (!ObjectWeaponList.FindValue (word, pObjIndex->value[0], true))
        LoadWarning ("Object %i weapon type \"%s\" not in weapon table",
                    vnum, (LPCTSTR) word);

	    pObjIndex->value[1]		= fread_number();
	    pObjIndex->value[2]		= fread_number();

      /* attack lookup */
      word = fread_word();
      if (!ObjectAttackList.FindValue (word, pObjIndex->value[3], true))
        LoadWarning ("Object %i attack type \"%s\" not in attack table",
                    vnum, (LPCTSTR) word);

	    pObjIndex->value[4]		= fread_number (true);
	    break;
	case ITEM_CONTAINER:
	    pObjIndex->value[0]		= fread_number();
	    pObjIndex->value[1]		= fread_number (true);
	    pObjIndex->value[2]		= fread_number();
	    pObjIndex->value[3]		= fread_number();
	    pObjIndex->value[4]		= fread_number();
	    break;
  case ITEM_DRINK_CON:
	case ITEM_FOUNTAIN:
            pObjIndex->value[0]         = fread_number();
            pObjIndex->value[1]         = fread_number();

            // liquid lookup
            word = fread_word();
            if (!ObjectLiquidList.FindValue (word, pObjIndex->value[2], true))
              LoadWarning ("Object %i liquid type \"%s\" not in liquid type table",
                          vnum, (LPCTSTR) word);

            pObjIndex->value[3]         = fread_number();
            pObjIndex->value[4]         = fread_number();
            break;
	case ITEM_WAND:
	case ITEM_STAFF:
	    pObjIndex->value[0]		= fread_number();
	    pObjIndex->value[1]		= fread_number();
	    pObjIndex->value[2]		= fread_number();
	    pObjIndex->value[3]		= ROM_skill_lookup(fread_word());
	    pObjIndex->value[4]		= fread_number();
	    break;
	case ITEM_POTION:
	case ITEM_PILL:
	case ITEM_SCROLL:
 	    pObjIndex->value[0]		= fread_number();
	    pObjIndex->value[1]		= ROM_skill_lookup(fread_word());
	    pObjIndex->value[2]		= ROM_skill_lookup(fread_word());
	    pObjIndex->value[3]		= ROM_skill_lookup(fread_word());
	    pObjIndex->value[4]		= ROM_skill_lookup(fread_word());
	    break;
	default:
      pObjIndex->value[0]    = fread_number (true);
      pObjIndex->value[1]    = fread_number (true);
      pObjIndex->value[2]    = fread_number (true);
      pObjIndex->value[3]    = fread_number (true);
	    pObjIndex->value[4]		 = fread_number (true);
	    break;
	}
	
  pObjIndex->level		= fread_number();
  pObjIndex->weight     = fread_number();
  pObjIndex->cost       = fread_number(); 

  /* condition */
  letter 				= fread_letter();
	switch (letter)
 	{
	    case ('P') :		pObjIndex->condition = 100; break;
	    case ('G') :		pObjIndex->condition =  90; break;
	    case ('A') :		pObjIndex->condition =  75; break;
	    case ('W') :		pObjIndex->condition =  50; break;
	    case ('D') :		pObjIndex->condition =  25; break;
	    case ('B') :		pObjIndex->condition =  10; break;
	    case ('R') :		pObjIndex->condition =   0; break;
	    default:			pObjIndex->condition = 100; break;
	}
 
    // look for programs, extras, affects

	  while (true)
	    {
	    letter = fread_letter();

	    if ( letter == 'A' )    // affects
	      {
		    CAffect *paf = new CAffect (RUNTIME_CLASS(CObjectAffectView), this);
        pObjIndex->affectlist.AddTail (paf);
		    paf->location		= fread_number ();
	      paf->modifier		= fread_number ();
	      } // end of flag A

      else if ( letter == 'F' )   // other sort of affect
        {
		    CAffect *paf = new CAffect (RUNTIME_CLASS(CObjectAffectView), this);
        pObjIndex->affectlist.AddTail (paf);

        letter 			= fread_letter();
        switch (letter)
          {
          case 'A':
            paf->where     = TO_AFFECTS;
            break;
          case 'I':
            paf->where		= TO_IMMUNE;
            break;
          case 'R':
            paf->where		= TO_RESIST;
            break;
          case 'V':
            paf->where		= TO_VULN;
            break;
          default:
            ThrowErrorException( "Load_objects: Bad where '%c' on flag set.", letter );
          }
		    paf->location		= fread_number ();
	      paf->modifier		= fread_bitvector ();
	      paf->bitvector	= fread_bitvector (true);

        } // end of flag F
      else if ( letter == 'E' )   // extra description
	      {
		    CExtraDescription *ed = new CExtraDescription (RUNTIME_CLASS(CObjectExtraDescriptionView), this);
        pObjIndex->extralist.AddTail (ed);

		    ed->keyword		= fread_string ();
		    ed->description		= fread_string (false);
	      }

      else if ( letter == '>' )   // programs
	      {
	       mprog_read_programs(pObjIndex, pObjIndex->programlist);
	      }   // end of reading programs

      else
  		  break;
	  }   // end of loooking for programs, extras, affects
    
    } // end of processing each object
  
  } // end of CAreaEditorDoc::load_ROM_objects

// ========================================================================

void CAreaEditorDoc::load_objects (void)
  {
  if (ROM)
    load_ROM_objects ();
  else if (SMAUG)
    load_SMAUG_objects ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  } // end of CAreaEditorDoc::load_objects


// ========================================================================

  void CAreaEditorDoc::load_SMAUG_rooms (void)
  {
  long vnum;
  CRoom * pRoomIndex;
  char letter;
  CString ln;
  int x1, x2, x3, x4, x5, x6;

  letter = fread_letter (); // priming read - end of loop reads another one

  while (true)
    {
    // room must start with #<object number>

    if (letter != '#')
        ThrowErrorException ("Room details must start with a \"#\"");

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of rooms list

    pRoomIndex = new CRoom (RUNTIME_CLASS(CRoomView), this);    // allocate a new mob class thingummy
    m_RoomList.AddTail (pRoomIndex);    // and add it to our list

  	pRoomIndex->vnum			= vnum;

    // update vnum ranges
    if (vnum > m_Area->room_hi_vnum)
      m_Area->room_hi_vnum = vnum;
    if (vnum < m_Area->room_low_vnum)
      m_Area->room_low_vnum = vnum;

    pRoomIndex->name		= fread_string ();
	  pRoomIndex->description		= fread_string (false);

 
	  ln = fread_line ();
	  x1=x2=x3=x4=x5=x6=0;
	  sscanf( ln, "%i %i %i %i %i %i",
	        &x1, &x2, &x3, &x4, &x5, &x6 );

	  pRoomIndex->room_flags		= x2;
	  pRoomIndex->sector_type		= x3;
	  pRoomIndex->tele_delay		= x4;
	  pRoomIndex->tele_vnum		= x5;
	  pRoomIndex->tunnel		= x6;

    if (!RoomSectorList.FindName (pRoomIndex->sector_type, ln)) 
	    ThrowErrorException( "Fread_rooms: vnum %d has bad sector_type %d.", 
                          vnum ,
	                        pRoomIndex->sector_type);

    // look for programs, extras, doors, maps

	  while (true)
	    {
	    letter = fread_letter();

      if ( letter == 'S' )    // pretty pathetic little flag :)
        {
  	    letter = fread_letter();    // prime for next room
        break;
        }
      else if ( letter == 'D' ) // door (exit)
	      {
		    int door;

		    door = fread_number ();
        CString strName;

		    if (!RoomDirectionList.FindName (door, strName))
		        ThrowErrorException ("Fread_rooms: vnum %d has bad door number %d.", 
                vnum,
		            door );

        CExit *pexit = new CExit (RUNTIME_CLASS(CRoomExitView), this);
        pRoomIndex->exitlist.AddTail (pexit);

		    pexit->description	= fread_string ();
		    pexit->keyword	= fread_string ();
		    pexit->exit_info	= 0;
		    ln = fread_line ();
		    x1=x2=x3=x4=0;
		    sscanf( ln, "%i %i %i %i",
		        &x1, &x2, &x3, &x4 );

		    pexit->exit_info = x1;
		    pexit->key		= x2;
		    pexit->vnum		= x3;
		    pexit->vdir		= door;
		    pexit->distance	= x4;

        // amend exit info slightly
		    switch ( pexit->exit_info )
		    {
		      case 1:  pexit->exit_info = EX_ISDOOR;                break;
		      case 2:  pexit->exit_info = EX_ISDOOR | EX_PICKPROOF; break;
		    }

	      }   // end of type D - door (exit)

      else if ( letter == 'E' ) // extra description
	      {
		    CExtraDescription *ed = new CExtraDescription (RUNTIME_CLASS(CObjectExtraDescriptionView), this);
        pRoomIndex->extralist.AddTail (ed);

		    ed->keyword		= fread_string ();
		    ed->description		= fread_string (false);
	      }   // end of type E - extra description

      else if ( letter == 'M' ) // map
	      {
		    CRoomMap *map = new CRoomMap (RUNTIME_CLASS(CRoomMapView), this);
        pRoomIndex->maplist.AddTail (map);

        map->vnum                     = fread_number ();
        map->x                        = fread_number (); 
        map->y                        = fread_number ();
        map->entry		      = fread_letter (); 

	      }   // end of type M - map

      else if ( letter == '>' )   // programs
	      {
	       mprog_read_programs(pRoomIndex, pRoomIndex->programlist);
	      } // end of reading programs

      else
  		  break;
	  }   // end of loooking for programs, extras, doors, maps
    
    } // end of processing each room
  
  } // end of CAreaEditorDoc::load_SMAUG_rooms

  // ========================================================================

  void CAreaEditorDoc::load_ROM_rooms (void)
  {
  long vnum;
  CRoom * pRoomIndex;
  char letter;
  CString ln;

  letter = fread_letter (); // priming read - end of loop reads another one

  while (true)
    {
    // room must start with #<object number>

    if (letter != '#')
        ThrowErrorException ("Room details must start with a \"#\"");

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of rooms list

    pRoomIndex = new CRoom (RUNTIME_CLASS(CRoomView), this);    // allocate a new mob class thingummy
    m_RoomList.AddTail (pRoomIndex);    // and add it to our list

  	pRoomIndex->vnum			= vnum;

    // update vnum ranges
    if (vnum > m_Area->room_hi_vnum)
      m_Area->room_hi_vnum = vnum;
    if (vnum < m_Area->room_low_vnum)
      m_Area->room_low_vnum = vnum;

    pRoomIndex->name		= fread_string ();
	  pRoomIndex->description		= fread_string (false);

 
	  /* Area number */		  fread_number();
	  pRoomIndex->room_flags		= fread_number(true);
	  pRoomIndex->sector_type		= fread_number();

    // look for programs, extras, doors, maps

	  while (true)
	    {
	    letter = fread_letter();

      if ( letter == 'S' )    // pretty pathetic little flag :)
        {
  	    letter = fread_letter();    // prime for next room
        break;
        }
      else  if ( letter == 'H') /* healing room */
        pRoomIndex->heal_rate = fread_number();
      
      else if ( letter == 'M') /* mana room */
        pRoomIndex->mana_rate = fread_number();
      
      else if ( letter == 'C') /* clan */
        {
        if (!pRoomIndex->clan.IsEmpty ())
          ThrowErrorException ("Load_rooms: duplicate clan fields.");
        pRoomIndex->clan = fread_string();
        }

      else if ( letter == 'D' ) // door (exit)
	      {
		    int door;

		    door = fread_number ();
        CString strName;

		    if (!RoomDirectionList.FindName (door, strName))
		        ThrowErrorException ("Fread_rooms: vnum %d has bad door number %d.", 
                vnum,
		            door );

        CExit *pexit = new CExit (RUNTIME_CLASS(CRoomExitView), this);
        pRoomIndex->exitlist.AddTail (pexit);

		    pexit->description	= fread_string();
		    pexit->keyword		= fread_string();
		    pexit->exit_info = fread_bitvector();
		    pexit->key		= fread_number();
		    pexit->vnum		= fread_number();
		    pexit->vdir		= door;

		    switch ( pexit->exit_info )
		      {
		    case 1: pexit->exit_info = ROM_EX_ISDOOR;                break;
		    case 2: pexit->exit_info = ROM_EX_ISDOOR | ROM_EX_PICKPROOF; break;
		    case 3: pexit->exit_info = ROM_EX_ISDOOR | ROM_EX_NOPASS;    break;
		    case 4: pexit->exit_info = ROM_EX_ISDOOR|ROM_EX_NOPASS|ROM_EX_PICKPROOF;
			    break;
		      }

	      }   // end of type D - door (exit)

      else if ( letter == 'E' ) // extra description
	      {
		    CExtraDescription *ed = new CExtraDescription (RUNTIME_CLASS(CObjectExtraDescriptionView), this);
        pRoomIndex->extralist.AddTail (ed);

		    ed->keyword		= fread_string ();
		    ed->description		= fread_string (false);
	      }   // end of type E - extra description

      else if (letter == 'O')
        {
        if (!pRoomIndex->owner.IsEmpty ())
		        ThrowErrorException ("Load_rooms: duplicate owner.");
        
        pRoomIndex->owner = fread_string();
        }

      else if ( letter == '>' )   // programs
	      {
	       mprog_read_programs(pRoomIndex, pRoomIndex->programlist);
	      } // end of reading programs

      else
  		  break;
	  }   // end of loooking for programs, extras, doors, maps
    
    } // end of processing each room
  
  } // end of CAreaEditorDoc::load_ROM_rooms

  // ========================================================================

  void CAreaEditorDoc::load_rooms (void)
  {

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  if (SMAUG)
    load_SMAUG_rooms ();
  else if (ROM)
    load_ROM_rooms ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  } // end of CAreaEditorDoc::load_rooms

// ========================================================================

  void CAreaEditorDoc::load_resets (void)
  {
  CRoom * pRoomIndex;
  CExit *pexit;
  CReset *reset;
  char letter;
	int extra, arg1, arg2, arg3, arg4;
  bool not01 = FALSE;
  CString strComment;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  while (true)
    {

	  if ( ( letter = fread_letter() ) == 'S' )
	      break;

	  if ( letter == '*' )
	  {
	      fread_to_eol();
	      continue;
	  }

	  extra	= fread_number();
	  arg1	= fread_number();
	  arg2	= fread_number();
	  arg3	= (letter == 'G' || letter == 'R')
		    ? 0 : fread_number();

    // ROM sometimes reads a 4th argument
    if (ROM)
	    arg4	= (letter == 'P' || letter == 'M')
			        ? fread_number() : 0;
    else
      arg4 = 0;

		strComment = fread_to_eol();

    strComment.TrimLeft ();
    strComment.TrimRight ();

    letter = toupper (letter);

	  /*
	   * Validate parameters.
	   * We're calling the index functions for the side effect.
	   */
	  switch ( letter )
	  {
	  default:
        ThrowErrorException( "Load_resets: bad command '%c'.",  letter );
	      return;

	  case 'M':
	      if ( FindMob ( arg1 ) == NULL)
		      LoadWarning( "Load_resets: 'M': mobile %d not in area.",
		       arg1 );
	      if ( FindRoom ( arg3 ) == NULL)
		      LoadWarning( "Load_resets: 'M': room %d not in area.",
		       arg3 );
	      break;

	  case 'O':
	      if ( FindObj(arg1) == NULL)
		      LoadWarning( "Load_resets: '%c': object %d not in area.",
		       letter, arg1 );
	      if ( FindRoom (arg3) == NULL)
		      LoadWarning( "Load_resets: '%c': room %d not in area.",
		       letter, arg3 );
	      break;

	  case 'P':
	      if ( FindObj(arg1) == NULL)
		      LoadWarning( "Load_resets: '%c': object %d not in area.",
		       letter, arg1 );
	      if ( arg3 > 0 )
		  if ( FindObj(arg3) == NULL)
		      LoadWarning( "Load_resets: 'P': destination object %d not in area.",
			   arg3 );
	      else if ( extra > 1 )
	        not01 = TRUE;
	      break;

	  case 'G':
	  case 'E':
	      if ( FindObj(arg1) == NULL)
		      LoadWarning( "Load_resets: '%c': object %d not in area.",
		       letter, arg1 );
	      break;

	  case 'T':
	      break;

	  case 'H':
	      if ( arg1 > 0 )
		      if ( FindObj(arg1) == NULL)
		          LoadWarning( "Load_resets: 'H': object %d not in area.",
			   arg1 );
	      break;

	  case 'D':
	      pRoomIndex = FindRoom ( arg1 );
	      if ( !pRoomIndex )
	      {
		     LoadWarning( "Load_resets: 'D': room %d not in area.",
			     arg1 );
		    break;
	      }

        if (!RoomDirectionList.IsValueInList (arg2))
          {
   
		       LoadWarning( "Load_resets: 'D': exit %d direction is not valid.",
			                   arg2 );
           break;

          }

	      if ((pexit = get_exit(pRoomIndex, arg2)) == NULL)
	      {
		     LoadWarning( "Load_resets: 'D': exit %d does not exist.",
			                 arg2 );
         break;
	      }

	      if (!IS_SET( pexit->exit_info, EX_ISDOOR ) )
	      {
		     LoadWarning( "Load_resets: 'D': exit %d not door.",
			                 arg2 );
         break;
	      }

	      if ( arg3 < 0 || arg3 > 2 )
	      {
		    LoadWarning( "Load_resets: 'D': bad 'locks': %d.",
			   arg3 );
	      }
	      break;

	  case 'R':
	      pRoomIndex = FindRoom ( arg1 );
	      if ( !pRoomIndex)
		  LoadWarning( "Load_resets: 'R': room %d not in area.",
		       arg1 );

	      if ( arg2 < 0 || arg2 > 6 )
	      {
		    LoadWarning( "Load_resets: 'R': bad exit %d.",
			   arg2 );
		  break;
	      }

	      break;
	  }

    reset = new CReset (RUNTIME_CLASS(CResetView), this);
    m_ResetList.AddTail (reset);

    reset->command = letter;
    reset->extra = extra;
    reset->arg1 = arg1;
    reset->arg2 = arg2;
    reset->arg3 = arg3;
    reset->arg4 = arg4;
    reset->strComment = strComment;
    
    } // end of processing each reset
  
  } // end of CAreaEditorDoc::load_resets

  // ========================================================================

  void CAreaEditorDoc::load_shops (void)
  {
  long vnum;

  CShop *pShop;

  CMobile *pMobIndex;
	int iTrade;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  while (true)
    {

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of shops list

    pShop = new CShop (RUNTIME_CLASS(CShopView), this);    // allocate a new mob class thingummy
    m_ShopList.AddTail (pShop);    // and add it to our list

  	pShop->keeper			= vnum;


	  for ( iTrade = 0; iTrade < MAX_TRADE; iTrade++ )
	      pShop->buy_type[iTrade]	= fread_number ();
	  pShop->profit_buy	= fread_number ();
	  pShop->profit_sell	= fread_number ();
	  pShop->open_hour	= fread_number ();
	  pShop->close_hour	= fread_number ();
				    fread_to_eol ();
	  pMobIndex		= FindMob ( pShop->keeper );
    if (!pMobIndex)
		  LoadWarning( "Load_shops: Mobile %i is not defined.", pShop->keeper );

    }   // end of processing each shop

  } // end of CAreaEditorDoc::load_shops

  // ========================================================================

  void CAreaEditorDoc::load_repairs (void)
  {
  long vnum;

  CRepair *rShop;

  CMobile *pMobIndex;
	int iFix;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  while (true)
    {

    vnum = fread_number ();
	  if (vnum == 0)
	      break;    // end of repairs list

    rShop = new CRepair (RUNTIME_CLASS(CRepairView), this);    // allocate a new mob class thingummy
    m_RepairList.AddTail (rShop);    // and add it to our list

  	rShop->keeper			= vnum;


	for ( iFix = 0; iFix < MAX_FIX; iFix++ )
	  rShop->fix_type[iFix] = fread_number ();
	rShop->profit_fix	= fread_number ();
	rShop->shop_type	= fread_number ();
	rShop->open_hour	= fread_number ();
	rShop->close_hour	= fread_number ();
				  fread_to_eol ();
	pMobIndex		= FindMob ( rShop->keeper );
	
  if (!pMobIndex)
		LoadWarning( "Load_repairs: Mobile %i is not defined.", rShop->keeper );

    }   // end of processing each repair

  } // end of CAreaEditorDoc::load_repairs

 // ========================================================================

  void CAreaEditorDoc::load_specials (void)
  {

  int vnum;
  CMobile *pMobIndex;

  if (!m_Area)
    ThrowErrorException ("No #AREA declared");

  while (true)
    {

	  char letter;

	  switch ( letter = fread_letter () )
	  {
	  default:
	      ThrowErrorException( "Load_specials: letter '%c' not *, M, or S.", letter );

	  case 'S':
	      return;

	  case '*':
	      break;

	  case 'M':

        vnum = fread_number  ();
	      pMobIndex		= FindMob	(vnum );

        if (!pMobIndex)
		        ThrowErrorException ("Load_specials: mob %i is not in area.", 
                vnum);
        
        CString strFunction;

        strFunction = fread_word    ();
		    if (!MobSpecialList.FindValue (strFunction, pMobIndex->spec_fun, false))
		        ThrowErrorException ("Load_specials: mob %i special function %s does not exist.", 
                                vnum,
		                            (LPCTSTR) strFunction );

	      break;
	  }

	  fread_to_eol ();
    }   // end of processing each special

  } // end of CAreaEditorDoc::load_specials

  // ========================================================================

  void CAreaEditorDoc::load_helps (void)
  {

  CHelp *pHelp;

  int iLevel;
  CString strKeyword;
  CString strText;

  while (true)
    {

	  iLevel	= fread_number ();
	  strKeyword	= fread_string ();
	  if (strKeyword.Left (1) == "$" )
	      break;

	  strText	= fread_string (false);

    if (strKeyword.IsEmpty ())
      continue;

    pHelp = new CHelp (RUNTIME_CLASS(CHelpView), this);    // allocate a new help item
    m_HelpList.AddTail (pHelp);    // and add it to our list

	  pHelp->level	= iLevel;
	  pHelp->keyword	= strKeyword;
	  pHelp->text	= strText;

    }   // end of processing each help

  } // end of CAreaEditorDoc::load_helps

// ========================================================================

void CAreaEditorDoc::fwrite_string (const char * s)
  {
  m_FileWrite.fwrite_string (s);
  } // end of CAreaEditorDoc::fwrite_string

// ========================================================================

void CAreaEditorDoc::save_SMAUG_area (void)
  {
  fwrite_string (CFormat ("#AREA %s~", (LPCTSTR) m_Area->strAreaName));
  fwrite_string (""); // blank line

  // version number and climate only apply to version 1 onwards
  if (m_Area->area_version)
    {
    // version number
    fwrite_string (CFormat ("#VERSION %i", m_Area->area_version));
    fwrite_string (""); // blank line

    // climate details
    fwrite_string (CFormat ("#CLIMATE %i %i %i", 
                  m_Area->climate_temp,  
                  m_Area->climate_precip,
                  m_Area->climate_wind   
                  ));
    fwrite_string (""); // blank line

    // neighbours
    for (POSITION pos = m_Area->neighboursList.GetHeadPosition (); pos; )
      {
      CString strNeighbour = m_Area->neighboursList.GetNext (pos);
      fwrite_string (CFormat ("#NEIGHBOR %s~", (LPCTSTR) strNeighbour));
      } // end of writing out neighhours
    if (!m_Area->neighboursList.IsEmpty ())
      fwrite_string (""); // blank line

    }   // end of not version zero
  
  // author
  fwrite_string (CFormat ("#AUTHOR %s~", (LPCTSTR) m_Area->strAuthor));
  fwrite_string (""); // blank line

  // ranges
  fwrite_string ("#RANGES");
  fwrite_string (CFormat ("%i %i %i %i", 	  
                          m_Area->low_soft_range,
	                        m_Area->hi_soft_range,
	                        m_Area->low_hard_range, 
	                        m_Area->hi_hard_range));
  fwrite_string ("$"); // end of ranges
  fwrite_string (""); // blank line

  // reset msg
  fwrite_string (CFormat ("#RESETMSG %s~", (LPCTSTR) m_Area->strResetMsg));
  fwrite_string (""); // blank line

  // flags
  fwrite_string ("#FLAGS");
  fwrite_string (CFormat ("%s %i", 	  
                          (LPCTSTR) print_bitvector (m_Area->flags),
	                        m_Area->reset_frequency));
  fwrite_string (""); // blank line

  // economy
  fwrite_string (CFormat ("#ECONOMY %i %i", 	  
                          m_Area->high_economy,
	                        m_Area->low_economy));

  } // end of CAreaEditorDoc::save_SMAUG_area

// ========================================================================

void CAreaEditorDoc::save_ROM_area (void)
  {
  fwrite_string ("#AREA");

  // for SMAUG to ROM conversion ...
  // if filename is empty - use document title which should be file name anyway
  if (m_Area->strFileName.IsEmpty ())
    m_Area->strFileName = GetTitle ();

  m_Area->max_vnum = 0;
  m_Area->min_vnum = INT_MAX;

  // update vnum ranges
  if (m_Area->max_vnum < m_Area->mob_hi_vnum)
    m_Area->max_vnum = m_Area->mob_hi_vnum;
  if (m_Area->min_vnum > m_Area->mob_low_vnum)
    m_Area->min_vnum = m_Area->mob_low_vnum;

  if (m_Area->max_vnum < m_Area->obj_hi_vnum)
    m_Area->max_vnum = m_Area->obj_hi_vnum;
  if (m_Area->min_vnum > m_Area->obj_low_vnum)
    m_Area->min_vnum = m_Area->obj_low_vnum;

  if (m_Area->max_vnum < m_Area->room_hi_vnum)
    m_Area->max_vnum = m_Area->room_hi_vnum;
  if (m_Area->min_vnum > m_Area->room_low_vnum)
    m_Area->min_vnum = m_Area->room_low_vnum;

  fwrite_string (CFormat ("%s~", (LPCTSTR) m_Area->strFileName));
  fwrite_string (CFormat ("%s~", (LPCTSTR) m_Area->strAreaName));
  fwrite_string (CFormat ("%s~", (LPCTSTR) m_Area->strAuthor));
  fwrite_string (CFormat ("%i %i", m_Area->min_vnum, m_Area->max_vnum));
  } // end of CAreaEditorDoc::save_ROM_area


// ========================================================================

void CAreaEditorDoc::save_area (void)
  {

  if (SMAUG)
    save_SMAUG_area ();
  else if (ROM)
    save_ROM_area ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);
  
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_area


// ========================================================================

void CAreaEditorDoc::save_programs (CMUDprogramList & programlist)
    {

  if (programlist.IsEmpty ())
    return;   // no programs, just exit

POSITION progPos;
CMUDprogram * prog;

  // save all programs

  for (progPos = programlist.GetHeadPosition (); progPos; )
    {
    prog = programlist.GetNext (progPos);

    fwrite_string (CFormat ("> %s %s~",
                            (LPCTSTR) ProgramNameList.ReturnName (prog->type, false),
                            (LPCTSTR) prog->arglist));

   fwrite_string (CFormat ("%s~", (LPCTSTR) prog->comlist));

    }   // end of each program

  fwrite_string ("|");    // program terminator

  }   // end of CAreaEditorDoc::save_programs

// ========================================================================

void CAreaEditorDoc::save_SMAUG_mobiles (void)
  {

POSITION mobPos;
CMobile * mob;

// cycle through all mobiles

for (mobPos = m_MobList.GetHeadPosition (); mobPos; )
  {
  mob = m_MobList.GetNext (mobPos);

  DoSaveMilestone ();

  fwrite_string (CFormat ("#%i", mob->vnum));
  fwrite_string (CFormat ("%s~", (LPCTSTR) mob->player_name));
  fwrite_string (CFormat ("%s~", (LPCTSTR) mob->short_descr));
  fwrite_string (CFormat ("%s" ENDLINE "~", (LPCTSTR) mob->long_descr));
  fwrite_string (CFormat ("%s~", (LPCTSTR) mob->description));
  fwrite_string (CFormat ("%s %s %i C", (LPCTSTR) print_bitvector (mob->act),
                                        (LPCTSTR) print_bitvector (mob->affected_by),
                                        mob->alignment));

  fwrite_string (CFormat ("%i %i %i %id%i+%i %id%i+%i", 
                mob->level,
                mob->mobthac0,
                mob->ac,
                mob->hitnodice,
                mob->hitsizedice,
                mob->hitplus,
                mob->damnodice,
                mob->damsizedice,
                mob->damplus));

  fwrite_string (CFormat ("%i %i", mob->gold,
                                   mob->exp));

  fwrite_string (CFormat ("%i %i %i", mob->position,
                                      mob->defposition,
                                      mob->sex));


  fwrite_string (CFormat ("%i %i %i %i %i %i %i", 
                mob->perm_str,
                mob->perm_int,
                mob->perm_wis,
                mob->perm_dex,
                mob->perm_con,
                mob->perm_cha,
                mob->perm_lck));

  fwrite_string (CFormat ("%i %i %i %i %i", 
                mob->saving_poison_death,
                mob->saving_wand,
                mob->saving_para_petri,
                mob->saving_breath,
                mob->saving_spell_staff));

  fwrite_string (CFormat ("%i %i %i %i %s %s %i", 
                mob->race,
                mob->mobclass,
                mob->height,
                mob->weight,
                (LPCTSTR) print_bitvector (mob->speaks),
                (LPCTSTR) print_bitvector (mob->speaking),
                mob->numattacks));

  fwrite_string (CFormat ("%i %i %s %s %s %s %s %s", 
                mob->hitroll,
                mob->damroll,
                (LPCTSTR) print_bitvector (mob->xflags),
                (LPCTSTR) print_bitvector (mob->resistant),
                (LPCTSTR) print_bitvector (mob->immune),
                (LPCTSTR) print_bitvector (mob->susceptible),
                (LPCTSTR) print_bitvector (mob->attacks),
                (LPCTSTR) print_bitvector (mob->defenses)));

  // now do all programs inside mobile

  save_programs (mob->programlist);

  } // end of each mobile


  } // end of CAreaEditorDoc::save_SMAUG_mobiles

// ========================================================================

void CAreaEditorDoc::save_ROM_mobiles (void)
  {

POSITION mobPos;
CMobile * mob;

// cycle through all mobiles

for (mobPos = m_MobList.GetHeadPosition (); mobPos; )
  {
  mob = m_MobList.GetNext (mobPos);

  DoSaveMilestone ();

  fwrite_string (CFormat ("#%i", mob->vnum));
  fwrite_string (CFormat ("%s~", (LPCTSTR) mob->player_name));
  fwrite_string (CFormat ("%s~", (LPCTSTR) mob->short_descr));
  fwrite_string (CFormat ("%s" ENDLINE "~", (LPCTSTR) mob->long_descr));
  fwrite_string (CFormat ("%s~", (LPCTSTR) mob->description));

  if (!MobRaceList.IsValueInList (mob->race))
    Warning (mob, 
            "Race %i not in race list, defaulting to first one",
            mob->race);

  fwrite_string (CFormat ("%s~", (LPCTSTR) MobRaceList.ReturnName (mob->race)));

  fwrite_string (CFormat ("%s %s %i %i", (LPCTSTR) ConvertROMFlag (mob->act),
                                         (LPCTSTR) ConvertROMFlag (mob->affected_by),
                                         mob->alignment,
                                         mob->group));

  if (!ObjectAttackList.IsValueInList (mob->dam_type))
    Warning (mob, 
            "Attack type %i not in attack list, defaulting to first one",
            mob->dam_type);

  fwrite_string (CFormat ("%i %i %id%i+%i %id%i+%i %id%i+%i %s", 
                mob->level,
                mob->hitroll,
                mob->hitnodice,
                mob->hitsizedice,
                mob->hitplus,
                mob->mananodice,
                mob->manasizedice,
                mob->manaplus,
                mob->damnodice,
                mob->damsizedice,
                mob->damplus,
                (LPCTSTR) ObjectAttackList.ReturnName (mob->dam_type)));

  fwrite_string (CFormat ("%i %i %i %i", mob->ac_pierce / 10,
                                         mob->ac_bash / 10,
                                         mob->ac_slash / 10,
                                         mob->ac_exotic / 10));


  fwrite_string (CFormat ("%s %s %s %s", (LPCTSTR) ConvertROMFlag (mob->attacks),
                                         (LPCTSTR) ConvertROMFlag (mob->immune),
                                         (LPCTSTR) ConvertROMFlag (mob->resistant),
                                         (LPCTSTR) ConvertROMFlag (mob->susceptible)));

  if (!MobPositionList.IsValueInList (mob->position))
    Warning (mob, 
            "Position %i not in position list, defaulting to first one",
            mob->position);

  if (!MobPositionList.IsValueInList (mob->defposition))
    Warning (mob, 
            "Default position %i not in position list, defaulting to first one",
            mob->defposition);

  if (!MobSexList.IsValueInList (mob->sex))
    Warning (mob, 
            "Sex %i not in sex list, defaulting to first one",
            mob->sex);

  fwrite_string (CFormat ("%s %s %s %i", 
                          (LPCTSTR) MobPositionList.ReturnName (mob->position),
                          (LPCTSTR) MobPositionList.ReturnName (mob->defposition),
                          (LPCTSTR) MobSexList.ReturnName (mob->sex),
                          mob->gold));

  // for SMAUG to ROM conversion - material cannot be empty
  if (mob->material.IsEmpty ())
    mob->material = "0";

  if (!MobSizeList.IsValueInList (mob->size))
    Warning (mob, 
            "Size %i not in size list, defaulting to first one",
            mob->size);

  fwrite_string (CFormat ("%s %s %s %s", (LPCTSTR) ConvertROMFlag (mob->form),
                                         (LPCTSTR) ConvertROMFlag (mob->xflags),
                                         (LPCTSTR) MobSizeList.ReturnName (mob->size),
                                         (LPCTSTR) mob->material));

  // save "remove" flags

  if (mob->remove_act)
    fwrite_string (CFormat ("F act %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_act)));
  if (mob->remove_aff)
    fwrite_string (CFormat ("F aff %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_aff)));
  if (mob->remove_off)
    fwrite_string (CFormat ("F off %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_off)));
  if (mob->remove_imm)
    fwrite_string (CFormat ("F imm %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_imm)));
  if (mob->remove_res)
    fwrite_string (CFormat ("F res %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_res)));
  if (mob->remove_vul)
    fwrite_string (CFormat ("F vul %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_vul)));
  if (mob->remove_for)
    fwrite_string (CFormat ("F for %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_for)));
  if (mob->remove_par)
    fwrite_string (CFormat ("F par %s", 
                   (LPCTSTR) ConvertROMFlag (mob->remove_par)));

  // now do all programs inside mobile if wanted

  if (App.GetProfileInt (sProfilePreferences, sProfileSaveROMPrograms, 0))
    save_programs (mob->programlist);

  } // end of each mobile


  } // end of CAreaEditorDoc::save_ROM_mobiles

// ========================================================================

void CAreaEditorDoc::save_mobiles (void)
  {
  if (m_MobList.IsEmpty ())
    return;

  fwrite_string ("#MOBILES");

  if (SMAUG)
    save_SMAUG_mobiles ();
  else if (ROM)
    save_ROM_mobiles ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  fwrite_string ("#0");    // mobiles terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_mobiles

// ========================================================================

void CAreaEditorDoc::save_SMAUG_objects (void)
  {

POSITION objPos;
CMUDObject * obj;

POSITION affectPos;
CAffect * affect;

POSITION extraPos;
CExtraDescription * extra;

// cycle through all objects

for (objPos = m_ObjectList.GetHeadPosition (); objPos; )
  {
  obj = m_ObjectList.GetNext (objPos);

  DoSaveMilestone ();

  fwrite_string (CFormat ("#%i", obj->vnum));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->name));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->short_descr));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->description));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->action_desc));

  fwrite_string (CFormat ("%i %s %s %i", 
                obj->item_type,
                (LPCTSTR) print_bitvector (obj->extra_flags),
                (LPCTSTR) print_bitvector (obj->wear_flags),
                obj->layers));


	int val0 = obj->value[0];
	int val1 = obj->value[1];
	int val2 = obj->value[2];
	int val3 = obj->value[3];
	int val4 = obj->value[4];
	int val5 = obj->value[5];

  if (m_Area->area_version)
	  switch ( obj->item_type )
	  {
	  case ITEM_PILL:
	  case ITEM_POTION:
	  case ITEM_SCROLL:
      val1 = HAS_SPELL_INDEX;
      val2 = HAS_SPELL_INDEX;
      val3 = HAS_SPELL_INDEX;
	    break;
	  case ITEM_STAFF:
	  case ITEM_WAND:
      val3 = HAS_SPELL_INDEX;
	    break;
	  case ITEM_SALVE:
      val4 = HAS_SPELL_INDEX;
      val5 = HAS_SPELL_INDEX;
      break;
	  }

	if ( val4 || val5 )
    fwrite_string (CFormat ("%i %i %i %i %i %i", 
                  val0,
                  val1,
                  val2,
                  val3,
                  val4,
                  val5));
  else
    fwrite_string (CFormat ("%i %i %i %i", 
                  val0,
                  val1,
                  val2,
                  val3));

  fwrite_string (CFormat ("%i %i %i", obj->weight,
                                      obj->cost,
                                      obj->rent));


  // we write out spell names in version 1 upwards
  if (m_Area->area_version)
    {
	    switch ( obj->item_type )
	    {
	    case ITEM_PILL:
	    case ITEM_POTION:
	    case ITEM_SCROLL:
          fwrite_string (CFormat ("'%s' '%s' '%s'", 
                          (LPCTSTR) skill_name (obj->value[1], true),
                          (LPCTSTR) skill_name (obj->value[2], true),
                          (LPCTSTR) skill_name (obj->value[3], true)));

	      break;
	    case ITEM_STAFF:
	    case ITEM_WAND:
          fwrite_string (CFormat ("'%s'", 
                          (LPCTSTR) skill_name (obj->value[3], true)));
	      break;
	    case ITEM_SALVE:
          fwrite_string (CFormat ("'%s' '%s'", 
                          (LPCTSTR) skill_name (obj->value[4], true),
                          (LPCTSTR) skill_name (obj->value[5], true)));
	      break;
	    }   // end of switch on object type


    }   // end of version not being zero

    // now do all programs, extras, affects inside the object

  // Extra descriptions first ...

  for (extraPos = obj->extralist.GetHeadPosition (); extraPos; )
    {
    extra = obj->extralist.GetNext (extraPos);

    fwrite_string ("E");    // Extra description flag
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->keyword));
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->description));
    }
  
  // Affects next ...

  for (affectPos = obj->affectlist.GetHeadPosition (); affectPos; )
    {
    affect = obj->affectlist.GetNext (affectPos);

    fwrite_string ("A");    // Affects flag
    fwrite_string (CFormat ("%i %i", affect->location,
                                        affect->modifier));
    }

  // Programs last ...

  // now do all programs inside object

  save_programs (obj->programlist);

  } // end of each object


  } // end of CAreaEditorDoc::save_SMAUG_objects

// ========================================================================

void CAreaEditorDoc::save_ROM_objects (void)
  {

POSITION objPos;
CMUDObject * obj;

POSITION affectPos;
CAffect * affect;

POSITION extraPos;
CExtraDescription * extra;

int i;

// cycle through all objects

for (objPos = m_ObjectList.GetHeadPosition (); objPos; )
  {
  obj = m_ObjectList.GetNext (objPos);

  DoSaveMilestone ();

  fwrite_string (CFormat ("#%i", obj->vnum));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->name));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->short_descr));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->description));
  fwrite_string (CFormat ("%s~", (LPCTSTR) obj->material));

  if (!ObjectItemList.IsValueInList (obj->item_type))
    Warning (obj, 
            "Item type %i not in item type list, defaulting to first one",
            obj->item_type);

  fwrite_string (CFormat ("%s %s %s", 
                (LPCTSTR) Quote (ObjectItemList.ReturnName (obj->item_type)),
                (LPCTSTR) ConvertROMFlag (obj->extra_flags),
                (LPCTSTR) ConvertROMFlag (obj->wear_flags)));

	switch(obj->item_type)
	{
	case ITEM_WEAPON:

      if (!ObjectWeaponList.IsValueInList (obj->value[0]))
        Warning (obj, 
                "Weapon type %i not in weapon type list, defaulting to first one",
                obj->value[0]);

      if (!ObjectAttackList.IsValueInList (obj->value[3]))
        Warning (obj, 
                "Attack type %i not in attack type list, defaulting to first one",
                obj->value[3]);

      fwrite_string (CFormat ("%s %i %i %s %s", 
                    (LPCTSTR) Quote (ObjectWeaponList.ReturnName (obj->value[0])),
                    obj->value[1],
                    obj->value[2],
                    (LPCTSTR) Quote (ObjectAttackList.ReturnName (obj->value[3])),
                    (LPCTSTR) ConvertROMFlag (obj->value[4])));

	    break;

	case ITEM_CONTAINER:

      fwrite_string (CFormat ("%i %s %i %i %i", 
                    obj->value[0],
                    (LPCTSTR) ConvertROMFlag (obj->value[1]),
                    obj->value[2],
                    obj->value[3],
                    obj->value[4]));
	    break;

  case ITEM_DRINK_CON:
	case ITEM_FOUNTAIN:

      if (!ObjectLiquidList.IsValueInList (obj->value[2]))
        Warning (obj, 
                "Liquid type %i not in liquid type list, defaulting to first one",
                obj->value[2]);

      fwrite_string (CFormat ("%i %i %s %i %i", 
                    obj->value[0],
                    obj->value[1],
                    (LPCTSTR) Quote (ObjectLiquidList.ReturnName (obj->value[2])),
                    obj->value[3],
                    obj->value[4]));

            break;

	case ITEM_WAND:
	case ITEM_STAFF:

      if (obj->value[3] != -1)
        if (!ROMSkillNameList.IsValueInList (obj->value[3]))
          Warning (obj, 
                  "Skill type %i not in skill type list, defaulting to first one",
                  obj->value[3]);
     

      fwrite_string (CFormat ("%i %i %i '%s' %i", 
                    obj->value[0],
                    obj->value[1],
                    obj->value[2],
                    (LPCTSTR) skill_name (obj->value[3]),
                    obj->value[4]));

	    break;
	case ITEM_POTION:
	case ITEM_PILL:
	case ITEM_SCROLL:

      for (i = 1; i <= 4; i++)
        if (obj->value[i] != -1)
          if (!ROMSkillNameList.IsValueInList (obj->value[i]))
            Warning (obj, 
                    "Skill type %i not in skill type list, defaulting to first one",
                    obj->value[i]);

      fwrite_string (CFormat ("%i '%s' '%s' '%s' '%s'", 
                    obj->value[0],
                    (LPCTSTR) skill_name (obj->value[1]),
                    (LPCTSTR) skill_name (obj->value[2]),
                    (LPCTSTR) skill_name (obj->value[3]),
                    (LPCTSTR) skill_name (obj->value[4])));
	    break;

	default:
    fwrite_string (CFormat ("%i %i %i %i %i", 
                  obj->value[0],
                  obj->value[1],
                  obj->value[2],
                  obj->value[3],
                  obj->value[4]));
	    break;
	}

          /* set up condition */
  char cCondition;
  if (obj->condition < 10)
    cCondition = 'R';   // rats?
  else if (obj->condition < 25)
    cCondition = 'B';   // broken?
  else if (obj->condition < 50)
    cCondition = 'D';   // damaged?
  else if (obj->condition < 75)
    cCondition = 'W';   // wobbly??
  else if (obj->condition < 90)
    cCondition = 'A';   // average?
  else if (obj->condition < 100)
    cCondition = 'G';   // good?
  else 
    cCondition = 'P'; // perfect?
 
  fwrite_string (CFormat ("%i %i %i %c", 
                obj->level,
                obj->weight,
                obj->cost,
                cCondition));

  // now do all programs, extras, affects inside the object
  
  // Affects first ...

  for (affectPos = obj->affectlist.GetHeadPosition (); affectPos; )
    {
    affect = obj->affectlist.GetNext (affectPos);

    if (affect->where == TO_OBJECT)
      {
      fwrite_string ("A");    // Affects flag
      fwrite_string (CFormat ("%i %i", affect->location,
                                       affect->modifier));
      }   // end of affect on object (TO_OBJECT)
    else
      {
      char cAffectType;
      switch (affect->where)
        {
        case TO_AFFECTS:  cAffectType = 'A'; break;
        case TO_IMMUNE:   cAffectType = 'I'; break;
        case TO_RESIST:   cAffectType = 'R'; break;
        case TO_VULN:     cAffectType = 'V'; break;
        default:          cAffectType = 'X'; break;
        } // end of switch

      fwrite_string ("F");    // Affects flag
      fwrite_string (CFormat ("%c %i %s %s",
                               cAffectType,
                               affect->location,
                               (LPCTSTR) print_bitvector (affect->modifier),
                               (LPCTSTR) ConvertROMFlag (affect->bitvector)));

      }   // end of flag affect
    }     // end of affects list

  // Extra descriptions next ...

  for (extraPos = obj->extralist.GetHeadPosition (); extraPos; )
    {
    extra = obj->extralist.GetNext (extraPos);

    fwrite_string ("E");    // Extra description flag
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->keyword));
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->description));
    }

  // Programs last ...

  // now do all programs inside object

  if (App.GetProfileInt (sProfilePreferences, sProfileSaveROMPrograms, 0))
    save_programs (obj->programlist);

  } // end of each object


  } // end of CAreaEditorDoc::save_ROM_objects

// ========================================================================

void CAreaEditorDoc::save_objects (void)
  {
  if (m_ObjectList.IsEmpty ())
    return;

  fwrite_string ("#OBJECTS");

  if (SMAUG)
    save_SMAUG_objects ();
  else if (ROM)
    save_ROM_objects ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  fwrite_string ("#0");    // objects terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_objects

// ========================================================================

void CAreaEditorDoc::save_SMAUG_rooms (void)
  {

POSITION roomPos;
CRoom * room;

POSITION exitPos;
CExit * exit;

POSITION mapPos;
CRoomMap * map;

POSITION extraPos;
CExtraDescription * extra;

// cycle through all rooms

for (roomPos = m_RoomList.GetHeadPosition (); roomPos; )
  {
  room = m_RoomList.GetNext (roomPos);

  DoSaveMilestone ();

  fwrite_string (CFormat ("#%i", room->vnum));
  fwrite_string (CFormat ("%s~", (LPCTSTR) room->name));
  fwrite_string (CFormat ("%s~", (LPCTSTR) room->description));

  fwrite_string (CFormat ("0 %s %i %i %i %i", 
                (LPCTSTR) print_bitvector (room->room_flags),
                room->sector_type,
                room->tele_delay,
                room->tele_vnum,
                room->tunnel));

  // now do all programs, extras, doors, maps inside the room

  // Doors first

  for (exitPos = room->exitlist.GetHeadPosition (); exitPos; )
    {
    exit = room->exitlist.GetNext (exitPos);

    int vdir = exit->vdir;

    if (!RoomDirectionList.IsValueInList (vdir))
      {
      Warning (exit, 
              "Exit direction %i not in direction list, defaulting to first one",
              vdir);
      vdir = 0;
      };

    fwrite_string (CFormat ("D%i", vdir));    // door flag and number
    fwrite_string (CFormat ("%s~", (LPCTSTR) exit->description));
    fwrite_string (CFormat ("%s~", (LPCTSTR) exit->keyword));

    fwrite_string (CFormat ("%s %i %i %i", 
                  (LPCTSTR) print_bitvector (exit->exit_info),
                  exit->key,
                  exit->vnum,
                  exit->distance));
    }   // end of each exit


  // Extra descriptions next ...

  for (extraPos = room->extralist.GetHeadPosition (); extraPos; )
    {
    extra = room->extralist.GetNext (extraPos);

    fwrite_string ("E");    // Extra description flag
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->keyword));
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->description));
    }   // end of each description
    
  // Maps next ...

  for (mapPos = room->maplist.GetHeadPosition (); mapPos; )
    {
    map = room->maplist.GetNext (mapPos);

    fwrite_string ("M");    // maps flag
    fwrite_string (CFormat ("%i %i %i %c", 
                              map->vnum,
                              map->x,
                              map->y,
                              map->entry));
    }

  // Programs last ...

  // now do all programs inside room

  save_programs (room->programlist);

  fwrite_string ("S");    // end of extra stuff terminator

  } // end of each room

  } // end of CAreaEditorDoc::save_SMAUG_rooms

// ========================================================================

void CAreaEditorDoc::save_ROM_rooms (void)
  {

POSITION roomPos;
CRoom * room;

POSITION exitPos;
CExit * exit;

POSITION extraPos;
CExtraDescription * extra;

// cycle through all rooms

for (roomPos = m_RoomList.GetHeadPosition (); roomPos; )
  {
  room = m_RoomList.GetNext (roomPos);

  DoSaveMilestone ();

  fwrite_string (CFormat ("#%i", room->vnum));
  fwrite_string (CFormat ("%s~", (LPCTSTR) room->name));
  fwrite_string (CFormat ("%s~", (LPCTSTR) room->description));

  fwrite_string (CFormat ("0 %s %i", 
                (LPCTSTR) ConvertROMFlag (room->room_flags),
                room->sector_type));

  // now do all programs, extras, doors, maps inside the room

  // Doors first

  EXT_BV exit_info;

  for (exitPos = room->exitlist.GetHeadPosition (); exitPos; )
    {
    exit = room->exitlist.GetNext (exitPos);

    int vdir = exit->vdir;

    if (!RoomDirectionList.IsValueInList (vdir))
      {
      Warning (exit, 
              "Exit direction %i not in direction list, defaulting to first one",
              vdir);
      vdir = 0;
      };

    exit_info = exit->exit_info;

    // go back to old style for compatibility

    if (exit_info == ROM_EX_ISDOOR)
      exit_info = 1;
    else if (exit_info == (ROM_EX_ISDOOR | ROM_EX_PICKPROOF))
      exit_info = 2;
    else if (exit_info == (ROM_EX_ISDOOR | ROM_EX_NOPASS))
      exit_info = 3;
    else if (exit_info == (ROM_EX_ISDOOR|ROM_EX_NOPASS|ROM_EX_PICKPROOF))
      exit_info = 4;

    fwrite_string (CFormat ("D%i", vdir));    // door flag and number
    fwrite_string (CFormat ("%s~", (LPCTSTR) exit->description));
    fwrite_string (CFormat ("%s~", (LPCTSTR) exit->keyword));
    fwrite_string (CFormat ("%s %i %i", 
                  (LPCTSTR) print_bitvector (exit_info),
                  exit->key,
                  exit->vnum));
    }   // end of each exit


  // now do various flags

  if (room->heal_rate)
    fwrite_string (CFormat ("H %i", room->heal_rate));    // healing room

  if (room->mana_rate)
    fwrite_string (CFormat ("M %i", room->mana_rate));    // mana room

  if (!room->clan.IsEmpty ())
    fwrite_string (CFormat ("C %s~", (LPCTSTR) room->clan));    // clan room

  if (!room->owner.IsEmpty ())
    fwrite_string (CFormat ("O %s", (LPCTSTR) room->owner));    // owner

  // Extra descriptions next ...

  for (extraPos = room->extralist.GetHeadPosition (); extraPos; )
    {
    extra = room->extralist.GetNext (extraPos);

    fwrite_string ("E");    // Extra description flag
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->keyword));
    fwrite_string (CFormat ("%s~", (LPCTSTR) extra->description));
    }   // end of each description
    
  // Programs last ...

  // now do all programs inside room

  if (App.GetProfileInt (sProfilePreferences, sProfileSaveROMPrograms, 0))
    save_programs (room->programlist);

  fwrite_string ("S");    // end of extra stuff terminator

  } // end of each room

  } // end of CAreaEditorDoc::save_ROM_rooms

// ========================================================================

void CAreaEditorDoc::save_rooms (void)
  {
  if (m_RoomList.IsEmpty ())
    return;

  fwrite_string ("#ROOMS");

  if (SMAUG)
    save_SMAUG_rooms ();
  else if (ROM) 
    save_ROM_rooms ();
  else
    ThrowErrorException ("Unknown area type: %i", m_AreaType);

  fwrite_string ("#0");    // rooms terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_rooms


// ========================================================================

void CAreaEditorDoc::save_resets (void)
  {
  if (m_ResetList.IsEmpty ())
    return;

  fwrite_string ("#RESETS");

POSITION resetPos;
CReset * reset;


// cycle through all resets

for (resetPos = m_ResetList.GetHeadPosition (); resetPos; )
  {
  reset = m_ResetList.GetNext (resetPos);

  DoSaveMilestone ();

  CString strComment = reset->strComment;
  if (strComment.IsEmpty ())
    {
    strComment = "; ";
    strComment += reset->Summary ();
    }

  // ROM sometimes writes out 4 args
  if (ROM && (reset->command == 'P' || reset->command == 'M'))
    fwrite_string (CFormat ("%c %i %i %i %i %i %s", 
                    reset->command,
                    reset->extra,
                    reset->arg1,
                    reset->arg2,
                    reset->arg3,
                    reset->arg4,
                    (LPCTSTR) strComment));
  // for G and R resets there are only 2 arguments
  else if (reset->command == 'G' || reset->command == 'R')
    fwrite_string (CFormat ("%c %i %i %i %s", 
                    reset->command,
                    reset->extra,
                    reset->arg1,
                    reset->arg2,
                    (LPCTSTR) strComment));
  // otherwise, 3 arguments
  else
    fwrite_string (CFormat ("%c %i %i %i %i  %s", 
                    reset->command,
                    reset->extra,
                    reset->arg1,
                    reset->arg2,
                    reset->arg3,
                    (LPCTSTR) strComment));


  } // end of each reset


  fwrite_string ("S");    // resets terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_resets

// ========================================================================

void CAreaEditorDoc::save_shops (void)
  {
  if (m_ShopList.IsEmpty ())
    return;

  fwrite_string ("#SHOPS");

POSITION shopPos;
CShop * shop;

CMobile * mob;

CString strMob;

  // cycle through all shops

  for (shopPos = m_ShopList.GetHeadPosition (); shopPos; )
    {
    shop = m_ShopList.GetNext (shopPos);

    DoSaveMilestone ();

    // make a comment giving the keeper's name
    if (mob	= FindMob (shop->keeper))
	    strMob = CFormat (" ; %s", (LPCTSTR) mob->short_descr);

    fwrite_string (CFormat ("%i %i %i %i %i %i %i %i %i %i%s", 
                  shop->keeper,
                  shop->buy_type [0],
                  shop->buy_type [1],
                  shop->buy_type [2],
                  shop->buy_type [3],
                  shop->buy_type [4],
                  shop->profit_buy,
                  shop->profit_sell,
                  shop->open_hour,
                  shop->close_hour,
                  (LPCTSTR) strMob));

    } // end of each shop


  fwrite_string ("0");    // shops terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_shops

// ========================================================================

void CAreaEditorDoc::save_repairs (void)
  {
  if (m_RepairList.IsEmpty ())
    return;

  fwrite_string ("#REPAIRS");

POSITION repairPos;
CRepair * repair;

CMobile * mob;

CString strMob;

  // cycle through all repairs

  for (repairPos = m_RepairList.GetHeadPosition (); repairPos; )
    {
    repair = m_RepairList.GetNext (repairPos);

    DoSaveMilestone ();

    // make a comment giving the keeper's name
    if (mob	= FindMob (repair->keeper))
	    strMob = CFormat (" ; %s", (LPCTSTR) mob->short_descr);

    fwrite_string (CFormat ("%i %i %i %i %i %i %i %i%s", 
                  repair->keeper,
                  repair->fix_type [0],
                  repair->fix_type [1],
                  repair->fix_type [2],
                  repair->profit_fix,
                  repair->shop_type,
                  repair->open_hour,
                  repair->close_hour,
                  (LPCTSTR) strMob));

    } // end of each repair


  fwrite_string ("0");    // repairs terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_repairs

// ========================================================================

void CAreaEditorDoc::save_specials (void)
  {
  if (m_MobList.IsEmpty ())
    return;

  fwrite_string ("#SPECIALS");

POSITION mobPos;
CMobile * mob;


// cycle through all specials

for (mobPos = m_MobList.GetHeadPosition (); mobPos; )
  {
  mob = m_MobList.GetNext (mobPos);

  // zero means no special function
  if (mob->spec_fun) 
    fwrite_string (CFormat ("M %i %s   ; %s", 
                            mob->vnum,
                            (LPCTSTR) MobSpecialList.ReturnName (mob->spec_fun, true),
                            (LPCTSTR) mob->short_descr));

  } // end of each special


  fwrite_string ("S");    // specials terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_specials


// ========================================================================

void CAreaEditorDoc::save_helps (void)
  {
  if (m_HelpList.IsEmpty ())
    return;

  fwrite_string ("#HELPS");

POSITION helpPos;
CHelp * help;

  // cycle through all helps

  for (helpPos = m_HelpList.GetHeadPosition (); helpPos; )
    {
    help = m_HelpList.GetNext (helpPos);

    DoSaveMilestone ();

    fwrite_string (CFormat ("%i %s~", 
                  help->level,
                  (LPCTSTR) help->keyword));

    fwrite_string (CFormat ("%s~", 
                  (LPCTSTR) help->text));

    } // end of each help


  fwrite_string ("0 $~");    // helps terminator
  fwrite_string (""); // blank line

  } // end of CAreaEditorDoc::save_helps

// ========================================================================

void CAreaEditorDoc::DoSaveMilestone (void)
  {
  m_FileWrite.DoSaveMilestone ();
  }

/////////////////////////////////////////////////////////////////////////////
// CAreaEditorDoc serialization

void CAreaEditorDoc::Serialize(CArchive& ar)
{
CString strSection;

  m_strErrorMessage.Empty ();
  m_nErrors = 0;

	if (ar.IsStoring())
	  {
    try 
      {

      m_strWarningType = "saving";

      if (m_Area && m_Area->area_version)
        if (App.m_SkillList.IsEmpty ())
          ThrowErrorException ("Skill list not loaded - cannot save area");

      long iItems =  m_MobList.GetCount ()
                       + m_ObjectList.GetCount ()
                       + m_RoomList.GetCount ()
                       + m_ResetList.GetCount ()
                       + m_ShopList.GetCount ()
                       + m_RepairList.GetCount ()
                       + m_HelpList.GetCount ();

      m_FileWrite.Init ("Saving area", &ar, iItems);

    // #AREA
    // #AUTHOR
    // #RANGES
    // #RESETMSG
    // #FLAGS
    // #ECONOMY

      if (m_Area)
        save_area ();

    // #MOBILES

      save_mobiles ();

    // #OBJECTS

      save_objects ();

    // #ROOMS

      save_rooms ();

      // #RESETS

      save_resets ();

    // #SHOPS

      save_shops ();

    // #REPAIRS

      save_repairs ();

    // #SPECIALS

      save_specials ();

    // #HELPS

      save_helps ();

      fwrite_string ("#$");    // area file terminator

      m_FileWrite.Wrapup ();

      }   // end of try block

    catch (CException * e)
      {
      m_FileWrite.Wrapup ();
      e->ReportError ();
      throw e;

      } // end of catch handler

    }
	else
	{

  m_Original_AreaType = m_AreaType; // so we know if they changed types

  try
    {

    m_strWarningType = "loading";

    m_FileRead.Init ("Loading area", &ar);

    while (true)
      {
      if (fread_letter () != '#')
        ThrowErrorException ("Area section must start with a \"#\"");
    
      strSection = fread_word ();
      strSection.MakeUpper ();

      if (strSection == "AREA")
        load_area ();
      else if (strSection == "AUTHOR")
        load_author ();
      else if (strSection == "VERSION")
        load_version ();
      else if (strSection == "NEIGHBOR")
        load_neighbour ();
      else if (strSection == "CLIMATE")
        load_climate ();
      else if (strSection == "RANGES")
        load_ranges ();
      else if (strSection == "RESETMSG")
        load_resetmsg ();
      else if (strSection == "FLAGS")
        load_flags ();
      else if (strSection == "ECONOMY")
        load_economy ();
      else if (strSection == "MOBILES")
        load_mobiles ();
      else if (strSection == "OBJECTS")
        load_objects ();
      else if (strSection == "ROOMS")
        load_rooms ();
      else if (strSection == "RESETS")
        load_resets ();
      else if (strSection == "SHOPS")
        load_shops ();
      else if (strSection == "REPAIRS")
        load_repairs ();
      else if (strSection == "SPECIALS")
        load_specials ();
      else if (strSection == "HELPS")
        load_helps ();
      else if (strSection == "$")    // end of file
        break;
      else
        ThrowErrorException ("Unrecognised area section header: %s",
                            (LPCTSTR) strSection);


      } // end of read loop

    // in case some turkey keys things in the wrong order with a text editor, sort them
    SortMobiles ();
    SortObjects ();
    SortRooms ();

    } // end of try block

// on an exception, show the error, then the line details, then throw
// an archive exception to tell the document we couldn't load it

  catch (CException * e)
    {
    m_FileRead.Wrapup ();
    e->ReportError ();
    e->Delete ();

    ::AfxMessageBox (CFormat ("Error occurred at (line %ld) \"%s\"",
                      m_FileRead.GetLineNumber (), 
                      (LPCTSTR) m_FileRead.GetLastLineRead ()),
                      MB_ICONINFORMATION);
    AfxThrowArchiveException (CArchiveException::badSchema);

    } // end of catch handler

  m_FileRead.Wrapup ();

  if (m_Area)
    {
    // don't show ridiculous numbers if no items in a category
    if (m_Area->mob_low_vnum == INT_MAX)
      {
      m_Area->mob_low_vnum = 9900;
      m_Area->mob_hi_vnum = 9950;
      }
    if (m_Area->obj_low_vnum == INT_MAX)
      {
      m_Area->obj_low_vnum = 9900;
      m_Area->obj_hi_vnum = 9950;
      }
    if (m_Area->room_low_vnum == INT_MAX)
      {
      m_Area->room_low_vnum = 9900;
      m_Area->room_hi_vnum = 9950;
      }
    }

  }   // end of loading

  // show any non-fatal errors, if wanted

  if (!m_strErrorMessage.IsEmpty ())
    if (App.GetProfileInt  (sProfilePreferences, sProfileShowWarnings, 1))
      {
      // show the errors

      // MODELESS dialog here

      CAreaLoadingProblems * dlg = new CAreaLoadingProblems;
      dlg->m_strErrors = CFormat ("Problems %s area: ", (LPCTSTR) m_strWarningType);
      dlg->m_strErrors += ar.GetFile ()->GetFileName () + ENDLINE + ENDLINE;
      dlg->m_strErrors += m_strErrorMessage + ENDLINE;
      dlg->m_strErrors += "Press <Esc> to dismiss this window";
      dlg->Create (ID_PROBLEMS_LOADING_AREA, NULL);  // create it
      dlg->ShowWindow(SW_SHOW);  // and, finally, show it - dialog will delete itself

      }

} // end of CAreaEditorDoc::Serialize

/////////////////////////////////////////////////////////////////////////////
// CAreaEditorDoc diagnostics

#ifdef _DEBUG
void CAreaEditorDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CAreaEditorDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CAreaEditorDoc commands

// overall find room - scan all open documents for the room
CRoom * FindRoom (const int vnum)
  {
CRoom * room;

  for (POSITION docPos = App.m_pDocTemplate->GetFirstDocPosition();
      docPos != NULL; )
    {

    CAreaEditorDoc * pDoc = (CAreaEditorDoc *) App.m_pDocTemplate->GetNextDoc(docPos);

    // look in this document for the room
    room = pDoc->get_room_index (vnum);

    // if found - don't bother looking up any more
    if (room)
      return room;

    } // end of doing each document

  return NULL;
  }
             
CString FullRoomName (const CRoom * room)
  {

  if (room == NULL)
    return "(NULL ROOM)";

CString strName = room->name;

  if (room->m_pDoc->m_Area && !room->m_pDoc->m_Area->strAreaName.IsEmpty ())
    {
    strName += " [";
    strName += room->m_pDoc->m_Area->strAreaName;
    strName += "]";
    }
  else
    strName += " [untitled area]";

  return strName;

  }

CRoom * CAreaEditorDoc::get_room_index (const int vnum) const
  {
POSITION roomPos;
CRoom * room;

for (roomPos = m_RoomList.GetHeadPosition (); roomPos; )
  {
  room = m_RoomList.GetNext (roomPos);

  if (room->vnum == vnum)
    return room;

  }   // end of scanning all rooms

// not found, return NULL

  return NULL;

  } // end of CAreaEditorDoc::get_room_index 

void CAreaEditorDoc::goto_room (const int vnum)
  {
CRoom * room = FindRoom (vnum);

  // can't find - obviously can't go to it

  if (room == NULL)
    return;

  room->GoTo ();

  } // end of CAreaEditorDoc::goto_room 

// overall find mob - scan all open documents for the mob
CMobile * FindMob (const int vnum)
  {
CMobile * mob;

  for (POSITION docPos = App.m_pDocTemplate->GetFirstDocPosition();
      docPos != NULL; )
    {

    CAreaEditorDoc * pDoc = (CAreaEditorDoc *) App.m_pDocTemplate->GetNextDoc(docPos);

    // look in this document for the mob
    mob = pDoc->get_mob_index (vnum);

    // if found - don't bother looking up any more
    if (mob)
      return mob;

    } // end of doing each document

  return NULL;
  }
             
CString FullMobName (const CMobile * mob)
  {

  if (mob == NULL)
    return "(NULL MOB)";

CString strName = mob->short_descr;

  if (mob->m_pDoc->m_Area && !mob->m_pDoc->m_Area->strAreaName.IsEmpty ())
    {
    strName += " [";
    strName += mob->m_pDoc->m_Area->strAreaName;
    strName += "]";
    }
  else
    strName += " [untitled area]";

  return strName;

  }

CMobile * CAreaEditorDoc::get_mob_index (const int vnum)
  {
POSITION mobPos;
CMobile * mob;

for (mobPos = m_MobList.GetHeadPosition (); mobPos; )
  {
  mob = m_MobList.GetNext (mobPos);

  if (mob->vnum == vnum)
    return mob;

  }   // end of scanning all mobs

// not found, return NULL

  return NULL;

  } // end of CAreaEditorDoc::get_mob_index 

void CAreaEditorDoc::goto_mob (const int vnum)
  {
CMobile * mob = FindMob (vnum);

  // can't find - obviously can't go to it

  if (mob == NULL)
    return;

  mob->GoTo ();

  } // end of CAreaEditorDoc::goto_mob 

void CAreaEditorDoc::goto_object (const int vnum) 
  {
CMUDObject * object = FindObj (vnum);

  // can't find - obviously can't go to it

  if (object == NULL)
    return;

  object->GoTo ();

  } // end of CAreaEditorDoc::goto_object 

// overall find obj - scan all open documents for the object
CMUDObject * FindObj (const int vnum)
  {
CMUDObject * obj;

  for (POSITION docPos = App.m_pDocTemplate->GetFirstDocPosition();
      docPos != NULL; )
    {

    CAreaEditorDoc * pDoc = (CAreaEditorDoc *) App.m_pDocTemplate->GetNextDoc(docPos);

    // look in this document for the object
    obj = pDoc->get_obj_index (vnum);

    // if found - don't bother looking up any more
    if (obj)
      return obj;

    } // end of doing each document

  return NULL;
  }
             
CString FullObjName (const CMUDObject * obj)
  {

  if (obj == NULL)
    return "(NULL OBJECT)";

CString strName = obj->name;

  if (obj->m_pDoc->m_Area && !obj->m_pDoc->m_Area->strAreaName.IsEmpty ())
    {
    strName += " [";
    strName += obj->m_pDoc->m_Area->strAreaName;
    strName += "]";
    }
  else
    strName += " [untitled area]";

  return strName;

  }

CMUDObject * CAreaEditorDoc::get_obj_index (const int vnum) const
  {
POSITION objPos;
CMUDObject * obj;

for (objPos = m_ObjectList.GetHeadPosition (); objPos; )
  {
  obj = m_ObjectList.GetNext (objPos);

  if (obj->vnum == vnum)
    return obj;

  }   // end of scanning all objects

// not found, return NULL

  return NULL;

  } // end of CAreaEditorDoc::get_obj_index 


void CAreaEditorDoc::goto_exit (const CRoom * pRoomIndex, const int vdir)
  {
CExit * exit = get_exit (pRoomIndex, vdir);

  // can't find - obviously can't go to it

  if (exit == NULL)
    return;

  exit->GoTo ();

  } // end of CAreaEditorDoc::goto_exit 


CExit * CAreaEditorDoc::get_exit (const CRoom * pRoomIndex, const int vdir) const
  {
POSITION exitPos;
CExit * exit;

if (pRoomIndex == NULL)
  return NULL;

for (exitPos = pRoomIndex->exitlist.GetHeadPosition (); exitPos; )
  {
  exit = pRoomIndex->exitlist.GetNext (exitPos);

  if (exit->vdir == vdir)
    return exit;

  }   // end of scanning all exits

// not found, return NULL

  return NULL;

  } // end of CAreaEditorDoc::get_exit 

// This is to record non-fatal warnings when loading the area

void CAreaEditorDoc::LoadWarning (LPCTSTR lpszFormat, ...)
{

	ASSERT(AfxIsValidString(lpszFormat, FALSE));

	va_list argList;
	va_start(argList, lpszFormat);
  m_strErrorMessage += CFormat ("[line %i] \"%s\" - ", 
                       m_FileRead.GetLineNumber (), 
                      (LPCTSTR) m_FileRead.GetLastLineRead ());
	m_strErrorMessage += CFormat (lpszFormat, argList);
  m_strErrorMessage += ENDLINE;
	va_end(argList);
  m_nErrors++;

  }

// This is to record non-fatal warnings when loading the area

void CAreaEditorDoc::Warning (CMUDitem * item, LPCTSTR lpszFormat, ...)
{

	ASSERT(AfxIsValidString(lpszFormat, FALSE));

	va_list argList;
	va_start(argList, lpszFormat);
  if (item)
    {

    CString strType = item->Type ();
    if (!strType.IsEmpty ())
	    strType.SetAt (0, UPPER(strType[0])); // capitalise first character

    m_strErrorMessage += "** ";
    m_strErrorMessage += strType;
    m_strErrorMessage += ": ";
    m_strErrorMessage += item->Summary ();
    if (strlen (lpszFormat) > 0)
      m_strErrorMessage += ": ";
    }

  m_strErrorMessage += CFormat (lpszFormat, argList);
  m_strErrorMessage += ENDLINE;
	va_end(argList);

  m_nErrors++;

}

void CAreaEditorDoc::OnFileAreaSummary() 
{
CAreaSummary dlg;

	dlg.m_helps = m_HelpList.GetCount ();
	dlg.m_mobiles = m_MobList.GetCount ();
	dlg.m_objects = m_ObjectList.GetCount ();
	dlg.m_repairs = m_RepairList.GetCount ();
	dlg.m_resets = m_ResetList.GetCount ();
	dlg.m_rooms = m_RoomList.GetCount ();
	dlg.m_shops = m_ShopList.GetCount ();

  for (POSITION mobPos = m_MobList.GetHeadPosition (); mobPos; )
    dlg.m_mobile_programs += m_MobList.GetNext (mobPos)->programlist.GetCount ();

  for (POSITION objPos = m_ObjectList.GetHeadPosition (); objPos; )
    {
    CMUDObject * object = m_ObjectList.GetNext (objPos);
    dlg.m_object_affects += object->affectlist.GetCount ();
    dlg.m_object_programs += object->programlist.GetCount ();
    }

  for (POSITION roomPos = m_RoomList.GetHeadPosition (); roomPos; )
    {
    CRoom * room = m_RoomList.GetNext (roomPos);
    dlg.m_room_programs += room->programlist.GetCount ();
    dlg.m_room_exits += room->exitlist.GetCount ();
    }

  dlg.DoModal ();

}

// we need to force an update on the current pane before closing the document

BOOL CAreaEditorDoc::SaveModified() 
{
 
  // find our splitter view

   for (POSITION pos = GetFirstViewPosition(); pos;)
   {
      CView* pView = GetNextView(pos);

      // if the splitter view, update current pane
      if (pView->IsKindOf(RUNTIME_CLASS(CAreaEditorView)))
        {
        pView = (CView *) ((CAreaEditorView *)pView)->m_pSplitterWindow->GetPane(0,1);
        if (!pView->UpdateData (TRUE))
          return 0;
        }
   }   
   
	return CDocument::SaveModified();
}

// we need to force an update on the current pane before saving the document

BOOL CAreaEditorDoc::OnSaveDocument(LPCTSTR lpszPathName) 
{
  // find our splitter view

   for (POSITION pos = GetFirstViewPosition(); pos;)
   {
      CView* pView = GetNextView(pos);

      // if the splitter view, update current pane
      if (pView->IsKindOf(RUNTIME_CLASS(CAreaEditorView)))
        {
        pView = (CView *) ((CAreaEditorView *)pView)->m_pSplitterWindow->GetPane(0,1);
        if (!pView->UpdateData (TRUE))
          {
          ::AfxMessageBox ("Document not saved.", MB_ICONEXCLAMATION);
          return 0;
          }
        }
   }   
	
  if (m_Original_AreaType != m_AreaType &&
      GetPathName () == lpszPathName)
    {
    CString strOriginalType = "SMAUG";
    CString strNewType = "SMAUG";

    if (m_Original_AreaType == eROM)
      strOriginalType = "ROM";

    if (m_AreaType == eROM)
      strNewType = "ROM";

    if (::AfxMessageBox (CFormat
      ("You have changed the type of this area from %s to %s.\n\n"
       "You are advised to do a \"Save As\" and save under a different name "
       "as some features of areas are not common to both area types.\n\n"
       "Do you wish to continue with the save?",
        (LPCTSTR) strOriginalType,
        (LPCTSTR) strNewType),
      MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES)
      return 0;

    }

	return CDocument::OnSaveDocument(lpszPathName);
}

void CAreaEditorDoc::OnUpdateStatusModified(CCmdUI* pCmdUI) 
{

  if (IsModified ())
    pCmdUI->SetText ("Modified");
  else
    pCmdUI->SetText ("");
	  
  pCmdUI->Enable (true);

}



void CAreaEditorDoc::OnCloseDocument() 
{
  Frame.FixUpTitleBar ();	

	CDocument::OnCloseDocument();
}

BOOL CAreaEditorDoc::OnOpenDocument(LPCTSTR lpszPathName) 
{

  if (m_AreaType == eAsk)
    {
    CAreaTypeDlg dlg;

    if (dlg.DoModal () != IDOK)
      return FALSE;   // cancelled so can't open document

    // add 1, as 0 is "ask", 1 = SMAUG (response zero) and so on
    m_AreaType = (t_areatype) (dlg.m_iAreaType + 1);

    }

	if (!CDocument::OnOpenDocument(lpszPathName))
		return FALSE;
	
  Frame.FixUpTitleBar (this);	
	
	return TRUE;
}


CMobile * CAreaEditorDoc::AddMobile (CAreaEditorView * pView,
                                    const bool bSelect)
  {
  int vnum = -1,
      prevVnum = INT_MAX;

  CMobile * mob,
          * prevMob = NULL;

  // look for a gap in the mob vnums, so we can allocate it to this mob
  for (POSITION mobPos = m_MobList.GetHeadPosition (); 
        mobPos && (vnum == -1); )
    {
    mob = m_MobList.GetNext (mobPos);
    if ((prevVnum != INT_MAX) && (mob->vnum > (prevVnum + 1)))
      vnum = prevVnum + 1;
    prevVnum = mob->vnum;
    prevMob = mob;    // remember previous mob
    }

  // no gap found
  if (vnum == -1)
    if (prevVnum == INT_MAX)    // no mobs - take lowest available vnum
      vnum = m_Area->mob_low_vnum;
    else
      {
      vnum = prevVnum + 1;      // take highest, plus 1
      prevMob = NULL;          // force an "addtail"
      }

  if (vnum < m_Area->mob_low_vnum || vnum > m_Area->mob_hi_vnum)
    {
    ::AfxMessageBox (CFormat ("No free mob vnums in the range %i to %i",
          m_Area->mob_low_vnum, m_Area->mob_hi_vnum),
        MB_ICONEXCLAMATION);
    return NULL;
    }

  mob = new CMobile (RUNTIME_CLASS(CMobileView), this);
  mob->vnum = vnum;   // use our new vnum

  // insert in numeric order (insert before because prevMob is *higher*)
  if (prevMob)
    m_MobList.InsertBefore (m_MobList.Find (prevMob, NULL), mob);    // and add it to our list
  else
    m_MobList.AddTail (mob);    // and add it to our list
  HTREEITEM hdlMobItem = pView->AddTreeItem (mob, pView->m_hdlTreeMobiles);
  pView->GetTreeCtrl().SortChildren (pView->m_hdlTreeMobiles);    // put in right part of tree ctrl
  if (bSelect)
    pView->GetTreeCtrl().Select (hdlMobItem, TVGN_CARET);   // select it
  SetModifiedFlag ();

  return mob;

  } // end of CAreaEditorDoc::AddMobile


CMUDObject * CAreaEditorDoc::AddObject (CAreaEditorView * pView,
                                 const bool bSelect)
  {
  int vnum = -1,
      prevVnum = INT_MAX;

  CMUDObject * obj,
             * prevObj = NULL;

  // look for a gap in the obj vnums, so we can allocate it to this obj
  for (POSITION ObjPos = m_ObjectList.GetHeadPosition (); 
        ObjPos && (vnum == -1); )
    {
    obj = m_ObjectList.GetNext (ObjPos);
    if ((prevVnum != INT_MAX) && (obj->vnum > (prevVnum + 1)))
      vnum = prevVnum + 1;
    prevVnum = obj->vnum;
    prevObj = obj;    // remember previous obj
    }

  // no gap found
  if (vnum == -1)
    if (prevVnum == INT_MAX)    // no objects - take lowest available vnum
      vnum = m_Area->obj_low_vnum;
    else
      {
      vnum = prevVnum + 1;      // take highest, plus 1
      prevObj = NULL;          // force an "addtail"
      }

  if (vnum < m_Area->obj_low_vnum || vnum > m_Area->obj_hi_vnum)
    {
    ::AfxMessageBox (CFormat ("No free object vnums in the range %i to %i",
          m_Area->obj_low_vnum, m_Area->obj_hi_vnum),
        MB_ICONEXCLAMATION);
    return NULL;
    }

  obj = new CMUDObject (RUNTIME_CLASS(CObjectView), this);
  obj->vnum = vnum;   // use our new vnum

  // insert in numeric order (insert before because prevObj is *higher*)
  if (prevObj)
    m_ObjectList.InsertBefore (m_ObjectList.Find (prevObj, NULL), obj);    // and add it to our list
  else
    m_ObjectList.AddTail (obj);    // and add it to our list
  HTREEITEM hdlMobItem = pView->AddTreeItem (obj, pView->m_hdlTreeObjects);
  pView->GetTreeCtrl().SortChildren (pView->m_hdlTreeObjects);    // put in right part of tree ctrl
  if (bSelect)
    pView->GetTreeCtrl().Select (hdlMobItem, TVGN_CARET);   // select it
  SetModifiedFlag ();

  return obj;

  } // end of CAreaEditorDoc::AddObject

CRoom * CAreaEditorDoc::AddRoom (CAreaEditorView * pView,
                                 const bool bSelect)
  {
    int vnum = -1,
      prevVnum = INT_MAX;

  CRoom * room,
        * prevRoom = NULL;

  // look for a gap in the room vnums, so we can allocate it to this room
  for (POSITION roomPos = m_RoomList.GetHeadPosition (); 
        roomPos && (vnum == -1); )
    {
    room = m_RoomList.GetNext (roomPos);
    if ((prevVnum != INT_MAX) && (room->vnum > (prevVnum + 1)))
      vnum = prevVnum + 1;
    prevVnum = room->vnum;
    prevRoom = room;    // remember previous room
    }

  // no gap found
  if (vnum == -1)
    if (prevVnum == INT_MAX)    // no rooms - take lowest available vnum
      vnum = m_Area->room_low_vnum;
    else
      {
      vnum = prevVnum + 1;      // take highest, plus 1
      prevRoom = NULL;          // force an "addtail"
      }

  if (vnum < m_Area->room_low_vnum || vnum > m_Area->room_hi_vnum)
    {
    ::AfxMessageBox (CFormat ("No free room vnums in the range %i to %i",
          m_Area->room_low_vnum, m_Area->room_hi_vnum),
        MB_ICONEXCLAMATION);
    return NULL;
    }

  room = new CRoom (RUNTIME_CLASS(CRoomView), this);
  room->vnum = vnum;   // use our new vnum

  // insert in numeric order (insert before because prevRoom is *higher*)
  if (prevRoom)
    m_RoomList.InsertBefore (m_RoomList.Find (prevRoom, NULL), room);    // and add it to our list
  else
    m_RoomList.AddTail (room);    // and add it to our list
  HTREEITEM hdlRoomItem = pView->AddTreeItem (room, pView->m_hdlTreeRooms);
  pView->GetTreeCtrl().SortChildren (pView->m_hdlTreeRooms);    // put in right part of tree ctrl
  if (bSelect)
    pView->GetTreeCtrl().Select (hdlRoomItem, TVGN_CARET);   // select it
  SetModifiedFlag ();

  return room;
  } // end of CAreaEditorDoc::AddRoom


void CAreaEditorDoc::OnAreaWalkthrough() 
{
CWalkthroughDlg dlg;

  dlg.m_pDoc = this;

HTREEITEM hdlItem = m_leftview->GetItem ();

  if (hdlItem)
    {
    CMUDitem * pItem = (CMUDitem *) m_leftview->GetTreeCtrl().GetItemData (hdlItem);

    // if a room item, start with that one
    if (pItem && strcmp (pItem->Type (), "room") == 0)
      dlg.m_room = (CRoom *) pItem;
    } // end of having a selected tree view item


  dlg.DoModal ();	

  // select the room we have travelled to - they probably want to amend it a bit

  if (dlg.m_room)
    dlg.m_room->GoTo ();
}

void CAreaEditorDoc::OnUpdateAreaWalkthrough(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable (!m_RoomList.IsEmpty ());
}


void CAreaEditorDoc::OnViewWarnings() 
{
  if (!m_strErrorMessage.IsEmpty ())
    {
    // show the errors

    // MODELESS dialog here

    CAreaLoadingProblems * dlg = new CAreaLoadingProblems;
    dlg->m_strErrors = CFormat ("Problems %s area: ", (LPCTSTR) m_strWarningType);
    dlg->m_strErrors += GetTitle () + ENDLINE + ENDLINE;
    dlg->m_strErrors += m_strErrorMessage + ENDLINE;
    dlg->m_strErrors += "Press <Esc> to dismiss this window";
    dlg->Create (ID_PROBLEMS_LOADING_AREA, NULL);  // create it
    dlg->ShowWindow(SW_SHOW);  // and, finally, show it - dialog will delete itself

    }
	
}

void CAreaEditorDoc::OnUpdateViewWarnings(CCmdUI* pCmdUI) 
{
	
	pCmdUI->Enable (!m_strErrorMessage.IsEmpty ());

}

void CAreaEditorDoc::OnUpdateFileSave(CCmdUI* pCmdUI) 
{
  pCmdUI->Enable (IsModified ());	
}



int CompareMobile (const void * elem1, const void * elem2)
  {
  CMobile * mob1 = (*((CMobile **) elem1));
  CMobile * mob2 = (*((CMobile **) elem2));

  if (mob1->vnum < mob2->vnum)
    return -1;
  else if (mob1->vnum > mob2->vnum)
    return 1;
  else return 0;

  }   // end of CompareMobile

void CAreaEditorDoc::SortMobiles (void)
  {
POSITION MobilePos;
CTypedPtrArray <CPtrArray, CMobile*> CMobilesArray;
int iCount = m_MobList.GetCount ();
int i;

  CMobilesArray.SetSize (iCount);

  // extract pointers into a simple array
  for (i = 0, MobilePos = m_MobList.GetHeadPosition (); MobilePos; i++)
    CMobilesArray.SetAt (i, m_MobList.GetNext (MobilePos)); 

  // sort the array
  qsort (CMobilesArray.GetData (), 
         iCount,
         sizeof (CMobile *),
         CompareMobile);

  // get rid of old list
  m_MobList.RemoveAll ();

  // re-add in sorted order
  for (i = 0; i < iCount; i++)
    m_MobList.AddTail (CMobilesArray [i]);

  }  // end of CAreaEditorDoc::SortMobiles 

int CompareRoom (const void * elem1, const void * elem2)
  {
  CRoom * room1 = (*((CRoom **) elem1));
  CRoom * room2 = (*((CRoom **) elem2));

  if (room1->vnum < room2->vnum)
    return -1;
  else if (room1->vnum > room2->vnum)
    return 1;
  else return 0;

  }   // end of CompareRoom

void CAreaEditorDoc::SortRooms (void)
  {
POSITION RoomPos;
CTypedPtrArray <CPtrArray, CRoom*> CRoomsArray;
int iCount = m_RoomList.GetCount ();
int i;

  CRoomsArray.SetSize (iCount);

  // extract pointers into a simple array
  for (i = 0, RoomPos = m_RoomList.GetHeadPosition (); RoomPos; i++)
    CRoomsArray.SetAt (i, m_RoomList.GetNext (RoomPos)); 

  // sort the array
  qsort (CRoomsArray.GetData (), 
         iCount,
         sizeof (CRoom *),
         CompareRoom);

  // get rid of old list
  m_RoomList.RemoveAll ();

  // re-add in sorted order
  for (i = 0; i < iCount; i++)
    m_RoomList.AddTail (CRoomsArray [i]);

  }  // end of CAreaEditorDoc::SortRooms 

int CompareObject (const void * elem1, const void * elem2)
  {
  CMUDObject * obj1 = (*((CMUDObject **) elem1));
  CMUDObject * obj2 = (*((CMUDObject **) elem2));

  if (obj1->vnum < obj2->vnum)
    return -1;
  else if (obj1->vnum > obj2->vnum)
    return 1;
  else return 0;

  }   // end of CompareObject

void CAreaEditorDoc::SortObjects (void)
  {
POSITION ObjectPos;
CTypedPtrArray <CPtrArray, CMUDObject*> CObjectsArray;
int iCount = m_ObjectList.GetCount ();
int i;

  CObjectsArray.SetSize (iCount);

  // extract pointers into a simple array
  for (i = 0, ObjectPos = m_ObjectList.GetHeadPosition (); ObjectPos; i++)
    CObjectsArray.SetAt (i, m_ObjectList.GetNext (ObjectPos)); 

  // sort the array
  qsort (CObjectsArray.GetData (), 
         iCount,
         sizeof (CObject *),
         CompareObject);

  // get rid of old list
  m_ObjectList.RemoveAll ();

  // re-add in sorted order
  for (i = 0; i < iCount; i++)
    m_ObjectList.AddTail (CObjectsArray [i]);

  }  // end of CAreaEditorDoc::SortObjects