Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * 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 St{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/

/***************************************************************************
 * math.c: math functions for server, duh.															   *
 ***************************************************************************/

#include "mud.h"

/*
	mathematical expression interpreter - Scandum
*/

#define EXP_VARIABLE         0
#define EXP_STRING           1
#define EXP_OPERATOR         2

#define EXP_PR_DICE          0
#define EXP_PR_INTMUL        1
#define EXP_PR_INTADD        2
#define EXP_PR_BITSHIFT      3
#define EXP_PR_LOGLTGT       4
#define EXP_PR_LOGCOMP       5
#define EXP_PR_BITAND        6
#define EXP_PR_BITXOR        7
#define EXP_PR_BITOR         8
#define EXP_PR_LOGAND        9
#define EXP_PR_LOGXOR       10
#define EXP_PR_LOGOR        11
#define EXP_PR_VAR          12
#define EXP_PR_LVL          13


MATH_DATA *mathnode_s;
MATH_DATA *mathnode_e;
MATH_DATA *mathnode_n;


lg_int mathexp(CHAR_DATA *ch, const char *str)
{
	MATH_DATA *node;

	push_call("mathexp(%p,%p)",ch,str);

	if (mathexp_tokenize(ch, str) == FALSE)
	{
		if (mud->f_math == NULL)
		{
			if (IS_NPC(ch))
			{
				log_build_printf(ch->pIndexData->vnum, "mathexp: invalid input");
			}
			else
			{
				ch_printf_color(ch, "mathexp: invalid input: ");
			}
		}
		pop_call();
		return 0;
	}

	node = mud->f_math;

	while (node->prev || node->next)
	{
		if (node->next == NULL || atoi(node->next->str1) < atoi(node->str1))
		{
			mathexp_level(ch, node);

			node = mud->f_math;
		}
		else
		{
			node = node->next;
		}
	}
	pop_call();
	return tintoi(node->str3);
}


void add_mathnode(char *buf1, char *buf2, char *buf3)
{
	MATH_DATA *node;

	push_call("add_mathnode(void)");

	ALLOCMEM(node, MATH_DATA, 1);

	node->str1 = STRALLOC(buf1);
	node->str2 = STRALLOC(buf2);
	node->str3 = STRALLOC(buf3);

	LINK(node, mud->f_math, mud->l_math, next, prev);

	pop_call();
	return;
}

void del_mathnode(MATH_DATA *node)
{
	push_call("del_mathnode(%p)",node);

	STRFREE(node->str1);
	STRFREE(node->str2);
	STRFREE(node->str3);

	UNLINK(node, mud->f_math, mud->l_math, next, prev);

	FREEMEM(node);

	pop_call();
	return;
}

#define MATH_NODE(buffercheck, priority, newstatus)          \
{                                                            \
	if (buffercheck && pta == buf3)                         \
	{                                                       \
		return FALSE;                                      \
	}                                                       \
	*pta = 0;                                               \
	sprintf(buf1, "%02d", level);                           \
	sprintf(buf2, "%02d", priority);                        \
	add_mathnode(buf1, buf2, buf3);                         \
	status = newstatus;                                     \
	pta = buf3;                                             \
}


lg_int mathexp_tokenize(CHAR_DATA *ch, const char *str)
{
	char buf1[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH], buf3[MAX_INPUT_LENGTH], *pti, *pta;
	int level, status;

	push_call("mathexp_tokenize(%p,%p)",ch,str);

	level  = 0;
	status = EXP_VARIABLE;

	pta = (char *) buf3;
	pti = (char *) str;

	while (mud->f_math)
	{
		del_mathnode(mud->f_math);
	}

	mud->f_math = mud->l_math = NULL;

	while (*pti)
	{
		switch (status)
		{
			case EXP_VARIABLE:
				switch (*pti)
				{
					case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
						*pta++ = *pti++;
						break;

					case '!':
					case '~':
					case '+':
					case '-':
						if (pta != buf3)
						{
							MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
						}
						else
						{
							*pta++ = *pti++;
						}
						break;

					case '"':
						if (pta != buf3)
						{
							if (IS_NPC(ch))
							{
								log_build_printf(ch->pIndexData->vnum, "mathexp: \" found inside integer");
							}
							else
							{
								ch_printf_color(ch, "mathexp: \" found inside integer: ");
							}
							pop_call();
							return FALSE;
						}
						*pta++ = *pti++;
						status = EXP_STRING;
						break;

					case '(':
						if (pta != buf3)
						{
							if (IS_NPC(ch))
							{
								log_build_printf(ch->pIndexData->vnum, "mathexp: ( found inside integer");
							}
							else
							{
								ch_printf_color(ch, "mathexp: ( found inside integer: ");
							}
							pop_call();
							return FALSE;
						}
						*pta++ = *pti++;
						MATH_NODE(TRUE, EXP_PR_LVL, EXP_VARIABLE);
						level++;
						break;

					case ' ':
						pti++;
						break;

					default:
						MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR);
						break;
				}
				break;

			case EXP_STRING:
				switch (*pti)
				{
					case '"':
						*pta++ = *pti++;
						MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
						break;

					default:
						*pta++ = *pti++;
						break;
				}
				break;

			case EXP_OPERATOR:
				switch (*pti)
				{
					case ' ':
						pti++;
						break;

					case ')':
						*pta++ = *pti++;
						level--;
						MATH_NODE(TRUE, EXP_PR_LVL, EXP_OPERATOR);
						break;

					case 'd':
						*pta++ = *pti++;
						MATH_NODE(FALSE, EXP_PR_DICE, EXP_VARIABLE);
						break;

					case '*':
					case '/':
					case '%':
						*pta++ = *pti++;
						MATH_NODE(FALSE, EXP_PR_INTMUL, EXP_VARIABLE);
						break;

					case '+':
					case '-':
						*pta++ = *pti++;
						MATH_NODE(FALSE, EXP_PR_INTADD, EXP_VARIABLE);
						break;

					case '<':
						*pta++ = *pti++;

						switch (*pti)
						{
							case '<':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE);
								break;

							case '=':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
								break;

							default:
								MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
								break;
						}
						break;

					case '>':
						*pta++ = *pti++;

						switch (*pti)
						{
							case '>':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE);
								break;

							case '=':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
								break;

							default:
								MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
								break;
						}
						break;

					case '&':
						*pta++ = *pti++;

						switch (*pti)
						{
							case '&':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_LOGAND, EXP_VARIABLE);
								break;

							default:
								MATH_NODE(FALSE, EXP_PR_BITAND, EXP_VARIABLE);
								break;
						}
						break;

					case '^':
						*pta++ = *pti++;

						switch (*pti)
						{
							case '^':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_LOGXOR, EXP_VARIABLE);
								break;

							default:
								MATH_NODE(FALSE, EXP_PR_BITXOR, EXP_VARIABLE);
								break;
						}
						break;

					case '|':
						*pta++ = *pti++;

						switch (*pti)
						{
							case '|':
								*pta++ = *pti++;
								MATH_NODE(FALSE, 7, EXP_VARIABLE);
								break;

							default:
								*pta++ = *pti++;
								MATH_NODE(FALSE, 5, EXP_VARIABLE);
								break;
						}
						break;

					case '=':
					case '!':
						*pta++ = *pti++;
						switch (*pti)
						{
							case '=':
								*pta++ = *pti++;
								MATH_NODE(FALSE, EXP_PR_LOGCOMP, EXP_VARIABLE);
								break;

							default:
								if (IS_NPC(ch))
								{
									log_build_printf(ch->pIndexData->vnum, "mathexp: unknown operator: %c%c", pti[0], pti[1]);
								}
								else
								{
									ch_printf_color(ch, "mathexp: unknown operator: %c%c: ", pti[0], pti[1]);
								}
								pop_call();
								return FALSE;
						}
						break;

					default:
						if (IS_NPC(ch))
						{
							log_build_printf(ch->pIndexData->vnum, "mathexp: unknown operator: %c", *pti);
						}
						else
						{
							ch_printf_color(ch, "mathexp: unknown operator: %c: ", *pti);
						}
						pop_call();
						return FALSE;
				}
				break;
		}
	}

	if (level != 0)
	{
		if (IS_NPC(ch))
		{
			log_build_printf(ch->pIndexData->vnum, "mathexp: unmatched parentheses, level: %d", level);
		}
		else
		{
			ch_printf_color(ch, "mathexp: unmatched parentheses, level: %d: ", level);
		}
		pop_call();
		return FALSE;
	}

	if (status != EXP_OPERATOR)
	{
		MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR);
	}
	pop_call();
	return TRUE;
}


void mathexp_level(CHAR_DATA *ch, MATH_DATA *node)
{
	int priority;

	mathnode_e = node;

	while (node->prev)
	{
		if (atoi(node->prev->str1) < atoi(node->str1))
		{
			break;
		}
		node = node->prev;
	}

	mathnode_s = node;

	for (priority = 0 ; priority < EXP_PR_VAR ; priority++)
	{
		for (node = mathnode_s ; node ; node = node->next)
		{
			if (atoi(node->str2) == priority)
			{
				mathexp_compute(ch, node);
			}
			if (node == mathnode_e)
			{
				break;
			}
		}
	}

	node = mathnode_s;

	while (node->prev && node->next)
	{
		if (atoi(node->prev->str2) == EXP_PR_LVL && atoi(node->next->str2) == EXP_PR_LVL)
		{
			STRFREE(node->str1);
			node->str1 = STRALLOC(node->next->str1);

			del_mathnode(node->next);
			del_mathnode(node->prev);
		}
		else
		{
			break;
		}
	}
	return;
}

void mathexp_compute(CHAR_DATA *ch, MATH_DATA *node)
{
	char temp[MAX_INPUT_LENGTH];
	int value;

	switch (node->str3[0])
	{
		case 'd':
			if (tintoi(node->next->str3) <= 0)
			{
				ch_printf_color(ch, "mathexp: invalid dice: ");
				value = 0;
			}
			else
			{
				value = tindice(node->prev->str3, node->next->str3);
			}
			break;
		case '*':
			value = tintoi(node->prev->str3) * tintoi(node->next->str3);
			break;
		case '/':
			if (tintoi(node->next->str3) == 0)
			{
				ch_printf_color(ch, "mathexp: division zero: ");
				value = tintoi(node->prev->str3) / 1;
			}
			else
			{
				value = tintoi(node->prev->str3) / tintoi(node->next->str3);
			}
			break;
		case '%':
			if (tintoi(node->next->str3) == 0)
			{
				ch_printf_color(ch, "mathexp: division zero: ");
				value = tintoi(node->prev->str3) / 1;
			}
			else
			{
				value = tintoi(node->prev->str3) % tintoi(node->next->str3);
			}
			break;
		case '+':
			value = tintoi(node->prev->str3) + tintoi(node->next->str3);
			break;
		case '-':
			value = tintoi(node->prev->str3) - tintoi(node->next->str3);
			break;
		case '<':
			switch (node->str3[1])
			{
				case '=':
					value = tincmp(node->prev->str3, node->next->str3) <= 0;
					break;
				case '<':
					value = tintoi(node->prev->str3) << tintoi(node->next->str3);
					break;
				default:
					value = tincmp(node->prev->str3, node->next->str3) < 0;
					break;
			}
			break;
		case '>':
			switch (node->str3[1])
			{
				case '=':
					value = tincmp(node->prev->str3, node->next->str3) >= 0;
					break;
				case '>':
					value = tintoi(node->prev->str3) >> tintoi(node->next->str3);
					break;
				default:
					value = tincmp(node->prev->str3, node->next->str3) > 0;
					break;
			}
			break;

		case '&':
			switch (node->str3[1])
			{
				case '&':
					value = tintoi(node->prev->str3) && tintoi(node->next->str3);
					break;
				default:
					value = tintoi(node->prev->str3) & tintoi(node->next->str3);
					break;
			}
			break;
		case '^':
			switch (node->str3[1])
			{
				case '^':
					value = ((tintoi(node->prev->str3) || tintoi(node->next->str3)) && (!tintoi(node->prev->str3) != !tintoi(node->next->str3)));
					break;

				default:
					value = tintoi(node->prev->str3) ^ tintoi(node->next->str3);
					break;
			}
			break;
		case '|':
			switch (node->str3[1])
			{
				case '|':
					value = tintoi(node->prev->str3) || tintoi(node->next->str3);
					break;
				default:
					value = tintoi(node->prev->str3) | tintoi(node->next->str3);
					break;
			}
			break;
		case '=':
			value = (tincmp(node->next->str3, node->prev->str3) == 0);
			break;
		case '!':
			value = (tincmp(node->next->str3, node->prev->str3) != 0);
			break;
		default:
			value = 0;
			break;
	}

	if (node->prev == mathnode_s)
	{
		mathnode_s = node;
	}

	if (node->next == mathnode_e)
	{
		mathnode_e = node;
	}

	del_mathnode(node->next);
	del_mathnode(node->prev);

	sprintf(temp, "%d", EXP_PR_VAR);
	STRFREE(node->str2);
	node->str2 = STRALLOC(temp);

	sprintf(temp, "%d", value);
	STRFREE(node->str3);
	node->str3 = STRALLOC(temp);
}

lg_int tintoi(const char *str)
{
	switch (str[0])
	{
		case '!':
			return !atoll(&str[1]);

		case '~':
			return ~atoll(&str[1]);

		case '+':
			return +atoll(&str[1]);

		case '-':
			return -atoll(&str[1]);

		default:
			return atoi(str);
	}
}

lg_int tincmp(const char *left, const char *right)
{
	switch (left[0])
	{
		case '"':
			return strcmp(left, right);

		default:
			return tintoi(left) - tintoi(right);
	}
}

lg_int tindice(const char *left, const char *right)
{
	lg_int cnt, numdice, sizedice, sum;

	numdice  = tintoi(left);
	sizedice = tintoi(right);

	for (cnt = sum = 0 ; cnt < numdice ; cnt++)
	{
		sum += lrand48() % sizedice + 1;
	}
	return sum;
}