wileymud-1.187b/
wileymud-1.187b/attic/
wileymud-1.187b/attic/bin/
wileymud-1.187b/attic/lib/
wileymud-1.187b/attic/lib/adm/
wileymud-1.187b/attic/lib/man/
wileymud-1.187b/attic/lib/new-wld/
wileymud-1.187b/attic/lib/new-wld/default/
wileymud-1.187b/attic/lib/old/
wileymud-1.187b/attic/lib/wld/
wileymud-1.187b/attic/public_html/
wileymud-1.187b/attic/public_html/gfx/
wileymud-1.187b/attic/src/bin/
wileymud-1.187b/attic/src/etc/
wileymud-1.187b/attic/src/libauth-4.0-p5/
wileymud-1.187b/attic/src/sedna/
wileymud-1.187b/backups/
wileymud-1.187b/bin/
wileymud-1.187b/docs/
wileymud-1.187b/etc/
wileymud-1.187b/lib/
wileymud-1.187b/lib/adm/
wileymud-1.187b/lib/boards/
wileymud-1.187b/lib/log/
wileymud-1.187b/lib/man/
wileymud-1.187b/lib/ply/
wileymud-1.187b/lib/ply/a/
wileymud-1.187b/lib/ply/b/
wileymud-1.187b/lib/ply/c/
wileymud-1.187b/lib/ply/d/
wileymud-1.187b/lib/ply/g/
wileymud-1.187b/lib/ply/k/
wileymud-1.187b/lib/ply/m/
wileymud-1.187b/lib/ply/s/
wileymud-1.187b/lib/ply/t/
wileymud-1.187b/public_html/gfx/
wileymud-1.187b/src/bin/
wileymud-1.187b/src/convert/attic/
wileymud-1.187b/src/convert/obj/
wileymud-1.187b/src/convert/perl/
wileymud-1.187b/src/convert/perl/MudConvert/
wileymud-1.187b/src/convert/perl/MudConvert/DUMP/
wileymud-1.187b/src/convert/perl/MudConvert/Report/
wileymud-1.187b/src/convert/perl/MudConvert/WileyMUD/
wileymud-1.187b/src/convert/perl/output/
wileymud-1.187b/src/convert/perl/output/DUMP/
wileymud-1.187b/src/convert/perl/output/Report/
wileymud-1.187b/src/convert/perl/output/WileyMUD/
wileymud-1.187b/src/etc/
wileymud-1.187b/src/etc/init.d/
wileymud-1.187b/src/etc/rc.d/
wileymud-1.187b/src/etc/rc.d/init.d/
wileymud-1.187b/src/lib/
wileymud-1.187b/src/lib/adm/
wileymud-1.187b/src/lib/boards/
wileymud-1.187b/src/lib/log/
wileymud-1.187b/src/lib/man/
wileymud-1.187b/src/lib/ply/
wileymud-1.187b/src/lib/ply/a/
wileymud-1.187b/src/lib/ply/b/
wileymud-1.187b/src/lib/ply/c/
wileymud-1.187b/src/lib/ply/d/
wileymud-1.187b/src/lib/ply/e/
wileymud-1.187b/src/lib/ply/f/
wileymud-1.187b/src/lib/ply/g/
wileymud-1.187b/src/lib/ply/h/
wileymud-1.187b/src/lib/ply/i/
wileymud-1.187b/src/lib/ply/j/
wileymud-1.187b/src/lib/ply/k/
wileymud-1.187b/src/lib/ply/l/
wileymud-1.187b/src/lib/ply/m/
wileymud-1.187b/src/lib/ply/n/
wileymud-1.187b/src/lib/ply/o/
wileymud-1.187b/src/lib/ply/p/
wileymud-1.187b/src/lib/ply/q/
wileymud-1.187b/src/lib/ply/r/
wileymud-1.187b/src/lib/ply/s/
wileymud-1.187b/src/lib/ply/t/
wileymud-1.187b/src/lib/ply/u/
wileymud-1.187b/src/lib/ply/v/
wileymud-1.187b/src/lib/ply/w/
wileymud-1.187b/src/lib/ply/x/
wileymud-1.187b/src/lib/ply/y/
wileymud-1.187b/src/lib/ply/z/
wileymud-1.187b/src/obj/
wileymud-1.187b/src/utils/
wileymud-1.187b/src/utils/mobmaker/
/*
 * file: limits.c , Limit and gain control module.        Part of DIKUMUD
 * Usage: Procedures controling gain and limit.
 * Copyright (C) 1990, 1991 - see 'license.doc' for complete information.
 */

#include <stdio.h>
#include <stdlib.h>
/* #include <unistd.h> */
#include <sys/types.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>

#include "global.h"
#include "bug.h"
#include "spells.h"
#include "comm.h"
#include "db.h"
#include "spell_parser.h"
#include "constants.h"
#include "utils.h"
#include "multiclass.h"
#include "fight.h"
#include "reception.h"
#include "interpreter.h"
#include "handler.h"
#include "act_obj.h"
#include "act_other.h"
#define _DIKU_LIMITS_C
#include "mudlimits.h"

#undef BOOT_IDLE    /* define this to kick idle people, undefine to let them stay idle all day */

char                                   *ClassTitles(struct char_data *ch)
{
    int                                     i = 0;
    int                                     count = 0;
    static char                             buf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    for (i = MAGE_LEVEL_IND; i <= DRUID_LEVEL_IND; i++) {
	if (GET_LEVEL(ch, i)) {
	    count++;
	    if (count > 1) {
		sprintf(buf + strlen(buf), "/%s", GET_CLASS_TITLE(ch, i, GET_LEVEL(ch, i)));
	    } else {
		sprintf(buf, "%s", GET_CLASS_TITLE(ch, i, GET_LEVEL(ch, i)));
	    }
	}
    }
    return (buf);
}

/* When age < 15 return the value p0 */
/* When age in 15..29 calculate the line between p1 & p2 */
/* When age in 30..44 calculate the line between p2 & p3 */
/* When age in 45..59 calculate the line between p3 & p4 */
/* When age in 60..79 calculate the line between p4 & p5 */
/* When age >= 80 return the value p6 */
int graf(int char_age, int p0, int p1, int p2, int p3, int p4, int p5, int p6)
{
    if (DEBUG > 2)
	log_info("called %s with %d, %d, %d, %d, %d, %d, %d, %d", __PRETTY_FUNCTION__, char_age,
		 p0, p1, p2, p3, p4, p5, p6);

    if (char_age < 15)
	return (p0);					       /* < 15 */
    else if (char_age <= 29)
	return (int)(p1 + (((char_age - 15) * (p2 - p1)) / 15));	/* 15..29 */
    else if (char_age <= 44)
	return (int)(p2 + (((char_age - 30) * (p3 - p2)) / 15));	/* 30..44 */
    else if (char_age <= 59)
	return (int)(p3 + (((char_age - 45) * (p4 - p3)) / 15));	/* 45..59 */
    else if (char_age <= 79)
	return (int)(p4 + (((char_age - 60) * (p5 - p4)) / 20));	/* 60..79 */
    else
	return (p6);					       /* >= 80 */
}

/* The three MAX functions define a characters Effective maximum */
/* Which is NOT the same as the ch->points.max_xxxx !!!          */
int mana_limit(struct char_data *ch)
{
    int                                     max = 100;
    int                                     extra = 0;
    int                                     cl = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    if (IS_PC(ch)) {
	if (HasClass(ch, CLASS_MAGIC_USER))
	    extra += GET_LEVEL(ch, MAGE_LEVEL_IND) * 5;
	if (HasClass(ch, CLASS_CLERIC))
	    extra += GET_LEVEL(ch, CLERIC_LEVEL_IND) * 4;
	if (HasClass(ch, CLASS_DRUID))
	    extra += GET_LEVEL(ch, DRUID_LEVEL_IND) * 3;
	if (HasClass(ch, CLASS_RANGER))
	    extra += (GET_LEVEL(ch, RANGER_LEVEL_IND) * 5) / 2;
	if (HasClass(ch, CLASS_THIEF))
	    extra += (GET_LEVEL(ch, THIEF_LEVEL_IND) * 4) / 2;
	if (HasClass(ch, CLASS_WARRIOR))
	    extra += (GET_LEVEL(ch, WARRIOR_LEVEL_IND) * 3) / 2;
	cl = HowManyClasses(ch);
	if ((cl = HowManyClasses(ch)) > 1)
	    extra = ((extra * 10) / ((cl * 10) + 5));
	max += extra;
    }
    max += ch->points.max_mana;				       /* bonus mana */
    return (max);
}

int hit_limit(struct char_data *ch)
{
    int                                     max = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    if (IS_PC(ch))
	max = (ch->points.max_hit) + (graf(age(ch).year, 2, 4, 17, 14, 8, 4, 3));
    else
	max = (ch->points.max_hit);

    /*
     * Class/Level calculations 
     */
    if (HowManyClasses(ch) == 1) {
	if (HasClass(ch, CLASS_RANGER))
	    max += (GET_LEVEL(ch, RANGER_LEVEL_IND) / 5) + 2;
	else if (HasClass(ch, CLASS_WARRIOR))
	    max += (GET_LEVEL(ch, WARRIOR_LEVEL_IND) / 2) + 1;
    }
    /*
     * Skill/Spell calculations 
     */

    return (max);
}

int move_limit(struct char_data *ch)
{
    int                                     max = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    if (!IS_NPC(ch))
	max = 70 + age(ch).year + (int)GET_CON(ch) + GetTotLevel(ch);
    else
	max = ch->points.max_move;

    switch (GET_RACE(ch)) {
	case RACE_DWARF:
	    max -= 15;
	    break;
	case RACE_GNOME:
	    max -= 10;
	    break;
	case RACE_HALFLING:
	    max += 5;
	    break;
	case RACE_ELVEN:
	    max += 10;
	    break;
    }
    return (max);
}

int mana_gain(struct char_data *ch)
{
    int                                     gain = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    if (IS_NPC(ch)) {
	/*
	 * Neat and fast 
	 */
	gain = GetTotLevel(ch);
    } else {
	gain = graf(age(ch).year, 2, 4, 6, 8, 10, 12, 16);

	if (GET_RACE(ch) == RACE_ELVEN)
	    gain += 5;
	if (GET_RACE(ch) == RACE_GNOME)
	    gain += 2;

	/*
	 * Class calculations 
	 */
	if (HasClass(ch, CLASS_MAGIC_USER))
	    gain += 2;
	if (HasClass(ch, CLASS_CLERIC))
	    gain += 2;
	if (HasClass(ch, CLASS_DRUID))
	    gain += 1;

	/*
	 * Skill/Spell calculations 
	 */
	/*
	 * Position calculations 
	 */

	switch (GET_POS(ch)) {
	    case POSITION_SLEEPING:
		gain += gain;
		break;
	    case POSITION_RESTING:
		gain += (gain >> 1);			       /* Divide by 2 */
		break;
	    case POSITION_SITTING:
		gain += (gain >> 2);			       /* Divide by 4 */
		break;
	}

	if (HasClass(ch, CLASS_MAGIC_USER) ||
	    HasClass(ch, CLASS_CLERIC) || HasClass(ch, CLASS_DRUID))
	    gain += gain;
    }

    if (number(1, 101) < ch->skills[SKILL_MEDITATION].learned)
	gain += 10;

    if (IS_AFFECTED(ch, AFF_POISON))
	gain >>= 2;

    if (GET_COND(ch, FULL) < 2)				       /* starving */
	gain >>= 2;

    if (GET_COND(ch, THIRST) < 2)			       /* parched */
	gain >>= 2;

    return (gain);
}

int hit_gain(struct char_data *ch)
{
    int                                     gain = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    if (IS_NPC(ch)) {
	gain = 8;
    } else {
	if (GET_POS(ch) == POSITION_FIGHTING) {
	    gain = 1;
	} else {
	    gain = graf(age(ch).year, 2, 5, 10, 18, 6, 4, 2);
	}

	/*
	 * Class/Level calculations 
	 */
	if (HasClass(ch, CLASS_MAGIC_USER))
	    gain -= 2;
	if (HasClass(ch, CLASS_CLERIC))
	    gain -= 1;
	if (HasClass(ch, CLASS_WARRIOR))
	    gain += 3;
	if (HasClass(ch, CLASS_RANGER))
	    gain += 2;

	/*
	 * Skill/Spell calculations 
	 */

	/*
	 * Position calculations 
	 */

	switch (GET_POS(ch)) {
	    case POSITION_SLEEPING:
		gain += gain >> 1;
		break;
	    case POSITION_RESTING:
		gain += gain >> 2;
		break;
	    case POSITION_SITTING:
		gain += gain >> 3;
		break;
	}

	if (GET_POS(ch) == POSITION_SLEEPING)
	    if (number(1, 101) < ch->skills[SKILL_MEDITATION].learned)
		gain += 3;
    }

    if (GET_RACE(ch) == RACE_DWARF)
	gain += 5;

    if (GET_RACE(ch) == RACE_HALFLING)
	gain += 2;

    if (GET_RACE(ch) == RACE_ELVEN)
	gain -= 1;

    if (GET_RACE(ch) == RACE_GNOME)
	gain += 1;

    if (IS_AFFECTED(ch, AFF_POISON)) {
	gain >>= 2;
	/* damage(ch, ch, 15, SPELL_POISON); */
    }

    if (IS_PC(ch) && IS_STARVING(ch)) {
	gain >>= 2;
	/* damage(ch, ch, number(1, 3), TYPE_HUNGER); */
    }

    if (IS_PC(ch) && IS_PARCHED(ch)) {
	gain >>= 2;
	/* damage(ch, ch, number(1, 3), TYPE_HUNGER); */
    }

    gain = MAX(gain, 1);

    //if (IS_PC(ch)) cprintf(ch, "You should be gaining %d hit points.\r\n", gain);

    return (gain);
}

/*
 * move gain pr. game hour 
 */
int move_gain(struct char_data *ch)
{
    int                                     gain = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    if (IS_NPC(ch))
	return (GetTotLevel(ch));
    else {
	if (GET_POS(ch) != POSITION_FIGHTING)
	    gain = 5 + (int)GET_CON(ch);
	else {
	    if (number(1, 101) < ch->skills[SKILL_ENDURANCE].learned)
		gain = 2;
	    else
		gain = 0;
	}

	if (HasClass(ch, CLASS_RANGER))
	    gain += 3;
	if (HasClass(ch, CLASS_DRUID))
	    gain += 2;

	/*
	 * Position calculations 
	 */
	switch (GET_POS(ch)) {
	    case POSITION_SLEEPING:
		gain += (gain >> 1);			       /* Divide by 2 */
		break;
	    case POSITION_RESTING:
		gain += (gain >> 2);			       /* Divide by 4 */
		break;
	    case POSITION_SITTING:
		gain += (gain >> 3);			       /* Divide by 8 */
		break;
	}
    }

    if (GET_RACE(ch) == RACE_DWARF)
	gain += 4;
    if (GET_RACE(ch) == RACE_HALFLING)
	gain += 3;
    if (GET_RACE(ch) == RACE_ELVEN)
	gain += 1;

    if (number(1, 101) < ch->skills[SKILL_ENDURANCE].learned)
	gain += 5;

    if (IS_AFFECTED(ch, AFF_POISON))
	gain >>= 2;

    if (GET_COND(ch, FULL) < 2)				       /* starving */
	gain = 1;

    if (GET_COND(ch, THIRST) < 2)			       /* parched */
	gain = 1;

    return (gain);
}

/* Gain maximum in various points */
void advance_level(struct char_data *ch, int class)
{
    int                                     add_hp = 0;
    int                                     i = 0;

    if (DEBUG > 1)
	log_info("called %s with %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), class);

    if (GET_LEVEL(ch, class) > 0 && GET_EXP(ch) <
#ifdef MOB_LEVELING
	((IS_PC(ch) || (IS_SET(ch->specials.act, ACT_POLYSELF) ||
			IS_SET(ch->specials.act, ACT_POLYSELF))) ?
	 titles[class][GET_LEVEL(ch, class) + 1].exp :
	 (titles[class][GET_LEVEL(ch, class) + 1].exp / 20))
#else
	titles[class][GET_LEVEL(ch, class) + 1].exp
#endif
	) {
	log_info("Bad advance_level");
	return;
    }
    GET_LEVEL(ch, class) += 1;

/* Constitution Bonus only for Fighter types */

    if ((class == RANGER_LEVEL_IND) || (class == WARRIOR_LEVEL_IND))
	add_hp = con_app[(int)GET_CON(ch)].hitp;
    else
	add_hp = MIN(con_app[(int)GET_CON(ch)].hitp, 2);

    switch (class) {
	case MAGE_LEVEL_IND:
	    {
		ch->specials.pracs += MAX(3, wis_app[(int)GET_INT(ch)].bonus);
		if (GET_LEVEL(ch, MAGE_LEVEL_IND) < 30)
		    add_hp += number(2, 4);
		else
		    add_hp += 1;
	    }
	    break;

	case DRUID_LEVEL_IND:
	    {
		if (GET_LEVEL(ch, DRUID_LEVEL_IND) < 20)
		    add_hp += number(2, 8);
		else
		    add_hp += 3;
	    }
	    break;

	case CLERIC_LEVEL_IND:
	    {
		ch->specials.pracs += MAX(3, wis_app[(int)GET_WIS(ch)].bonus);
		if (GET_LEVEL(ch, CLERIC_LEVEL_IND) < 30)
		    add_hp += number(2, 8);
		else
		    add_hp += 3;
	    }
	    break;

	case THIEF_LEVEL_IND:
	    {
		ch->specials.pracs += MAX(3, wis_app[(int)GET_DEX(ch)].bonus);
		if (GET_LEVEL(ch, THIEF_LEVEL_IND) < 30)
		    add_hp += number(2, 6);
		else
		    add_hp += 2;
	    }
	    break;

	case RANGER_LEVEL_IND:
	    {
		ch->specials.pracs +=
		    MAX(3,
			wis_app[((int)GET_DEX(ch) >=
				 (int)GET_STR(ch) ? (int)GET_DEX(ch) : (int)
				 GET_STR(ch))].bonus);
		if (GET_LEVEL(ch, RANGER_LEVEL_IND) < 30)
		    add_hp += number(2, 10);
		else
		    add_hp += 4;
	    }
	    break;

	case WARRIOR_LEVEL_IND:
	    {
		ch->specials.pracs += MAX(3, wis_app[(int)GET_STR(ch)].bonus);
		if (GET_LEVEL(ch, WARRIOR_LEVEL_IND) < 30)
		    add_hp += number(2, 10);
		else
		    add_hp += 4;
	    }
	    break;
    }

    add_hp /= HowManyClasses(ch);

    add_hp++;

    if (GET_LEVEL(ch, class) <= 5)
	add_hp++;

    ch->points.max_hit += MAX(1, add_hp);

    if (GetMaxLevel(ch) >= LOW_IMMORTAL)
	for (i = 0; i < 3; i++)
	    ch->specials.conditions[i] = -1;

    ch->points.max_move = GET_MAX_MOVE(ch);
//  if (IS_PC(ch))
//    update_player_list_entry(ch->desc);
    log_info("%s advances to level %d.\r\n", GET_NAME(ch), GetMaxLevel(ch));
}

/* Lose in various points */
/*
 * ** Damn tricky for multi-class...
 */

void drop_level(struct char_data *ch, int class)
{
    int                                     add_hp = 0;
    int                                     lin_class = 0;
    int                                     old_style = 0;

    if (DEBUG > 1)
	log_info("called %s with %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), class);

/*
 * if (GetMaxLevel(ch) >= LOW_IMMORTAL)
 *   return;
 */
    if (GetMaxLevel(ch) == 1)
	return;

    add_hp = con_app[(int)GET_CON(ch)].hitp;

    switch (class) {

	case CLASS_MAGIC_USER:{
		lin_class = MAGE_LEVEL_IND;
		if (GET_LEVEL(ch, MAGE_LEVEL_IND) < 30)
		    add_hp += number(2, 4);
		else
		    add_hp += 1;
	    }
	    break;

	case CLASS_DRUID:{
		lin_class = DRUID_LEVEL_IND;
		if (GET_LEVEL(ch, DRUID_LEVEL_IND) < 30)
		    add_hp += number(2, 8);
		else
		    add_hp += 3;
	    }
	    break;

	case CLASS_CLERIC:{
		lin_class = CLERIC_LEVEL_IND;
		if (GET_LEVEL(ch, CLERIC_LEVEL_IND) < 30)
		    add_hp += number(2, 8);
		else
		    add_hp += 3;
	    }
	    break;

	case CLASS_THIEF:{
		lin_class = THIEF_LEVEL_IND;
		if (GET_LEVEL(ch, THIEF_LEVEL_IND) < 30)
		    add_hp += number(2, 6);
		else
		    add_hp += 2;
	    }
	    break;

	case CLASS_WARRIOR:{
		lin_class = WARRIOR_LEVEL_IND;
		if (GET_LEVEL(ch, WARRIOR_LEVEL_IND) < 30)
		    add_hp += number(2, 10);
		else
		    add_hp += 4;
	    }
	    break;

	case CLASS_RANGER:{
		lin_class = RANGER_LEVEL_IND;
		if (GET_LEVEL(ch, RANGER_LEVEL_IND) < 30)
		    add_hp += number(2, 10);
		else
		    add_hp += 4;
	    }
	    break;
    }

    add_hp /= HowManyClasses(ch);

    if (IS_NPC(ch)) {
	gain_exp(ch, -GET_EXP(ch) / 4);
    } else if (GetMaxLevel(ch) < 7) {
	gain_exp(ch, -GET_EXP(ch) / (12 * HowManyClasses(ch)));
    } else if (GetMaxLevel(ch) < 14) {
	gain_exp(ch, -GET_EXP(ch) / (10 * HowManyClasses(ch)));
    } else if (GetMaxLevel(ch) < 21) {
	gain_exp(ch, -GET_EXP(ch) / (8 * HowManyClasses(ch)));
    } else if (GetMaxLevel(ch) < 28) {
	gain_exp(ch, -GET_EXP(ch) / (6 * HowManyClasses(ch)));
    } else
	gain_exp(ch, -GET_EXP(ch) / (4 * HowManyClasses(ch)));

    GET_LEVEL(ch, class) -= 1;
    if (GET_LEVEL(ch, class) < 1)
	GET_LEVEL(ch, class) = 1;

    ch->points.max_hit -= MAX(1, add_hp);
    if (ch->points.max_hit < 1)
	ch->points.max_hit = 1;

    ch->specials.pracs -= MAX(3, wis_app[(int)GET_WIS(ch)].bonus);

    if (old_style) {
	ch->points.exp = MIN(titles[lin_class][(int)GET_LEVEL(ch, lin_class)].exp, GET_EXP(ch));
    }

    if (ch->points.exp < 0)
	ch->points.exp = 0;
//  update_player_list_entry(ch->desc);
    log_info("%s drops to level %d.\r\n", GET_NAME(ch), GetMaxLevel(ch));
}

void set_title(struct char_data *ch)
{
    char                                    buf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";

    if (DEBUG > 1)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    sprintf(buf, "the %s %s", RaceName[ch->race], ClassTitles(ch));
    if (GET_TITLE(ch)) {
	DESTROY(GET_TITLE(ch));
	CREATE(GET_TITLE(ch), char, strlen      (buf) + 1);
    } else {
	CREATE(GET_TITLE(ch), char, strlen      (buf) + 1);
    }
    strcpy(GET_TITLE(ch), buf);
}

void gain_exp(struct char_data *ch, int gain)
{
    int                                     i = 0;

    if (DEBUG > 2)
	log_info("called %s with %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), gain);

/*  save_char(ch,NOWHERE); */

    if (!IS_IMMORTAL(ch)) {
	if (gain > 0) {
	    gain = MIN(100000, gain);

	    if (IS_PC(ch) || (IS_SET(ch->specials.act, ACT_POLYSELF) ||
			      IS_SET(ch->specials.act, ACT_POLYSELF))) {
		gain /= HowManyClasses(ch);
	    } else {
#ifdef MOB_LEVELING
		GET_EXP(ch) += gain;
		for (i = MAGE_LEVEL_IND; i <= DRUID_LEVEL_IND; i++) {
		    if (GET_LEVEL(ch, i)) {
			if (GET_EXP(ch) >= ((titles[i][GET_LEVEL(ch, i) + 1].exp / 20))) {
			    advance_level(ch, i);
			    act("$n seems to be looking more healthy today.", FALSE, ch, 0, 0,
				TO_ROOM);
			}
		    }
		}
		return;
#endif
	    }

	    if (IS_PC(ch) && (GetMaxLevel(ch) == 1))
		gain *= 2;

	    if (IS_PC(ch) || (IS_SET(ch->specials.act, ACT_POLYSELF) ||
			      IS_SET(ch->specials.act, ACT_POLYSELF))) {
		for (i = MAGE_LEVEL_IND; i <= DRUID_LEVEL_IND; i++) {
		    if (GET_LEVEL(ch, i)) {
			if (GET_EXP(ch) >= titles[i][GET_LEVEL(ch, i) + 2].exp) {
			    cprintf(ch,
				    "You will not gain anymore exp until you practice at a guild.\r\n");
			    GET_EXP(ch) = titles[i][GET_LEVEL(ch, i) + 2].exp - 1;
			    return;
			} else if (GET_EXP(ch) >= titles[i][GET_LEVEL(ch, i) + 1].exp) {
			    /*
			     * do nothing..this is cool 
			     */
			} else if (GET_EXP(ch) + gain >= titles[i][GET_LEVEL(ch, i) + 1].exp) {
			    cprintf(ch, "You have gained enough to be a(n) %s\r\n",
				    GET_CLASS_TITLE(ch, i, GET_LEVEL(ch, i) + 1));
			    cprintf(ch,
				    "You will not gain anymore exp until you practice at a guild.\r\n");
			    if (GET_EXP(ch) + gain >= titles[i][GET_LEVEL(ch, i) + 2].exp) {
				GET_EXP(ch) = titles[i][GET_LEVEL(ch, i) + 2].exp - 1;
				return;
			    }
			}
		    }
		}
	    }
	    GET_EXP(ch) += gain;
	    if (IS_PC(ch) || IS_SET(ch->specials.act, ACT_POLYSELF)) {
		for (i = MAGE_LEVEL_IND; i <= DRUID_LEVEL_IND; i++) {
		    if (GET_LEVEL(ch, i)) {
			if (GET_EXP(ch) > titles[i][GET_LEVEL(ch, i) + 2].exp) {
			    GET_EXP(ch) = titles[i][GET_LEVEL(ch, i) + 2].exp - 1;
			}
		    }
		}
	    }
	}
	if (gain < 0) {
	    GET_EXP(ch) += gain;
	    if (GET_EXP(ch) < 0)
		GET_EXP(ch) = 0;
	}
    }
}

void gain_exp_regardless(struct char_data *ch, int gain, int class)
{
    int                                     i = 0;
    int                                     is_altered = FALSE;

    if (DEBUG > 2)
	log_info("called %s with %s, %d, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), gain, class);

    save_char(ch, NOWHERE);
    if (!IS_NPC(ch)) {
	if (gain > 0) {
	    GET_EXP(ch) += gain;

	    for (i = 0; (i < ABS_MAX_LVL) && (titles[class][i].exp <= GET_EXP(ch)); i++) {
		if (i > GET_LEVEL(ch, class)) {
		    cprintf(ch, "You raise a level\r\n");
/*        GET_LEVEL(ch,class) = i; */
		    advance_level(ch, class);
		    is_altered = TRUE;
		}
	    }
	}
	if (gain < 0)
	    GET_EXP(ch) += gain;

	if (GET_EXP(ch) < 0)
	    GET_EXP(ch) = 0;
    }
    if (is_altered)
	set_title(ch);
}

void gain_condition(struct char_data *ch, int condition, int value)
{
    int                                     intoxicated = FALSE;

    if (DEBUG > 2)
	log_info("called %s with %s, %d, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), condition,
		 value);

    if (GET_COND(ch, condition) == -1)			       /* No change */
	return;

    intoxicated = (GET_COND(ch, DRUNK) > 0);

    GET_COND(ch, condition) += value;

    GET_COND(ch, condition) = MAX(0, GET_COND(ch, condition));
    GET_COND(ch, condition) = MIN(24, GET_COND(ch, condition));

    /*
     * if (GET_COND(ch, condition)) return; 
     */

    switch (condition) {
	case FULL:
	    if (IS_GETTING_HUNGRY(ch)) {
		if (IS_HUNGRY(ch)) {
		    if (IS_STARVING(ch)) {
			cprintf(ch, "You are starving!\r\n");
		    } else {
			cprintf(ch, "You are hungry.\r\n");
		    }
		} else {
		    cprintf(ch, "You are getting hungry.\r\n");
		}
	    }

	    break;
	case THIRST:
	    if (IS_GETTING_THIRSTY(ch)) {
		if (IS_THIRSTY(ch)) {
		    if (IS_PARCHED(ch)) {
			cprintf(ch, "You are parched!\r\n");
		    } else {
			cprintf(ch, "You are thirsty.\r\n");
		    }
		} else {
		    cprintf(ch, "You are getting thirsty.\r\n");
		}
	    }
	    break;
	case DRUNK:
	    if (IS_HOPELESSLY_DRUNK(ch)) {
		cprintf(ch, "You are homelessly DRUNK!\r\n");
	    } else if (intoxicated && GET_COND(ch, DRUNK) < 1) {
		cprintf(ch, "You are now sober.\r\n");
	    }
	    break;
	default:
	    break;
    }
}

void check_idling(struct char_data *ch)
{
#ifdef BOOT_IDLE
    struct obj_cost                         cost;
#endif

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    ++(ch->specials.timer);

    /*
     * if (ch->specials.timer > 5 && ch->specials.timer < 10) { 
     */
    if (!ch->specials.timer % 5) {
	do_save(ch, "", 0);
	return;
    }
#ifdef BOOT_IDLE
    if (ch->specials.timer >= 15) {
	log_info("LOG:%s AUTOSAVE:Timer %d.", GET_NAME(ch), ch->specials.timer);

	if (ch->specials.fighting) {
	    stop_fighting(ch->specials.fighting);
	    stop_fighting(ch);
	}
	GET_POS(ch) = POSITION_STANDING;
	char_from_room(ch);
	char_to_room(ch, 0);
	if (IS_IMMORTAL(ch))
	    GET_HOME(ch) = 1000;
	else
	    GET_HOME(ch) = 3008;

	if (recep_offer(ch, NULL, &cost)) {
	    cost.total_cost = 0;
	    new_save_equipment(ch, &cost, FALSE);
	    save_obj(ch, &cost, TRUE);
	}
	extract_char(ch);

	if (ch->desc)
	    close_socket(ch->desc);

	/*
	 * ch->desc = 0; already done inside close_socket, thanks valgrind! 
	 */

	log_info("Done Auto-Saving.");
    }
#endif
}

/* Update both PC's & NPC's and objects */
void point_update(int current_pulse)
{
    struct char_data                       *i = NULL;
    struct char_data                       *next_dude = NULL;
    struct obj_data                        *j = NULL;
    struct obj_data                        *next_thing = NULL;
    int                                     count = 0;

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, current_pulse);

    /*
     * characters 
     */
    for (i = character_list; i; i = next_dude) {
	next_dude = i->next;
        /* So, the first thing we need to do is apply damage() effects
         * so that poison, hunger, thirst, or other spells can do their
         * periodic damage BEFORE all the calculations below happen.
         */
        if (IS_AFFECTED(i, AFF_POISON)) {
	    damage(i, i, 15, SPELL_POISON);
        }

        if (IS_PC(i) && IS_STARVING(i)) {
	    damage(i, i, number(1, 3), TYPE_HUNGER);
        }

        if (IS_PC(i) && IS_PARCHED(i)) {
	    damage(i, i, number(1, 3), TYPE_HUNGER);
        }

	if (GET_POS(i) >= POSITION_STUNNED) {
	    if (!affected_by_spell(i, SPELL_AID)) {
                /* A problem here... damage() calls within hit_gain() are being
                 * overridden, because this code calls GET_HIT() before the call to
                 * hit_gain(), and thus no damage can happen.
                 *
                 * Basically, the damage() calls for spells/poison/hunger need
                 * to be moved in here, so they happen BEFORE hit_gain() is
                 * calculated.
                 */
		GET_HIT(i) = MIN(GET_HIT(i) + hit_gain(i), hit_limit(i));
	    } else {
		if (GET_HIT(i) < hit_limit(i)) {
		    GET_HIT(i) = MIN(GET_HIT(i) + hit_gain(i), hit_limit(i));
		}
	    }

	    GET_MANA(i) = MIN(GET_MANA(i) + mana_gain(i), mana_limit(i));
	    GET_MOVE(i) = MIN(GET_MOVE(i) + move_gain(i), move_limit(i));

	    //if (GET_POS(i) == POSITION_STUNNED)
		//update_pos(i);
	} else if (GET_POS(i) == POSITION_INCAP) {
	    /*
	     * damage(i, i, 0, TYPE_SUFFERING); 
	     */
	    GET_HIT(i) += 1;
	    //update_pos(i);
	} else if (IS_PC(i) && (GET_POS(i) == POSITION_MORTALLYW)) {
	    damage(i, i, 1, TYPE_SUFFERING);
        }
	update_pos(i);

	if (!IS_NPC(i)) {
	    update_char_objects(i);
	    if (GetMaxLevel(i) < CREATOR)
		check_idling(i);
	}

        if (GET_POS(i) != POSITION_DEAD) {
	    gain_condition(i, FULL, -1);
	    gain_condition(i, DRUNK, -1);
	    gain_condition(i, THIRST, -1);
        }
    }

    /*
     * objects 
     */
    for (j = object_list; j; j = next_thing) {
	next_thing = j->next;
	count++;

	if ((GET_ITEM_TYPE(j) == ITEM_FOOD) && IS_OBJ_STAT(j, ITEM_PARISH)) {
	    if (j->obj_flags.value[0] > 0)
		j->obj_flags.value[0]--;

	    switch (j->obj_flags.value[0]) {
		case 3:
		    {
			if (j->carried_by)
			    act("$p begins to look a little brown.", FALSE, j->carried_by, j, 0,
				TO_CHAR);
			else if (j->in_room != NOWHERE && (real_roomp(j->in_room)->people)) {
			    act("$p begins to look a little brown.", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_CHAR);
			    act("$p begins to look a little brown.", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_ROOM);
			}
		    }
		    break;
		case 2:
		    {
			if (j->carried_by)
			    act("$p begins to smell funny.", FALSE, j->carried_by, j, 0,
				TO_CHAR);
			else if (j->in_room != NOWHERE && (real_roomp(j->in_room)->people)) {
			    act("$p begins to smell funny.", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_CHAR);
			    act("$p begins to smell funny.", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_ROOM);
			}
		    }
		    break;
		case 1:
		    {
			j->obj_flags.value[3] = 1;	       /* poison the sucker */
			if (j->carried_by)
			    act("$p begins to smell spoiled.", FALSE, j->carried_by, j, 0,
				TO_CHAR);
			else if (j->in_room != NOWHERE && (real_roomp(j->in_room)->people)) {
			    act("$p begins to smell spoiled.", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_CHAR);
			    act("$p begins to smell spoiled.", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_ROOM);
			}
		    }
		    break;
		case 0:
		    {
			if (j->carried_by)
			    act("$p dissolves into dust...", FALSE, j->carried_by, j, 0,
				TO_CHAR);
			else if ((j->in_room != NOWHERE) && (real_roomp(j->in_room)->people)) {
			    act("$p dissolves into dust...", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_ROOM);
			    act("$p dissolves into dust...", TRUE,
				real_roomp(j->in_room)->people, j, 0, TO_CHAR);
			}
			extract_obj(j);
		    }
	    }
	}
	if ((GET_ITEM_TYPE(j) == ITEM_CONTAINER) && (j->obj_flags.value[3])) {
	    if (j->obj_flags.timer > 0)
		j->obj_flags.timer--;
	    if (!j->obj_flags.timer) {
		if (j->carried_by) {
		    act("$p biodegrades in your hands.", FALSE, j->carried_by, j, 0, TO_CHAR);
		    ObjFromCorpse(j);
		} else if (j->in_room != NOWHERE) {
		    if ((number(0, 99) < 40) && (real_roomp(j->in_room)->zone != 10)
			&& (real_roomp(j->in_room)->zone != 11)) {
			struct char_data                       *mob;
			struct obj_data                        *obj_object,
			                                       *next_obj;
			int                                     mobset[] =
			    { 9002, 9001, 4616, 4615, 4613, 9003, 4603 };
			char                                    newbuffer[MAX_INPUT_LENGTH],
			                                        newtmp[MAX_INPUT_LENGTH];

			mob = read_mobile(mobset[number(0, 6)], VIRTUAL);
			strcpy(newtmp, j->short_description);
			if (!strncmp(newtmp, "the corpse of ", 14)) {
			    newtmp[14] = tolower(newtmp[14]);
			    sprintf(newbuffer, "%s of %s has arisen to KILL!\r\n",
				    GET_SDESC(mob), &newtmp[14]);
			} else {
			    sprintf(newbuffer, "%s has recently risen to KILL!\r\n",
				    GET_SDESC(mob));
			}
			/*
			 * this loses memory... can we free it first? 
			 */
			if (mob->player.long_descr)
			    DESTROY(mob->player.long_descr);
			mob->player.long_descr = strdup(newbuffer);
			char_to_room(mob, j->in_room);
			GET_EXP(mob) = 75 * GetMaxLevel(mob);
			IS_CARRYING_W(mob) = 0;
			IS_CARRYING_N(mob) = 0;
			for (obj_object = j->contains; obj_object; obj_object = next_obj) {
			    next_obj = obj_object->next_content;
			    obj_from_obj(obj_object);
			    obj_to_char(obj_object, mob);
			}
			mob->points.max_hit =
			    dice(GetMaxLevel(mob), 8) + number(20, 10 * GetMaxLevel(mob));
			GET_HIT(mob) = GET_MAX_HIT(mob);
			GET_EXP(mob) =
			    dice(GetMaxLevel(mob), 10) * 10 + number(1, 10 * GetMaxLevel(mob));
			mob->player.sex = 0;
			GET_RACE(mob) = RACE_UNDEAD;
			if (!IS_SET(mob->specials.act, ACT_AGGRESSIVE)) {
			    SET_BIT(mob->specials.act, ACT_AGGRESSIVE);
			}
			if (IS_SET(mob->specials.act, ACT_SENTINEL)) {
			    REMOVE_BIT(mob->specials.act, ACT_SENTINEL);
			}
			if (real_roomp(j->in_room)->people) {
			    act("$p slowly rises as $N and screams 'DIE!'", TRUE,
				real_roomp(j->in_room)->people, j, mob, TO_ROOM);
			    act("$p slowly rises as $N and screams 'DIE!'", TRUE,
				real_roomp(j->in_room)->people, j, mob, TO_CHAR);
			}
			do_wear(mob, "all", 0);
			extract_obj(j);
		    } else if (real_roomp(j->in_room)->people) {
			act("$p dissolves into a lump of fertile soil.", TRUE,
			    real_roomp(j->in_room)->people, j, 0, TO_ROOM);
			act("$p dissolves into a lump of fertile soil.", TRUE,
			    real_roomp(j->in_room)->people, j, 0, TO_CHAR);
			ObjFromCorpse(j);
		    } else
			ObjFromCorpse(j);
		}
	    }
	}
    }
}

int ObjFromCorpse(struct obj_data *c)
{
    struct obj_data                        *jj = NULL;
    struct obj_data                        *next_thing = NULL;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_ONAME(c));

    for (jj = c->contains; jj; jj = next_thing) {
	next_thing = jj->next_content;			       /* Next in inventory */
	if (jj->in_obj) {
	    obj_from_obj(jj);
	    if (c->in_obj)
		obj_to_obj(jj, c->in_obj);
	    else if (c->carried_by)
		obj_to_room(jj, c->carried_by->in_room);
	    else if (c->in_room != NOWHERE)
		obj_to_room(jj, c->in_room);
	    else
		return (FALSE);
	} else {
	    /*
	     * **  hmm..  it isn't in the object it says it is in.
	     * **  deal with the memory lossage
	     */
	    c->contains = 0;
	    extract_obj(c);
	    log_error("Memory lost in ObjFromCorpse.");
	    return (TRUE);
	}
    }
    extract_obj(c);
    return TRUE;
}