
#include <stdio.h>
#include	"config.h"
#include "utils.h"
#include	"structs.h"
#include "db.h"
#include "functions.h"
#include "skills.h"

extern long seed;
extern int axis_limit,minus,shift,mask;
char	tmp_buf[SMALL_BUFSIZE];


/* EXTERNAL VARIABLES ETC */
extern int top_of_v_rooms,top_of_z_table,top_of_rooms,top_of_mob_protos;
extern int top_of_mobs,top_of_obj_protos,sock_playing,sock_connected;
extern struct none_room_element *none_rooms;
extern struct npc_index_element *mob_index, *mob_proto_index;
extern struct object_index_element *object_index, *obj_proto_index;
extern struct zone_data *zone_list;

/*********************************************************************************/
/*  These following string comparers are CASE INSENSITIVE and will only compare  */
/*  Alphabetic characters reliably.                                              */
/*********************************************************************************/
/*   Scan until different or until the end of both  */
int str_cmp(char *arg1, char *arg2)
{
   int  chk;
   while(*arg1 || *arg2)
       if((chk = (*arg1++ | 32) - (*arg2++ | 32)))
           return (chk); 
   return(0);
}

/* Scan while arg1 is greater than ' ' and arg1 == arg2 */
/* This is a short compare */
int shrt_cmp(char *arg1, char *arg2)
{
   int chk=0;
   while(*arg1 > ' ')
		if((chk = (*arg1++ | 32) - (*arg2++ | 32)))
			return(chk);
   return(0);
}
/*********************************************************************************/
/*********************************************************************************/

char *str_dup(char *source)
{
	char *malloc(int size);
   char	*new;
	int	len=strlen(source);

	if(len == 0)
		return(0);

   new=malloc(len+1);
   return(strcpy(new, source));
}

int get_arg(char *store, char *arg, char which)
{
   int i=0;
	int count=0;
   int d=which+1;

	/* Set the string to zero for starters :-} */
	*store='\0';

   if(which < 0) {      /* If which is negative: We want all the arguements */
      which=which * -1; /* after arguement #n, otherwise we just want the */
      d=MAX_INP_LEN;    /* single numbered arguement. */
   }

	/* Move to the beginning of the arguement wanted */
   for(i=0;i != which;i++) {
      for(;isspace(*arg);arg++) ;							 /* Remove the white characters */
      for(;*arg != ' ' && *arg != '\n' && *arg;arg++); /* Skip all chars except white chars */
   }

   for(;isspace(*arg);arg++);
   if(!*arg)
      return(0);  /* Arguement #n doesn't exist! */

   for(d=d;d != which && *arg > 31;d--) {
   	*store++ = *arg++;		/* We want the spaces seperating the arguements */
      count++;						/* Add 1 to the character count */
      while(*arg > 32) {		/* Get the next arguement */
         *store++ = *arg++;
         count++;
      }
   }
   *store='\0';   /* Terminate string */
   return(count+1);  /* Exit positive (success) */
}


int	get_target(char *store, char *arg, char which)
{
	int	c;
	static char *skip[] = {
		"to","the","at","from","in","with","out","of","\n",
	};
		
	for(c=0;*skip[c] != '\n' && get_arg(store,arg,which);c++) {
		if(!str_cmp(store,skip[c])) {
			which++;
			c=0;
		}
	}
	if(*skip[c] == '\n')
		return(1);
	return(0);
}

int	get_which(char source[])
{
	int i,c,which;

	for(i=0;source[i] != '\0';i++) {
		tmp_buf[i] = source[i];
		if(source[i] == '.') {
			tmp_buf[i] = '\0';
			for(c=0,i++;source[i] != '\0';c++,i++)
				source[c] = source[i];
			source[c] = '\0';
			if((_numeric(&which,tmp_buf)) && which)
				return(which);
			else if(!str_cmp(tmp_buf,"all"))
				return(-1);
			else return(1);
		}
	}
	return(1);
}

void send_2_char(struct char_data *ch,struct char_data *pl,char invis_msg,char *txt)
{
	/* Are they playing the game? */
	if((!pl->desc) || !pl->desc->port_desc)
		return;

	/* Are they in the same private mode? */
	if(pl->CON_STATE != CON_PLYNG)
		if((!ch->desc) || ch->CON_STATE != pl->CON_STATE) 
			return;

   /* Need to check if they can see me! ch=me pl=destination */
	if(pl->LEVEL < ch->INVIS) {
		if(!invis_msg)
			return;
		sprintf(tmp_buf,"Someone %s\n\r",txt);
   } else if(pl->LEVEL >= WIZARD)
      sprintf(tmp_buf,"%s %s\n\r",ch->name,txt);
   else if(ch->INVIS && !BTST(pl->SPECIALS,SEE_INVIS)) {
		if(!invis_msg)
			return;
      sprintf(tmp_buf,"Someone %s\n\r",txt);
   } else
      sprintf(tmp_buf,"%s %s\n\r",ch->name,txt);

   send_2_output(tmp_buf,pl->desc);

   return;
}

/* ch = The seeker, pl = the target */
int see_char_chk(struct char_data *ch, struct char_data *pl)
{
	int c_invis=0;

	if(!pl || !ch)
		return(0);		/* Check we have both players */

	if(ch->LEVEL >= WIZARD || BTST(ch->SPECIALS,SEE_INVIS))
		c_invis=ch->LEVEL;

	if(pl->INVIS <= c_invis)
		return(1);		/* I can see them */
	else
		return(0);		/* No I can't see them */
}


void	get_flags(char *text, struct char_data *pl)
{
	int i;

	/* Are they invisible? */
   if((i = pl->INVIS) > 1) {
		sprintf(tmp_buf,"(Invis %d)",i);
		strcat(text,tmp_buf);
	} else if(i)
		strcat(text,"(Invis)");

	/* Are they an ILLEGAL player killer? */
	if(BTST(pl->SPECIALS,PKILLER))
		strcat(text,"(KILLER)");

	/* Are they linkless? */
	if((pl->desc) && !pl->desc->port_desc)
		strcat(text,"(Linkless)\n\r");
	else
		strcat(text,"\n\r");

	return;
}

int   _random(int die, int sides)
{
   int   roll=0;
   long  new;

	if(!die || !sides)
		return(0);

   for(;die>0;--die) {
		new = random();
      roll += ((new & 0xcad) % sides)+1;
   }

   return(roll);
}

/* check if a string is numeric: return 1 for true, 0 for false */
int	_numeric(int (*target),char *string)
{
	int c;
	int count=0;
	int neg=1;

	*target=0;
	if(*string == '-') {
		neg = -1;
		*string++;
	} else if(*string == '+')
		*string++;

	while((c = *string++) && c != '\n') {
		if(c < '0' || c > '9')
			return(0);
		*target = (*target * 10) + (c - '0');
		count++;
	}
	if(!count)
		return(0);

	*target=(*target * neg);
	return(1);
}

int	obj_cmp(char *name, struct object_data *object)
{
	int  c;

	if(!object)
		return(0);

   for(c=0;get_arg(tmp_buf,object->name,c*-1);c++)
      if(!shrt_cmp(name,tmp_buf))
         return(1);

	if(!object->aliases)
		return(0);

	for(c=0;get_arg(tmp_buf,object->aliases,c);c++) {
		if(!shrt_cmp(name,tmp_buf))
			return(1);
	}

	return(0);
}

int   mob_cmp(char *name, struct char_data *mob)
{
   int  c;

   if(!mob)
      return(0);

	if(!shrt_cmp(name,mob->name))
		return(1);

   return(0);
}

void obj_weight_adjust(struct object_data *obj, int adjuster)
{
	obj->OBJ_WEIGHT += adjuster;

	if(obj->contained_in)
		obj_weight_adjust(obj->contained_in, adjuster);
	if(obj->carried_by)
		char_weight_adjust(obj->carried_by, adjuster);
}

void char_weight_adjust(struct char_data *ch, int adjuster)
{
	ch->CUR_WGHT += adjuster;

	if(ch->contained_in)
		obj_weight_adjust(ch->contained_in, adjuster);
	if(ch->carried_by)
		char_weight_adjust(ch->carried_by, adjuster);
}

int	can_take(struct char_data *ch, struct object_data *obj, int mode)
{

	if((ch->CUR_WGHT + obj->OBJ_WEIGHT) > ch->MAX_WGHT)
		return(0);
	return(1);
}

void	dump_leader(struct char_data *ch)
{
	struct char_data *who;

	if(ch==ch->leader->followers) {
		ch->leader->followers=ch->next_follower;
	} else {
		for(who=ch->leader->followers;(who->next_follower != ch) && who;who=who->next_follower)
			;
		who->next_follower=ch->followers;
	}
	send_2_char(ch,ch->leader,INVIS_CHK,"has stopped following you.\n\r");
	ch->leader = ch->next_follower = 0;
	return;
}

void	disband_followers(struct char_data *ch)
{
	struct char_data *who, *who_next;

	for(who=ch->followers;who;who=who_next) {
		who_next=who->next_follower;
		send_2_char(ch,who,IGNR_INVIS,"has disbanded the group.\n\r");
		who->next_follower = who->leader = 0;
	}
	ch->followers=0;
	return;
}

void	follow_leader(struct char_data *ch, struct char_data *pl)
{

	if(ch->leader) {
		send_2_output("You are already in following someone!\n\r",ch->desc);
	} else {
		ch->leader=pl;
		ch->next_follower = pl->followers;
		pl->followers=ch;
		sprintf(tmp_buf,"You are now a member of %s's group.\n\r",pl->name);
		send_2_output(tmp_buf,ch->desc);
		send_2_char(ch,pl,IGNR_INVIS,"has joined your group.");
	}
	return;
}

void	obj_affects(struct char_data *ch, struct object_data *obj, char mode)
{
	struct affect_data *aff, *aff_next;

	for(aff=obj->affect;aff;aff=aff_next) {
		aff_next=aff->next;
		if(mode)
			ch->data[aff->affect]-=aff->value;
		else
			ch->data[aff->affect]+=aff->value;
	}

	update_char(ch);
}

int	check_can_wear(struct char_data *ch, struct object_data *object)
{
	int c;

	if(!object) {
		printf("**FATAL ERROR** no object for %s\n",ch);
		return(1);
	} else if(ch->LEVEL == IMP) {
		return(0);
   } if(BTST(object->CLASS,class_bits[ch->CLASS])) {
      sprintf(tmp_buf,"%s characters can't use %s\n\r",class[ch->CLASS].full,object->name);
      send_2_output(tmp_buf,ch->desc);
		return(1);
   } else if(BTST(object->SEX,sex_bits[ch->SEX])) {
      sprintf(tmp_buf,"%s characters can't use %s\n\r",sex[ch->SEX].c,object->name);
      send_2_output(tmp_buf,ch->desc);
		return(1);
   } else if(object->ALIGN) {
      for(c=0;*align[c].name  != '\n';c++)
         if(ch->ALIGN < align[c].min)
            break;
      if(BTST(object->ALIGN,ch->ALIGN)) {
         sprintf(tmp_buf,"You drop %s in pain.\n\r",object->name);
         send_2_output(tmp_buf,ch->desc);
			return(1);
      }
   }
	return(0);
}

struct char_data *get_ch_in_room(struct char_data *ch, char *name)
{
	extern struct room_data *where;
	struct char_data *who, *who_next;

	for(who=ch->in_room->people;who;who=who_next) {
		who_next=who->next_in_room;
		if(!shrt_cmp(name,who->name)) {
			if(!see_char_chk(ch,who))
				return(0);
			return(who);
		}
	}
	return(0);
}

struct char_data *get_xuser(struct char_data *ch, char *name)
{
	struct char_data *tgt, *next_tgt;

	if((tgt = get_player(name))) {
		return(tgt);
	} else if((tgt = get_ch_in_room(ch,name))) {
		return(tgt);
	} else {
		return(0);
	}
}

struct char_data *get_player(char *name)
{
	extern struct descriptor_data *desc_list;
	struct descriptor_data *who, *next_who;
	struct char_data *rmy, *next_rmy;

	for(who=desc_list;who;who=next_who) {
		next_who=who->next_in_list;
		if(!shrt_cmp(name,who->NAME))
			return(who->character);
	}

	return(0);
}

int	get_weap_dam(struct object_data *weapon)
{
	int	damage;

	if(!weapon)
		return(0);

	if(weapon->OBJ_DIE && weapon->OBJ_SIZE)
		damage = _random(weapon->OBJ_DIE,weapon->OBJ_SIZE);
	else
		damage = 0;

	return(damage += weapon->OBJ_BONUS);
}

void  update_char(struct char_data *ch)
{
	int c;
	extern long levels[];

   ch->MAX_WGHT=((((ch->HEIGHT+ch->WEIGHT)/30)+ch->CLASS+(ch->STR/2.5))*ch->LEVEL);
   if(ch->LEVEL < GOD || BTST(ch->SPECIALS,NPC)) {
      ch->THAC0=thac0_list[ch->CLASS-1][ch->LEVEL];
      ch->THAC0 -= ((((ch->STR+ch->WIS+ch->INT)-40)/1.5) + ch->TOHIT);
      if(ch->THAC0 < 1)
         ch->THAC0 = 1;
   } else
      ch->THAC0 = 1;

	/* Check to see if they have gained a level */
	for(c=ch->LEVEL;ch->EXP > levels[c+1] && c < IMP;c++);
	if(c > ch->LEVEL) {
		sprintf(tmp_buf,"Congratulations, you are now level %d\n\r",c);
		send_2_output(tmp_buf,ch->desc);
	}
   return;
}

void change_status(struct char_data *ch, int status)
{
	extern struct char_data *channel[];

	if(ch->desc->connected == status)
		return;

	if(ch->desc->connected == CON_PLYNG) {
		--sock_playing;
	} else if(ch->CON_STATE >= CON_PRIVATE) {
		channel[ch->CON_STATE - CON_PRIVATE] = 0;
	}

	if(status == CON_PLYNG) {
		++sock_playing;
	} else if(status >= CON_PRIVATE) {
		channel[status - CON_PRIVATE] = ch;
	}
	ch->desc->connected = status;
}

/*********************************************************************/
/* This routine will always return a room (even if it doesn't exist) */
/* That way, you can drop an object/or meet someone else in a non-   */
/* existant room - cool, eh?                                         */
/*********************************************************************/
struct room_data *get_room(int x,int y,int z,int room)
{
	int  zone,found;

	if(((zone=get_zone(x,y,z)) > -1) && ZONE_ROOM(zone,room)) {
		/* We have a Zone and a room! */
		return(zone_list[zone].room[room]);
	} else {
		/* I have to create a room (if it doesn't exist already) */
		if((found=get_void_room(x,y,z,room)) > -1) {
			/* One exists already */
			return(none_rooms[found].room);
		} else {
			/* Have to make one */
			return(create_void_room(x,y,z,room));
		}
	}
}

int  get_void_room(int x,int y,int z,int r)
{
	int top = top_of_v_rooms;
	int mid,low=0,result;

   /* binary search for idnum */
   while(low <= top) {
      mid = (low + top)/2;
      if((result = ((x != NONE_X(mid)) ? (x - NONE_X(mid)) :
                   ((y != NONE_Y(mid)) ? (y - NONE_Y(mid)) :
                   ((z != NONE_Z(mid)) ? (z - NONE_Z(mid)) :
                   ((r != NONE_R(mid)) ? (r - NONE_R(mid)) :  0 ))))) == 0)
         return(mid); /* Found it */
      else if(result > 0)
         low = mid + 1; /* Search higher */
      else if(result < 0)
         top = mid - 1; /* Search lower  */
	}
   return(-1);
}

int  get_zone(int x,int y, int z)
{
	extern struct grid_index_element *grid_list;
	int top=top_of_z_table;
	int mid,low=0,result;

   /* binary search for idnum */
   while(low <= top) {
      mid = (low + top)/2;
      if((result = ((x != GRID_X(mid)) ? (x - GRID_X(mid)) :
                   ((y != GRID_Y(mid)) ? (y - GRID_Y(mid)) :
                   ((z != GRID_Z(mid)) ? (z - GRID_Z(mid)) : 0 )))) == 0)
         return(GRID_ZONE(mid)); /* Found it */
      else if(result > 0)
         low = mid + 1; /* Search higher */
      else if(result < 0)
         top = mid - 1; /* Search lower  */
	}
   return(-1);
}

struct char_data *get_proto_mob(int mob)
{
   int  top=top_of_mob_protos-1;
   int  mid,low=0;

   /* binary search for mob */
   while(low <= top) {
      mid = (low + top)/2;
      if(mob == mob_proto_index[mid].mob->IDNUM)
         return(mob_proto_index[mid].mob); /* Found it */
      else if(mob > mob_proto_index[mid].mob->IDNUM)
         low = mid + 1; /* Search higher */
      else if(mob < mob_proto_index[mid].mob->IDNUM)
         top = mid - 1; /* Search lower  */
   }
   return(0);
}


struct object_data *get_proto_object(int object)
{
   int  top=top_of_obj_protos-1;
   int  mid,low=0;

   /* binary search for object */
   while(low <= top) {
      mid = (low + top)/2;
      if(object == obj_proto_index[mid].object->IDNUM)
         return(obj_proto_index[mid].object); /* Found it */
      else if(object > obj_proto_index[mid].object->IDNUM)
         low = mid + 1; /* Search higher */
      else if(object < obj_proto_index[mid].object->IDNUM)
         top = mid - 1; /* Search lower  */
   }
   return(0);
}

void  roll_char(struct char_data *pl)
{
   int   rolled[5];
   int   c,i,tmp,s;

   /* Get the 5*4 die rolls */
   for(c=0;c<5;c++)
         rolled[c] = 6 + _random(2,6);

   /* sort them into numerical descending order */
   do {
      c=1;
      for(i=0;i<4;i++)
         if(rolled[i] < rolled[i+1]) {
            tmp=rolled[i+1];
            rolled[i+1]=rolled[i];
            rolled[i]=tmp;
            c=0;
         }
   } while(!c);

   for(c=0;c<5;c++)
      pl->data[stat_order[pl->CLASS-1][c]]=rolled[c];
}

int _power(int x)
{
	static int tmp;
	for(tmp=2;x>1;x--)
		tmp *= 2;
	return(tmp);
}
//CRTH
// Function will return 0 if character does not have the skill.
int	HasSkill(struct char_data *ch,char *skill)
{
	int	RetVal = 0;		// Return value.
	int	cnt;				// Counter.

	for(cnt = 0;cnt < ch->NO_SKILLS;cnt++)
	{
		if(strcmp(ch->skills[cnt].name,skill) == 0)
		{
			RetVal = 1;
			break;
		}
	}	
	return(RetVal);
}
//	Function will return 0 if not a skill.
int	IsSkill(char *skill)
{
	int	RetVal = 1;		// Return value.
	int	cnt;				// Counter.

	for(cnt = 0;show_skill[cnt].useby != LAST_SKL;cnt++)
	{
		if(strcmp(show_skill[cnt].name,skill) == 0)
			break;
	}	
	if(show_skill[cnt].useby == LAST_SKL)
		RetVal = 0;

	return(RetVal);

}
// Gets the skill number for the characters skill.
int	GetSkillCharIndex(struct char_data *ch,char *skill)
{
	int	cnt;

	for(cnt = 0;cnt < ch->NO_SKILLS;cnt++)
	{
		if(strcmp(ch->skills[cnt].name,skill) == 0)
			break;	
	}
	return(cnt);
}
// Gets the base skill index.
int	GetSkillIndex(char *skill)
{
	int	cnt;

	for(cnt = 0;show_skill[cnt].useby != LAST_SKL;cnt++)
	{
		if(strcmp(show_skill[cnt].name,skill) == 0)
			break;	
	}
	return(cnt);
}
// returns 0 if the character can't have the skill.
int	CanHaveSkill(struct char_data *ch,char *skill)
{
	int	idx;
	int 	RetVal = 0;

	idx = GetSkillIndex(skill);

	if(show_skill[idx].useby != UALL_SKL)
	{
		if(show_skill[idx].useby == ch->CLASS || ch->LEVEL >= GOD)
			RetVal = 1;
	}
	else
		RetVal = 1;

	return(RetVal);
}
// assigning a skill to a character.
void	GiveSkill(struct char_data *ch,int idx)
{
	int	cnt;

	ch->skills[ch->NO_SKILLS].name = show_skill[idx].name;
	ch->skills[ch->NO_SKILLS].desc = show_skill[idx].desc;
	ch->skills[ch->NO_SKILLS].useby = show_skill[idx].useby;
	ch->skills[ch->NO_SKILLS].type = show_skill[idx].type;
	ch->skills[ch->NO_SKILLS].useon = show_skill[idx].useon;

	for(cnt=0;cnt<3;cnt++)
	{
		ch->skills[ch->NO_SKILLS].attempt[cnt] = show_skill[idx].attempt[cnt];
		ch->skills[ch->NO_SKILLS].fail[cnt] = show_skill[idx].fail[cnt];
		ch->skills[ch->NO_SKILLS].success[cnt] = show_skill[idx].success[cnt];
	}
	for(cnt=0;cnt<5;cnt++)
	{
		ch->skills[ch->NO_SKILLS].oftype[cnt] = show_skill[idx].oftype[cnt];
		ch->skills[ch->NO_SKILLS].stat[cnt] = show_skill[idx].stat[cnt];
		ch->skills[ch->NO_SKILLS].statto[cnt] = show_skill[idx].statto[cnt];
		ch->skills[ch->NO_SKILLS].opper[cnt] = show_skill[idx].opper[cnt];
		ch->skills[ch->NO_SKILLS].value[cnt] = show_skill[idx].value[cnt];
		ch->skills[ch->NO_SKILLS].special[cnt] = show_skill[idx].special[cnt];
		ch->skills[ch->NO_SKILLS].durat[cnt] = show_skill[idx].durat[cnt];

	}

	ch->skills[ch->NO_SKILLS+1].useby = LAST_SKL;

}
// Get the last base skill number.
int	GetLastSkillIndex(void)
{
	int	idx;

	for(idx = 0;show_skill[idx].useby != LAST_SKL;idx++);
		
	return(idx);
}
// CRTH - this function will switch on or off the save location flag.
void	do_autoloc(struct char_data *ch)
{
	if(BTST(ch->PREFS,SAVE_LOC))
	{
		BCLR(ch->PREFS,SAVE_LOC);
		send_2_output("\033[31mautoloc is now off.\033[0m\n\r",ch->desc);
	}
	else
	{
		BSET(ch->PREFS,SAVE_LOC);
		send_2_output("\033[1mautoloc is now on.\033[0m\n\r",ch->desc);
	}
}
// CRTH - function switches auto loot on and off.
void	do_autoloot(struct char_data *ch)
{
	if(BTST(ch->PREFS,LOOT))
	{
		BCLR(ch->PREFS,LOOT);
		send_2_output("\033[31mautoloot is now off.\033[0m\n\r",ch->desc);
	}
	else
	{
		BSET(ch->PREFS,LOOT);
		send_2_output("\033[1mautoloot is now on.\033[0m\n\r",ch->desc);
	}
}

