/* ** 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 */ #include "stdafx.h" #include "AreaEditor.h" #include "MainFrm.h" #include "CommandsListDlg.h" typedef struct { char * sFunctionName; int FollowedBy; // is command followed by something, eg. MPMLOAD 1234 } t_specialcommands; const t_specialcommands SpecialCommands [] = { { "do_mpmload", eCmdMobile }, // load a mob { "do_mpoload", eCmdObject }, // load an object { "do_mpforce", eCmdName | eCmdRepeat }, // force a named something to do something { "do_mpat", eCmdRoom | eCmdRepeat}, // do a command at another location { "do_mptransfer", eCmdName | eCmdRoom }, // transfer <name> to <room> { "do_mpgoto", eCmdRoom }, // go to another room { "do_mpechoaround", eCmdName }, // echo around that person { "do_cast", eCmdSpell }, // cast a spell // This is for Brian Carter { "do_mpoput", eCmdObject }, // put an object into a container }; void CAreaEditorApp::fread_command (CFileRead & FileRead) { CString buf; CString word; bool fMatch; CCommand * command; command = new CCommand; m_CommandList.AddTail (command); while (true) { word = FileRead.fread_word (); fMatch = FALSE; switch ( UPPER(word[0]) ) { case '*': fMatch = TRUE; FileRead.fread_to_eol (); break; case 'C': KEY( "Code", command->do_fun, FileRead.fread_word () ); break; case 'E': if (word == "End" ) { if (command->name.IsEmpty () ) ThrowErrorException ( "Name not found"); if (command->do_fun.IsEmpty () ) ThrowErrorException( "Function not found"); // look for commands with special flags for (int i = 0; i < NUMITEMS (SpecialCommands); i++) if (command->do_fun == SpecialCommands [i].sFunctionName) { // found a match - copy across flags command->FollowedBy = SpecialCommands [i].FollowedBy; break; } return; } break; case 'F': KEY ("Flags", command->flags, FileRead.fread_number ()); break; case 'L': KEY( "Level", command->level, FileRead.fread_number () ); KEY( "Log", command->log, FileRead.fread_number () ); break; case 'N': KEY( "Name", command->name, FileRead.fread_string () ); break; case 'P': KEY( "Position", command->position, FileRead.fread_number () ); break; } if ( !fMatch ) ThrowErrorException ("No match: %s", (LPCTSTR) word ); } // end of read loop } // end of CAreaEditorApp::fread_command void CAreaEditorApp::LoadCommands (const CString strFileName) { CWaitCursor wait; try { CFileRead FileRead; // Open commands file CFile fCommands (strFileName, CFile::modeRead|CFile::shareDenyWrite); CArchive ar(&fCommands, CArchive::load); FileRead.Init ("Loading commands", &ar); try { while (true) { char letter; CString word; letter = FileRead.fread_letter(); if ( letter == '*' ) { FileRead.fread_to_eol (); continue; } if ( letter != '#' ) ThrowErrorException ( "# not found."); word = FileRead.fread_word (); if (word == "COMMAND") { fread_command (FileRead); continue; } else if (word == "END") break; else ThrowErrorException ( "Bad section."); } // end of read loop } // end of try block catch(CException* e) { FileRead.Wrapup (); DeleteCommands (); // error on load - don't keep half of them e->ReportError (); ::AfxMessageBox (CFormat ("Error occurred in file %s at (line %ld) \"%s\"", (LPCTSTR) strFileName, FileRead.GetLineNumber (), (LPCTSTR) FileRead.GetLastLineRead ()), MB_ICONINFORMATION); e->Delete(); ar.Close(); return; } ar.Close(); FileRead.Wrapup (); } catch(CException* e) { ::AfxMessageBox ( CFormat ("Unable to open commands file: %s", (LPCTSTR) strFileName), MB_ICONEXCLAMATION); e->Delete(); return; } SortCommands (); } // end of CAreaEditorApp::LoadCommands void CAreaEditorApp::OnCommandsLoad() { CFileDialog dlg(TRUE, // TRUE for FileOpen "dat", // default extension "commands.dat", // initial file name OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, "Commands files (*.dat)|*.dat||"); dlg.m_ofn.lpstrTitle = "Select commands file"; if (dlg.DoModal() != IDOK) return; LoadCommands (dlg.GetPathName()); } void CMainFrame::OnCommandsEdit() { CCommandsListDlg dlg; dlg.DoModal (); } void CMainFrame::OnUpdateCommandsEdit(CCmdUI* pCmdUI) { pCmdUI->Enable (!App.m_CommandList.IsEmpty ()); } void CAreaEditorApp::OnCommandsSave() { CFileDialog dlg(FALSE, // FALSE for FileSave "dat", // default extension "commands.dat", OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST, "Data files (*.dat)|*.dat||", &Frame); dlg.m_ofn.lpstrTitle = "Commands file"; if (dlg.DoModal() != IDOK) return; try { POSITION commandPos; CCommand * command; CFile f (dlg.GetPathName(), CFile::modeCreate|CFile::modeWrite|CFile::shareExclusive); CArchive ar(&f, CArchive::store); for (commandPos = App.m_CommandList.GetHeadPosition (); commandPos; ) { command = App.m_CommandList.GetNext (commandPos); ar.WriteString ("#COMMAND" ENDLINE); // these two lines are required ar.WriteString (CFormat ("Name %s~" ENDLINE, (LPCTSTR) command->name)); ar.WriteString (CFormat ("Code %s" ENDLINE, (LPCTSTR) command->do_fun)); ar.WriteString (CFormat ("Position %i" ENDLINE, command->position)); ar.WriteString (CFormat ("Level %i" ENDLINE, command->level)); ar.WriteString (CFormat ("Log %i" ENDLINE, command->log)); if (command->flags) ar.WriteString (CFormat ("Flags %i" ENDLINE, command->flags)); ar.WriteString ("End" ENDLINE); // end of this command ar.WriteString (ENDLINE); // blank line } // end of each command ar.WriteString ("#END" ENDLINE); // commands file terminator ar.Close (); } catch (CException* e) { e->ReportError(); e->Delete(); } m_bCommandsDirty = false; } void CAreaEditorApp::OnUpdateCommandsSave(CCmdUI* pCmdUI) { pCmdUI->Enable (!m_CommandList.IsEmpty () && m_bCommandsDirty); } void CAreaEditorApp::OnUpdateCommandsLoad(CCmdUI* pCmdUI) { pCmdUI->Enable (m_CommandList.IsEmpty ()); } void CAreaEditorApp::OnCommandsClose() { if (m_bCommandsDirty) { int iResponse = ::AfxMessageBox ("Commands file has changed. Save it?", MB_YESNOCANCEL | MB_ICONQUESTION); if (iResponse == IDCANCEL) return; if (iResponse == IDYES) OnCommandsSave (); } DeleteCommands (); } void CAreaEditorApp::OnUpdateCommandsClose(CCmdUI* pCmdUI) { pCmdUI->Enable (!m_CommandList.IsEmpty ()); } void CAreaEditorApp::DeleteCommands (void) { // ======================================================================== // delete all commands for (POSITION commandPos = m_CommandList.GetHeadPosition (); commandPos; ) delete m_CommandList.GetNext (commandPos); m_CommandList.RemoveAll (); // pointers are deleted, remove list items m_bCommandsDirty = false; } /* * Compare strings, case insensitive, for prefix matching. * Return TRUE if astr not a prefix of bstr */ bool str_prefix (const char *astr, const char *bstr) { if ( !astr ) return TRUE; if ( !bstr ) return TRUE; if (*astr == 0) return TRUE; for ( ; *astr; astr++, bstr++ ) { if ( LOWER(*astr) != LOWER(*bstr) ) return TRUE; } return FALSE; } int CompareCommand (const void * elem1, const void * elem2) { // ah yes, *this* line was fun to get working :) return (*((CCommand **) elem1))->name.CompareNoCase ((*((CCommand **) elem2))->name); } void CAreaEditorApp::SortCommands (void) { POSITION CommandPos; CTypedPtrArray <CPtrArray, CCommand*> CCommandsArray; int iCount = App.m_CommandList.GetCount (); int i; try { CCommandsArray.SetSize (iCount); // extract pointers into a simple array for (i = 0, CommandPos = m_CommandList.GetHeadPosition (); CommandPos; i++) CCommandsArray.SetAt (i, m_CommandList.GetNext (CommandPos)); // sort the array qsort (CCommandsArray.GetData (), iCount, sizeof (CCommand *), CompareCommand); // get rid of old list m_CommandList.RemoveAll (); // re-add in sorted order for (i = 0; i < iCount; i++) m_CommandList.AddTail (CCommandsArray [i]); } // end of try block catch(CException* e) { e->ReportError (); e->Delete(); } }