/***************************************************************************
 *  file: changes.c , Implementation of new commands.      Part of DIKUMUD *
 *  Usage : New commands while new datastructures are developed.           *
 *  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
 *                                                                         *
 *  Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse  *
 *  Performance optimization and bug fixes by MERC Industries.             *
 *  You can use our stuff in any way you like whatsoever so long as this   *
 *  copyright notice remains intact.  If you like it please drop a line    *
 *  to mec@garnet.berkeley.edu.                                            *
 *                                                                         *
 *  This is free software and you are benefitting.  We hope that you       *
 *  share your changes too.  What goes around, comes around.               *
 ***************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "structs.h"
#include "mob.h"
#include "obj.h"
#include "utils.h"
#include "interp.h"
#include "handler.h"
#include "db.h"
#include "spells.h"
#include "limits.h"

/*   external vars  */

extern struct room_data *world;
extern struct descriptor_data *descriptor_list;
extern struct index_data *mob_index;
extern struct index_data *obj_index;
extern struct int_app_type int_app[26];
extern const char *color_table[];
extern const char *color_name[];
extern const char *channel_table[];
extern bool wizlock;

/* external functs */

int truegod(struct char_data *ch);
void set_title(struct char_data *ch);
int str_cmp(char *arg1, char *arg2);
char *skip_spaces(char *string);
struct time_info_data age(struct char_data *ch);
int mana_limit(struct char_data *ch);
int hit_limit(struct char_data *ch);
int move_limit(struct char_data *ch);
int mana_gain(struct char_data *ch);
int hit_gain(struct char_data *ch);
int move_gain(struct char_data *ch);
void assign_colors(struct char_data *ch);

/* To be moved to moved to act.wizard.c */

void do_freeze(struct char_data *ch, char *argument, int cmd)
{
    struct char_data *vict;
    struct obj_data *dummy;
    struct descriptor_data *d;
    char buf[MAX_INPUT_LENGTH];
    char log_buf[MAX_STRING_LENGTH];

    if (IS_NPC(ch))
	return;
    d = descriptor_list;
    if (!(truegod(ch))) {
     	sprintf(log_buf,"A HACKER (%s@%s) is trying to freeze someone.\r\n",
		GET_NAME(ch), d->host);
        log(log_buf);
	return;
    }

    one_argument(argument, buf);

    if (!*buf)
	send_to_char("Freeze who?\r\n", ch);

    else if (!generic_find(argument, FIND_CHAR_WORLD, ch, &vict, &dummy))
	send_to_char("Couldn't find any such creature.\r\n", ch);
    else if (IS_NPC(vict))
	send_to_char("Can't do that to a beast.\r\n", ch);
    else if (GET_LEVEL(vict) >= GET_LEVEL(ch))
	act("$E might object to that.. better not.", 0, ch, 0, vict, TO_CHAR);
    else if (IS_SET(vict->specials.act, PLR_FREEZE))
    {
	send_to_char("You now can do things again.\r\n", vict);
	send_to_char("FREEZE removed.\r\n", ch);
	REMOVE_BIT(vict->specials.act, PLR_FREEZE);
    }
    else
    {
	send_to_char("The gods take away your ability to ...\r\n", vict);
	send_to_char("FREEZE set.\r\n", ch);
	SET_BIT(vict->specials.act, PLR_FREEZE);
    }
}

void do_color(struct char_data *ch, char *arg, int cmd)
{
char buf[MAX_INPUT_LENGTH];
char buf2[MAX_INPUT_LENGTH];
char tmp[MAX_INPUT_LENGTH];
int i,j;
buf[0]=0;
buf2[0]=0;
if (*arg) sscanf(arg,"%s %s",buf,buf2);
if (!*arg) {
  assign_colors(ch);
  send_to_char("Your current color options are ",ch);
  send_to_char((IS_SET(ch->player.kalbits,KAL_COLOR)?"[ON]\r\n":"[OFF]\r\n"),ch);
  for (i=0;i<MAX_COL_CHAN;i++)
    {
    sprintf(tmp,"%-9s: %s\r\n",channel_table[i],color_name[ch->player.col[i]]);
    send_to_char(tmp,ch);
  }
  return;
}
else if (!strcmp(buf,"on")) {
  send_to_char("Color mode on.\r\n",ch);
  SET_BIT(ch->player.kalbits,KAL_COLOR);
  assign_colors(ch);
  return;
}
else if (!strcmp(buf,"off")) {
  send_to_char("Color mode off.\r\n",ch);
  REMOVE_BIT(ch->player.kalbits,KAL_COLOR);
  return;
}
else {
  for(i=0;strcmp(buf,channel_table[i])&&strcmp(channel_table[i],"\n");i++) ;
  if (!strcmp(channel_table[i],"\n"))
    send_to_char("How about a VALID channel?\r\n",ch);
  else
    {
    for(j=0;strcmp(buf2,color_name[j])&&strcmp(color_name[j],"\n");j++) ;
    if (!strcmp(color_name[j],"\n"))
      send_to_char("Try a color name..\r\n",ch);
    else
      {
      ch->player.col[i]=j;
      sprintf(buf,"%s will now appear in %s.\r\n",channel_table[i],color_name[j]);
      send_to_char(buf,ch);
    }
  }
  return;
}
return;
}

void assign_colors(struct char_data *ch)
{
int i;
for (i=0;i<10;i++) {
 if(ch->player.col[i]<0 || ch->player.col[i]>19)
   {
     send_to_char("Default values used.\r\n",ch);
     for (i=0;i<10;i++) ch->player.col[i]=9;
     do_color(ch,"normal grey",9);
     do_color(ch,"say RED",9);
     do_color(ch,"tell GREEN",9);
     do_color(ch,"group CYAN",9);
     do_color(ch,"shout YELLOW",9);
     do_color(ch,"gossip BLUE",9);
     do_color(ch,"auction PURPLE",9);
     return;
   }
}
}

void do_log(struct char_data *ch, char *argument, int cmd)
{
    struct char_data *vict;
    struct obj_data *dummy;
    struct descriptor_data *d;
    char buf[MAX_INPUT_LENGTH];
    char log_buf[MAX_STRING_LENGTH];

    if (IS_NPC(ch))
	return;
    d = descriptor_list;
    if (!(truegod(ch))) {
	sprintf(log_buf,"A HACKER (%s@%s) is trying to log/unlog a player.\r\n",
		GET_NAME(ch), d->host);
	log(log_buf);
	return;
    }

    one_argument(argument, buf);

    if (!*buf)
	send_to_char("Log who?\r\n", ch);

    else if (!generic_find(argument, FIND_CHAR_WORLD, ch, &vict, &dummy))
	send_to_char("Couldn't find any such creature.\r\n", ch);
    else if (IS_NPC(vict))
	send_to_char("Can't do that to a beast.\r\n", ch);
    else if (GET_LEVEL(vict) >= GET_LEVEL(ch))
	act("$E might object to that.. better not.", 0, ch, 0, vict, TO_CHAR);
    else if (IS_SET(vict->specials.act, PLR_LOG))
    {
	send_to_char("LOG removed.\r\n", ch);
	REMOVE_BIT(vict->specials.act, PLR_LOG);
    }
    else
    {
	send_to_char("LOG set.\r\n", ch);
	SET_BIT(vict->specials.act, PLR_LOG);
    }
}

void do_wizlock(struct char_data *ch, char *argument, int cmd)
{
   struct descriptor_data *d;
   char log_buf[MAX_STRING_LENGTH];

    wizlock = !wizlock;

    if (IS_NPC(ch)){
      send_to_char("A monster should never lock players out for its own safety.\r\n", ch);
      return;
    }
    d = descriptor_list;
    if (!(truegod(ch))) {
	sprintf(log_buf,"A HACKER (%s@%s) is trying to lock the game out.\r\n",
		GET_NAME(ch), d->host);
	log(log_buf);
	return;
    }

    if ( wizlock ) {
	sprintf(log_buf,"Game has been wizlocked by %s.",GET_NAME(ch));
	log(log_buf);
	send_to_char("Game wizlocked.\r\n", ch);
    } else {
	sprintf(log_buf,"Game has been un-wizlocked by %s.",GET_NAME(ch));
	log(log_buf);
	send_to_char("Game un-wizlocked.\r\n", ch);
    }
}




/* This routine is used by 34+ level ONLY to set 
   specific char/npc variables, including skills */

void do_set(struct char_data *ch, char *argument, int cmd)
{
    /* from spell_parser.c */
    char *spells[]= {
	"armor","teleport","bless","blindness","burning hands","call lightning",
	"charm person","chill touch","clone","colour spray","control weather",
	"create food","create water","cure blind","cure critic","cure light",
	"curse","detect evil","detect invisibility","detect magic",
	"detect poison","dispel evil","earthquake","enchant weapon",
	"energy drain","fireball","harm","heal","invisibility",
	"lightning bolt","locate object","magic missile","poison",
	"protection from evil","remove curse","sanctuary","shocking grasp",
	"sleep","strength","summon","ventriloquate","word of recall",
	"remove poison","sense life",
	"sneak","hide","steal","backstab","pick lock",
	"kick","bash","rescue","\n"
    };
    char *values[] = {
	"age","sex","class","level","height","weight","str","stradd",
	"int","wis","dex","con","gold","exp","mana","hit","move",
	"sessions","alignment","thirst","drunk","full","mage","cleric",
	"thief","warrior","\n"
    };
    struct char_data *vict;
    struct descriptor_data *d;
    char name[100], buf2[100], buf[100], help[MAX_STRING_LENGTH];
    char log_buf[MAX_STRING_LENGTH];
    int skill, value, i, qend;

    argument = one_argument(argument, name);

    if (IS_NPC(ch)){
       send_to_char("Like you really need to set something...\r\n", ch);
       return;
    }
    d = descriptor_list;
    if (!(truegod(ch))) {
	sprintf(log_buf,"A HACKER (%s@%s) is trying set something.\r\n",
		GET_NAME(ch), d->host);
	log(log_buf);
	return;
    }

    if (!*name) /* no arguments. print an informative text */
    {
	send_to_char(
	    "Syntax:\r\nset <name> skill '<skill>' <value> <value>\r\n", ch);
	send_to_char(
	    "or:\r\nset <name> value <field> <value>\r\n", ch);

	strcpy(help, "Skill being one of the following:\r\n");
	for (i = 1; *spells[i] != '\n'; i++)
	{
	    sprintf(help + strlen(help), "%18s", spells[i]);
	    if (!(i % 4))
	    {
		strcat(help, "\r\n");
		send_to_char(help, ch);
		*help = '\0';
	    }
	}
	if (*help)
	    send_to_char(help, ch);

	strcpy(help, "\r\nField being one of the following:\r\n");
	for (i = 1; *values[i] != '\n'; i++)
	{
	    sprintf(help + strlen(help), "%18s", values[i]);
	    if (!(i % 4))
	    {
		strcat(help, "\r\n");
		send_to_char(help, ch);
		*help = '\0';
	    }
	}
	if (*help)
	    send_to_char(help, ch);
	send_to_char("\r\n", ch);
	return;
    }
    if (!(vict = get_char_vis(ch, name)))
    {
	send_to_char("No living thing by that name.\r\n", ch);
	return;
    }
    argument = one_argument(argument, buf);
    if (!*buf)
    {
	send_to_char("'skill' or 'value' expected.\r\n", ch);
	return;
    }
    if (str_cmp(buf,"skill") && str_cmp(buf,"s")
    && str_cmp(buf,"value") && str_cmp(buf,"v"))
    {
	send_to_char("'skill' or 'value' expected.\r\n", ch);
	return;
    }
    if (!str_cmp(buf,"skill") || !str_cmp(buf,"s"))
    /* This is a skill */
    {
	argument = skip_spaces(argument);

	/* If there is no chars in argument */
	if (!(*argument)) {
	    send_to_char("Skill name expected.\r\n", ch);
	    return;
	}

	if (*argument != '\'') {
	    send_to_char("Skill must be enclosed in: ''\r\n",ch);
	    return;
	}

	/* Locate the last quote && lowercase the magic words (if any) */

	for (qend=1; *(argument+qend) && (*(argument+qend) != '\'') ; qend++)
	    *(argument+qend) = LOWER(*(argument+qend));

	if (*(argument+qend) != '\'') {
	    send_to_char("Skill must be enclosed in: ''\r\n",ch);
	    return;
	}

	if ((skill = old_search_block(argument, 1, qend-1, spells, 0)) < 0)
	{
	    send_to_char("Unrecognized skill.\r\n", ch);
	    return;
	}
	skill--;
	argument += qend+1; /* skip to next parameter */
	argument = one_argument(argument,buf);
	if (!*buf)
	{
	    send_to_char("Learned value expected.\r\n", ch);
	    return;
	}
	value = atoi(buf);
	if (value < 0)
	{
	    send_to_char("Minimum value for learned is 0.\r\n", ch);
	    return;
	}
	if (value > 100)
	{
	    send_to_char("Max value for learned is 100.\r\n", ch);
	    return;
	}
	argument = one_argument(argument,buf);
	if (!*buf)
	{
	    send_to_char("Learned value expected.\r\n", ch);
	    return;
	}
	if (str_cmp(buf,"y") && str_cmp(buf,"n"))
	{
	    send_to_char("Recognice value must be 'y' or 'n'", ch);
	    return;
	}
	sprintf(buf2,
	    "%s changes %s's %s to %d,%s.",GET_NAME(ch),GET_NAME(vict),
	    spells[skill],value,buf);
	vict->skills[skill].learned = value;
	vict->skills[skill].recognise = (!str_cmp(buf,"y"));
    } else {
	/* it is another value */
	argument = one_argument(argument,buf);
	if (!*buf)
	{
	    send_to_char("Field name expected.\r\n", ch);
	    return;
	}
	if ((skill = old_search_block(buf, 0, strlen(buf), values, 1)) < 0)
	{
	    send_to_char(
		"No such field is known. Try 'set' for list.\r\n", ch);
	    return;
	}
	skill--;
	argument = one_argument(argument,buf);
	if (!*buf)
	{
	    send_to_char("Value for field expected.\r\n", ch);
	    return;
	}
	sprintf(buf2,
		"%s sets %s's %s to %s.",
		GET_NAME(ch),GET_NAME(vict),values[skill],buf);
	switch (skill) {
	    case 0: /* age */
	    {
		value = atoi(buf);
		if ((value < 16) || (value > 79))
		{
		    send_to_char("Age must be more than 16 years\r\n", ch);
		    send_to_char("and less than 80 years.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set age of victim */
		vict->player.time.birth = 
		    time(0) - (long)value*(long)SECS_PER_MUD_YEAR;
	    };
	    break;
	    case 1: /* sex */
	    {
		if (str_cmp(buf,"m") && str_cmp(buf,"f") && str_cmp(buf,"n"))
		{
		    send_to_char("Sex must be 'm','f' or 'n'.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set sex of victim */
		switch(*buf) {
		    case 'm':vict->player.sex = SEX_MALE;   break;
		    case 'f':vict->player.sex = SEX_FEMALE; break;
		    case 'n':vict->player.sex = SEX_NEUTRAL;break;
		}
	    }
	    break;
	    case 2: /* class */
	    {
		if (str_cmp(buf,"m") && str_cmp(buf,"c") && 
		    str_cmp(buf,"w") && str_cmp(buf,"t"))
		{
		    send_to_char("Class must be 'm','c','w' or 't'.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set class of victim */
		switch(*buf) {
		    case 'm':vict->player.class = CLASS_MAGIC_USER; break;
		    case 'c':vict->player.class = CLASS_CLERIC;     break;
		    case 'w':vict->player.class = CLASS_WARRIOR;    break;
		    case 't':vict->player.class = CLASS_THIEF;      break;
		}
	    }
	    break;
	    case 3: /* level */
	    {
		value = atoi(buf);
		if ((value < 0) || (value > 31))
		{
		    send_to_char(
			"Level must be between 0 and 31.\r\n", ch );
		    return;
		}
		log(buf2);
		/* set level of victim */
		vict->player.level = value;
	    }
	    break;
	    case 4: /* height */
	    {
		value = atoi(buf);
		if ((value < 100) || (value > 250))
		{
		    send_to_char("Height must be more than 100 cm\r\n", ch);
		    send_to_char("and less than 251 cm.\r\n", ch); 
		    return;
		}
		log(buf2);
		/* set hieght of victim */
		vict->player.height = value;
	    }       
	    break;
	    case 5: /* weight */
	    {
		value = atoi(buf);
		if ((value < 100) || (value > 250))
		{
		    send_to_char("Weight must be more than 100 pound\r\n", ch);
		    send_to_char("and less than 251 pounds.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set weight of victim */
		vict->player.weight = value;
	    }
	    break;
	    case 6: /* str */
	    {
		value = atoi(buf);
		if ((value <= 0) || (value > 25))
		{
		    send_to_char("Strength must be more than 0\r\n", ch);
		    send_to_char("and less than 25.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original strength of victim */
		vict->tmpabilities.str = value;
		vict->abilities.str = value;
	    }
	    break;
	    case 7: /* stradd */
	    {
		send_to_char( "Strength addition not supported.\r\n", ch );
	    }
	    break;
	    case 8: /* int */
	    {
		value = atoi(buf);
		if ((value <= 0) || (value > 25))
		{
		    send_to_char("Intelligence must be more than 0\r\n", ch);
		    send_to_char("and less than 25.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original INT of victim */
		vict->tmpabilities.intel = value;
		vict->abilities.intel = value;
	    }
	    break;
	    case 9: /* wis */
	    {
		value = atoi(buf);
		if ((value <= 0) || (value > 25))
		{
		    send_to_char("Wisdom must be more than 0\r\n", ch);
		    send_to_char("and less than 25.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original WIS of victim */
		vict->tmpabilities.wis = value;
		vict->abilities.wis = value;
	    }
	    break;
	    case 10: /* dex */
	    {
		value = atoi(buf);
		if ((value <= 0) || (value > 25))
		{
		    send_to_char("Dexterity must be more than 0\r\n", ch);
		    send_to_char("and less than 25.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original DEX of victim */
		vict->tmpabilities.dex = value;
		vict->abilities.dex = value;
	    }
	    break;
	    case 11: /* con */
	    {
		value = atoi(buf);
		if ((value <= 0) || (value > 25))
		{
		    send_to_char("Constitution must be more than 0\r\n", ch);
		    send_to_char("and less than 25.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original CON of victim */
		vict->tmpabilities.con = value;
		vict->abilities.con = value;
	    }
	    break;
	    case 12: /* gold */
	    {
		value = atoi(buf);
		log(buf2);
		/* set original gold of victim */
		vict->points.gold = value;
	    }
	    break;
	    case 13: /* exp */
	    {
		value = atoi(buf);
		if ((value <= 0) || (value > 900000001))
		{
		    send_to_char(
			"Experience-points must be more than 0\r\n", ch);
		    send_to_char("and less than 900000001.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original exp of victim */
		vict->points.exp = value;
	    }
	    break;
	    case 14: /* mana */
	    {
		value = atoi(buf);
		if ((value <= -100) || (value > 9994))
		{
		    send_to_char("Mana-points must be more than -100\r\n", ch);
		    send_to_char("and less than 9995.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original mana of victim */
		vict->points.max_mana = value;
	    }
	    break;
	    case 15: /* hit */
	    {
		value = atoi(buf);
		if ((value <= -10) || (value > 30000))
		{
		    send_to_char("Hit-points must be more than -10\r\n", ch);
		    send_to_char("and less than 30001.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original hit of victim */
		vict->points.max_hit = value;
	    }
	    break;
	    case 16: /* move */
	    {
		value = atoi(buf);
		if ((value <= -100) || (value > 9917))
		{
		    send_to_char("Move-points must be more than -100\r\n", ch);
		    send_to_char("and less than 9918.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original move of victim */
		vict->points.max_move = value;
	    }
	    break;
	    case 17: /* sessions */
	    {
		value = atoi(buf);
		if ((value < 0) || (value > 100))
		{
		    send_to_char("Sessions must be more than 0\r\n", ch);
		    send_to_char("and less than 101.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original sessions of victim */
		vict->specials.practices = value;
	    }
	    break;
	    case 18: /* alignment */
	    {
		value = atoi(buf);
		if ((value < -1000) || (value > 1000))
		{
		    send_to_char("Alignment must be more than -1000\r\n", ch);
		    send_to_char("and less than 1000.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original alignment of victim */
		vict->specials.alignment = value;
	    }
	    break;
	    case 19: /* thirst */
	    {
		value = atoi(buf);
		if ((value < -1) || (value > 100))
		{
		    send_to_char("Thirst must be more than -2\r\n", ch);
		    send_to_char("and less than 101.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original thirst of victim */
		vict->specials.conditions[THIRST] = value;
	    }
	    break;
	    case 20: /* drunk */
	    {
		value = atoi(buf);
		if ((value < -1) || (value > 100))
		{
		    send_to_char("Drunk must be more than -2\r\n", ch);
		    send_to_char("and less than 101.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original drunk of victim */
		vict->specials.conditions[DRUNK] = value;
	    }
	    break;
	    case 21: /* full */
	    {
		value = atoi(buf);
		if ((value < -1) || (value > 100))
		{
		    send_to_char("Full must be more than -2\r\n", ch);
		    send_to_char("and less than 101.\r\n", ch);
		    return;
		}
		log(buf2);
		/* set original full of victim */
		vict->specials.conditions[FULL] = value;
	    }
	    break;
            case 22: /* Mage */
	    {
	        value = atoi(buf);
		if (value<0 || value > GET_LEVEL(ch))
		  {
		    send_to_char("Yeah, sure..\r\n",ch);
		    return;
		  }
		log(buf2);
		GET_MM(vict)=value;
	    }
	    break;
	    case 23:
            {
                value = atoi(buf);
                if (value<0 || value > GET_LEVEL(ch))
                  {
                    send_to_char("Yeah, sure..\r\n",ch);
                    return;
                  }
                log(buf2);
                GET_CC(vict)=value;
	      }
            break;
	    case 24:
            {
                value = atoi(buf);
                if (value<0 || value > GET_LEVEL(ch))
                  {
                    send_to_char("Yeah, sure..\r\n",ch);
                    return;
                  }
                log(buf2);
                GET_TT(vict)=value;
	      }
            break;
	    case 25:
            {
                value = atoi(buf);
                if (value<0 || value > GET_LEVEL(ch))
                  {
                    send_to_char("Yeah, sure..\r\n",ch);
                    return;
                  }
                log(buf2);
                GET_WW(vict)=value;
	      }
            break;

	}
    }
    send_to_char("Ok.\r\n", ch);
}


void do_tap(struct char_data *ch, char *argument, int cmd)
{
    struct obj_data *obj;
    char name[100];
    char buf[MAX_INPUT_LENGTH];
    int xp;

    one_argument (argument, name);

    if (!*name || !str_cmp(name, GET_NAME(ch)) )
    {
	act( "$n offers $mself to $m god, who graciously declines.",
	    FALSE, ch, 0, 0, TO_ROOM);
	act( "Your god appreciates your offer and may accept it later.",
	    FALSE, ch, 0, 0, TO_CHAR);
	return;
    }

    obj = get_obj_in_list_vis( ch, name, world[ch->in_room].contents );
    if ( obj == NULL )
    {
	act( "You can't find that object.",
	    FALSE, ch, 0, 0, TO_CHAR);
	return;
    }

    if ( !CAN_WEAR(obj, ITEM_TAKE) )
    {
	act( "$p is not an acceptable sacrifice.",
	    FALSE, ch, obj, 0, TO_CHAR );
	return;
    }

    if ( GET_ITEM_TYPE(obj) != ITEM_CONTAINER || obj->obj_flags.value[3] != 1 )
    {
	act( "$n sacrifices $p to $s god.", FALSE, ch, obj, 0, TO_ROOM );
	act( "You get one gold coin for your sacrifice.",
	    FALSE, ch, obj, 0, TO_CHAR );
	GET_GOLD(ch) += 1;
	extract_obj(obj);
    }
    else if ( obj->obj_flags.cost_per_day != 100000 )
    {
	act( "Such a sacrifice would be very unwise.",
	    FALSE, ch, obj, 0, TO_CHAR );
	return;
    }
    else
    {
	xp = 10 * GET_LEVEL(ch);
	sprintf( buf,
	    "You get %d experience points for your sacrifice.", xp );
	act( "$n sacrifices $p to $s god.", FALSE, ch, obj, 0, TO_ROOM);
	act( buf, FALSE, ch, obj, 0, TO_CHAR );
	gain_exp(ch, xp);
	extract_obj(obj);
    }
}

void do_trip(struct char_data *ch, char *argument, int cmd)
{
    struct char_data *victim;
    char name[256];
    byte percent;

    /* Mobs auto-trip in fight.c */
    if ( IS_NPC(ch) )
        return;
/*
    if ((GET_CLASS(ch) != CLASS_THIEF) && GET_LEVEL(ch)<33){
        send_to_char("You better leave all the agile fighting to thieves.\r\n",
		     ch);
        return;
    }
*/
    one_argument(argument, name);

    victim = get_char_room_vis( ch, name );
    if ( victim == NULL )
        victim = ch->specials.fighting;

    if ( victim == NULL )
    {
        send_to_char( "Trip whom?\r\n", ch );
        return;
    }

    if (victim == ch) {
        send_to_char("Aren't we funny today...\r\n", ch);
        return;
    }

     /* 101% is a complete failure */
    percent=number(1,101) + (GET_LEVEL(victim) - GET_LEVEL(ch));

    if (percent > ch->skills[SKILL_TRIP].learned * 2 / 3 ) {
        check_killer(ch, victim);
        damage(ch, victim, 0, SKILL_TRIP);
    } else {
      check_killer(ch, victim);
      act( "$n trips you and you go down!",
	  FALSE, ch, NULL, victim, TO_VICT );
      act( "You trip $N and $N goes down!",
	  FALSE, ch, NULL, victim, TO_CHAR );
      act( "$n trips $N and $N goes down!",
	  FALSE, ch, NULL, victim, TO_NOTVICT );
      damage(ch, victim, 1, SKILL_TRIP);
      WAIT_STATE(ch, PULSE_VIOLENCE*2);
      WAIT_STATE(victim, PULSE_VIOLENCE*3);
      GET_POS(victim) = POSITION_SITTING;
    }

}

void do_disarm( struct char_data *ch, char *argument, int cmd )
{
    struct char_data *victim;
    char name[256];
    int percent;
    struct obj_data *obj;

    /* Mobs auto-disarm in fight.c */
    if ( IS_NPC(ch) )
	return;
/*
    if ( GET_CLASS(ch) != CLASS_WARRIOR && GET_CLASS(ch) != CLASS_THIEF
    && GET_LEVEL(ch) < 33 )
    {
	send_to_char( "You don't know how to disarm opponents.\r\n", ch );
	return;
    }
*/
    if ( ch->equipment[WIELD] == NULL )
    {
	send_to_char( "You must wield a weapon to disarm.\r\n", ch );
	return;
    }

    one_argument( argument, name );
    victim = get_char_room_vis( ch, name );
    if ( victim == NULL )
	victim = ch->specials.fighting;

    if ( victim == NULL )
    {
	send_to_char( "Disarm whom?\r\n", ch );
	return;
    }

    if ( victim == ch )
    {
	act( "$n disarms $mself!", FALSE, ch, NULL, victim, TO_NOTVICT );
	send_to_char( "You disarm yourself!\r\n", ch );
	obj = unequip_char( ch, WIELD );
	obj_to_room( obj, ch->in_room );
	return;
    }

    if ( victim->equipment[WIELD] == NULL )
    {
	send_to_char( "Your opponent is not wielding a weapon!\r\n", ch );
	return;
    }

    percent = number( 1, 100 ) + GET_LEVEL(victim) - GET_LEVEL(ch);
    if ( percent < ch->skills[SKILL_DISARM].learned * 2 / 3 )
    {
	disarm( ch, victim );
	one_hit( victim, ch, TYPE_UNDEFINED );
	WAIT_STATE( ch, 3 * PULSE_VIOLENCE );
    }
    else
    {
	one_hit( victim, ch, TYPE_UNDEFINED );
	WAIT_STATE( ch, PULSE_VIOLENCE*2 );
    }
}

void do_title(struct char_data *ch, char *argument, int cmd)
{
  char buf[100];

  if (!*argument){
    send_to_char("Change your title to what?\r\n", ch);
    return;
  }
    
  for (; isspace(*argument); argument++);

  if (strlen(argument)>60){
    send_to_char("Title field too big.  60 characters max.\r\n", ch);
    return;
  }
  
  ch->player.title = str_dup(argument);
  sprintf(buf,"Your title is now: %s\r\n", argument);
  send_to_char(buf,ch);
  
}

void do_split(struct char_data *ch, char *argument, int cmd)
{
  int amount;
  char buf[256], number[10];
  int no_members, share, extra;
  struct char_data *k;
  struct follow_type *f;


  if (!*argument){
    send_to_char("Split what?\r\n", ch);
    return;
  }

  one_argument (argument, number);
  
  if (strlen(number)>7){
    send_to_char("Number field too big.\r\n", ch);
    return;
  }

  amount = atoi(number);

  if ( amount < 0 )
  {
    send_to_char( "Your group wouldn't like that!\r\n", ch );
    return;
  }

  if ( amount == 0 )
  {
    send_to_char(
    "You hand out zero coins to everyone, but no one notices.\r\n", ch );
    return;
  }

  if (GET_GOLD(ch)<amount)
  {
    send_to_char( "You don't have that much gold!\r\n", ch );
    return;
  }
  
  if (ch->master)
    k = ch->master;
  else
    k = ch;

  if ( !IS_AFFECTED(k, AFF_GROUP) || k->in_room != ch->in_room )
  {
    send_to_char("You must be grouped to split your money!\r\n", ch);
    return;
  }

  no_members = 1;
  for (f=k->followers; f; f=f->next)
  {
    if (IS_AFFECTED(f->follower, AFF_GROUP) &&
    (f->follower->in_room == ch->in_room))
      no_members++;
  }

  share = amount / no_members;
  extra = amount % no_members;
  GET_GOLD(ch) -= amount;

  if ( k==ch )
  {
      sprintf( buf, "You split %d gold coins.  ", amount );
      extra = amount - ( no_members * share );
  }
  else 
  {
      sprintf( buf, "%s splits %d gold coins.  ", GET_NAME(ch), amount );
  }
  send_to_char( buf, k );
  sprintf( buf, "Your share is %d gold coins.\r\n", share + extra );
  send_to_char( buf, k );
  GET_GOLD(k) += share + extra;

  for (f=k->followers; f; f=f->next) {
    if (IS_AFFECTED(f->follower, AFF_GROUP) &&
    (f->follower->in_room == ch->in_room)) {
      if (f->follower==ch)
	sprintf( buf, "You split %d gold coins.  ", amount );
      else 
	sprintf( buf, "%s splits %d gold coins.  ", GET_NAME(ch), amount );
      send_to_char( buf, f->follower );
      sprintf( buf, "Your share is %d gold coins.\r\n", share );
      send_to_char( buf, f->follower );
      GET_GOLD(f->follower) += share;
      } 
  }
}

void do_grouptell(struct char_data *ch, char *argument, int cmd)
{
  char buf[256];
  struct char_data *k;
  struct follow_type *f;

  if (!*argument){
    send_to_char("Tell your group what?\r\n", ch);
    return;
  }

  for (; isspace(*argument); argument++);

  if (!IS_AFFECTED(ch, AFF_GROUP))
    {
      send_to_char("You don't have a group to talk to!\r\n", ch);
      return;
    }

  if (!(k=ch->master))
    k = ch;

  if (IS_AFFECTED(k, AFF_GROUP)) {
    sprintf(buf,"%s%s tells the group, '%s'%s\r\n",
	    (IS_SET(k->player.kalbits,KAL_COLOR)?color_table[k->player.col[CHA_GROUP]]:""),
	    GET_NAME(ch),argument, (IS_SET(k->player.kalbits,KAL_COLOR)?
	     color_table[k->player.col[CHA_NORMAL]]:""));
    send_to_char(buf, k);
  }

  for(f=k->followers; f; f=f->next) {
    if (IS_AFFECTED(f->follower, AFF_GROUP)){
      sprintf(buf,"%s%s tells the group, '%s'%s\r\n",
	      (IS_SET(f->follower->player.kalbits,KAL_COLOR)?
	       color_table[f->follower->player.col[CHA_GROUP]]:""),
	   GET_NAME(ch),argument,
	      (IS_SET(f->follower->player.kalbits,KAL_COLOR)?
	       color_table[f->follower->player.col[CHA_NORMAL]]:""));
      send_to_char(buf,f->follower);
    }
  }
}

void do_report(struct char_data *ch, char *argument, int cmd)
{
  char buf[256];

  sprintf(buf, "$n says, 'My stats are: [%d/%dhp %d/%dm %d/%dmv]'.",
      GET_HIT(ch),
      GET_MAX_HIT(ch),
      GET_MANA(ch),
      GET_MAX_MANA(ch),
      GET_MOVE(ch),
      GET_MAX_MOVE(ch));
  act(buf, FALSE, ch, 0, 0, TO_CHAR);
  act(buf, FALSE, ch, 0, 0, TO_ROOM);
}

bool CAN_SEE( struct char_data *sub, struct char_data *obj )
{
    if (obj == sub )
      return TRUE;
    
    if ( obj->specials.wizInvis>GET_LEVEL(sub) && GET_LEVEL(sub)<34 )
    {
	if ( IS_NPC(sub) || (GET_LEVEL(sub) < GET_LEVEL(obj)))
	    return FALSE;
    }

    if ( sub->specials.holyLite )
	return TRUE;

    if ( IS_AFFECTED(sub, AFF_BLIND) )
	return FALSE;

    if ( !IS_LIGHT(sub->in_room) && !IS_AFFECTED(sub, AFF_INFRARED) )
	return FALSE;

    if ( ( IS_AFFECTED( obj, AFF_SNEAK ) || ( IS_AFFECTED( obj, AFF_HIDE ) ) )
	&& IS_NPC(sub) )
    {
	if ( number( 1, 100 ) < GET_LEVEL(sub) * 3 / 2 )
	    return FALSE;
    }

    if ( !IS_AFFECTED( obj, AFF_INVISIBLE ) )
	return TRUE;

    if ( IS_AFFECTED( sub, AFF_DETECT_INVISIBLE ) )
	return TRUE;

    return FALSE;
}



bool CAN_SEE_OBJ( struct char_data *sub, struct obj_data *obj )
{
    if ( sub->specials.holyLite )
	return TRUE;

    if ( IS_AFFECTED( sub, AFF_BLIND ) )
	return FALSE;

    if ( !IS_LIGHT(sub->in_room) && !IS_AFFECTED(sub, AFF_INFRARED) )
	return FALSE;

    if ( !IS_SET(obj->obj_flags.extra_flags, ITEM_INVISIBLE) )
	return TRUE;

    if ( IS_AFFECTED(sub, AFF_DETECT_INVISIBLE) )
	return TRUE;

    return FALSE;
}



bool check_blind( struct char_data *ch )
{
    if ( IS_AFFECTED(ch, AFF_BLIND) )
    {
	send_to_char( "You can't see a damn thing!\r\n", ch );
	return TRUE;
    }

    return FALSE;
}



/*
 * Given a mob, determine what level its eq is.
 * Wormhole to shopping_buy.
 */
int map_eq_level( struct char_data *mob )
{
    if ( mob_index[mob->nr].func == shop_keeper )
	return 1000;
    if ( GET_LEVEL(mob) <= 5 )
	return 0;
    else if ( GET_LEVEL(mob) <= 30 )
	return GET_LEVEL(mob) - 5;
    else
	return 25;
}



void do_tick( struct char_data *ch, char *argument, int cmd )
{
    int	ntick;
    char buf[256];

    if ( IS_NPC(ch) )
    {
	send_to_char( "Monsters don't wait for anything.\r\n", ch );
	return;
    }

    if ( ch->desc == NULL )
	return;

    while ( *argument == ' ' )
	argument++;

    if ( *argument == '\0' )
	ntick = 1;
    else
	ntick = atoi( argument );

    if ( ntick == 1 )
	sprintf( buf, "$n is waiting for one tick." );
    else
	sprintf( buf, "$n is waiting for %d ticks.", ntick );

    act(buf, TRUE, ch, 0, 0, TO_CHAR);
    act(buf, TRUE, ch, 0, 0, TO_ROOM);

    ch->desc->tick_wait	= ntick;
}