SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (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.    *
 * ------------------------------------------------------------------------ *
 *			    Smaug Menus Implementation file									*
 ****************************************************************************/

#include	"stdafx.h"

#define		MENUS_CPP

#include	"smaug.h"
#include	"SysData.h"
#include	"mobiles.h"
#include	"affect.h"
#include	"objects.h"
#include	"rooms.h"
#include	"menus.h"
#include	"descriptor.h"
#include	"character.h"

void	MenuStringEditReturn (CCharacter* ch, BOOL bDone);


void CSmaugMenu::DisplayData (CCharacter& Ch)
{
	CMenuFnData	MfData;

	if (m_pPageData) {
		for (int i=0; i < m_nDataItems; ++i) {
			CMenuData	&Md = m_pPageData [i];

			void	*addr = m_Base [Md.Base] + Md.Offset;
			int		len = Md.Len;

			switch (Md.Type) {
			  case MT_INT:
				Ch.SendColorf ("\x1b[%d;%dH&p%-*d", Md.y, Md.x, len,
					*(int*) addr);
				break;

			  case MT_SINT:
				Ch.SendColorf ("\x1b[%d;%dH&p%-+*d ", Md.y, Md.x, len,
					*(int*) addr);
				break;

			  case MT_BOOL:
				{
				BOOL	b = *(BOOL*) addr;
				Md.SetCheck (Ch, b, TRUE);	// TRUE for soft color
				}
				break;

			  case MT_GROUP: 
				MfData.pMd = &Md;
				MfData.Base = addr;
				MfData.ch = &Ch;
				HandleBooleanGroup (MfData, TRUE);	// TRUE for display
				break;

			  case MT_RIS:
				Ch.SendColorf ("\x1b[%d;%dH&p%-*.*s", Md.y, Md.x, len, len, addr);
				break;

			  case MT_CSTRING: 
				if (Md.MenuFn) {
					MfData.Op = MF_DISP;
					MfData.pMd = &Md;
					MfData.Base = m_Base [Md.Base];
					MfData.ch = &Ch;
					Md.MenuFn (MfData);
				} else {
					CString	&s = *(CString*) addr;
					Ch.SendColorf ("\x1b[%d;%dH&p%-*.*s", Md.y, Md.x,
						len, len, NCCP s);
				}
				break;

			  case MT_STRING:
				Ch.SendColorf ("\x1b[%d;%dH&p%-*.*s", Md.y, Md.x, len, len,
					*(char**) addr);
			}
  		}
	}

	Ch.SendTextf ("\x1b[%d;1H", m_CurrPageLines + 1);
}


BOOL CSmaugMenu::Edit (CCharacter& Ch, char* arg, const char* cmd)
{
	CMenuFnData	MfData;
	char		arg1 [MAX_STRING_LENGTH];

	if (! stricmp (cmd, "Unlock")) {
		Ch.LockMenus (FALSE);
		ClearToEnd (Ch);
		Ch.SendText ("OK");
		return TRUE;
	}
	if (! stricmp (cmd, "Lock")) {
		Ch.LockMenus (TRUE);
		ClearToEnd (Ch);
		Ch.SendText ("OK");
		return TRUE;
	}
	else if (! stricmp (cmd, "pagelen")) {
		Ch.SetMenuPagelen (max (15, atoi (arg)));
		ClearToEnd (Ch);
		Ch.SendTextf ("Page length set to %d.", Ch.GetMenuPagelen ());
		return TRUE;
	}
	if (m_pPageData) {
		for (int i=0; i < m_nDataItems; ++i) {
			CMenuData	&Md = m_pPageData [i];
			// IF 1st char matches && 2nd CHAR MATCHES...
			if (Md.Section == cmd [0]) {
				// just check the 1st char
				if (Md.Choice == arg [0]) {
					BOOL	bClearToEnd = TRUE;
					void	*addr = m_Base [Md.Base] + Md.Offset;
					int		len = Md.Len;
					arg = one_argument (arg, arg1);

					switch (Md.Type) {
					  case MT_INT: 
						{
						int		&i = *(int*) addr;
						i = atoi (arg);
						Ch.SendColorf ("\x1b[%d;%dH&P%-*d", Md.y, Md.x, len, i);
						}
						break;

					  case MT_SINT: 
						{
						int		&i = *(int*) addr;
						i = atoi (arg);
						Ch.SendColorf ("\x1b[%d;%dH&P%-+*d ", Md.y, Md.x, len, i);
						}
						break;

					  case MT_BOOL: 
						{
						BOOL	&b = *(BOOL*) addr;
						b ^= 1;							// toggling
						Md.SetCheck (Ch, b);
						}
						break;

					  case MT_GROUP: 
						MfData.pMd = &Md;
						MfData.Base = addr;
						MfData.ch = &Ch;
						HandleBooleanGroup (MfData, FALSE);	// FALSE for set
						break;

					  case MT_RIS: 
						{
						int		i;
						char	*ptr = (char*) addr;
						char	c = toupper (arg [0]);
						if (c == 'R') then i = 0;
						else if (c == 'I') then i = 1;
						else if (c == 'S') then i = 2;
						else return FALSE;
						if (c == ptr [i]) then c = '.';
						ptr [i] = c;
						Ch.SendColorf ("\x1b[%d;%dH&P%c", Md.y, Md.x + i, c);
						}
						break;

					  case MT_STRING: 
						Ch.SendColorf ("\x1b[%d;%dH&P%-*.*s", Md.y, Md.x,
							len, len, arg);
						break;

					  case MT_CSTRING: 
						if (Md.MenuFn) {
							MfData.Op = MF_EDIT;
							MfData.pMd = &Md;
							MfData.Base = m_Base [Md.Base];
							MfData.ch = &Ch;
							MfData.pArg = arg;
							bClearToEnd = Md.MenuFn (MfData);
						} else {
							CString	&s = *(CString*) addr;
							s = arg;
							Ch.SendColorf ("\x1b[%d;%dH&P%-*.*s%s", Md.y, Md.x,
								len, len, arg,
								s.GetLength () > len ? "..." : "   ");
						}
						break;
					}
					if (bClearToEnd) then ClearToEnd (Ch);
					return TRUE;
				}
			}
		}
	}

	if (Ch.AreMenusLocked ()) {
		ClearToEnd (Ch);
		Ch.SendTextf ("Invalid Menu Command: %s", cmd);
		return TRUE;
	}
	return FALSE;
}


void CSmaugMenu::ClearToEnd (CCharacter& Ch)
{
	int		nLines = 4;
	if (Ch.GetMenuPagelen () > (m_CurrPageLines + 4))
		nLines = Ch.GetMenuPagelen () - m_CurrPageLines;

	for (int n=1; n <= nLines; ++n)
		Ch.SendTextf ("\x1b[%d;1H\x1b[K", m_CurrPageLines + n);
	Ch.SendTextf ("\x1b[%d;1H", m_CurrPageLines + 1);
}


int GetLineCount (const char* str)
{
	int		Count = 0;

	while (*str)
		if (*str++ == '\n') then ++Count;

	return Count;
}


int GetItemCount (CMenuData* pMd)
{
	int		Count = 0;

	while (pMd [Count].Type != MT_END)
		++Count;

	return Count + 1;
}


int GetAffect (CAffectList& AList, int apply)
{
	int		total = 0;

	POSITION	pos = AList.GetHeadPosition ();
	while (pos) {
		CAffectData	*aff = AList.GetNext (pos);
		if (aff->location == apply) {
			total += aff->modifier;
		}
	}
	return total;
}


BOOL GetAffected (CAffectList& AList, int bit)
{
	POSITION	pos = AList.GetHeadPosition ();
	while (pos) {
		CAffectData	&Af = *AList.GetNext (pos);
		if (Af.location == APPLY_AFFECT
			&& IS_SET (Af.modifier, 1 << bit))
				return TRUE;
	}

	return FALSE;
}


void SetRisString (char* str, CAffectList& AList, int bit)
{
	strcpy (str, "...");

	POSITION	pos = AList.GetHeadPosition ();
	while (pos) {
		CAffectData	*aff = AList.GetNext (pos);
		if (aff->location >= APPLY_RESISTANT
		  && aff->location <= APPLY_SUSCEPTIBLE) {
			if (IS_SET (aff->modifier, bit))
				switch  (aff->location) {
				  case APPLY_RESISTANT:
					str [0] = 'R';
					break;
				  case APPLY_IMMUNE:
					str [1] = 'I';
					break;
				  case APPLY_SUSCEPTIBLE:
					str [2] = 'S';
					break;
				}
		}
	}
}


void SetAffect (CAffectList& AList, int apply, int value)
{
	CAffectData	*aff = new CAffectData;
	aff->location = apply;
	aff->modifier = value;

	AList.AddTail (aff);
}


CAffectData	*CAffectList::GetAffect (int loc)
{
	POSITION	apos = GetHeadPosition ();
	while (apos) {
		CAffectData	*aff = GetNext (apos);
		if (aff->location == loc) then return aff;
	}
	return NULL;
}



CString GetNextString (CString& Str, int len, BOOL bLast);


BOOL MenuStringEditFn (CMenuFnData& Fd)
{
	if (Fd.Op == MF_DISP) then return MenuStringFn (Fd);

	CMenuData	&Md = *Fd.pMd;

	if (Md.Type != MT_CSTRING) then return TRUE;

	CString	&s = *(CString*) ((char*) Fd.Base + Md.Offset);

	CCharacter	&Ch = *Fd.ch;
	Ch.tempnum = SUB_NONE;
	Ch.SetSubstate (SUB_ROOM_DESC);	// have to have something here
	Ch.dest_buf = NULL;
	Ch.GetMenu ().m_pRetFun = MenuStringEditReturn;
	Ch.GetMenu ().m_pRetData = &s;

	Ch.SendText ("\x1b[2J");		// clear page & home cursor
	start_editing (&Ch, s);

	return FALSE;		// FALSE means don't clear rest of screen
}


void MenuStringEditReturn (CCharacter* ch, BOOL bDone)
{
	CSmaugMenu	&Menu = ch->GetMenu ();

	if (bDone) {
		CString	&s = *(CString*) Menu.m_pRetData;
		char	*ptr = ch->GetEditBuffer ();
		s = ptr;
		STRFREE (ptr);
	}
	ch->StopEditing ();
	Menu.Display (*ch, Menu.m_PageNr);
}


BOOL MenuStringFn (CMenuFnData& Fd)
{
	CMenuData	&Md = *Fd.pMd;
	CCharacter	&Ch = *Fd.ch;

	if (Md.Type != MT_CSTRING) then return FALSE;

	CString	&s = *(CString*) ((char*) Fd.Base + Md.Offset);
	if (Fd.Op == MF_EDIT)
		s = Fd.pArg;

	CString		Str = s;
	Ch.StartColorStack ();
	set_char_color (Fd.Op == MF_EDIT ? AT_PINK : AT_PURPLE, &Ch);

	for (int n=0; n <= Md.Xlines; ++n) {
		BOOL	bFirst = n == 0;
		int		len = bFirst ? Md.Len : Md.Xlen;

		CString OutStr = GetNextString (Str, len, n == Md.Xlines);

		Ch.SendColorf ("\x1b[%d;%dH%-*.*s", Md.y + n,
			bFirst ? Md.x : Md.Xcol, len, len, OutStr);
	}
	Ch.EndColorStack ();

	return TRUE;
}


BOOL HandleBooleanGroup (CMenuFnData& Fd, BOOL bDisplay)
{
	CMenuData	*pMd = Fd.pMd;
	CCharacter	&Ch = *Fd.ch;

	UINT	nCur = pMd [0].Xlines;
	UINT	nGroup = pMd [0].Xcol;
	UINT	&val = *(UINT*) Fd.Base;

	if (bDisplay)
		pMd [0].SetCheck (Ch, nCur == (int) val, bDisplay);

	// Make sure we have valid indexes (just skip it if not).
	else if (val < nGroup && nCur < nGroup) {
		// Set the pointer to the first MenuData item in the group
		pMd -= nCur;

		pMd [val].SetCheck (Ch, FALSE);	// Turn off the old check

		val = nCur;						// Update the value

		pMd [val].SetCheck (Ch, TRUE);	// Turn on the new check
	}

	return FALSE;
}


void CMenuData::SetCheck (CCharacter& Ch, BOOL bSet, BOOL bDisplay /* = FALSE */)
{
	// bSet indicates to set or unset the check.
	// bDisplay indicates to use soft color.
	Ch.SendColorf ("\x1b[%d;%dH&%c%c", y, x, bDisplay ? 'p' : 'P',
		bSet ? 'X' : ' ');
}


CString GetNextString (CString& Str, int len, BOOL bLast)
{
	CString	Line;

	if (len > 0) {
		char	*ptr = NCCP Str;
		int     i;
		for (i=0; *ptr; ++i, ++ptr)
			if (*ptr == '\r' || *ptr == '\n') then break;

		int	npos = *ptr ? i : 0;
		int nlen;

		// Get length of eol
		for (nlen=0; *ptr && (*ptr == '\r' || *ptr == '\n'); ++nlen)
			++ptr;

		if (npos && npos <= len) {
			int	LineLen = npos;
			if (bLast && *ptr) then LineLen = min (npos, len-3);

			Line = Str.Left (LineLen);
			if (bLast && *ptr) then Line += "...";
			Str = Str.Mid (npos + nlen);
		}
		else if (npos && npos > len) {
			Line = Str.Left (bLast ? len-3 : len);
			if (bLast) then Line += "...";
			else Str = Str.Mid (len);
		}
		else if (Str.GetLength () > len) {
			Line = Str.Left (bLast ? len-3 : len);
			if (bLast) then Line += "...";
			else Str = Str.Mid (len);
		} else {
			Line = Str;
			Str.Empty ();
		}
	}
	return Line;
}


void do_pagelen (CCharacter* ch, char* argument)
{
	char	buf [MAX_STRING_LENGTH];
	char	arg [MAX_INPUT_LENGTH];
	int		lines;

	one_argument (argument, arg);

	if (arg [0] == '\0')
		lines = 24;
	else
		lines = atoi (arg);

	if (lines < 1) {
		ch->SendText ("Negative or Zero values for a page length is not legal.\n\r");
		return;
	}

	ch->SetMenuPagelen (lines);
	sprintf (buf, "Page length set to %d lines.\n\r", lines);
	ch->SendText (buf);
}