1stMUD4.0/bin/
1stMUD4.0/doc/MPDocs/
1stMUD4.0/player/
1stMUD4.0/win32/
1stMUD4.0/win32/rom/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2003 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
#include "merc.h"
#include "globals.h"
#include "recycle.h"
#include "interp.h"
#include "tables.h"

//Attempting to guess URL with hostname and username
//www url of the directory with mud images
#define IMAGE_URL FORMATF("http://www.%s/~%s/images", HOSTNAME, UNAME)
//www url of the directory with mud sounds
#define SOUND_URL FORMATF("http://www.%s/~%s/msp", HOSTNAME, UNAME)

void setup_mxp(DESCRIPTOR_DATA * d)
{
	if (!IS_MXP(d))
		return;

	d_print(d, MXPTAG
			("!ELEMENT RName '<FONT COLOR=Green>;<B>;' FLAG=\"RoomName\""), 0);
	d_print(d, MXPTAG("!ELEMENT RDesc FLAG='RoomDesc' OPEN"), 0);
	d_print(d, MXPTAG
			("!ELEMENT RExits '<FONT COLOR=Blue>;' FLAG='RoomExit' OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT Ex '<SEND>;'"), 0);
	d_print(d, MXPTAG("!ELEMENT Prompt FLAG=\"Prompt\" OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT Hp FLAG=\"Set hp\" OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT MaxHp FLAG=\"Set maxhp\" OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT Mana FLAG=\"Set mana\" OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT MaxMana FLAG=\"Set maxmana\" OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT Move FLAG=\"Set move\" OPEN"), 0);
	d_print(d, MXPTAG("!ELEMENT MaxMove FLAG=\"Set maxmove\" OPEN"), 0);
	/* Bid an item tag (for things in the auction) */
	d_print(d, MXPTAG
			("!ELEMENT Bid \"<send href='bid &#39;&name;&#39;' "
			 "hint='Bid for &desc;' prompt>\" ATT='name desc'"), 0);
	d_print(d, MXPTAG
			("!ELEMENT Help \"<send href='help &#39;&name;&#39;' "
			 "hint='Help on &desc;'>\" ATT='name desc'"), 0);
	/* List an item tag (for things in a shop) */
	d_print(d, MXPTAG
			("!ELEMENT List \"<send href='buy &#39;&name;&#39;' "
			 "hint='Buy &desc;'>\" ATT='name desc'"), 0);
	/* Player tag (for who lists, tells etc.) */
	d_print(d, MXPTAG
			("!ELEMENT Tell \"<send href='tell &#39;&name;&#39; ' "
			 "hint='print a message to &name;' prompt>\" ATT='name'"), 0);
	d_print(d, MXPTAG
			("!ELEMENT Fwho \"<send href='whois &#39;&name;&#39;|"
			 "buddy &#39;&name;&#39;' "
			 "hint='Right mouse click to use this object|Whois &desc;|"
			 "Buddy &desc;'>\" ATT='name desc'"), 0);
	d_print(d, MXPTAG
			("!ELEMENT Olc \"<send href='commands|show|done' "
			 "hint='Show commands|Show|Finish editing'>\""), 0);
	return;
}

void init_mxp(DESCRIPTOR_DATA * d)
{
	d_print(d, MXPTAG("VERSION"), 0);
	d_print(d, MXPTAG("SUPPORT"), 0);
}

CH_CMD(do_mxp)
{
	if (!ch->desc || IS_NPC(ch))
	{
		chprintln(ch, "No descriptor.");
		return;
	}

	set_on_off(ch, &ch->desc->d_flags, DESC_MXP,
			   "MUD eXtension Protocol enabled.",
			   "MUD eXtension Protocol disabled.");
	if (IS_SET(ch->desc->d_flags, DESC_MXP))
	{
		if ((ch->desc->mxp.mxp_ver * 10) < 4)
			init_mxp(ch->desc);
		else
			setup_mxp(ch->desc);
	}
}

/*
* Count number of mxp tags need converting
*    ie. < becomes &lt;
*        > becomes &gt;
*        & becomes &amp;
*/

int count_mxp_tags(DESCRIPTOR_DATA * d, const char *txt, int length)
{
	char c;
	const char *p;
	int count;
	bool bInTag = FALSE;
	bool bInEntity = FALSE;
	bool bMXP = IS_SET(d->d_flags, DESC_MXP);

	for (p = txt, count = 0; length > 0; p++, length--)
	{
		c = *p;

		if (bInTag)				/* in a tag, eg. <send> */
		{
			if (!bMXP)
				count--;		/* not output if not MXP */
			if (c == MXP_ENDc)
				bInTag = FALSE;
		}						/* end of being inside a tag */
		else if (bInEntity)		/* in a tag, eg. <send> */
		{
			if (!bMXP)
				count--;		/* not output if not MXP */
			if (c == ';')
				bInEntity = FALSE;
		}						/* end of being inside a tag */
		else
			switch (c)
			{

			case MXP_BEGc:
				bInTag = TRUE;
				if (!bMXP)
					count--;	/* not output if not MXP */
				else
					count += 4;	/* allow for ESC [1z */
				break;

			case MXP_ENDc:		/* shouldn't get this case */
				if (!bMXP)
					count--;	/* not output if not MXP */
				break;

			case MXP_AMPc:
				bInEntity = TRUE;
				if (!bMXP)
					count--;	/* not output if not MXP */
				break;

			default:
				if (bMXP)
				{
					switch (c)
					{
					case '<':	/* < becomes &lt; */
					case '>':	/* > becomes &gt; */
						count += 3;
						break;

					case '&':
						count += 4;	/* & becomes &amp; */
						break;

					case '"':	/* " becomes &quot; */
						count += 5;
						break;

					}			/* end of inner switch */
				}				/* end of MXP enabled */
			}					/* end of switch on character */

	}							/* end of counting special characters */

	return count;
}								/* end of count_mxp_tags */

void convert_mxp_tags(DESCRIPTOR_DATA * d, char *dest, const char *src,
					  int length)
{
	char c;
	const char *ps;
	char *pd;
	bool bInTag = FALSE;
	bool bInEntity = FALSE;
	bool bMXP = IS_SET(d->d_flags, DESC_MXP);

	for (ps = src, pd = dest; length > 0; ps++, length--)
	{
		c = *ps;
		if (bInTag)				/* in a tag, eg. <send> */
		{
			if (c == MXP_ENDc)
			{
				bInTag = FALSE;
				if (bMXP)
				{
					*pd++ = '>';
				}
			}
			else if (bMXP)
				*pd++ = c;		/* copy tag only in MXP mode */
		}						/* end of being inside a tag */
		else if (bInEntity)		/* in a tag, eg. <send> */
		{
			if (bMXP)
				*pd++ = c;		/* copy tag only in MXP mode */
			if (c == ';')
				bInEntity = FALSE;
		}						/* end of being inside a tag */
		else
			switch (c)
			{
			case MXP_BEGc:
				bInTag = TRUE;
				if (bMXP)
				{
					memcpy(pd, MXPMODE(1), 4);
					pd += 4;
					*pd++ = '<';
				}
				break;

			case MXP_ENDc:		/* shouldn't get this case */
				bInTag = FALSE;
				if (bMXP)
				{
					*pd++ = '>';
				}
				break;

			case MXP_AMPc:
				bInEntity = TRUE;
				if (bMXP)
					*pd++ = '&';
				break;

			default:
				if (bMXP)
				{
					switch (c)
					{
					case '<':
						memcpy(pd, "&lt;", 4);
						pd += 4;
						break;

					case '>':
						memcpy(pd, "&gt;", 4);
						pd += 4;
						break;

					case '&':
						memcpy(pd, "&amp;", 5);
						pd += 5;
						break;

					case '"':
						memcpy(pd, "&quot;", 6);
						pd += 6;
						break;

					default:
						*pd++ = c;
						break;	/* end of default */

					}			/* end of inner switch */
				}
				else
					*pd++ = c;	/* not MXP - just copy character */
				break;

			}					/* end of switch on character */
	}							/* end of converting special characters */
}								/* end of convert_mxp_tags */

const char *mxp_obj(OBJ_DATA * obj, CHAR_DATA * ch, bool fShort)
{
	if (!obj)
		return "!bug!";

	if (!ch || !IS_MXP(ch->desc))
		return fShort ? obj->short_descr : obj->description;

	if (obj->in_room)
	{
	}
	else if (obj->in_obj)
	{
	}
	else if (obj->in_room)
	{
	}
	else
	{
	}
	return fShort ? obj->short_descr : obj->description;
}

const char *mxp_char(CHAR_DATA * mch, CHAR_DATA * ch, bool fShort)
{

	if (!mch)
		return "!bug!";

	if (!ch || !IS_MXP(ch->desc))
		return fShort ? mch->short_descr : mch->long_descr;

	if (mch->in_room == ch->in_room)
	{
	}
	return fShort ? mch->short_descr : mch->long_descr;
}

const char *create_tag(CHAR_DATA * ch, const char *text, const char *hint,
					   const char *command, ...)
{
	va_list args;
	char format[MSL];
	static char buf_new[5][MSL];
	static int i;
	char *result;

	if (!ch || !ch->desc || IS_NULLSTR(command))
		return text;

	// rotate buffers
	++i;
	i %= 5;
	result = buf_new[i];

	va_start(args, command);
	vsnprintf(format, sizeof(format), command, args);
	va_end(args);

	if (IS_MXP(ch->desc))
	{
		if (!IS_NULLSTR(hint))
			sprintf(result,
					MXPTAG("send href='%s' hint='%s'") "%s"
					MXPTAG("/send"), format, hint, text);
		else
			sprintf(result, MXPTAG("send href='%s'") "%s" MXPTAG("/send"),
					format, text);
	}
	else if (IS_PUEBLO(ch->desc))
	{
		if (!IS_NULLSTR(hint))
			sprintf(result,
					"</xch_mudtext><img xch_mode=html><a xch_cmd=\"%s\" xch_hint=\"%s\">"
					"%s</a><br><img xch_mode=text>", format, hint, text);
		else
			sprintf(result,
					"</xch_mudtext><img xch_mode=html><a xch_cmd=\"%s\">"
					"%s</a><br><img xch_mode=text>", format, text);
	}
	else
		return text;

	return (result);
}

const struct flag_type mxp_support_flags[] = {
	{"a", BIT_A, FALSE},
	{"a.href", BIT_B, FALSE},
	{"a.xch_cmd", BIT_C, FALSE},
	{"a.xch_hint", BIT_D, FALSE},
	{"b", BIT_E, FALSE},
	{"body", BIT_F, FALSE},
	{"bold", BIT_G, FALSE},
	{"br", BIT_H, FALSE},
	{"c", BIT_I, FALSE},
	{"c.back", BIT_J, FALSE},
	{"c.fore", BIT_K, FALSE},
	{"color", BIT_L, FALSE},
	{"color.back", BIT_M, FALSE},
	{"color.fore", BIT_N, FALSE},
	{"em", BIT_O, FALSE},
	{"expire", BIT_P, FALSE},
	{"font", BIT_Q, FALSE},
	{"font.back", BIT_R, FALSE},
	{"font.bgcolor", BIT_S, FALSE},
	{"font.color", BIT_T, FALSE},
	{"font.fgcolor", BIT_U, FALSE},
	{"gauge", BIT_V, FALSE},
	{"h", BIT_W, FALSE},
	{"head", BIT_X, FALSE},
	{"high", BIT_Y, FALSE},
	{"hr", BIT_Z, FALSE},
	{"html", BIT_a, FALSE},
	{"i", BIT_b, FALSE},
	{"image", BIT_c, FALSE},
	{"image.url", BIT_d, FALSE},
	{"img", BIT_e, FALSE},
	{"img.src", BIT_f, FALSE},
	{"img.xch_mode", BIT_Ax, FALSE},
	{"italic", BIT_Bx, FALSE},
	{"li", BIT_Cx, FALSE},
	{"music", BIT_Dx, FALSE},
	{"mxp", BIT_Ex, FALSE},
	{"mxp.off", BIT_Fx, FALSE},
	{"nobr", BIT_Gx, FALSE},
	{"ol", BIT_Hx, FALSE},
	{"option", BIT_Ix, FALSE},
	{"p", BIT_Jx, FALSE},
	{"pass", BIT_Kx, FALSE},
	{"password", BIT_Lx, FALSE},
	{"pre", BIT_Mx, FALSE},
	{"relocate", BIT_Nx, FALSE},
	{"reset", BIT_Ox, FALSE},
	{"s", BIT_Px, FALSE},
	{"samp", BIT_Qx, FALSE},
	{"sbr", BIT_Rx, FALSE},
	{"send", BIT_Sx, FALSE},
	{"send.hint", BIT_Tx, FALSE},
	{"send.href", BIT_Ux, FALSE},
	{"send.prompt", BIT_Vx, FALSE},
	{"send.xch_cmd", BIT_Wx, FALSE},
	{"send.xch_hint", BIT_Xx, FALSE},
	{"sound", BIT_Yx, FALSE},
	{"stat", BIT_Zx, FALSE},
	{NULL, 0, FALSE}
};

const struct flag_type mxp_support_flags2[] = {
	{"strike", BIT_A, FALSE},
	{"strong", BIT_B, FALSE},
	{"support", BIT_C, FALSE},
	{"title", BIT_D, FALSE},
	{"u", BIT_E, FALSE},
	{"ul", BIT_F, FALSE},
	{"underline", BIT_G, FALSE},
	{"user", BIT_H, FALSE},
	{"username", BIT_I, FALSE},
	{"v", BIT_J, FALSE},
	{"var", BIT_K, FALSE},
	{"version", BIT_L, FALSE},
	{"xch_page", BIT_M, FALSE},
	{"dd", BIT_N, FALSE},
	{"dest", BIT_O, FALSE},
	{"dl", BIT_P, FALSE},
	{"dt", BIT_Q, FALSE},
	{"frame", BIT_R, FALSE},
	{"h1", BIT_S, FALSE},
	{"h2", BIT_T, FALSE},
	{"h3", BIT_U, FALSE},
	{"h4", BIT_V, FALSE},
	{"h5", BIT_W, FALSE},
	{"h6", BIT_X, FALSE},
	{"small", BIT_Y, FALSE},
	{"tt", BIT_Z, FALSE},
	{"xch_mudtext", BIT_a, FALSE},
	{"xch_pane", BIT_b, FALSE},
	{NULL, 0, FALSE}
};

flag_t mxp_lookup(const char *name, const struct flag_type *flag_table)
{
	int flag;

	for (flag = 0; flag_table[flag].name != NULL; flag++)
	{
		if (LOWER(name[0]) == LOWER(flag_table[flag].name[0])
			&& !str_cmp(name, flag_table[flag].name))
			return flag_table[flag].bit;
	}

	return 0;
}

void mxp_support(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf)
{
	unsigned char *buf = &inbuf[i];
	const char *supports;
	char arg[MIL];
	const char *argument;
	static char tbuf[MSL];
	int n = 10;
	flag_t bit;

	do
	{
		n++;
	}
	while (buf[n] != '>');
	buf[n] = NUL;

	sprintf(tbuf, "%s", buf + 10);

	buf[n] = '>';

	supports = str_dup(tbuf);

	n++;

	for (;;)
	{
		switch (buf[n])
		{
		default:
		case NUL:
			break;

		case '\n':
		case '\r':
			n++;
			continue;
			break;
		}
		break;
	}

	telopt_lskip = n + 9;

	if (supports)
	{
		argument = supports;
		tbuf[0] = NUL;

		do
		{
			argument = one_argument(argument, arg);
			if (arg[0] != NUL)
			{
				if ((bit = mxp_lookup(arg + 1, mxp_support_flags)) > 0)
				{
					SET_BIT(d->mxp.flags, bit);
				}
				else if ((bit = mxp_lookup(arg + 1, mxp_support_flags2)) > 0)
				{
					SET_BIT(d->mxp.flags2, bit);
				}
				else
					sprintf(tbuf + strlen(tbuf), " %s", arg);
			}
		}
		while (arg[0] != NUL);

		free_string(supports);
		replace_string(d->mxp.supports, tbuf);

	}
	return;

}

void mxp_version(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf)
{
	unsigned char *buf = &inbuf[i];
	char cbuf[MIL];
	char rbuf[MIL];
	char *arg;
	int n = 0;
	int c = 0;

	n = 0;
	cbuf[0] = NUL;
	rbuf[0] = NUL;

	do
	{
		buf[n] = UPPER(buf[n]);
		n++;
	}
	while (buf[n] != '>');

	c = n;
	buf[c] = NUL;

	arg = (char *) &buf[5];

	do
	{
		do
		{
			arg++;
		}
		while (*arg != ' ' && *arg != NUL);

		if (*arg == NUL)
			break;

#define scan_arg(format1, format2, into) \
    if(sscanf(arg, format2, into) != 1) \
        sscanf(arg, format1, into);

		switch (UPPER(*(arg + 1)))
		{
		default:
			break;
		case 'C':
			scan_arg(" CLIENT=\"%s\"", " CLIENT=%s", cbuf);
			break;
		case 'M':
			scan_arg(" MXP=\"%f\"", " MXP=%f", &d->mxp.mxp_ver);
			break;
		case 'R':
			scan_arg(" REGISTERED=\"%s\"", " REGISTERED=%s", rbuf);
			break;
		case 'S':
			scan_arg(" STYLE=\"%f\"", " STYLE=%f", &d->mxp.style_ver);
			break;
		case 'V':
			scan_arg(" VERSION=\"%f\"", " VERSION=%f", &d->mxp.client_ver);
			break;
		}
	}
	while (*arg != NUL);

	replace_string(d->mxp.client, cbuf);

	d->mxp.registered = IS_NULLSTR(rbuf) ? 0 : (UPPER(rbuf[0]) == 'Y') ? 2 : 1;

	n = c + 1;
	buf[c] = '>';

	for (;;)
	{
		switch (buf[n])
		{
		default:
		case NUL:
			break;

		case '\n':
		case '\r':
			n++;
			continue;
			break;
		}
		break;
	}

	telopt_lskip = n - 1;

	if ((d->mxp.mxp_ver * 10) >= 4)
	{
		setup_mxp(d);
	}
	else
		REMOVE_BIT(d->d_flags, DESC_MXP);

	return;

}

void list_mxp_flags(CHAR_DATA * ch, DESCRIPTOR_DATA * d)
{
	int flag;
	char *color;
	int max_col, col = 0;

	max_col = get_scr_cols(ch) / 13;

	for (flag = 0; mxp_support_flags[flag].name != NULL; flag++)
	{
		if (IS_SET(d->mxp.flags, mxp_support_flags[flag].bit))
			color = "{G";
		else
			color = "{r";

		chprintf(ch, "|%s%s{x", color,
				 stringf(ch, 12, ALIGN_CENTER, " ",
						 mxp_support_flags[flag].name));

		if (++col % max_col == 0)
			chprintln(ch, "|");
	}

	for (flag = 0; mxp_support_flags2[flag].name != NULL; flag++)
	{
		if (IS_SET(d->mxp.flags, mxp_support_flags2[flag].bit))
			color = "{G";
		else
			color = "{r";

		chprintf(ch, "|%s%s{x", color,
				 stringf(ch, 12, ALIGN_CENTER, " ",
						 mxp_support_flags2[flag].name));

		if (++col % max_col == 0)
			chprintln(ch, "|");
	}

	if (col % max_col != 0)
		chprintln(ch, "|");

	return;
}

void mxp_details(CHAR_DATA * ch, DESCRIPTOR_DATA * d)
{
	if (IS_SET(d->d_flags, DESC_MXP))
	{
		chprintlnf(ch, "Status: {G%s{x", IS_MXP(d) ? "ON" : "OFF");
		chprintlnf(ch, "Client: {G%s %1.2f %s{x", d->mxp.client,
				   d->mxp.client_ver,
				   d->mxp.registered ==
				   1 ? "{D[{RUnregistered{D]" : d->mxp.registered ==
				   2 ? "{D[{GRegistered{D]" : "");
		chprintlnf(ch, "MXP Version: {Y%1.2f{x", d->mxp.mxp_ver);
		if (d->mxp.style_ver > 0)
			chprintlnf(ch, "Style Version: {M%1.2f{x", d->mxp.style_ver);
		if (!IS_NULLSTR(d->mxp.supports))
			chprintlnf(ch, "MXP Info: %s", d->mxp.supports);

		chprintln(ch, draw_line(ch, NULL, 0));

		if (d->mxp.flags || d->mxp.flags2)
			list_mxp_flags(ch, d);

		chprintln(ch, draw_line(ch, NULL, 0));
	}

	return;
}

void send_portal(DESCRIPTOR_DATA * d, const char *format, ...)
{
	char out[MSL];
	char buf[MSL];
	va_list args;
	int len;

	if (!IS_PORTAL(d) || IS_NULLSTR(format))
		return;

	va_start(args, format);
	len = vsnprintf(buf, sizeof(buf), format, args);
	va_end(args);

	sprintf(out, "#K%%%05u%03d%s", d->portal.keycode, len, buf);

	d_write(d, out, 0);

	return;
}

void portal_sound(CHAR_DATA * ch, const char *sound)
{
	send_portal(ch->desc, "%s%s", CL_SEND_SOUND, sound);
	return;
}

void portal_music(CHAR_DATA * ch, const char *sound, int iterations)
{
	send_portal(ch->desc, "%s%s~%d", CL_SEND_MUSIC, sound, iterations);
	return;
}

void portal_image(CHAR_DATA * ch, const char *img)
{
	send_portal(ch->desc, "%s%s~%s", CL_SEND_IMAGE, img, img);
	return;
}

void portal_map(CHAR_DATA * ch, ROOM_INDEX_DATA * pRoom)
{
	int i;
	char buf[MSL];
	bool found = FALSE;

	if (!IS_PORTAL(ch->desc) || !pRoom)
		return;

	buf[0] = NUL;
	for (i = 0; i < MAX_DIR; i++)
	{
		if (pRoom->exit[i] != NULL)
		{
			strcat(buf, " ");
			strcat(buf, &dir_name[i][0]);
			found = TRUE;
		}
	}
	if (found)
		send_portal(ch->desc, "%s%s", CL_SEND_ROOMCODE, buf + 1);
}

bool bust_a_portal(CHAR_DATA * ch)
{
	static char buf[MSL];
	char buf2[MSL];
	DESCRIPTOR_DATA *d = ch->desc;
	CHAR_DATA *victim;

	// client is not portal client or no descriptor.
	if (!IS_PORTAL(d))
		return FALSE;

	buf[0] = NUL;

	sprintf(buf2, "%s", CL_SEND_COMPOSITE);
	strcat(buf, buf2);
	sprintf(buf2, "%s%ld", CL_SEND_HP, ch->hit);
	strcat(buf, buf2);
	sprintf(buf2, "~%s%ld", CL_SEND_MAXHP, ch->max_hit);
	strcat(buf, buf2);
	sprintf(buf2, "~%s%ld", CL_SEND_SP, ch->mana);
	strcat(buf, buf2);
	sprintf(buf2, "~%s%ld", CL_SEND_MAXSP, ch->max_mana);
	strcat(buf, buf2);
	sprintf(buf2, "~%s%ld", CL_SEND_GP1, ch->move);
	strcat(buf, buf2);
	sprintf(buf2, "~%s%ld", CL_SEND_MAXGP1, ch->max_move);
	strcat(buf, buf2);

	victim = ch->fighting;

	if (victim && can_see(ch, victim))
	{
		int percent;

		if (victim->max_hit > 0)
			percent = victim->hit * 100 / victim->max_hit;
		else
			percent = 0;

		sprintf(buf2, "~%s%s", CL_SEND_ATTACKER, victim->short_descr);
		strcat(buf, buf2);
		sprintf(buf2, "~%s%d", CL_SEND_ATTCOND, percent);
		strcat(buf, buf2);
	}
	else
	{
		sprintf(buf2, "~%s0", CL_SEND_ATTCOND);
		strcat(buf, buf2);
	}

	sprintf(buf2,
			"~%s<r[><sNext Level:> %d<sexp><r][><sPurse:> <y%ld> <sgold><r]>",
			CL_SEND_GLINE2, (ch->level + 1) * exp_per_level(ch,
															ch->pcdata->
															points) - ch->exp,
			ch->gold);
	strcat(buf, buf2);

	sprintf(buf2, "~%s<r", CL_SEND_GLINE1);

	if (IS_SET(ch->comm, COMM_AFK))
	{
		strcat(buf, "[><yAFK><r]");
	}

	strcat(buf, ">");

	strcat(buf, buf2);

	send_portal(d, buf);

	return TRUE;
}

CH_CMD(do_portal)
{
	if (IS_NPC(ch) || !ch->desc)
		return;

	set_on_off(ch, &ch->desc->d_flags, DESC_PORTAL,
			   "You now recieve portal enhancements.",
			   "You no longer recieve portal enhancements.");
}

void send_imp(DESCRIPTOR_DATA * d, char *buf)
{
	if (d == NULL)
		return;

	if (!IS_FIRECL(d))
		return;

	d_write(d, buf, 0);

	return;
}

void imp_sound(CHAR_DATA * ch, const char *sound)
{
	char buf[MSL];

	sprintf(buf, "<AUDIO FILE=%s%s>", SOUND_URL, sound);
	send_imp(ch->desc, buf);

	return;
}

void imp_image(CHAR_DATA * ch, const char *img)
{
	char buf[MSL];

	sprintf(buf, "<IMG SRC=%s%s ALT=%s>", IMAGE_URL, img, img);
	send_imp(ch->desc, buf);

	return;
}

CH_CMD(do_imp)
{
	if (IS_NPC(ch) || !ch->desc)
		return;

	set_on_off(ch, &ch->desc->d_flags, DESC_IMP,
			   "Interactive Mudding Protocol on.",
			   "Interactive Mudding Protocol off.");
}

CH_CMD(do_pueblo)
{
	if (IS_NPC(ch) || !ch->desc)
		return;

	set_on_off(ch, &ch->desc->d_flags, DESC_PUEBLO,
			   "You now recieve pueblo enhancements.",
			   "You no longer recieve pueblo enhancements.");
}

void image_to_char(CHAR_DATA * ch, const char *image)
{
	if (!ch || !image)
		return;

	if (IS_PUEBLO(ch->desc))
	{
		chprint(ch, "</xch_mudtext><img xch_mode=html>");
		chprintf(ch, "<img src=\"%s%s\">", IMAGE_URL, image);
		chprint(ch, "<br><br><img xch_mode=text>");
	}
	else if (IS_PORTAL(ch->desc))
	{
		portal_image(ch, image);
	}
	else if (IS_FIRECL(ch->desc))
		imp_image(ch, image);
	else if (IS_MXP(ch->desc))
		chprintf(ch, MXPTAG("IMAGE %s %s"), image, IMAGE_URL);
}

CH_CMD(do_msp)
{
	if (IS_NPC(ch) || !ch->desc)
		return;

	set_on_off(ch, &ch->desc->d_flags, DESC_MSP, "MUD Sound Protocol on.",
			   "MUD Sound Protocol off.");
}

void send_sound(CHAR_DATA * ch, const char *sfile, int prio)
{
	if (!ch || IS_NPC(ch) || IS_NULLSTR(sfile))
		return;

	if (IS_MXP(ch->desc))
		chprintf(ch, MXPTAG("SOUND %s 100 1 %d %s"), sfile, prio, SOUND_URL);
	else if (IS_MSP(ch->desc))
		chprintf(ch, "!!SOUND( %s V=100 p=%d U=%s)", sfile, prio, SOUND_URL);
	else if (IS_PUEBLO(ch->desc))
		chprintf(ch, "</xch_mudtext><img xch_mode=html>"
				 "<img xch_sound=play xch_volume=100 src=\"%s%s\">"
				 "<br><img xch_mode=text>", SOUND_URL, sfile);
	else if (IS_PORTAL(ch->desc))
		portal_sound(ch, sfile);
	else if (IS_FIRECL(ch->desc))
		imp_sound(ch, sfile);
	return;
}

void sound_to_room(ROOM_INDEX_DATA * room, const char *sfile, int prio)
{
	CHAR_DATA *rch;

	if (room == NULL || IS_NULLSTR(sfile))
		return;

	for (rch = room->first_person; rch != NULL; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) || rch->desc == NULL)
			continue;

		send_sound(rch, sfile, prio);
	}
	return;
}

void sound_to_area(AREA_DATA * area, const char *sfile, int prio)
{
	DESCRIPTOR_DATA *d;

	if (area == NULL || IS_NULLSTR(sfile))
		return;

	for (d = descriptor_first; d != NULL; d = d->next)
	{
		if (d->connected != CON_PLAYING)
			continue;
		if (d->character != NULL && d->character->in_room != NULL
			&& d->character->in_room->area == area)
			send_sound(d->character, sfile, prio);
	}
	return;
}

void sound_to_world(const char *sfile, int prio)
{
	DESCRIPTOR_DATA *d;

	if (IS_NULLSTR(sfile))
		return;

	for (d = descriptor_first; d != NULL; d = d->next)
	{
		if (d->connected != CON_PLAYING)
			continue;

		send_sound(d->character, sfile, prio);
	}
	return;
}

void send_music(CHAR_DATA * ch, const char *url, const char *sound)
{
	if (!ch || IS_NPC(ch) || !sound)
		return;

	if (IS_MXP(ch->desc))
		chprintf(ch, MXPTAG("MUSIC %s V=100 L=-1 U=%s"), sound, SOUND_URL);
	else if (IS_MSP(ch->desc))
		chprintf(ch, "!!MUSIC(%s V=100 L=-1 U=%s)", sound, SOUND_URL);
	else if (IS_PUEBLO(ch->desc))
		chprintf(ch, "</xch_mudtext><img xch_mode=html>"
				 "<img xch_sound=loop xch_volume=100 src=\"%s%s\">"
				 "<br><img xch_mode=text>", SOUND_URL, sound);
	else if (IS_PORTAL(ch->desc))
		portal_music(ch, sound, 1);
	else if (IS_FIRECL(ch->desc))
		imp_sound(ch, sound);
}

void music_to_room(ROOM_INDEX_DATA * room, const char *url, const char *sound)
{
	CHAR_DATA *rch;

	if (room == NULL || IS_NULLSTR(sound))
		return;

	for (rch = room->first_person; rch != NULL; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) || rch->desc == NULL)
			continue;

		send_music(rch, url, sound);
	}
	return;
}

void music_to_area(AREA_DATA * area, const char *url, const char *sound)
{
	DESCRIPTOR_DATA *d;
	CHAR_DATA *ch;

	if (area == NULL || IS_NULLSTR(sound))
		return;

	for (d = descriptor_first; d != NULL; d = d->next)
	{
		if (d->connected != CON_PLAYING)
			continue;
		if ((ch = CH(d)) != NULL && ch->in_room != NULL
			&& ch->in_room->area == area)
			send_music(ch, url, sound);
	}
	return;
}

void music_to_world(const char *url, const char *sound)
{
	DESCRIPTOR_DATA *d;

	if (IS_NULLSTR(sound))
		return;

	for (d = descriptor_first; d != NULL; d = d->next)
	{
		if (d->connected != CON_PLAYING)
			continue;

		send_music(CH(d), url, sound);
	}
	return;
}