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
*/

// WalkthroughDlg.cpp : implementation file
//

#include "stdafx.h"
#include "AreaEditor.h"
#include "AreaEditorDoc.h"
#include "defaults.h"

#include "WalkthroughDlg.h"

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

// save here so we return same settings next time

static BOOL bShowEquipment = FALSE;
static BOOL bShowVnums = FALSE;

/////////////////////////////////////////////////////////////////////////////
// CWalkthroughDlg dialog


CWalkthroughDlg::CWalkthroughDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CWalkthroughDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CWalkthroughDlg)
	m_bShowEquipment = FALSE;
	m_bShowVnums = FALSE;
	//}}AFX_DATA_INIT

  m_pDoc = NULL;
  m_room = NULL;
  m_font = NULL;

}

CWalkthroughDlg::~CWalkthroughDlg ()
  {
  delete m_font;
  }

void CWalkthroughDlg::DoDataExchange(CDataExchange* pDX)
{

  if (!pDX->m_bSaveAndValidate)
    {
    m_bShowEquipment = bShowEquipment;
    m_bShowVnums = bShowVnums;
    }

	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CWalkthroughDlg)
	DDX_Control(pDX, IDC_SHOW_EQUIPMENT, m_ctlShowEquipment);
	DDX_Control(pDX, IDC_SHOW_VNUMS, m_ctlShowVnums);
	DDX_Control(pDX, IDC_OUTPUT, m_ctlOutput);
	DDX_Control(pDX, IDC_COMMAND, m_ctlCommand);
	DDX_Check(pDX, IDC_SHOW_EQUIPMENT, m_bShowEquipment);
	DDX_Check(pDX, IDC_SHOW_VNUMS, m_bShowVnums);
	//}}AFX_DATA_MAP

  if (!pDX->m_bSaveAndValidate)
    {
    FixFont (m_font, m_ctlOutput);

    CDC dc;

    RECT rect;

    dc.CreateCompatibleDC (NULL);
    dc.SelectObject(m_font);
  
    TEXTMETRIC tm;
    dc.GetTextMetrics(&tm);

    int m_FontHeight = tm.tmHeight; 
    int m_FontWidth = tm.tmAveCharWidth; 

    int iDialogWidth = m_FontWidth * (MAXDESCRIPTIONWIDTH - 1) +
                      (GetSystemMetrics (SM_CXFRAME) * 2) +  // allow for border
                      GetSystemMetrics (SM_CXVSCROLL) +  // allow for vertical scroll bar
                      4;      // allow for Windows 95 frame

    // size output window to correct width
    m_ctlOutput.GetWindowRect (&rect);
    m_ctlOutput.SetWindowPos (NULL, 0, 0, iDialogWidth, rect.bottom - rect.top, 
                SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);

    // make command window same width
//    m_ctlCommand.GetWindowRect (&rect);
//    m_ctlCommand.SetWindowPos (NULL, 0, 0, iDialogWidth, rect.bottom - rect.top, 
//                SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);

    // now do the dialog itself

    // this calculation here is a complete wank - there is no reason for it to work
    iDialogWidth += GetSystemMetrics (SM_CXFRAME) + GetSystemMetrics (SM_CXVSCROLL);
    
    GetWindowRect (&rect);
    SetWindowPos (NULL, 0, 0, iDialogWidth, rect.bottom - rect.top, 
                  SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);

    if (!m_room)
      m_room = m_pDoc->m_RoomList.GetHead ();
    RedrawRoom ();
    }

}


BEGIN_MESSAGE_MAP(CWalkthroughDlg, CDialog)
	//{{AFX_MSG_MAP(CWalkthroughDlg)
	ON_BN_CLICKED(IDC_SHOW_VNUMS, OnShowVnums)
	ON_BN_CLICKED(IDC_SHOW_EQUIPMENT, OnShowEquipment)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWalkthroughDlg message handlers

#define BUFFER_SIZE 50000

// ===============================================================================
static char * printline_buff = NULL;

static void WriteLine (CArchive & ar, 
                      int skip, 
                      const char * theline, ...)
  {
va_list arglist;

  if (!printline_buff)
    ThrowErrorException ("Not enough memory for printing buffer");

  // output the message as if it was a PRINTF type message
  va_start (arglist, theline);
  _vsnprintf (printline_buff, BUFFER_SIZE, theline, arglist);
  va_end (arglist);

  ar.WriteString (printline_buff);

  // now output the line feeds
  for (int i = 0; i < skip; i++)
    ar.WriteString (ENDLINE);

  } // end of WriteLine

void CWalkthroughDlg::RedrawRoom (void)
  {
  if (!m_room)
    return;

  // make title reflect which room we are in
  SetWindowText (m_room->Summary ());

POSITION pos;
POSITION resetPos;
CReset * reset;
CMobile * mob;
CMUDObject * object;

  bShowVnums = m_ctlShowVnums.GetCheck ();
  bShowEquipment = m_ctlShowEquipment.GetCheck ();

  CMemFile f;      // open memory file for writing
  CArchive ar (&f, CArchive::store);
  printline_buff = new char [BUFFER_SIZE];


  // display m_room
  if (bShowVnums)
    WriteLine (ar, 1, "%s", (LPCTSTR) m_room->Summary ());
  else
    WriteLine (ar, 1, "%s", (LPCTSTR) m_room->name);
  WriteLine (ar, 1, "%s", (LPCTSTR) m_room->description);

  // show all exits

  if (!m_room->exitlist.IsEmpty ())
    {
    WriteLine (ar, 1, "Exits: ");

    for (pos = m_room->exitlist.GetHeadPosition (); pos; )
      {
      CExit * exit = m_room->exitlist.GetNext (pos);

      if (bShowVnums)
        WriteLine (ar, 1, "%s %s", (LPCTSTR) exit->Summary (), 
                      (LPCTSTR) convert_exit_flags (exit->exit_info));
      else
        {
        CRoom * exitroom;
        CString strExitName;
        CString strExitDirection;

        strExitDirection = RoomDirectionList.ReturnName (exit->vdir, false);

        if (exitroom = FindRoom (exit->vnum))
          strExitName = exitroom->name;
        else
          strExitName = "not in area";

        WriteLine (ar, 1, "%-10s - %s",
                    (LPCTSTR) strExitDirection,
                    (LPCTSTR) strExitName);

        }
      }   // end of processing each exit

    WriteLine (ar, 1, "");    // wrap up line after exits
    } // end of having some exits

    // process each reset to show mobs and objects in the room

    bool bThisRoom = false;

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

      switch (reset->command)
        {
        case 'M': // load mobile

          bThisRoom = reset->arg3 == m_room->vnum;

          if (!bThisRoom)
            break;    // wrong room - forget it

          if ((mob = FindMob ( reset->arg1)) == NULL)
            break;    // mob not found

          if (bShowVnums)
            WriteLine (ar, 1, "Mobile %s", (LPCTSTR) mob->Summary ());
          WriteLine (ar, 1, "%s", (LPCTSTR) mob->long_descr);
          
          break;

        case 'O': // load object
          if (reset->arg3 != m_room->vnum)
            break;    // wrong room - forget it

          if ((object = FindObj ( reset->arg1)) == NULL)
            break;    // object not found

          if (bShowVnums)
            WriteLine (ar, 1, "Object %s", (LPCTSTR) object->Summary ());
          WriteLine (ar, 1, "%s", (LPCTSTR) object->description);
          
          break;

          case 'G': // give object
          case 'E': // equip object
            if (!bThisRoom)   // not given to a mob in this room
              break;    // wrong room - forget it

            if ((object = FindObj ( reset->arg1)) == NULL)
              break;    // object not found

            if (bShowEquipment)
              if (bShowVnums)
                WriteLine (ar, 1, "%s", (LPCTSTR) reset->Summary ());
              else
                {
                CString str1;

                if (object = FindObj (reset->arg1))
                  str1 = object->short_descr;
                else
                  str1 = CFormat ("#%i (not in area)", reset->arg1);
      
                if (reset->command == 'E')
                  WriteLine (ar, 1, "-- Equip: %s", (LPCTSTR) str1);
                else
                  WriteLine (ar, 1, "-- Give:  %s", (LPCTSTR) str1);
                } // end of vnums not wanted

            break;


        } // end of switch

      }   // end of processing each reset

  WriteLine (ar, 1, "");


  // remove work buffer and close archive
  delete [] printline_buff;
  printline_buff = NULL;
  ar.Write ("", 1);   // write trailing null
  ar.Close ();

  // set window text to CFile contents
  int nLength = f.GetLength ();
  char * p = (char *) f.Detach ();
  CString strOutput (p, nLength);
  m_ctlOutput.SetWindowText (p);
  free (p);   // remove memory allocated in CMemFile

  m_ctlCommand.SetSel (0, -1);   // select all of command window
  
  }   // end of CWalkthroughDlg::RedrawRoom 

const char * pWord;
const char * pWordStart;
int iWordLength;

static bool skip_spaces (void)
  {
  for ( ; *pWord && isspace (*pWord); pWord++)
    if (*pWord == '\n')
      return true;    // end of line reached

  return false;
  } // end of skip_spaces

static void fread_to_eol (void)
  {
  for ( ; *pWord && *pWord != '\n'; pWord++)
    ;

  if (*pWord == '\n')
    pWord++;    // skip end-of-line
  } // end of fread_to_eol

static CString fread_word (bool bQuoted = true)
  {
CString strResult;

  iWordLength = 0;

  if (skip_spaces ())
    return "";        // stop at end of line
  
  if (*pWord == 0)    // stop at end of file
    return "";

  char cEnd = ' ';

  if (bQuoted)
    {
    cEnd = *pWord;    // remember as trailing delimiter
	  if (cEnd == '\'' || cEnd == '"')
		  pWord++;
    else
      cEnd = ' ';
    }

  pWordStart = pWord;   // remember where word starts

  // a non-alpha start sounds like 'Hi there
  if (!bQuoted && !isalnum (*pWord))
    {
    pWord++;
    iWordLength++;
    strResult = CString (pWordStart, 1);
    }
  else
    {
    for (; *pWord; pWord++, iWordLength++)
      if (cEnd == ' ' ? isspace(*pWord) : *pWord == cEnd )
        break;
    strResult = CString (pWordStart, pWord - pWordStart);
    if (*pWord)
      pWord++;    // skip trailing delimiter
    }

  return strResult;

  } // end of fread_word

void CWalkthroughDlg::OnOK() 
  {
CString strCommands;

  m_ctlCommand.GetWindowText (strCommands);

  pWord = strCommands;

CString strCommand = fread_word (false);

  // no command, ignore it
  if (strCommand.IsEmpty ())
    return;

  strCommand.MakeLower ();

int i;
POSITION pos;
POSITION resetPos;
CReset * reset;
CMobile * mob;
CMUDObject * object;

  // translate common direction abbreviations

  if (strCommand == "ne")
    strCommand = "northeast";
  else if (strCommand == "nw")
    strCommand = "northwest";
  else if (strCommand == "se")
    strCommand = "southeast";
  else if (strCommand == "sw")
    strCommand = "southwest";
  else if (strCommand == "enter")   // enter and leave go to "somewhere"
    strCommand = "somewhere";
  else if (strCommand == "leave")
    strCommand = "somewhere";

  // ==================== DIRECTION ==============================
  // first, see if the command is a direction (north, south etc.)

  if (RoomDirectionList.FindValue (strCommand, i, true))  // found it!
    {

    // look for that exit
    for (pos = m_room->exitlist.GetHeadPosition (); pos; )
      {
      CExit * exit = m_room->exitlist.GetNext (pos);
      if (exit->vdir == i)
        {
        CRoom * new_room = FindRoom (exit->vnum);
        if (new_room)
          {
          m_room = new_room;
          RedrawRoom ();
          }   // end of leading to a valid room
        else
          AppendToOutput (exit->Summary ());   // not a valid room

        return;
        } // end of exit found in room

      }   // end of processing each exit

    AppendToOutput (CFormat ("No exit to %s", (LPCTSTR) RoomDirectionList.ReturnName (i, false)));
    return;
    }   // end of processing each possible direction (and finding a match)


  // ==================== EXIT keywords ==============================
  // check all exits
  for (pos = m_room->exitlist.GetHeadPosition (); pos; )
    {
    CExit * exit = m_room->exitlist.GetNext (pos);
    if (!str_prefix (strCommand, exit->keyword))  // found it!
      {
      CRoom * new_room = FindRoom (exit->vnum);
      if (new_room)
        {
        m_room = new_room;
        RedrawRoom ();
        }   // end of leading to a valid room
      else
        AppendToOutput (exit->Summary ());   // not a valid room

      return;
      } // end of exit found with keyword matching entered word (eg. opening)
    }   // end of processing each exit


  // ==================== GOTO ==============================
  // look for "goto" command
  if (!str_prefix (strCommand, "goto"))  // found it!
    {
    CString strDest = fread_word (false);

    if (strDest.IsEmpty ())
      {
      AppendToOutput ("GOTO must be followed by room vnum, eg. GOTO 1234");
      return;
      }

    int iDest = atoi (strDest);

    if (iDest)
      {
      CRoom * new_room = FindRoom (iDest);
      if (new_room)
        {
        m_room = new_room;
        RedrawRoom ();
        return;
        }   // end of leading to a valid room
      }
    else
      {
      AppendToOutput ("GOTO vnum must be a number, eg. GOTO 1234");
      return;
      }

    AppendToOutput (CFormat ("Room %s not in this area", strDest));
    return;
    }

  // ==================== LOOK ==============================
  // look for "look" command
  if (!str_prefix (strCommand, "look"))  // found it!
    {
    CString strTarget = fread_word (false);
    if (strTarget.IsEmpty ())   // 'Look' on its own redraws current room
      {
      RedrawRoom ();
      return;
      }

    if (strTarget == "self")
      {
      AppendToOutput ("You see a slightly bemused builder standing here.  :)");
      return;
      }

    strTarget += ' ';   // trailing space, so we match on whole words

    // look for word in extra descriptions
    for (POSITION extraPos = m_room->extralist.GetHeadPosition (); extraPos; )
      {
      CExtraDescription * extra = m_room->extralist.GetNext (extraPos);
      CString strKeyword = extra->keyword;

      strKeyword.MakeLower ();
      strKeyword += ' ';

      // see if our word matches a description keyword
      if (strKeyword.Find (strTarget) != -1)
        {
        AppendToOutput (extra->description);
        return;
        } // end of match on keyword
      } // end of checking each extra description

    strTarget.TrimRight ();   // get rid of trailing space

    if (RoomDirectionList.FindValue (strTarget, i, true))  // found it!
      {
      // look for that exit
      for (pos = m_room->exitlist.GetHeadPosition (); pos; )
        {
        CExit * exit = m_room->exitlist.GetNext (pos);
        if (exit->vdir == i)
          {
          AppendToOutput (exit->description);

          CRoom * new_room = FindRoom (exit->vnum);
          if (new_room)
            {
            AppendToOutput (new_room->name);
            AppendToOutput (new_room->description);
            }   // end of leading to a valid room

          return;
          } // end of exit found in room

        }   // end of processing each exit

      }   // end of exit name found

    // OK, look for mobs and objects

    bool bThisRoom = false;
    CString strKeyword;

    strTarget += ' ';   // trailing space, so we match on whole words

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

      switch (reset->command)
        {
        case 'M': // load mobile

          bThisRoom = reset->arg3 == m_room->vnum;

          if (!bThisRoom)
            break;    // wrong room - forget it

          if ((mob = FindMob ( reset->arg1)) == NULL)
            break;    // mob not found

          strKeyword = mob->player_name;
          strKeyword.MakeLower ();
          strKeyword += ' ';

          // see if our word matches a description keyword
          if (strKeyword.Find (strTarget) != -1)
            {
            AppendToOutput (mob->description);
            return;
            } // end of match on keyword
          
          break;

        case 'O': // load object
          if (reset->arg3 != m_room->vnum)
            break;    // wrong room - forget it

          if ((object = FindObj ( reset->arg1)) == NULL)
            break;    // object not found

          // look for word in extra descriptions
          for (POSITION extraPos = object->extralist.GetHeadPosition (); extraPos; )
            {
            CExtraDescription * extra = object->extralist.GetNext (extraPos);
            CString strKeyword = extra->keyword;

            strKeyword.MakeLower ();
            strKeyword += ' ';

            // see if our word matches a description keyword
            if (strKeyword.Find (strTarget) != -1)
              {
              AppendToOutput (extra->description);
              return;
              } // end of match on keyword
            } // end of checking each extra description

          strKeyword = object->name;
          strKeyword.MakeLower ();
          strKeyword += ' ';

          // see if our word matches a description keyword
          if (strKeyword.Find (strTarget) != -1)
            {
            AppendToOutput (object->description);
            return;
            } // end of match on keyword
          
          break;

        } // end of switch

      }   // end of processing each reset

    AppendToOutput ("You do not see that here");
    return;
    }   // end of doing a LOOK

  AppendToOutput ("Huh?");

  } // end of CWalkthroughDlg::OnOK


void CWalkthroughDlg::AppendToOutput (const char * sText)
  {
CString strOutput;

  m_ctlOutput.GetWindowText (strOutput);

  strOutput += ENDLINE;
  strOutput += sText;

  m_ctlOutput.SetWindowText (strOutput);
  m_ctlOutput.SetSel (SHRT_MAX, SHRT_MAX);   // select end of window

  m_ctlCommand.SetSel (0, -1);   // select command window

  } // end of CWalkthroughDlg::AppendToOutput

void CWalkthroughDlg::OnShowVnums() 
{
  RedrawRoom ();
}

void CWalkthroughDlg::OnShowEquipment() 
{
  RedrawRoom ();
}