/****************************************************************************\
 *									      *
 *			C R E A T O R    O F   L E G E N D S		      *
 *				(AberMud Version 5)			      *
 *									      *
 *  The Creator Of Legends System is (C) Copyright 1989 Alan Cox, All Rights  *
 *  Reserved.		  						      *
 *									      *
 \****************************************************************************/

#include "System.h"
#include <string.h>

Module "Table Drivers";
Version "1.18";
Author "Alan Cox";

TABLE *TableList=NULL;		/* Linked list of tables */
TPTR TxtArg=NULL;		/* {$} */
TPTR TxtArg2=NULL;		/* {$2} */
char TxBuf[512];		/* Working buffer */
#ifdef BOOT_DEBUG
int System_Debug=1;		/* Decide if debugging starts ON */
#else
int System_Debug=0;
#endif
static short FlagData[512];	/* The flags - change number here and in GetFlag
				   SetFlag, if desired. */
short Traf[4]={-1,-1,-1,-1};	/* four flags max being tracked */

static unsigned short *DataPointer;	/* Where we are in table  */
LINE *CurrentLine;			/* Line we are executing  */
TABLE *CurrentTable;			/* Table we are executing */
ITEM *Item1,*Item2;			/* $1 and $2 data values  */
int Noun1,Adj1,Noun2,Adj2,Verb,Prep;	/* The word codes needed  */
int Ord1,Ord2;
ITEM *Debugger;				/* Dest for flag traps    */

/*
 *	1.00	AGC	Created
 *	1.01-1.05 AGC	Added lots of action calls
 *	1.06	AGC	Added DAEMON entry points
 *	1.07	AGC	Added background executes
 *	1.08	AGC	Misc Bug Fixes and added stack protection
 *	1.09	AGC	Next batch of commands added
 *	1.10	AGC	Commands added in the right place this time
 *	1.11	AGC	Fixed debugging crashes	(NOT RIGHT STILL) and mended NOT
 *	1.12	AGC	Allowed DAEMON etc to nest DOCLASS calls.
 *	1.13	AGC	Made END and KILLOFF do DONE so they don't crash(as often)
 *	1.14	AGC	Added Flag Tracking Code
 *	1.15	AGC	Added Ordinate Code
 *	1.16	AGC	Fixed UArgNum 512 byte error areas problem.
 *			Also added DEBUG 2 - second level debugging
 *	1.17	AGC	5.08 changes for item bound tables
 *	1.18	AGC	Fixed stupid bugs assuming top bit of address was 0
 *	1.19	AGC	Private Daemon Table. Superclasses
 */

void WipeFlags()
{
	short ct=0;
	while(ct<512)
		FlagData[ct++]=0;	/* Clear all the flags */
}

int GetFlag(n)
int n;
{
	if(n<0||n>=512)
	{
		Log("Flag Out Of Range");
		SendItem(Me(),"Flag Out Of Range [%d].\n",n);
		return(-1);
	}
	return((int)FlagData[n]);	/* Get the value of a flag */
}

void PCurrentLine()
{
	char x[512];
	if(Debugger&&CurrentLine)
	{
		Decompress(CurrentLine,x);	/* Print it to DEBUG */
		SendItem(Debugger,"%d:%s:%s\n",(int)CurrentTable->tb_Number,
			CNameOf(Me()),x);
	}
}

void FPCurrentLine()
{
	char x[512];
	if(Debugger&&CurrentLine)
	{
		Decompress(CurrentLine,x);	/* Print it to DEBUG */
		fprintf(stderr,"%d:%s:%s\n",(int)CurrentTable->tb_Number,
			CNameOf(Me()),x);
	}
}

void SetFlag(n,m)			/* Set the value of a flag */
int n,m;
{
	if((n<0)||(n>511))
	{
		Log("Flag Out Of Range");
		SendItem(Me(),"Flag Out Of Range [%d].\n",n);
		return;
	}
	if(Traf[0]==n)
		if(Debugger)
		{
			SendItem(Debugger,"Flag %d -> %d\n",n,m);
			PCurrentLine();
		}
	if(Traf[1]==n)
		if(Debugger)
		{
			SendItem(Debugger,"Flag %d -> %d\n",n,m);
			PCurrentLine();
		}
	if(Traf[2]==n)
		if(Debugger)
		{
			SendItem(Debugger,"Flag %d -> %d\n",n,m);
			PCurrentLine();
		}
	if(Traf[3]==n)
		if(Debugger)
		{
			SendItem(Debugger,"Flag %d -> %d\n",n,m);
			PCurrentLine();
		}
	FlagData[n]=m;
}

static ITEM *CurrentPlayer;		/* $ME */
static ITEM *ActorPlayer;		/* $AC */

/*
 *	A fake item which does the set up...
 */
TXT DummyTxt={"<GLOBAL DUMMY>",-1,NULL};
ITEM DummyItem={NULL,NULL,NULL,NULL,-1,-1,-1,-1,0,0,0,0,NULL,NULL,NULL,NULL,NULL,NULL};
	
ITEM *Me()
{
	DummyItem.it_Name=&DummyTxt;	/* Set up item */
	if(CurrentPlayer==NULL)
		return(&DummyItem);	/* No player so is dummy */
	return(CurrentPlayer);		/* Else is player */
}

void SetMe(i)			/* Set current player ($ME) */
ITEM *i;
{
	CurrentPlayer=i;
}

ITEM *Actor()
{
	DummyItem.it_Name=&DummyTxt;
	if(ActorPlayer==NULL)
		return(&DummyItem);
	return(ActorPlayer);
}


int ArgNum()		/* Read numeric (number/flag) argument from line */
{
	int a=((int)*DataPointer++);
	if((a>=30000)&&(a<30512))
		return(GetFlag(a-30000));
	else
		return(a);
}

unsigned int UArgNum()		/* Read word, unmodified from text */
{
	unsigned int a=((unsigned int)(*DataPointer++));
	return(a);
}

int ArgWord()		/* Read word from line */
{
	return((int)(*DataPointer++));
}

ITEM *ArgItem()		/* Read item from line */
{
	ITEM *x=(ITEM *)PairArg(DataPointer);
	DataPointer+=2;
	if(x==(ITEM *)1)
		x=Item1;	/* $1 */
	if(x==(ITEM *)3)	/* $2 */
		x=Item2;	
	if(x==(ITEM *)5)	/* $ME */
		x=Me();
	if(x==(ITEM *)7)	/* $AC */
		x=Actor();
	if(x==(ITEM *)9)	/* $RM */
		x=O_PARENT(Me());
	if(x==NULL)
	{
		Broadcast("Invalid Item Reference: -> Using ME as dummy.\n",0);
		FPCurrentLine();
		Log("Invalid Item Reference: -> Using ME as dummy.\n");
		return(Me());
	}
	return(x);
}

TPTR ArgText()		/* Read a text argument */
{
	TPTR x=(TPTR)PairArg(DataPointer);
	DataPointer+=2;
	if(x==(TPTR )1)
	{
		x=TxtArg;		/* {$} */
		WordPtr=NULL;
	}
	if(x==(TPTR )3)
	{
		x=TxtArg2;		/* {$2} */
		WordPtr=NULL;
	}
	return(x);
}

void ParseArgs(v)		/* Break input using parser */
int v;
{
	Verb=v;
	GetRestOfInput(WordPtr,TxBuf);
	if(TxtArg)
		FreeText(TxtArg);	/* Free any old {$} */
	while(*WordPtr)
		if(isspace(*WordPtr))
			WordPtr++;
		else
			break;
	TxtArg=AllocText(WordPtr);	/* Set new {$} */
	Ord1=GetOrd();
	if(GetThing(&Adj1,&Noun1)==-1)
	{
		Adj1=-1;		/* Parse arg 1 */
		Noun1=-1;
	}
	if(TxtArg2)			/* Delete old and create new {$2} */
		FreeText(TxtArg2);
	while(*WordPtr)
		if(isspace(*WordPtr))
			WordPtr++;
		else
			break;
	TxtArg2=AllocText(WordPtr);
	Prep=GetPrep();
	Ord2=GetOrd();
	if(GetThing(&Adj2,&Noun2)==-1)
	{
		Adj2=-1;		/* Parse arg 2 */
		Noun2=-1;
	}
	Item1=FindAnItem(Adj1,Noun1,Ord1);	/* Find $1 and $2 */
	if(Item1)
		SetItData(-1,Item1,(short)Adj1,(short)Noun1);
	Item2=FindAnItem(Adj2,Noun2,Ord2);
	if(Item2)
		SetItData(-1,Item2,(short)Adj2,(short)Noun2);
}

#define WORDMATCH(a,b)	(((a)==-1)||((a)==(b))||(((a)==-2)&&((b)==-1)))

int ArgMatch(l)		/* Decide if line should be run */
LINE *l;
{
	if(!WORDMATCH(l->li_Verb,Verb))
		return(0);
	if(!WORDMATCH(l->li_Noun1,Noun1))
		return(0);
	if(!WORDMATCH(l->li_Noun2,Noun2))
		return(0);
	return(1);
}

int ExecBackground(t,i)		/* Run a table in background (WHEN) */
TABLE *t;
ITEM *i;
{
	int n;
	ITEM *p=CurrentPlayer;
	CurrentPlayer=i;
	n=ExecTable(t);
	CurrentPlayer=p;
	return(n);
}

int ExecTable(t)		/* Run a table */
TABLE *t;
{
	static short TCount=0;
	short lcm1=ClassMode1,lcm2=ClassMode2;	/* Lots of junk to save   */
	register LINE *lcl=ClassLine;			/* For recursions PROCESS */
	short lcmsk=ClassMask;
	int stat=-1;
	LINE *l=t->tb_First;			/* First line to run */
	TABLE *ot=CurrentTable;
	LINE *ol=CurrentLine;
	unsigned short *op=DataPointer;
	ITEM *lfp=GetNextPointer();		/* Stack DOCLASS search info */
	ClassMode1=0;
	ClassMode2=0;
	ClassLine=NULL;
	ClassMask=0;	/* Clear DOCLASS prev */
	TCount++;
	if(TCount>40)	/* Stack Max */
	{
		TCount=0;
		SendItem(Me(),"STACK OVERFLOW: KERNEL RESET\n");
		printf("STACK OVERFLOW: KERNEL RESET\n");
		longjmp(Oops,1);		/* Escape to top level ! */
	}
	CurrentTable=t;
l1:	while(l)			/* For each line */
	{
		CurrentLine=l;
		if(ArgMatch(l))		/* If it should be run */
		{
			if(System_Debug)
			{
				static char x[512];
				Decompress(l,x);	/* Print it to DEBUG */
				printf("%d:%s:%s\n",(int)t->tb_Number,
							CNameOf(Me()),
							x);
			}
			stat=0;
			DataPointer=l->li_Data;
			if((stat=RunLine(l))!=0)	/* Run the line */
				break;
		}
		l=l->li_Next;
	}
	if(ClassMode1)		/* Check for DOCLASS logic */
	{
		Item1=NextInByClass(LevelOf(Me()),Item1,ClassMask);
		if(!Item1)
		{
			ClassMode1=0;
		}
		else
		{
			l=ClassLine;	/* Rescanner */
			goto l1;
		}
	}
	if(ClassMode2)
	{
		Item2=NextInByClass(LevelOf(Me()),Item2,ClassMask);
		if(!Item2)
		{
			ClassMode2=0;
		}
		else
		{
			l=ClassLine;	/* Rescanner */
			goto l1;
		}
	}
	if(stat==-10)		/* stat -10 return means rescan table */
	{
		l=t->tb_First;
		goto l1;
	}
	DataPointer=op;		/* Put it all back */
	CurrentLine=ol;
	CurrentTable=ot;
	ClassMode1=lcm1;
	ClassMode2=lcm2;
	ClassLine=lcl;
	ClassMask=lcmsk;
	TCount--;
	SetNextPointer(lfp);	/* Restore DOCLASS search context */
	return(stat);		/* Return answer */
}


int RunLine(x)			/* Execute a line of database */
register LINE *x;
{
	register int c,v,neg;
	while((c=ArgNum())!=CMD_EOL)	/* End marker ? */
	{
		neg=0;
		if(c==203)		/* NOT */
		{
			neg=1;		/* Note in NOT mode */
			c=ArgNum();
			if(c==CMD_EOL)	/* Trap NOT as last command! */
				break;
		}
		v=1;
		if(System_Debug==2)
			printf("Action: %s\n",Cnd_Table[c]);
/* Apple A/UX can't handle big switch statements (sigh)
   oh for a real compiler */
		if(c<200) {
		switch(c)	/* Run correct condition/action */
		{
/*
 *			BASIC CONDITIONS
 */
			case 0:v=Cnd_At();break;
			case 1:v=Cnd_NotAt();break;
			case 2:v=Cnd_Present();break;
			case 3:v=Cnd_Absent();break;
			case 4:v=Cnd_Worn();break;
			case 5:v=Cnd_NotWorn();break;
			case 6:v=Cnd_Carried();break;
			case 7:v=Cnd_NotCarr();break;
			case 8:v=Cnd_IsAt();break;
			case 9:v=Cnd_IsNotAt();break;
			case 10:v=Cnd_IsBy();break;
			case 11:v=Cnd_IsNotBy();break;
			case 12:v=Cnd_Zero();break;
			case 13:v=Cnd_NotZero();break;
			case 14:v=Cnd_Eq();break;
			case 15:v=Cnd_NotEq();break;
			case 16:v=Cnd_Gt();break;
			case 17:v=Cnd_Lt();break;
			case 18:v=Cnd_EqF();break;
			case 19:v=Cnd_NeF();break;
			case 20:v=Cnd_LtF();break;
			case 21:v=Cnd_GtF();break;
			case 22:v=Cnd_IsIn();break;
			case 23:v=Cnd_IsNotIn();break;
			case 24:v=Cnd_Adj1();break;
			case 25:v=Cnd_Adj2();break;
			case 26:v=Cnd_Noun1();break;
			case 27:v=Cnd_Noun2();break;
			case 28:v=Cnd_Prep();break;
			case 29:v=Cnd_Chance();break;
			case 30:v=Cnd_IsPlayer();break;
			case 31:v=Cnd_IsUser();break;
			case 32:v=Cnd_IsRoom();break;
			case 33:v=Cnd_IsObject();break;
			case 34:v=Cnd_State();break;
			case 35:v=Cnd_PFlag();break;
			case 36:v=Cnd_OFlag();break;
			case 37:v=Cnd_CanPut();break;
			case 38:v=Cnd_RFlag();break;
			case 39:v=Cnd_Level();break;
			case 40:v=Cnd_IfDeaf();break;
			case 41:v=Cnd_IfBlind();break;
			case 42:v=Cnd_Arch();break;
/*
 *		MAIN ACTION LIST
 */
			case 43:Act_Get();break;
			case 44:Act_Drop();break;
			case 45:Act_Remove();break;
			case 46:Act_Wear();break;
			case 47:Act_Create();break;
			case 48:Act_Destroy();break;
			case 49:Act_PutO();break;	
			case 50:Act_Swap();break;
			case 51:Act_Place();break;
			case 52:Act_PutIn();break;
			case 53:Act_TakeOut();break;
			case 54:Act_CopyOF();break;
			case 55:Act_CopyFO();break;
			case 56:Act_CopyFF();break;
			case 57:Act_WhatO();break;
			case 58:Act_GetO();break;
			case 59:Act_Weigh();break;
			case 60:Act_Set();break;
			case 61:Act_Clear();break;
			case 62:Act_PSet();break;
			case 63:Act_PClear();break;
			case 64:Act_Let();break;
			case 65:Act_Add();break;
			case 66:Act_Sub();break;
			case 67:Act_AddF();break;
			case 68:Act_SubF();break;
			case 69:Act_Mul();break;
			case 70:Act_Div();break;
			case 71:Act_MulF();break;
			case 72:Act_DivF();break;
			case 73:Act_Mod();break;
			case 74:Act_ModF();break;
			case 75:Act_Random();break;
			case 76:Act_Move();break;
			case 77:Act_Goto();break;
			case 78:Act_Weight();break;
			case 79:Act_Size();break;
			case 80:Act_OSet();break;
			case 81:Act_OClear();break;
			case 82:Act_RSet();break;
			case 83:Act_RClear();break;
			case 84:Act_PutBy();break;
			case 85:Act_Inc();break;
			case 86:Act_Dec();break;
			case 87:Act_SetState();break;
			case 88:Act_Prompt();break;
			case 89:Act_Print();break;
			case 90:Act_Score();break;
			case 91:Act_Message();break;
			case 92:Act_Msg();break;
			case 93:Act_ListObj();break;
			case 94:Act_ListAt();break;
			case 95:Act_Inven();break;
			case 96:Act_Desc();break;
			case 97:Act_End();return(1);
			case 98:return(1);		/* DONE */
			case 99:return(-1);		/* NOTDONE */
			case 100:Act_Ok();return(1);
			case 101:Act_Abort();break;
			case 102:Act_Save();break;
			case 103:Act_Parse();break;
			case 104:Act_NewText();break;
			case 105:Act_Process();break;
			case 106:Act_DoClass();break;
			case 107:Act_Give();break;
			case 108:Act_SetUText();break;
			case 109:Act_DoesAction();break;
			case 110:Act_DoesTo();break;
			case 111:Act_DoesToPlayer();break;
			case 112:Act_PObj();break;
			case 113:Act_PLoc();break;
			case 114:Act_PName();break;
			case 115:Act_PCName();break;
			case 116:Act_Daemon();break;
			case 117:Act_AllDaemon();break;
			case 118:Act_HDaemon();break;
			case 119:Act_When();break;
			case 120:Act_SetName();break;
			case 121:Act_Dup();break;
			case 122:Act_Frig();break;
			case 123:Act_Points();break;
			case 124:Act_Hurt();break;
			case 125:Act_Cured();break;
			case 126:Act_KillOff();return(1);
			case 127:Act_AutoVerb();break;
			case 128:v=Act_If1();break;
			case 129:v=Act_If2();break;
			case 130:Act_Bug();break;
			case 131:Act_Typo();break;
			case 132:Act_NArg();break;
			case 133:v=Act_IsMe();break;
			case 134:Act_Broadcast();break;
			case 135:v=Cnd_IsCalled();break;
			case 136:v=Cnd_Is();break;
			case 137:Act_SetMe();break;
			case 138:Act_Pronouns();break;
			case 139:v=Cnd_ChanceLev();break;
			case 140:Act_Exits();break;
			case 141:Act_PWChange();break;
			case 142:Act_Snoop();break;
			case 143:Act_UnSnoop();break;
			case 144:Act_GetUText();break;
			case 145:Act_Cat();break;
			case 146:Act_Become();break;
			case 147:Act_Alias();break;
			case 148:Act_UnAlias();break;
			case 149:Act_Field();break;
			case 150:Act_NeedField();break;
			case 151:Act_Unveil();break;
			case 152:Act_Debug();break;
			case 153:Act_GetScore();break;
			case 154:Act_GetStr();break;
			case 155:Act_GetLev();break;
			case 156:Act_SetScore();break;
			case 157:Act_SetLev();break;
			case 158:Act_SetStr();break;
			case 159:Act_Shell();break;
			case 160:Act_CSet();break;
			case 161:Act_CClear();break;
			case 162:v=Cnd_CFlag();break;
			case 163:v=Cnd_CanSee();break;
			case 164:return(-10);
			case 165:Act_Means();break;
			case 166:Act_TreeDaemon();break;
			case 167:Act_SetIn();break;
			case 168:Act_SetOut();break;
			case 169:Act_SetHere();break;
			case 170:Act_CanGoto();break;
/*
 *		Must re-write these one day  -  but are they needed ?
 */
			case 171:Act_Mobiles();break;
			case 172:Act_Dir();break;
			case 173:Act_Rooms();break;
/*
 *		ITE/EXIT MANIPULATORS
 */
			case 174:Act_ChainDaemon();break;
			case 175:Act_CanGoBy();break;
			case 176:Act_SetIFlag();break;
			case 177:Act_GetIFlag();break;
			case 178:Act_ClearIFlag();break;
			case 180:Act_WhereTo();break;
			case 181:Act_DoorExit();break;
/*
 *		ROPE LOGIC - NOT YET REWRITTEN
 */
			case 182:v=Cnd_CanMoveRope();break;
			case 183:Act_PlaceRope();break;
			case 184:v=Cnd_IsRope();break;
			case 185:v=Cnd_IsTied();break;
			case 186:Act_RopePrev();break;
			case 187:Act_RopeNext();break;
			case 188:Act_Tie();break;
			case 189:Act_Untie();break;
			case 190:Act_Join();break;
			case 191:Act_CutRope();break;
			case 192:Act_Distance();break;
			case 193:Act_WhichWay();break;
/*
 *		ASSORTED ADDITIONS
 */
			case 194:v=Cnd_ClassAt();break;
			case 195:v=Cnd_DupOf();break;
			case 196:Act_MasterOf();break;
			case 197:Act_TiedTo();break;
			case 198:Act_Comment();break;
			case 199:Act_ComVocab();break;
		}
	} else {  /* Sigh.. */
		switch(c){
			case 200:Act_Command();break;
			case 201:Act_BSXScene();break;
			case 202:Act_BSXObject();break;
			case 204:v=Cnd_IfDark();break;
			case 205:Act_Visibility();break;
			case 206:Act_GetParent();break;
			case 207:Act_GetNext();break;
			case 208:Act_GetChild();break;
			case 209:Act_PExit();break;
			case 210:Act_SetDesc();break;
			case 211:Act_SetLong();break;
			case 212:Act_SetShort();break;
			case 213:Act_GetLong();break;
			case 214:Act_GetShort();break;
			case 215:Act_GetDesc();break;
			case 216:Act_GetName();break;
			case 217:Act_Swat();break;
			case 218:Act_Flat();break;
			case 219:Act_FindMaster();break;
			case 220:Act_NextMaster();break;
			case 221:Act_FindIn();break;
			case 222:Act_NextIn();break;
			case 223:Act_LenText();break;
/*
 *	5.21 extensions for superclasses
 */
			case 224:v=Cnd_ProcSubject();break;
			case 225:v=Cnd_ProcObject();break;
			case 226:v=Cnd_ProcDaemon();break;
			case 227:v=Cnd_GetSuper();break;
			case 228:Act_SetSuper();break;
			case 229:v=Cnd_Member();break;
/*
 *		Blizzad.uni uses this function, so we have to implement it
 */
			case 231:Act_Cls();break;

/*
 *		Aber5 extensions for better game control
 *		variable saving etc.
 */
			case 243:v=Cnd_IsClass();break;
			case 244:v=Cnd_SubStr();break;
			case 245:Act_GetIn();break;
			case 246:Act_GetOut();break;
			case 247:Act_GetHere();break;
			case 248:Act_Log();break;
			case 249:Act_SetClass();break;
			case 250:Act_UnSetClass();break;
			case 251:Act_BitClear();break;
			case 252:Act_BitSet();break;
			case 253:v=Cnd_BitTest();break;
			case 254:Act_SPrint();break;
			case 255:Act_User();break;
			case 256:Act_SetI();break;
			case 257:Act_CDaemon();break;
			case 258:v=Cnd_Delete();break;
			case 259:v=Cnd_ULoad();break;
			case 260:v=Cnd_USave();break;
			case 261:v=Cnd_FLoad();break;
			case 262:v=Cnd_FSave();break;
			case 263:Act_Getvis();break;
			case 264:Act_MessageTo();break;
			case 265:Act_MsgTo();break;
			case 266:Act_RwhoDeclareUp();break;
			case 267:Act_RwhoDeclareDown();break;
			case 268:Act_RwhoLogin();break;
			case 269:Act_RwhoLogout();break;
			case 270:Act_RwhoDeclareAlive();break;
			case 271:Act_SetExit();break;
			case 272:Act_DelExit();break;
/*			case 273:Act_GetExit();break;*/
			case 274:Act_ForkDump();break;
			default:SendItem(Me(),"Error Invalid Action(%d)\n",c);
		}
	}
		if(v==neg)	/* Check for failed break */
			break;
	}
	return(0);
}

ITEM *FindAnItem(ad,no,ord)	/* Find an item in normal path (ROOM/CARRIED) */
int ad,no,ord;
{
	ITEM *a;
	a=FindIn(LevelOf(Me()),O_PARENT(Me()),(short)ad,(short)no);
	if(!a)
		goto l2;
	while(--ord)
	{
		a=NextIn(LevelOf(Me()),a,(short)ad,(short)no);
		if(!a)
			goto l2;
	}
	return(a);
l2:	a=FindIn(LevelOf(Me()),Me(),(short)ad,(short)no);
	if(!a)
		return(a);
	while(--ord)
	{
		a=NextIn(LevelOf(Me()),a,(short)ad,(short)no);
		if(!a)
			return(a);
	}
	return(a);
}

int FindTableByName(x)
char *x;
{
	TABLE *t=TableList;
	while(t)
	{
		if(stricmp(TextOf(t->tb_Name),x)==0)
			return(t->tb_Number);
		t=t->tb_Next;
	}
	return(-1);
}

TABLE *FindTable(n)		/* Find a table by number */
int n;
{
	TABLE *t=TableList;
	while(t)
	{
		if(t->tb_Number==n)
			return(t);
		t=t->tb_Next;
	}
	return(NULL);
}

TABLE *NewTable(n,name)	/* Create a table */
int n;
char *name;
{
	TABLE *l=Allocate(TABLE);
	l->tb_Number=n;
	l->tb_Next=TableList;
	l->tb_First=NULL;
	if(name)
		l->tb_Name=AllocText(name);
	TableList=l;
	return(l);
}

LINE *NewLine(t,n)	/* Create a line */
TABLE *t;
int n;
{
	LINE *a=Allocate(LINE);
	LINE *p=NULL;
	LINE *l=t->tb_First;
	if(l==NULL)
		goto skisc;
	while(n)
	{
		p=l;
		l=l->li_Next;
		if(l==NULL)
			break;
		n--;
	}
skisc:	if(p)
	{
		p->li_Next=a;
		a->li_Next=l;
	}
	else
	{
		a->li_Next=t->tb_First;
		t->tb_First=a;
	}
	a->li_Data=Allocate(unsigned short);
	a->li_Data[0]=CMD_EOL;
	return(a);
}

LINE *FindLine(t,n)		/* Find specific line */
TABLE *t;
int n;
{
	LINE *l=t->tb_First;
	while(n)
	{
		if(l==NULL)
			return(l);
		l=l->li_Next;
		n--;
	}
	return(l);
}

int DeleteLine(t,n)		/* Delete a line (lowest level) */
TABLE *t;
int n;
{
	LINE *l=FindLine(t,n);
	LINE *p;
	if(n==0)
	{
		if(l==NULL)
			return(-1);
		t->tb_First=l->li_Next;
	}
	else
	{
		p=FindLine(t,n-1);
		if(l==NULL)
			return(-1);	/* No Such Line */
		p->li_Next=l->li_Next;
	}
	free((char *)l->li_Data);
	free((char *)l);
	return(0);
}

int WipeDeleteLine(t,n)		/* Delete a line (lowest level) */
TABLE *t;
int n;
{
	LINE *l=FindLine(t,n);
	LINE *p;
	if(n==0)
	{
		if(l==NULL)
			return(-1);
		t->tb_First=l->li_Next;
	}
	else
	{
		p=FindLine(t,n-1);
		if(l==NULL)
			return(-1);	/* No Such Line */
		p->li_Next=l->li_Next;
	}
	WipeLine(l);
	free((char *)l->li_Data);
	free((char *)l);
	return(0);
}

int UserAction(u,v)
ITEM *u;
int v;		/* Pregot Verb */
{	
	TABLE *t;
	int ct=0;	/* Safety on superclass loops */
	ITEM *i1;
	ITEM *i2;

	CurrentPlayer=u;
	ParseArgs(v);

	i1=Item1;
	i2=Item2;

	if(Item2)
	{
		while(i2 && ct<20)
		{
			ct++;
			if(i2->it_ObjectTable)
			{
				if(ExecTable(i2->it_ObjectTable)==1)
					return(1);
			}
			i2=i2->it_Superclass;
		}
			
	}
	if(Item1)
	{
		ct=0;
		while(i1 && ct<20)
		{
			ct++;
			if(i1->it_SubjectTable)
			{
				if(ExecTable(i1->it_SubjectTable)==1)
					return(1);
			}
			i1=i1->it_Superclass;
		}
	}
	t=FindTable(u->it_ActorTable);
	if(t==NULL)
		return(-1);
	return(ExecTable(t));
}

int UserDaemon(i)
ITEM *i;
{
	TABLE *t;
	ITEM *op=CurrentPlayer;
	ITEM *oa=ActorPlayer;
	ITEM *o1=Item1,*o2=Item2;
	ITEM *ip=i;
	int n=0;
	int ct=0;

	ActorPlayer=CurrentPlayer;
	CurrentPlayer=i;


	while(ip && n!=1 && ct<20)
	{
		t=ip->it_DaemonTable;
		if(t!=NULL)
			n=ExecTable(t);
		ct++;
		ip=ip->it_Superclass;
	}
	if(n!=1)
	{
		t=FindTable(i->it_ActionTable);
		if(t!=NULL)
			ExecTable(t);	/* System table last */
	}
		
/*
 *	We have to restore NULL if the person we are returning to has just
 *	gone and died. If this is the case his item pointer will be invalid.
 *	This relies on the fact that no creation occurs during the daemons too
 *	(Basically this one is a problem, and a little dodgy at the moment).
 */
	if(((op!=i)||(CurrentPlayer!=NULL))&&(ValidItem(op)))
		CurrentPlayer=op;
	ActorPlayer=oa;
	Item1=o1;
	Item2=o2;
	return(n);
}

int RandPerc()		/* Random %age chance */
{
	return((rand()>>7)%100);
}