/**************************************************************************** * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | * * -----------------------------------------------------------| \\._.// * * SmaugWizard (C) 1998 by Russ Pillsbury (Windows NT version)| (0...0) * * -----------------------------------------------------------| ).:.( * * SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} * * -----------------------------------------------------------| / ' ' \ * * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~* * Scryn, Swordbearer, Rennard, Tricops, and Gorog. | * * ------------------------------------------------------------------------ * * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe. * ****************************************************************************/ // SmaugWizDoc.cpp : implementation of the CSmaugWizDoc class #include "stdafx.h" #include "SmaugWiz.h" #define SMAUGSERVER_CPP #include "swtime.h" #include "smaug.h" #include "SmaugSocket.h" #include "SysData.h" #include "language.h" #include "skill.h" #include "social.h" #include "commands.h" #include "area.h" #include "mobiles.h" #include "objects.h" #include "rooms.h" #include "boards.h" #include "help.h" #include "log.h" #include "races.h" #include "class.h" #include "ActFlags.h" #include "Affect.h" #include "Exits.h" #include "SmaugWizDoc.h" #include "SmaugFiles.h" #include "character.h" #include "descriptor.h" #include "Smaugx.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif char *ProgVersion = "2.03"; void SetHelpFilePath (); CString MakeKeyFromPath (CString s); ///////////////////////////////////////////////////////////////////////////// // CSmaugWizDoc IMPLEMENT_DYNCREATE(CSmaugWizDoc, CDocument) BEGIN_MESSAGE_MAP(CSmaugWizDoc, CDocument) //{{AFX_MSG_MAP(CSmaugWizDoc) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CSmaugWizDoc construction/destruction CSmaugWizDoc::CSmaugWizDoc () { #ifdef _DEBUG afxDump.SetDepth (1); // Specifies deep dump #endif gpDoc = this; // Set up global pointer m_pLog = NULL; m_pControl = NULL; m_bInLoop = FALSE; m_bShuttingDown = m_bDenyNewPlayers = FALSE; m_ScrPos = 0; bSmaugDown = TRUE; } CSmaugWizDoc::~CSmaugWizDoc() { delete m_pControl; delete m_pLog; } BOOL CSmaugWizDoc::OnNewDocument() { FileTable.m_FileRoot = GetRegisteredSmaugWizPath ("SmaugWiz"); SetHelpFilePath (); m_StartTime.SetCurrentTime (); SetTitle (); // set the window title string SysData.Load (); if (SysData.IsAutoStart ()) { m_TimeTilStart = SysData.GetStartDelay (); POSITION pos = GetFirstViewPosition (); GetNextView (pos)->SetTimer (3, 1000, NULL); // 1 Second } AllAreasList.RemoveAll (); AllAreasList.AddTail ((void*) &AreaList); AllAreasList.AddTail ((void*) &BuildList); return CDocument::OnNewDocument (); } BOOL CSmaugWizDoc::BootAndStart () { CString Msg; m_bReboot = FALSE; m_bInLoop = FALSE; m_bShuttingDown = FALSE; m_bDenyNewPlayers = FALSE; m_ScrPos = 0; bSmaugDown = TRUE; m_ExceptionCount = 0; if (! FileTable.Exists (FileTable.GetDir (SD_SYSTEM_DIR))) { CString s = "No System Directory. The SmaugWizard system directory\n" "does not exist or it is incorrectly set.\n" "(it is currently set to " + FileTable.GetDir (SD_SYSTEM_DIR) + ")."; MessageBox (0, s, "Serv", MB_ICONHAND | MB_OK); return FALSE; } // Reserve two channels for our use. if ((fpReserve = fopen (FileTable.GetName (SM_NULL_FILE), "a")) == NULL) { MessageBox (0, "Open Failure on Reserve File", "Serv", MB_ICONHAND | MB_OK); return FALSE; } if ((fpLOG = fopen (FileTable.GetName (SM_NULL_FILE), "a")) == NULL) { MessageBox (0, "Open Failure on Log File", "Serv", MB_ICONHAND | MB_OK); return FALSE; } SysData.Load (); if (! m_pLog) m_pLog = new CLog (STDERR_FILE); m_pLog->SetFromSysData (); m_pLog->SetType (LOG_NORMAL); // always log normal messages if (m_pLog->GetSize () > (SysData.GetMaxStdErrSize () * 0x100000)) m_pLog->Archive (); SetBootTime (); // must do before boot_db LogStringf (LOG_NORMAL, LEVEL_LOG, "SmaugWizard %s, Booting Database", ProgVersion); TRY boot_db (); CATCH (CException, ex) return FALSE; END_CATCH m_pControl = new CSmaugSocket (this); if (! m_pControl->Create (SysData.GetPort ())) { Msg.Format ("Create Failure %d on Control Socket", GetLastError ()); MessageBox (0, Msg, "Serv", MB_ICONHAND | MB_OK); return FALSE; } if (! m_pControl->Listen ()) { Msg.Format ("Listen Failure %d on Control Socket", GetLastError ()); MessageBox (0, Msg, "Serv", MB_ICONHAND | MB_OK); return FALSE; } // Run the game. LogString ("Initializing sockets"); // Tell Winsock we are listening for accepted sockets if (! m_pControl->AsyncSelect (FD_ACCEPT)) { Msg.Format ("AsyncSelect failure %d on FD_ACCEPT", GetLastError ()); MessageBox (0, Msg, "Serv", MB_ICONHAND | MB_OK); m_pControl->Close (); return FALSE; } Msg.Format ("SmaugWizard Ready: port %d.", SysData.GetPort ()); LogString (Msg); return TRUE; } // Remove this define to test exceptions in debug mode. #ifdef _DEBUG #define NOEXCEPTIONS #endif void CSmaugWizDoc::GameLoop (CDC* pDC) { static int count = 0; CDescriptor *pCurDes = NULL; if (m_bInLoop || bSmaugDown) return; #ifndef NOEXCEPTIONS try { #endif m_bInLoop = TRUE; CurrentTime.SetCurrentTime (); // This loop is entered 4 times per second. if (! m_bShuttingDown) { // For each Descriptor, kick out descriptors which have been // idle. Then process input. POSITION pos = DList.GetHeadPosition (); while (pos) { pCurDes = DList.GetNext (pos); CDescriptor &Ds = *pCurDes; CCharacter &Ch = *Ds.m_pCharacter; Ds.m_Idle++; if ((! Ds.m_pCharacter && Ds.m_Idle > 360) // 1.5 mins || (Ds.m_Connected != CON_PLAYING && Ds.m_Idle > 1200) // 5 mins || Ds.m_Idle > 28800) { // 2 hrs Ds.WriteToDescriptor ("Idle timeout... disconnecting.\n\r"); RemoveCharacter (Ds); Ds.m_Outtop = 0; continue; } // Don't process any more input if char shutting down. if (Ds.IsDisconnecting ()) continue; Ds.m_bFcommand = FALSE; if (&Ch && Ch.GetWait () > 0) { Ch.AddWait (-1); continue; } Ds.ReadFromBuffer (); if (Ds.IsCommandReady ()) { Ds.m_bFcommand = TRUE; Ds.m_Idle = 0; if (&Ch) Ch.StopIdling (); if (Ds.pagepoint) Ds.GetPagerInput (); else switch (Ds.m_Connected) { case CON_PLAYING: interpret (&Ch, Ds.m_Incomm); break; case CON_EDITING: Ch.Edit (Ds.m_Incomm); break; default: nanny (&Ds, Ds.m_Incomm); break; } Ds.m_Incomm [0] = 0; } } update_handler (); check_requests (); // Check REQUESTS pipe } // Output. POSITION pos = DList.GetHeadPosition (); while (pos) { POSITION CurPos = pos; pCurDes = DList.GetNext (pos); CDescriptor &Ds = *pCurDes; if ((Ds.m_bFcommand || Ds.m_Outtop > 0)) { if (! Ds.ProcessOutput (TRUE) && WSAGetLastError () != WSAEWOULDBLOCK) { if (Ds.m_pCharacter != NULL) save_char_obj (Ds.m_pCharacter); Ds.m_Outtop = 0; RemoveCharacter (Ds); } } // Note that at this point everything having to do with // the char has been saved and freed. We kept the // descriptor around long enough to output the final // messages. if (Ds.IsDisconnecting () && Ds.m_Outtop == 0) { Ds.Shutdown (); // close socket & free pointers // Now remove the descriptor from the descriptor list // and add it to the free list. DList.RemoveAt (CurPos); DFreeList.AddHead (&Ds); } } if (m_bShuttingDown) if (CloseAllDescriptors ()) { SysData.LogBytes (); LogString ("Normal termination of game.\n\n"); fclose (fpReserve); fclose (fpLOG); bSmaugDown = TRUE; } m_bInLoop = FALSE; #ifndef NOEXCEPTIONS } #ifdef _DEBUG #undef MAX_EXCEPTIONS #define MAX_EXCEPTIONS 10 #endif catch (CSwException ex) { if (m_ExceptionCount < MAX_EXCEPTIONS) { if (pCurDes) { pCurDes->m_Incomm [0] = 0; pCurDes->m_Outtop = 0; } LogString (ex.m_buf, LOG_COMM, 61); LogStringf (LOG_COMM, 61, "Exception %d. Attempting to continue\n", ++m_ExceptionCount); } else DoEmergencyReboot (); } catch (CException* ex) { char buf [128]; UINT id; if (ex->GetErrorMessage (buf, sizeof (buf), &id)) { LogStringf (LOG_COMM, 61, "CException %u:%s\n", id, buf); DoEmergencyReboot (); } else throw ex; } catch (...) { LogString ("Unknown CException.\n", LOG_COMM, 61); DoEmergencyReboot (); } #endif m_bInLoop = FALSE; } void CSmaugWizDoc::DoEmergencyReboot () { LogString ("Unable to continue. Attempting Reboot.\n", LOG_COMM, 61); if (! bSmaugDown) { do_shutdown (supermob, "mud now"); CloseAllDescriptors (TRUE); // TRUE means force close SysData.LogBytes (); fclose (fpReserve); fclose (fpLOG); bSmaugDown = TRUE; SetReboot (); } } void CSmaugWizDoc::LogToScreen (LogTypes type, const char* msg) { if (! SysData.IsLoggingToScreen (type)) return; CString s = msg; while (! s.IsEmpty ()) { if (m_ScrPos >= MAX_LINES) { memmove (&m_ScrBuf [0], &m_ScrBuf [1], LINE_LEN * (MAX_LINES - 1)); --m_ScrPos; } int len = s.Find ('\n'); if (len < 0) len = s.GetLength (); strcpy (m_ScrBuf [m_ScrPos], s.Left (len)); if (len < s.GetLength ()) s = s.Mid (len+1); else s.Empty (); int NewLine = m_ScrPos++; UpdateAllViews (NULL, NewLine); } } char *CSmaugWizDoc::GetData (int row) { char *s = ""; if (row < m_ScrPos) s = (char*) &m_ScrBuf [row]; return s; } void CSmaugWizDoc::LogStringf (LogTypes type, short level, char *fmt, ...) { char buf [MAX_STRING_LENGTH*2]; // better safe than sorry va_list args; va_start (args, fmt); vsprintf (buf, fmt, args); va_end (args); LogString (buf, type, level); } void CSmaugWizDoc::LogString (const char *str, LogTypes type, short level) { LogToScreen (type, str); if (m_pLog) m_pLog->Printf (type, "%s :: %s\n", NCCP CurrentTime.GetString (), str); int offset = (strncmp (str, "Log ", 4)) ? 0 : 4; if (! bSmaugDown) switch (type) { case LOG_NORMAL: case LOG_ALWAYS: case LOG_HIGH: to_channel (str + offset, CHANNEL_LOG, "Log", level); break; case LOG_BUILD: if (SysData.IsLogBuildToChannel ()) to_channel (str + offset, CHANNEL_BUILD, "Build", level); break; case LOG_COMM: if (SysData.IsLogCommToChannel ()) to_channel (str + offset, CHANNEL_COMM, "Comm", level); break; case LOG_BUG: if (SysData.IsLogBugsToChannel ()) to_channel (str + offset, CHANNEL_LOG, "Bug", level); break; case LOG_PLAYER: if (SysData.IsLogPlayersToChannel ()) to_channel (str + offset, CHANNEL_LOG, "Player", level); break; } } void CSmaugWizDoc::SetBootTime () { // Init time. CurrentTime.SetCurrentTime (); m_BootTime = CurrentTime; // Init boot time. m_RebootTime.SetCurrentTime (); m_RebootTime.SetSecond (0); m_RebootTime.SetMinute (0); m_RebootTime.SetHour (SysData.GetRebootHour ()); m_RebootTime += SysData.GetRebootInterval (); m_RebootTime.SetManual (FALSE); } #ifdef XXXX new_boot_time = update_time (localtime (¤t_time)); // Copies *new_boot_time to new_boot_struct, and then points // new_boot_time to new_boot_struct again. -- Alty // RCP: This is because update_time returns a pointer to a static // structure which may get overwritten, thus we need to copy the data // into new_boot_struct and then point to it. It would be much simpler // to only use the new_boot_struct (or a SYSTEMTIME struct), and get it's // address when a pointer is needed. I'll fix this up someday :) RCP new_boot_struct = *new_boot_time; new_boot_time = &new_boot_struct; new_boot_time->tm_sec = 0; new_boot_time->tm_min = 0; new_boot_time->tm_hour = SysData.GetRebootHour (); new_boot_time->tm_mday += SysData.GetRebootInterval (); // ++new_boot_time->tm_mday; // if (new_boot_time->tm_hour > 12) // ++new_boot_time->tm_mday; // new_boot_time->tm_sec = 0; // new_boot_time->tm_min = 0; // new_boot_time->tm_hour = 6; // Update new_boot_time (due to day increment) new_boot_time = update_time (new_boot_time); new_boot_struct = *new_boot_time; new_boot_time = &new_boot_struct; new_boot_time_t = mktime (new_boot_time); // Set reboot time string for do_time SetRebootTime (*new_boot_time); } #endif ///////////////////////////////////////////////////////////////////////////// // CSmaugWizDoc serialization void CSmaugWizDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } } ///////////////////////////////////////////////////////////////////////////// // CSmaugWizDoc diagnostics #ifdef _DEBUG void CSmaugWizDoc::AssertValid() const { CDocument::AssertValid(); } void CSmaugWizDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CSmaugWizDoc commands void CSmaugWizDoc::SetTitle () { CString Tit; Tit.Format ("Smaug Wizard v%s", ProgVersion); CDocument::SetTitle (Tit); } void CSmaugWizDoc::ShutSmaugDown () { CString Msg = "You are forced from " + SysData.GetShortDesc () + " by a strong magical presence\n\ras life here is reconstructed."; echo_to_all (AT_YELLOW, Msg, ECHOTAR_ALL); m_bShuttingDown = TRUE; } BOOL CSmaugWizDoc::CloseAllDescriptors (BOOL bForce /* = FALSE */) { if (! DList.IsEmpty ()) { POSITION pos = DList.GetHeadPosition (); while (pos) { POSITION CurPos = pos; CDescriptor *d = DList.GetNext (pos); if (d->m_pCharacter) RemoveCharacter (*d); if (d->m_Outtop == 0 || bForce) { d->Shutdown (); // close socket & free pointers DList.RemoveAt (CurPos); // Remove from DList delete d; // kill it } } } if (DList.IsEmpty ()) { DList.RemoveAll (); // Clear out the free list while (! DFreeList.IsEmpty ()) delete (CDescriptor*) DFreeList.RemoveTail (); DFreeList.RemoveAll (); } return DList.IsEmpty (); } void RemoveCharacter (CDescriptor& Dclose) { if (Dclose.m_pSnoopBy) { Dclose.m_pSnoopBy->WriteToBuffer ( "Your victim has left the game.\n\r", 0); } // stop snooping everyone else POSITION pos = DList.GetHeadPosition (); while (pos) { CDescriptor &Ds = * (CDescriptor*) DList.GetNext (pos); if (Ds.m_pSnoopBy == &Dclose) Ds.m_pSnoopBy = NULL; } CCharacter *ch = Dclose.m_pCharacter; // Check for switched people who go link-dead. -- Altrag if (Dclose.m_pOriginal) { if (ch) do_return (ch, ""); else { bug ("RemoveCharacter: dclose->original without character %s", (Dclose.m_pOriginal->GetName () ? Dclose.m_pOriginal->GetName () : "unknown")); Dclose.m_pCharacter = Dclose.m_pOriginal; Dclose.m_pOriginal = NULL; } } ch = Dclose.m_pCharacter; // in case it got changed above! if (ch) { sprintf (log_buf, "Closing link to %s.", ch->GetName ()); gpDoc->LogString (log_buf, LOG_COMM, UMAX ((int)SysData.LogLevel, (int)ch->GetLevel ())); if (Dclose.m_Connected == CON_PLAYING || Dclose.m_Connected == CON_EDITING) { act (AT_ACTION, "$n has lost $s link.", ch, NULL, NULL, TO_ROOM); ch->SetDesc (NULL); } else { delete ch; Dclose.m_pCharacter = NULL; } } Dclose.SetDisconnecting (); } BOOL is_reserved_name (CString Name) { Name.MakeLower (); POSITION pos = ReservedNamesList.GetHeadPosition (); while (pos) { CString Rname = ReservedNamesList.GetNext (pos); if ((Rname [0] == '*' && Name.Find (Rname.Mid (1)) > -1) || ! Name.CompareNoCase (Rname)) return TRUE; } return FALSE; } // Parse a name for acceptability. BOOL check_parse_name (const char* name, BOOL bNewchar /* = TRUE */) { // Names checking should really only be done on new characters, otherwise // we could end up with people who can't access their characters. Would // have also provided for that new area havoc mentioned below, while still // disallowing current area mobnames. I personally think that if we can // have more than one mob with the same keyword, then may as well have // players too though, so I don't mind that removal. -- Alty if (bNewchar) if (is_reserved_name (name)) return FALSE; // Length restrictions. if (strlen (name) < 3) return FALSE; if (strlen (name) > 12) return FALSE; // Alphanumerics only. // Lock out IllIll twits. const char *pc; BOOL bOk = FALSE; for (pc = name; *pc != '\0'; pc++) { if (! isalpha (*pc)) return FALSE; if (LOWER (*pc) != 'i' && LOWER (*pc) != 'l') bOk = TRUE; } return bOk; } void write_to_pager (CDescriptor *d, const char *txt, int length) { if (length <= 0) length = strlen (txt); if (length == 0) return; if (!d->pagebuf) { d->pagesize = MAX_STRING_LENGTH; d->pagebuf = (char*) malloc (d->pagesize); } if (!d->pagepoint) { d->pagepoint = d->pagebuf; d->pagetop = 0; d->pagecmd = '\0'; } if (d->pagetop == 0 && !d->m_bFcommand) { d->pagebuf[0] = '\n'; d->pagebuf[1] = '\r'; d->pagetop = 2; } while ((ULONG)d->pagetop + (ULONG)length >= d->pagesize) { if (d->pagesize >= MAX_PAGER_BUFFER) { bug ("Pager overflow. Ignoring.\n\r"); d->pagetop = 0; d->pagepoint = NULL; free (d->pagebuf); d->pagebuf = NULL; d->pagesize = MAX_STRING_LENGTH; return; } d->pagesize *= 2; d->pagebuf = (char*) realloc (d->pagebuf, d->pagesize); if (! d->pagebuf) { perror ("realloc failure in pager"); abort (); } } strncpy (d->pagebuf + d->pagetop, txt, length); d->pagetop += length; d->pagebuf [d->pagetop] = '\0'; } void send_to_pager (const char *txt, CCharacter *ch) { ASSERT (ch); CDescriptor *d = ch->GetDesc (); if (txt && d) { ch = d->m_pOriginal ? d->m_pOriginal : d->m_pCharacter; if (ch->IsNpc () || ! ch->IsPagerOn ()) { d->m_pCharacter->SendText (txt); return; } write_to_pager (d, txt, 0); } } UCHAR ConvertATypeToColor (int AType); void set_char_color (short AType, CCharacter *ch) { CDescriptor *d = ch->GetDesc (); char buf [32]; if (!ch || !d) return; int len = MakeColorSequence (ConvertATypeToColor (AType), buf, d); if (len) d->WriteToBuffer (buf); } void set_pager_color (short AType, CCharacter *ch) { CDescriptor *d = ch->GetDesc (); char buf [32]; if (!ch || !d) return; int len = MakeColorSequence (ConvertATypeToColor (AType), buf, d); if (len) send_to_pager (buf, ch); } void pager_printf (CCharacter *ch, char *fmt, ...) { char buf [MAX_STRING_LENGTH*2]; va_list args; va_start (args, fmt); vsprintf (buf, fmt, args); va_end (args); send_to_pager (buf, ch); } char *obj_short (CObjData *obj) { static char buf [MAX_STRING_LENGTH]; if (obj->count > 1) { sprintf (buf, "%s (%d)", obj->GetShortDescr (), obj->count); return buf; } return NCCP obj->GetShortDescr (); } // The primary output interface for formatted output. // Major overhaul. -- Alty #define NAME(ch) (ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()) char *act_string (const char *format, CCharacter *to, CCharacter *ch, const void *arg1, const void *arg2) { static char *const he_she [] = { "it", "he", "she" }; static char *const him_her [] = { "it", "him", "her" }; static char *const his_her [] = { "its", "his", "her" }; static char buf [MAX_STRING_LENGTH]; char fname [MAX_INPUT_LENGTH]; char *point = buf; const char *str = format; const char *i; CCharacter *vch = (CCharacter*) arg2; CObjData *obj1 = (CObjData*) arg1; CObjData *obj2 = (CObjData*) arg2; while (*str != '\0') { if (*str != '$') { *point++ = *str++; continue; } ++str; if (!arg2 && *str >= 'A' && *str <= 'Z') { bug ("Act: missing arg2 for code %c:", *str); bug (format); i = " <@@@> "; } else { switch (*str) { default: bug ("Act: bad code %c.", *str); i = " <@@@> "; break; case 't': i = (char*) arg1; break; case 'T': i = (char*) arg2; break; case 'n': i = (to ? PERS (ch, to) : NAME (ch)); break; case 'N': i = (to ? PERS (vch, to) : NAME (vch)); break; case 'e': if (ch->GetSex () > 2 || ch->GetSex () < 0) { bug ("act_string: player %s has sex set at %d!", ch->GetName (), ch->GetSex ()); i = "it"; } else i = he_she [URANGE (0, ch->GetSex (), 2)]; break; case 'E': if (vch->GetSex () > 2 || vch->GetSex () < 0) { bug ("act_string: player %s has sex set at %d!", vch->GetName (), vch->GetSex ()); i = "it"; } else i = he_she [URANGE (0, vch->GetSex (), 2)]; break; case 'm': if (ch->GetSex () > 2 || ch->GetSex () < 0) { bug ("act_string: player %s has sex set at %d!", ch->GetName (), ch->GetSex ()); i = "it"; } else i = him_her [URANGE (0, ch->GetSex (), 2)]; break; case 'M': if (vch->GetSex () > 2 || vch->GetSex () < 0) { bug ("act_string: player %s has sex set at %d!", vch->GetName (), vch->GetSex ()); i = "it"; } else i = him_her [URANGE (0, vch->GetSex (), 2)]; break; case 's': if (ch->GetSex () > 2 || ch->GetSex () < 0) { bug ("act_string: player %s has sex set at %d!", ch->GetName (), ch->GetSex ()); i = "its"; } else i = his_her [URANGE (0, ch->GetSex (), 2)]; break; case 'S': if (vch->GetSex () > 2 || vch->GetSex () < 0) { bug ("act_string: player %s has sex set at %d!", vch->GetName (), vch->GetSex ()); i = "its"; } else i = his_her [URANGE (0, vch->GetSex (), 2)]; break; case 'q': i = (to == ch) ? "" : "s"; break; case 'Q': i = (to == ch) ? "your" : his_her [URANGE (0, ch->GetSex (), 2)]; break; case 'p': i = (!to || can_see_obj (to, *obj1) ? obj_short (obj1) : "something"); break; case 'P': i = (!to || can_see_obj (to, *obj2) ? obj_short (obj2) : "something"); break; case 'd': if (!arg2 || ((char*) arg2) [0] == '\0') i = "door"; else { one_argument ((char*) arg2, fname); i = fname; } break; } } ++str; while ((*point = *i) != '\0') ++point, ++i; } strcpy (point, "\n\r"); buf [0] = UPPER (buf [0]); return buf; } #undef NAME void act (short AType, const char *format, CCharacter *ch, const void *arg1, const void *arg2, int type) { ASSERT (ch); char *txt; CCharacter *to; CCharacter *vch = (CCharacter*) arg2; // Discard null and zero-length messages. if (!format || format [0] == '\0') return; if (!ch->GetInRoom ()) to = NULL; else if (type == TO_CHAR) to = ch; else to = ch->GetInRoom ()->first_person; // ACT_SECRETIVE handling if (ch->IsNpc () && ch->IsSecretive () && type != TO_CHAR) return; if (type == TO_VICT) { if (!vch) { bug ("Act: null vch with TO_VICT."); bug ("%s (%s)", ch->GetName (), format); return; } if (!vch->GetInRoom ()) { bug ("Act: vch in NULL room!"); bug ("%s -> %s (%s)", ch->GetName (), vch->GetName (), format); return; } to = vch; // to = vch->GetInRoom ()->first_person; } if (MOBtrigger && type != TO_CHAR && type != TO_VICT && to) { CObjData *to_obj; txt = act_string (format, NULL, ch, arg1, arg2); if (to->GetInRoom ()->m_Progtypes.IsSet (ACT_PROG)) rprog_act_trigger (txt, to->GetInRoom (), ch, (CObjData*) arg1, (void*) arg2); POSITION pos = to->GetInRoom ()->GetHeadContentPos (); while (to_obj = to->GetInRoom ()->GetNextContent (pos)) if (to_obj->pIndexData->HasActProg ()) oprog_act_trigger (txt, to_obj, ch, (CObjData*) arg1, (void*) arg2); } /* Anyone feel like telling me the point of looping through the whole room when we're only sending to one char anyways..? -- Alty */ for (; to; to = (type == TO_CHAR || type == TO_VICT) ? NULL : to->GetNextInRoom ()) { if ((!to->GetDesc () && (to->IsNpc () && ! to->GetMobIndex ()->m_Progtypes.IsSet (ACT_PROG))) || ! to->IsAwake ()) continue; if (type == TO_CHAR && to != ch) continue; if (type == TO_VICT && (to != vch || to == ch)) continue; if (type == TO_ROOM && to == ch) continue; if (type == TO_NOTVICT && (to == ch || to == vch)) continue; txt = act_string (format, to, ch, arg1, arg2); if (to->GetDesc ()) { set_char_color (AType, to); to->SendColor (txt); // to->GetDesc ()->WriteToBuffer (txt); } if (MOBtrigger) { // Note: use original string, not string with ANSI. -- Alty mprog_act_trigger (txt, to, ch, (CObjData*) arg1, (void*) arg2); } } MOBtrigger = TRUE; } void do_name (CCharacter *ch, char *argument) { ASSERT (ch); char arg1 [MAX_INPUT_LENGTH]; char arg2 [MAX_INPUT_LENGTH]; char *pArg; if (ch->IsNpc ()) return; if (ch->IsAuthed () || ch->GetPcData ()->auth_state != 2) { ch->SendText ("Huh?\n\r"); return; } // Can't use one_argument here because it smashes case. // So we just steal all its code. Bleagh. pArg = arg1; while (isspace (*argument)) argument++; char cEnd = ' '; if (*argument == '\'' || *argument == '"') cEnd = *argument++; while (*argument != '\0') { if (*argument == cEnd) { argument++; break; } *pArg++ = *argument++; } *pArg = '\0'; pArg = arg2; while (isspace (*argument)) argument++; cEnd = ' '; if (*argument == '\'' || *argument == '"') cEnd = *argument++; while (*argument != '\0') { if (*argument == cEnd) { argument++; break; } *pArg++ = *argument++; } *pArg = '\0'; if (arg1 [0] == '\0' || arg2 [0] == '\0') { ch->SendText ("Syntax: name <new> <password>.\n\r"); return; } arg1 [0] = toupper (arg1 [0]); if (! check_parse_name (arg1)) { ch->SendText ("Illegal name, try another.\n\r"); return; } if (! stricmp (ch->GetName (), arg1)) { ch->SendText ("That's already your name!\n\r"); return; } CCharacter *tmp; for (tmp = first_char; tmp; tmp = tmp->GetNext ()) { if (! stricmp (argument, tmp->GetName ())) break; } if (tmp) { ch->SendText ("That name is already taken. Please choose another.\n\r"); return; } CString Fname = FileTable.PlayerName (arg1); if (FileTable.Exists (Fname)) { ch->SendText ("That name is already taken. Please choose another.\n\r"); return; } if (strcmp (Crypt (arg2, ch->GetName ()), ch->GetPcData ()->GetPassWord ())) { ch->SendText ("Wrong password.\n\r"); return; } ch->SetName (arg1); CString NewPwd = Crypt (arg2, ch->GetName ()); delete ch->GetPcData ()->GetPassWord (); ch->GetPcData ()->SetPassWord (str_dup (NewPwd)); ch->SendText ("Your name has been changed. Please apply again.\n\r"); ch->GetPcData ()->auth_state = 0; } char *default_prompt (CCharacter *ch) { static char buf [60]; strcpy (buf, "&w<&Y%hhp "); if (ch->IsVampire ()) strcat (buf, "&R%bbp"); else strcat (buf, "&C%mm"); strcat (buf, " &G%vmv&w> "); if (ch->IsNpc () || ch->IsImmortal ()) strcat (buf, "%i%R"); return buf; } int getcolor (char clr) { static const char colors [] = "xrgObpcwzRGYBPCW"; int r; for (r = 0; r < sizeof (colors); r++) if (clr == colors [r]) return r; return -1; } UCHAR ConvertATypeToColor (int AType) { UCHAR col = AType & 0x0F; if (AType & 0x10) col |= 0x80; // set blink return col; } bool IsColor (const char* pCol) { if (pCol [0] != '&') return FALSE; if (pCol [1] == ']' || pCol [1] == '[' || getcolor (pCol [1]) > -1) return TRUE; return FALSE; } bool HasColor (const char* str) { int len = strlen (str); for (int i=0; i < len; ++i) if (str [i] == '&' && IsColor (&str [i])) return TRUE; return FALSE; } int ColorLen (const char* str) { int len = strlen (str); int j, i; if (! HasColor (str)) return len; for (j=0,i=0; i < len; ++i) { if (str [i] == '&' && IsColor (&str [i])) ++i; else ++j; } return j; } int GetColorSize (const char* str) { if (! HasColor (str)) return 0; int len = strlen (str); int j, i; for (j=0,i=0; i < len; ++i) { if (str [i] == '&' && IsColor (&str [i])) { j += 2; ++i; } } return j; } CString StripColor (const char* str) { if (! HasColor (str)) return str; int len = strlen (str); char *pBuf = new char [len + 1]; char *p1 = pBuf; for (int i=0; i < len; ++i) { if (*str == '&' && IsColor (str)) str += 2; else *p1++ = *str++; } *p1 = 0; CString s = pBuf; delete [] pBuf; return s; } int MakeColorSequence (UCHAR Col, char *buf, CDescriptor *d) { ASSERT (d); int ln; CCharacter *och; bool bAnsi; och = d->m_pOriginal ? d->m_pOriginal : d->m_pCharacter; bAnsi = ! och->IsNpc () && och->IsAnsi (); *buf = 0; if (! bAnsi) return 0; strcpy (buf, "\x1B["); // Set up escape sequence // if bold or blink are not the same as before if ((Col & 0x88) != (d->m_PrevColor & 0x88)) { strcat (buf, "0m\x1B["); // clear all text characteristics & set up new escape seq. if ((Col & 0x08)) strcat (buf, "1;"); // set BOLD if ((Col & 0x80)) strcat (buf, "5;"); // set BLINK ln = strlen (buf); } else ln = 2; // Set foreground and background colors sprintf (buf+ln, "3%d;4%dm", Col & 0x07, (Col & 0x70) >> 4); d->m_PrevColor = Col; return strlen (buf); } int MakeColorSequence (const char *col, char *buf, CDescriptor *d) { ASSERT (d); int ln; char ctype = *col; unsigned char cl; CCharacter *och; bool bAnsi; och = d->m_pOriginal ? d->m_pOriginal : d->m_pCharacter; bAnsi = ! och->IsNpc () && och->IsAnsi (); *buf = 0; ++col; if (! *col) return -1; if (ctype != '&' && ctype != '^') { bug ("MakeColorSequence: command '%c' not '&' or '^'.", ctype); return -1; } if (*col == ctype) { buf [0] = *col; buf [1] = '\0'; return 1; } if (! bAnsi) return 0; cl = d->m_PrevColor; if (ctype == '&' && *col == '-') { buf [0] = '~'; buf [1] = '\0'; return 1; } int newcol = getcolor (*col); if (newcol < 0) return 0; if (ctype == '&') cl = (cl & 0xF0) | newcol; else cl = (cl & 0x0F) | (newcol << 4); strcpy (buf, "\x1B["); // Set up escape sequence // if bold or blink are not the same as before if ((cl & 0x88) != (d->m_PrevColor & 0x88)) { strcat (buf, "0m\x1B["); // clear all text characteristics & set up new escape seq. if ((cl & 0x08)) strcat (buf, "1;"); // set BOLD if ((cl & 0x80)) strcat (buf, "5;"); // set BLINK ln = strlen (buf); } else ln = 2; // Set foreground and background colors sprintf (buf+ln, "3%d;4%dm", cl & 0x07, (cl & 0x70) >> 4); d->m_PrevColor = cl; return strlen (buf); } /* This is the old make color sequence code. It attempts to save a little bandwidth by not sending color codes when it detects that the new color has already been set on the user's terminal screen. This has been replaced with MakeColorSequence (above) which always sends out the necessary color sequences whenever colors are specified. (RCP) int make_color_sequence (const char *col, char *buf, CDescriptor *d) { ASSERT (d); int ln; const char *ctype = col; unsigned char cl; CCharacter *och; BOOL ansi; och = (d->m_pOriginal ? d->m_pOriginal : d->m_pCharacter); ansi = (! och->IsNpc () && och->IsAnsi ()); col++; if (!*col) ln = -1; else if (*ctype != '&' && *ctype != '^') { bug ("Make_color_sequence: command '%c' not '&' or '^'.", *ctype); ln = -1; } else if (*col == *ctype) { buf [0] = *col; buf [1] = '\0'; ln = 1; } else if (!ansi) ln = 0; else { cl = d->m_PrevColor; switch (*ctype) { default: bug ("Make_color_sequence: bad command char '%c'.", *ctype); ln = -1; break; case '&': if (*col == '-') { buf [0] = '~'; buf [1] = '\0'; ln = 1; break; } // NOTE FALL THRU !!! case '^': { int newcol; if ((newcol = getcolor (*col)) < 0) { ln = 0; break; } else if (*ctype == '&') cl = (cl & 0xF0) | newcol; else cl = (cl & 0x0F) | (newcol << 4); } if (cl == d->m_PrevColor) { // Removed RCP ln = 0; break; } strcpy (buf, "\033["); // clear all text characteristics // if bold or blink are not the same as before if ((cl & 0x88) != (d->m_PrevColor & 0x88)) { strcat (buf, "m\033["); if ((cl & 0x08)) strcat (buf, "1;"); // set BOLD if ((cl & 0x80)) strcat (buf, "5;"); // set BLINK d->m_PrevColor = 0x07 | (cl & 0x88); ln = strlen (buf); } else ln = 2; if ((cl & 0x07) != (d->m_PrevColor & 0x07)) { sprintf (buf+ln, "3%d;", cl & 0x07); // set foreground color ln += 3; } if ((cl & 0x70) != (d->m_PrevColor & 0x70)) { sprintf (buf+ln, "4%d;", (cl & 0x70) >> 4); // set background color ln += 3; } // make sure it is properly terminated if (buf [ln-1] == ';') buf [ln-1] = 'm'; else { buf [ln++] = 'm'; buf [ln] = '\0'; } d->m_PrevColor = cl; } } if (ln <= 0) *buf = '\0'; return ln; } */ void set_pager_input (CDescriptor *d, char *argument) { ASSERT (d); while (isspace (*argument)) argument++; d->pagecmd = *argument; } void ResendExitMsg () { // same as double-clicking on main window close box ASSERT (AfxGetMainWnd() != NULL); AfxGetMainWnd ()->SendMessage (WM_CLOSE); } void SetHelpFilePath () { extern CSmaugWizApp theApp; // This is a public CWinApp member variable named m_pszHelpFilePath that // the user can change if desired. CString Hlp = FileTable.GetDir (SD_SYSTEM_DIR) + "/SmaugWiz.hlp"; theApp.m_pszHelpFilePath = _strdup (Hlp); } CString CSmaugWizDoc::GetRegisteredSmaugWizPath (const char* name) { GetCurrentDirectory (256, m_CurrentDir.GetBuffer (256)); m_CurrentDir.ReleaseBuffer (); CString key; key.Format ("Software\\%s\\%s", name, NCCP MakeKeyFromPath (m_CurrentDir)); HKEY hk; int rv = RegOpenKeyEx (HKEY_CURRENT_USER, key, 0, KEY_READ, &hk); UCHAR buf [256]; if (rv == 0) { DWORD Size = sizeof (buf); rv = RegQueryValueEx (hk, "Root", 0, 0, buf, &Size); RegCloseKey (hk); } return rv ? m_CurrentDir : buf; } void CSmaugWizDoc::SetRegisteredSmaugWizPath (const char* name, CString& path) { HKEY hk; DWORD Disposition; CString key; key.Format ("Software\\%s\\%s", name, MakeKeyFromPath (m_CurrentDir)); int rv = RegCreateKeyEx (HKEY_CURRENT_USER, key, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hk, &Disposition); if (rv == 0) { RegSetValueEx (hk, "Root", 0, REG_SZ, (UCHAR*) NCCP path, path.GetLength ()); RegCloseKey (hk); } } CString MakeKeyFromPath (CString s) { for (int i=0; i < s.GetLength (); ++i) { char c = s [i]; if (c == '/' || c == '\\') s.SetAt (i, '_'); } return s; }