/****************************************************************************\
 *									      *
 *			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 "User.h"

extern USER UserList[];

Module  "System Support";
Version "1.17";
Author  "Alan Cox";

/*
 *	Useful routines we use a lot, these use the low level facilities to
 *	provide handy facilities we use a lot, and to save the programmer
 *	going potty....
 *
 *	1.00	PlayerOf,RoomOf,ObjectOf SetName SendItem and basics added
 *	1.01	RemovePlayer,ExitPlayer extracted from CommandDriver
 *	1.02	Added DoesAction and Broadcast type drivers
 *	1.03	Added Exit Support Code
 *	1.04	Drivers for Prompt and Edit packets
 *	1.05	Fixed Carry when Quit - Items now dropped
 *	1.06	Understands BOSS1/2 defines
 *	1.07	Added BACKTRACK and UserOf
 *	1.08	Added Gfx Driver support
 *	1.09	Added SNOOP support
 *	1.10	IsDarkFor implemented and moved to DarkLight.c
 *	1.11	#<id> support added
 *	1.12	Added register optimisations and short array references
 *	1.13	Added Setin Setout Sethere
 *	1.14	Clear Item1/Item2/Me correctly on player exit (still wrong on stacked cases)
 *	1.15	Support for ACKnowledge on clear.
 *		Protected CNameOf from crashes on long names (returns lowercase)
 *	1.16	RemoveUser doesn't remove blank names
 *	1.17	Strict ANSIfication
 */

PLAYER *PlayerOf(x)	/* Return player pointer of item (or NULL) */
ITEM *x;
{
	return((PLAYER *)FindSub(x,KEY_PLAYER));
}

OBJECT *ObjectOf(x)	/* Guess ... */
ITEM *x;
{
	return((OBJECT *)FindSub(x,KEY_OBJECT));
}

ROOM *RoomOf(x)		/* Keep guessing */
ITEM *x;
{
	return((ROOM *)FindSub(x,KEY_ROOM));
}

USERFLAG *UserFlagOf(x)	/* Fun this */
ITEM *x;
{
	return((USERFLAG *)FindSub(x,KEY_USERFLAG));
}

USERFLAG *UserFlag2Of(x)
ITEM *x;
{
	return((USERFLAG *)FindSub(x,KEY_USERFLAG2));
}

CONTAINER *ContainerOf(x)	/* Dum de dum */
ITEM *x;
{
	return((CONTAINER *)FindSub(x,KEY_CONTAINER));
}

int UserOf(x)			/* DIFFERENT (STOP!!!) - returns an integer
				   holding player number -1 if not a user */
ITEM *x;
{
	register PLAYER *p=PlayerOf(x);
	if(p==NULL)
		return(-1);
	return(p->pl_UserKey);
}

int IsUser(x)			/* Tests for all of the above */
ITEM *x;
{
	return(UserOf(x)!=-1);
}

int IsRoom(i)
ITEM *i;
{
	return(RoomOf(i)!=NULL);
}

int IsPlayer(i)
ITEM *i;
{
	return(PlayerOf(i)!=NULL);
}

int IsObject(i)
ITEM *i;
{
	return(ObjectOf(i)!=NULL);
}

/*
 *	This low level text driver is used to send SNOOP data.	You shouldn't
 *	need to touch this bit
 */

void SendItemDirect(i,x)
ITEM *i;
char *x;
{
	register char *y=x;
	short u;
	PORT *pr;
	register char v;
	register PLAYER *p=PlayerOf(i);
	if(p==NULL)
		return;
	if(p->pl_UserKey==-1)
		return;
	u=p->pl_UserKey;
	pr=UserList[u].us_Port;
	while(strlen(y)>510)
	{
		v=y[510];
		y[510]=0;
		SendTPacket(pr,PACKET_SNOOPTEXT,y);
		y[510]=v;
		y+=510;
	}
	SendTPacket(pr,PACKET_SNOOPTEXT,y);
}

/*
 *	Send a message to an item (VarArgs)
 *
 */
void SendItem(x,a,b,c,d,e,f)
ITEM *x;
char *a,*b,*c,*d,*e,*f;
{
	register PLAYER *p=PlayerOf(x);
	if(p==NULL)		/* Dont send to non players */
	{
		SendUser(-1,a,b,c,d,e,f);
		goto l1;
	}
	SendUser(p->pl_UserKey,a,b,c,d,e,f);	/* Send message   */
l1:	SnoopCheckString(x,UserLastLine);	/* Check snooping */
}

int IsCalled(i,s)		/* Check item name against string */
ITEM *i;
char *s;
{
	if(stricmp(TextOf(i->it_Name),s)==0)
		return(1);
	return(0);
}

void SetName(i,s)	/* Change the name of an item. This is how all text works */
			/* YOU MUST FREE the old text before creating the new */
register ITEM *i;
char *s;
{
	FreeText(i->it_Name);
	i->it_Name=AllocText(s);
}
	
int ArchWizard(i)		/* Check for debugging persona */
register ITEM *i;
{
	if(IsCalled(i,"Anarchy"))
		return(1);
	if(IsCalled(i,"Debugiit"))
		return(1);
	if(IsCalled(i,BOSS1))
		return(1);
	if(IsCalled(i,BOSS2))
		return(1);
	if(IsCalled(i,BOSS3))
		return(1);
	if(IsCalled(i,BOSS4))
		return(1);
	if(IsCalled(i,BOSS5))
		return(1);
	return(0);
}

char *NameOf(x)		/* Return the name of an item */
ITEM *x;
{
	return(TextOf(x->it_Name));
}

char *CNameOf(x)	/* Return the name of an item with 1st letter capital
			   size limit of 128 bytes */
ITEM *x;
{
	static char bf[128];
	if(strlen(NameOf(x))>127)
		return(NameOf(x));
	strcpy(bf,NameOf(x));
	if(*bf)
	{
		if(islower(*bf))
			*bf=toupper(*bf);
	}
	return(bf);
}

short LevelOf(x)	/* Return level of user */
ITEM *x;
{
	register PLAYER *p=PlayerOf(x);
	if(p==NULL)
		return(0);
	if((ArchWizard(x))&&(p->pl_Level<10000))
		return(10000);
	return(p->pl_Level);
}

void Place(x,y)		/* Place item X at Y, doing all links/unlinks needed */
register ITEM *x,*y;
{
	if(IsObject(x))
		ObjectOf(x)->ob_Flags&=~OB_WORN;	/* Clear worn when move */
	if(!O_FREE(x))
		UnlinkItem(x);
	LinkItem(x,y);
}

void XPlace(x,y)	/* Place item X at Y, doing all links/unlinks needed */
register ITEM *x,*y;	/* But only doing a temporary movement while thinking */
{
	if(!O_FREE(x))
		UnlinkItem(x);
	LinkItem(x,y);
}

/*
 *	Remove a user from the game system.
 *
 */

void RemoveUser(u)
unsigned int u;
{
	extern ITEM *Item1,*Item2;
	register ITEM *i;
	register ITEM *j;
	SendUser(-2, "");
	if(UserList[(unsigned short)u].us_Item!=NULL)
	{
		i=O_CHILDREN(UserList[(unsigned short)u].us_Item);
		while(i)	/* Drop all items */
		{
			j=O_NEXT(i);	/* Before Move! - NEXT will change otherwise */
			Place(i,O_PARENT(UserList[(unsigned short)u].us_Item));
			i=j;
		}
#ifdef ATTACH
		if(UserList[u].us_RealPerson&&UserList[u].us_Port!=NULL)
		{
			Act_UnAlias();
			return;
		}
#endif
		if(UserList[u].us_Port!=NULL)
			SendUser(-2, "");
		i=UserList[(unsigned short)u].us_Item;
		UnUserFlag(i);				/* Remove properties */
		UnlinkItem(i);
		UnlockItem(i);
		KillEventQueue(i);
		UnPlayer(i);
		i->it_Perception=-1;			/* Delayed Expunge in case */
		StopAllSnoops(i);
		StopSnoopsOn(i);
		KillIOH(i);
		UnUserText(i);
		if(Debugger==i)
			Debugger=NULL;
		if(Me()==i)
			SetMe(NULL);
		if(Item1==i)
			Item1=NULL;
		if(Item2==i)
			Item2=NULL;
		FreeItem(i);				/* Remove it all */
	}
	UserList[(unsigned short)u].us_Item=NULL;
	if(*UserList[u].us_Name)
		FreeWord(UserList[(unsigned short)u].us_Name,WD_NOUN);
	strcpy(UserList[(unsigned short)u].us_Name,"");
	if(UserList[(unsigned short)u].us_Port!=NULL)
		CloseMPort(UserList[(unsigned short)u].us_Port);	/* Bye bye */
	UserList[u].us_Port=NULL;		/* Port now free */
}

void ExitUser(u)	/* Kicks user back to login */
unsigned int u;
{
	extern ITEM *Item1,*Item2;
	register ITEM *i=O_CHILDREN(UserList[(unsigned short)u].us_Item);
	register ITEM *j;
	while(i)
	{
		j=O_NEXT(i);	/* Before Move! - NEXT will change otherwise */
		Place(i,O_PARENT(UserList[(unsigned short)u].us_Item));
		i=j;
	}
#ifdef ATTACH
	if(UserList[u].us_RealPerson)
	{
		PlayerOf(UserList[u].us_Item)->pl_UserKey=-1;
		UnlockItem(UserList[u].us_Item);
		UserList[u].us_Item=UserList[u].us_RealPerson;
	}
#endif
	i=UserList[(short)u].us_Item;
	UnUserFlag(i);
	UnlinkItem(i);
	UnlockItem(i);
	KillEventQueue(i);
	UnPlayer(i);
	StopAllSnoops(i);
	StopSnoopsOn(i);
	KillIOH(i);
	UnUserText(i);
	if(Debugger==i)
		Debugger=NULL;
	if(Me()==i)
		SetMe(NULL);
	if(Item1==i)
		Item1=NULL;
	if(Item2==i)
		Item2=NULL;
	i->it_Perception=-1;		/* Delayed Expunge */
	FreeItem(i);
	UserList[(unsigned short)u].us_Item=NULL;
	FreeWord(UserList[(unsigned short)u].us_Name,WD_NOUN);
	UserList[(unsigned short)u].us_State=AWAIT_NAME;
}
	
int CountUsers()		/* Count number of users on system */
{
	register short ct=0;
	register short c2=0;
	while(ct<MAXUSER)
	{
		if(UserList[ct].us_Port)
			c2++;
		ct++;
	}
	return((int)c2);
}

int IsBlind(i)	/* Basic checks */
ITEM *i;
{
	register PLAYER *p=PlayerOf(i);
	if(p==NULL)
		return(0);
	if(p->pl_Flags&PL_BLIND)
		return(1);
	return(0);
}

int IsDeaf(i)
ITEM *i;
{
	register PLAYER *p=PlayerOf(i);
	if(p==NULL)
		return(0);
	if(p->pl_Flags&PL_DEAF)
		return(1);
	return(0);
}


void ByeBye(i,m)	/* Clear a user with a message */
ITEM *i;
char *m;
{
	if(UserOf(i)==-1)
		return;
	UserList[UserOf(i)].us_State=AWAIT_ACK;
	SendTPacket(UserList[UserOf(i)].us_Port,PACKET_CLEAR,m);
}


static int AnyMore(u,i)
ITEM *u;
ITEM *i;
{
l1:	if(!O_NEXT(i))
		return(0);
	else
	{
		if((IsObject(O_NEXT(i)))&&
			(ObjectOf(O_NEXT(i))->ob_Flags&OB_NOSEECARRY))
		{
			i=O_NEXT(i);
			goto l1;
		}
		if(!CanSee(LevelOf(u),O_NEXT(i)))
		{
			i=O_NEXT(i);
			goto l1;
		}
	}
	return(1);
}

static ITEM *NextFor(u,i)
ITEM *u;
ITEM *i;
{
l1:	if(!O_NEXT(i))
		return(NULL);
	else
	{
		i=O_NEXT(i);
		if((IsObject(i))&&
			(ObjectOf(i)->ob_Flags&OB_NOSEECARRY))
		{
			goto l1;
		}
		if(!CanSee(LevelOf(u),i))
		{
			goto l1;
		}
	}
	return(i);
}

static void ShowInventory(i,x)
ITEM *i,*x;
{
	short f=0;
	if(x==NULL)
	{
		SendItem(i,".\n");
		return;
	}
	while(x)
	{
		if(f)
		{
			if(!AnyMore(i,x))
				SendItem(i," and ");
			else
				SendItem(i,",");
		}
		if(!((IsObject(x))&&(ObjectOf(x)->ob_Flags&OB_NOSEECARRY)))
		{
			if(!f)
				SendItem(i," carrying ");
			SendItem(i,NameOf(x));
			f=1;
		}
		if(!AnyMore(i,x))
		{
			SendItem(i,".\n");
			return;
		}
		x=NextFor(i,x);
	}
}

void DescribeItem(i,x)	/* Describe an item (AberMUD style) */
ITEM *i;
ITEM *x;
{
	OBJECT *o=ObjectOf(x);
	PLAYER *p=PlayerOf(x);
	if(CanSee(LevelOf(i),x)==0)
		return;
	if(o)
	{
		if(o->ob_Flags&OB_FLANNEL)	/* Not flannels */
			return;
		SendItem(i,"%s\n",TextOf(o->ob_Text[O_STATE(x)]));
		if(UserOf(i)>=0)
			SetItData((short)UserOf(i),x,x->it_Adjective,x->it_Noun);
		return;
	}
	if(p)
	{
		SendItem(i,"%s %s",CNameOf(x),GetHereMsg(x));	/* Players */
		if(UserOf(i)>=0)
			SetItData((short)UserOf(i),x,x->it_Adjective,x->it_Noun);
		ShowInventory(i,O_CHILDREN(x));
		/* Will do inventory too later */
	}
}

void DoesAction(user,flag,msg,p1,p2,p3,p4,p5,p6)
register ITEM *user;
int flag;	/* 1=Someone 0=Nothing if cant see */
		/* 2=Nothing if deaf */
		/* 4=Must Be Present */
char *msg,*p1,*p2,*p3,*p4,*p5,*p6;
{
/*
 *	See the DOESACTION database action documentation
 */
	register short ct=0;
	register ITEM *i;
	while(ct<MAXUSER)	/* For each user */
	{
		i=UserList[ct].us_Item;
		if((i)&&(i!=user))	/* Each but me ... */
		{
			if(flag&4)	/* Must be present */
			{
				if(O_PARENT(i)!=O_PARENT(user))
					goto ne;
			}
			if(IsDeaf(i)&&(flag&2))	/* Must be able to here */
				goto ne;
			if((IsBlind(i))||(CanSee(LevelOf(i),user)==0))
			{	/* Cant see */
				if((flag&1)==0)
					goto ne;	/* No one */
				SendItem(i,"Someone ");	/* Someone */
				SendItem(i,msg,p1,p2,p3,p4,p5,p6);
				if(!strchr(msg,'\n'))
					SendItem(i,"\n");
				goto ne;
			}
			SendItem(i,"%s ",CNameOf(user));
			SendItem(i,msg,p1,p2,p3,p4,p5,p6);	/* Send message */
			if(UserOf(i)>=0)
				SetItData((short)UserOf(i),
					user,user->it_Adjective,user->it_Noun);
			if(!strchr(msg,'\n'))
				SendItem(i,"\n");
		}
ne:		ct++;
	}
}		

void DoesTo(user,flag,thing,msg,p1,p2,p3,p4,p5,p6)
register ITEM *user;
int flag;
ITEM *thing;
/* 1=Someone 0=Nothing if cant see 	*/
/* 2=Nothing if deaf 			*/
/* 4=Must Be Present 			*/
/* 8=Aware if target			*/
char *msg,*p1,*p2,*p3,*p4,*p5,*p6;
{
/*
 *	See database Documentation here
 */
	register short ct=0;
	register ITEM *i;
	while(ct<MAXUSER)
	{
		i=UserList[ct].us_Item;
		if((i)&&(i!=user))
		{
			if(flag&4)
			{
				if(O_PARENT(i)!=O_PARENT(user))
					goto ne;
			}
			if(thing==i&&(flag&8))
				goto uncs;
			if(IsDeaf(i)&&(flag&2))
				goto ne;
			if((IsBlind(i))||((CanSee(LevelOf(i),user)==0)&&
				(CanSee(LevelOf(i),thing)==0)))
			{
				if(flag&1)
					goto ne;
			}
uncs:			if((IsBlind(i))||(CanSee(LevelOf(i),user)==0))
			{
				if(IsPlayer(user))
					SendItem(i,"Someone ");
				else
					SendItem(i,"Something ");
				SendItem(i,msg,p1,p2,p3,p4,p5,p6);
				goto it1;
			}
			SendItem(i,"%s ",CNameOf(user));
			if(UserOf(i)>=0)
				SetItData((short)UserOf(i),
					user,user->it_Adjective,user->it_Noun);
			SendItem(i,msg,p1,p2,p3,p4,p5,p6);
it1:			if(CanSee(LevelOf(i),thing)==0||IsBlind(i))
			{
				if(IsPlayer(thing))
					SendItem(i," someone.\n");
				else
					SendItem(i," something.\n");
			}
			else
			{
				if(UserOf(i)>=0)
					SetItData((short)UserOf(i),
						thing,thing->it_Adjective,thing->it_Noun);
				SendItem(i," %s.\n",NameOf(thing));
			}
		}
ne:		ct++;
	}
}		

void DoesToPlayer(user,flag,thing,msg,p1,p2,p3,p4,p5,p6)
register ITEM *user;
int flag;
ITEM *thing;
/* 1=Someone 0=Nothing if cant see 	*/
/* 2=Nothing if deaf 			*/
/* 4=Must Be Present 			*/
/* 8=Always if target */
char *msg,*p1,*p2,*p3,*p4,*p5,*p6;
{
	register int ct=0;
	register ITEM *i;
	while(ct<MAXUSER)
	{
		i=UserList[ct].us_Item;
		if((i)&&(i!=user))
		{
			if(flag&4)
			{
				if(O_PARENT(i)!=O_PARENT(user))
					goto ne;
			}
			if((flag&8)&&i==thing)
				goto uncst;
			if(IsDeaf(i)&&(flag&2))
				goto ne;
			if(IsBlind(i)||(CanSee(LevelOf(i),user)==0&&
				CanSee(LevelOf(i),thing)==0))
			{
				if(flag&1)
					goto ne;
			}
uncst:			if((IsBlind(i))||(CanSee(LevelOf(i),user)==0))
			{
				if(IsPlayer(user))
					SendItem(i,"Someone ");
				else
					SendItem(i,"Something ");
				SendItem(i,msg,p1,p2,p3,p4,p5,p6);
				goto it1;
			}
			SendItem(i,"%s ",CNameOf(user));
			if(UserOf(i)>=0)
				SetItData((short)UserOf(i),
					user,user->it_Adjective,user->it_Noun);
			SendItem(i,msg,p1,p2,p3,p4,p5,p6);
it1:			if(thing==i)
				SendItem(i," you.\n");
			else
			{
				if(CanSee(LevelOf(i),thing)==0||IsBlind(i))
				{
					if(IsPlayer(i))
						SendItem(i," someone.\n");
					else
						SendItem(i," something.\n");
				}
				else
				{
					if(UserOf(i)>=0)
						SetItData((short)UserOf(i),
						thing,thing->it_Adjective,thing->it_Noun);
					SendItem(i," %s.\n",NameOf(thing));
				}
			}
		}
ne:		ct++;
	}
}		


ITEM *FindSomething(i,defloc)
ITEM *i;
ITEM *defloc;
{
/*
 *	Standard routine for finding item, checking specific room first, then 
 *	whole game, used by editing commands.
 */
	register ITEM *a;
	char *r;
	int refid=0;
	int ad,no;
	r=WordPtr;
	GetParsedWord();
	if(*WordBuffer=='#')	/* #n item */
	{
		if(sscanf(WordBuffer,"#%d",&refid)==0)
			return(NULL);
	}
	else
		WordPtr=r;
	if(GetThing(&ad,&no)==-1)
	{
		return(NULL);
	}
	if(refid==0)
	{
		a=FindIn(LevelOf(i),defloc,(short)ad,(short)no);
		if(a)
			return(a);
		a=FindMaster(LevelOf(i),(short)ad,(short)no);
		return(a);
	}
	else
	{
		a=FindMaster(LevelOf(i),(short)ad,(short)no);
		while((--refid)&&(a))
			a=NextMaster(LevelOf(i),a,(short)ad,(short)no);
		return(a);
	}
}

void SetPrompt(i,p)	/* Set the prompt a user has on input line */
ITEM *i;
char *p;
{
	short v=(short)UserOf(i);
	if(v!=-1)
	{
		SendTPacket(UserList[v].us_Port,PACKET_SETPROMPT,p);
	}
}

ITEM *ExitOf(i,d)	/* Return the exit in direction d. This routine should
			   be in InsAndOuts.c and may move one day */
ITEM *i;
unsigned int d;
{
	register ITEM *x;
	GENEXIT *g;
	g=(GENEXIT *)FindSub(i,KEY_GENEXIT);
	if(g==NULL)
		return(NULL);
	if(d>11)
		return(NULL);
	x=g->ge_Dest[(unsigned short)d];
	if(x==NULL)
		return(NULL);
	if(IsRoom(x))
		return(x);
	if(O_STATE(x)!=0)
		return(NULL);
	return(O_PARENT(x));
}

ITEM *DoorOf(i,d)		/* Find the door in direction - comments as above */
ITEM *i;
unsigned int d;
{
	register ITEM *x;
	register GENEXIT *g;
	g=(GENEXIT *)FindSub(i,KEY_GENEXIT);
	if(g==NULL)
		return(NULL);
	x=g->ge_Dest[(unsigned short)d];
	if(x==NULL)
		return(NULL);
	if(IsRoom(x))
		return(NULL);
	return(x);
}


void Broadcast(msg,flag)
int flag;	/* 1=Someone 0=Nothing if cant see */
		/* 2=Nothing if deaf */
		/* 4=Must Be Present */
register char *msg;
{
/*
 *	Send a message to 'everyone'
 */
	register short ct=0;
	register ITEM *i;
	while(ct<MAXUSER)
	{
		i=UserList[ct].us_Item;
		if(i)
		{
			if(!(IsDeaf(i)&&(flag&2)))
				SendItem(i,"%s\n",msg);
		}
		ct++;
	}
}

void SendEdit(i,x,a1,a2,a3,a4,a5,a6,a7)
ITEM *i;
char *x;
char *a1,*a2,*a3,*a4,*a5,*a6,*a7;	/* VARARGS */
{
/*
 *	Send a string to an item, to be edited.
 */
	static char sbf[512];
	short u=(short)UserOf(i);
	if(u==-1)
		return;
	sprintf(sbf,x,a1,a2,a3,a4,a5,a6,a7);
	SendTPacket(UserList[u].us_Port,PACKET_EDIT,sbf);
}

void TimeOut(u)
int u;
{
/*
 *	Time out controller
 */
	char x[MAXNAME+30];
	if(u<0)
		return;
	if(UserList[(unsigned short)u].us_Port!=NULL)
		CloseMPort(UserList[(unsigned short)u].us_Port);	/* Bye bye */
	UserList[u].us_Port=NULL;				/* Port now free */
	if(*UserList[u].us_Name==0)
	{
		return;
	}
	sprintf(x,"%s has been timed out.",UserList[u].us_Name);
	RemoveUser(u);
	Broadcast(x,0);
}

void SetUserTitle(i,p,x)	/* AberMUD gfx drivers */
ITEM *i;
char *p,*x;
{
	char a[128];
	short v=(short)UserOf(i);
	if(v!=-1)
	{
		sprintf(a,p,x);
		SendTPacket(UserList[v].us_Port,PACKET_SETTITLE,a);
	}
}


int IsLit(i)		/* Check if an item is lit - should be in DarkLight.c */
ITEM *i;
{
	register OBJECT *o=ObjectOf(i);
	if(!o)
		return(0);
	if(o->ob_Flags&OB_LIGHTSOURCE)
		return(1);
	if(o->ob_Flags&OB_LIGHT0)
		return((O_STATE(i)==0));
	return(0);
}

int IsUnique(short pe, short a, short n)	
/* True if adj noun refer to unique item  (for perception pe) */
{
	register ITEM *x=FindMaster(pe,a,n);
	if(x==NULL)
		return(1);
	if(NextMaster(pe,x,a,n))
		return(0);
	return(1);
}

int ItemNumber(short pe, ITEM *i)		/* Get number (#n) of an item */
{
	register ITEM *j;
	register short ct=2;
	j=FindMaster(pe,i->it_Adjective,i->it_Noun);
	if(j==i)
		return(1);
	if(j==NULL)
		return(-1);
	while((j=NextMaster(pe,j,i->it_Adjective,i->it_Noun)))
	{
		if(i==j)
			return(ct);
		ct++;
	}
	return(-1);
}