circle-2.01/circle/
circle-2.01/circle/lib/boards/
circle-2.01/circle/lib/misc/
circle-2.01/circle/lib/plrobjs/
circle-2.01/circle/lib/text/
circle-2.01/circle/lib/world/shp/
/* ************************************************************************
*  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 <assert.h>
#include "structs.h"
#include "limits.h"
#include "utils.h"
#include "spells.h"
#include "comm.h"
#include "db.h"

#define READ_TITLE(ch) (GET_SEX(ch) == SEX_MALE ?   \
	titles[GET_CLASS(ch)-1][GET_LEVEL(ch)].title_m :  \
	titles[GET_CLASS(ch)-1][GET_LEVEL(ch)].title_f)


extern struct char_data *character_list;
extern struct obj_data *object_list;
extern struct title_type titles[4][35];
extern struct room_data *world;

/* External procedures */

void	update_pos( struct char_data *victim );                 /* in fight.c */
void	damage(struct char_data *ch, struct char_data *victim,  /*    do      */
int damage, int weapontype);
struct time_info_data age(struct char_data *ch);



/* 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 age, int p0, int p1, int p2, int p3, int p4, int p5, int p6)
{

   if (age < 15)
      return(p0);                               /* < 15   */
   else if (age <= 29)
      return (int) (p1 + (((age - 15) * (p2 - p1)) / 15));  /* 15..29 */
   else if (age <= 44)
      return (int) (p2 + (((age - 30) * (p3 - p2)) / 15));  /* 30..44 */
   else if (age <= 59)
      return (int) (p3 + (((age - 45) * (p4 - p3)) / 15));  /* 45..59 */
   else if (age <= 79)
      return (int) (p4 + (((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;

   if (!IS_NPC(ch)) {
      max = (100);  /* + (graf(age(ch).year, 0,0,10,30,50,70,60)); */
      max = MAX(ch->points.max_mana, max);
   } else {
      max = 100;
   }
   return(MAX(1, max));
}


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

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

   return (max);
*/

   return (MAX(1, ch->points.max_hit));
}


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

   if (!IS_NPC(ch)) {
      /* HERE SHOULD BE CON CALCULATIONS INSTEAD */
      max = graf(age(ch).year, 50, 70, 160, 120, 100, 40, 20);
      max = MAX(ch->points.max_move, max);
   } else {
      max = ch->points.max_move;
   }
   /* Class/Level calculations */

   /* Skill/Spell calculations */

   return (MAX(1, max));
}




/* manapoint gain pr. game hour */
int	mana_gain(struct char_data *ch)
{
   int	gain;

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

      /* Class calculations */

      /* Skill/Spell calculations */

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

      if ((GET_CLASS(ch) == CLASS_MAGIC_USER) || (GET_CLASS(ch) == CLASS_CLERIC))
	 gain <<= 1;
   }

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

   if ((GET_COND(ch, FULL) == 0) || (GET_COND(ch, THIRST) == 0))
      gain >>= 2;

   return (gain);
}


int	hit_gain(struct char_data *ch)
/* Hitpoint gain pr. game hour */
{
   int	gain;

   if (IS_NPC(ch)) {
      gain = GET_LEVEL(ch);
      /* Neat and fast */
   } else {

      gain = graf(age(ch).year, 4, 6, 10, 18, 8, 5, 2);

      /* Class/Level calculations */

      /* Skill/Spell calculations */

      /* 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_CLASS(ch) == CLASS_MAGIC_USER) || (GET_CLASS(ch) == CLASS_CLERIC))
	 gain >>= 1;
   }

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

   if ((GET_COND(ch, FULL) == 0) || (GET_COND(ch, THIRST) == 0))
      gain >>= 2;

   return (gain);
}



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

   if (IS_NPC(ch)) {
      return(GET_LEVEL(ch));
      /* Neat and fast */
   } else {
      gain = graf(age(ch).year, 16, 20, 24, 20, 16, 12, 10);

      /* Class/Level calculations */

      /* Skill/Spell calculations */


      /* 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 (IS_AFFECTED(ch, AFF_POISON))
      gain >>= 2;

   if ((GET_COND(ch, FULL) == 0) || (GET_COND(ch, THIRST) == 0))
      gain >>= 2;

   return (gain);
}


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

   extern struct wis_app_type wis_app[];
   extern struct con_app_type con_app[];

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

   switch (GET_CLASS(ch)) {

   case CLASS_MAGIC_USER :
      add_hp += number(3, 8);
      add_mana = number(GET_LEVEL(ch), (int)(1.5 * GET_LEVEL(ch)));
      add_mana = MIN(add_mana, 10);
      break;

   case CLASS_CLERIC :
      add_hp += number(5, 10);
      add_mana = number(GET_LEVEL(ch), (int)(1.5 * GET_LEVEL(ch)));
      add_mana = MIN(add_mana, 10);
      break;

   case CLASS_THIEF :
      add_hp += number(7, 13);
      add_mana = 0;
      break;

   case CLASS_WARRIOR :
      add_hp += number(10, 15);
      add_mana = 0;
      break;
   }

   ch->points.max_hit += MAX(1, add_hp);
   if ((GET_LEVEL(ch) != 1) && (ch->points.max_mana < 160)) {
      ch->points.max_mana = ch->points.max_mana + (sh_int)add_mana;
   } /* if */

   if (GET_CLASS(ch) == CLASS_MAGIC_USER || GET_CLASS(ch) == CLASS_CLERIC)
      SPELLS_TO_LEARN(ch) += MAX(2, wis_app[GET_WIS(ch)].bonus);
   else
      SPELLS_TO_LEARN(ch) += MIN(2, MAX(1, wis_app[GET_WIS(ch)].bonus));

   if (GET_LEVEL(ch) >= LEVEL_IMMORT)
      for (i = 0; i < 3; i++)
	 GET_COND(ch, i) = (char) -1;

   save_char(ch, NOWHERE);

   sprintf(buf, "%s advanced to level %d", GET_NAME(ch), GET_LEVEL(ch));
   syslog(buf, BRF, MAX(LEVEL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
}


void	set_title(struct char_data *ch)
{
   if (GET_TITLE(ch))
      RECREATE(GET_TITLE(ch), char, strlen(READ_TITLE(ch)) + 1);
   else
      CREATE(GET_TITLE(ch), char, strlen(READ_TITLE(ch)));

   strcpy(GET_TITLE(ch), READ_TITLE(ch));
}


void check_autowiz(struct char_data *ch)
{
   char	buf[100];
   extern byte	use_autowiz;
   extern int	min_wizlist_lev;

   if (use_autowiz && GET_LEVEL(ch) >= LEVEL_IMMORT) {
      sprintf(buf, "nice ../bin/autowiz %d %s %d %s %d &", min_wizlist_lev,
         WIZLIST_FILE, LEVEL_IMMORT, IMMLIST_FILE, getpid());
      syslog("Initiating autowiz.", CMP, LEVEL_IMMORT, FALSE);
      system(buf);
   }
}



void	gain_exp(struct char_data *ch, int gain)
{
   int	i;
   bool is_altered = FALSE;

   if (IS_NPC(ch) || ((GET_LEVEL(ch) < LEVEL_IMMORT) && (GET_LEVEL(ch) > 0))) {

      if (gain > 0) {
	 gain = MIN(100000, gain);
	 GET_EXP(ch) += gain;
	 if (!IS_NPC(ch)) {
	    for (i = 0; titles[GET_CLASS(ch)-1][i].exp <= GET_EXP(ch); i++) {
	       if (i > GET_LEVEL(ch)) {
		  send_to_char("You rise a level!\n\r", ch);
		  GET_LEVEL(ch) = i;
		  advance_level(ch);
		  is_altered = TRUE;
	       }
	    }
	 }
      }

      if (gain < 0) {
	 gain = MAX(-500000, gain);  /* Never loose more than 1/2 mil */
	 GET_EXP(ch) += gain;
	 if (GET_EXP(ch) < 0)
	    GET_EXP(ch) = 0;
      }

      if (is_altered) {
	 set_title(ch);
	 check_autowiz(ch);
      }
   }
}


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

   if (!IS_NPC(ch)) {
      if (gain > 0) {
	 GET_EXP(ch) += gain;
	 for (i = 0; (i <= LEVEL_IMPL) && (titles[GET_CLASS(ch)-1][i].exp <= GET_EXP(ch)); i++) {
	    if (i > GET_LEVEL(ch)) {
	       send_to_char("You rise a level!\n\r", ch);
	       GET_LEVEL(ch) = i;
	       advance_level(ch);
	       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);
      check_autowiz(ch);
   }
}


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

   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) || PLR_FLAGGED(ch, PLR_WRITING))
      return;

   switch (condition) {
   case FULL :
      send_to_char("You are hungry.\n\r", ch);
      return;
   case THIRST :
      send_to_char("You are thirsty.\n\r", ch);
      return;
   case DRUNK :
      if (intoxicated)
	 send_to_char("You are now sober.\n\r", ch);
      return;
   default :
      break;
   }

}


void	check_idling(struct char_data *ch)
{
   if (++(ch->specials.timer) > 8)
      if (ch->specials.was_in_room == NOWHERE && ch->in_room != NOWHERE) {
	 ch->specials.was_in_room = ch->in_room;
	 if (ch->specials.fighting) {
	    stop_fighting(ch->specials.fighting);
	    stop_fighting(ch);
	 }
	 act("$n disappears into the void.", TRUE, ch, 0, 0, TO_ROOM);
	 send_to_char("You have been idle, and are pulled into a void.\n\r", ch);
	 save_char(ch, NOWHERE);
	 Crash_crashsave(ch);
	 char_from_room(ch);
	 char_to_room(ch, 1);
      } 
      else if (ch->specials.timer > 48) {
	 if (ch->in_room != NOWHERE) 
	    char_from_room(ch);
	 char_to_room(ch, 3);
	 if (ch->desc)
	    close_socket(ch->desc);
	 ch->desc = 0;
	 Crash_idlesave(ch);
	 sprintf(buf, "%s force-rented and extracted (idle).", GET_NAME(ch));
	 syslog(buf, CMP, LEVEL_GOD, TRUE);
	 extract_char(ch);
      }
}



/* Update both PC's & NPC's and objects*/
void	point_update( void )
{
   void	update_char_objects( struct char_data *ch ); /* handler.c */
   void	extract_obj(struct obj_data *obj); /* handler.c */
   struct char_data *i, *next_dude;
   struct obj_data *j, *next_thing, *jj, *next_thing2;

   /* characters */
   for (i = character_list; i; i = next_dude) {
      next_dude = i->next;
      if (GET_POS(i) > POSITION_STUNNED) {
	 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));
      } else if (GET_POS(i) == POSITION_STUNNED) {
	 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));
	 update_pos( i );
      } else if (GET_POS(i) == POSITION_INCAP)
	 damage(i, i, 1, TYPE_SUFFERING);
      else if (!IS_NPC(i) && (GET_POS(i) == POSITION_MORTALLYW))
	 damage(i, i, 2, TYPE_SUFFERING);
      if (!IS_NPC(i)) {
	 update_char_objects(i);
	 if (GET_LEVEL(i) < LEVEL_GOD)
	    check_idling(i);
      }
      gain_condition(i, FULL, -1);
      gain_condition(i, DRUNK, -1);
      gain_condition(i, THIRST, -1);
   } /* for */

   /* objects */
   for (j = object_list; j ; j = next_thing) {
      next_thing = j->next; /* Next in object list */

      /* If this is a corpse */
      if ( (GET_ITEM_TYPE(j) == ITEM_CONTAINER) && (j->obj_flags.value[3]) ) {
	 /* timer count down */
	 if (j->obj_flags.timer > 0)
	    j->obj_flags.timer--;

	 if (!j->obj_flags.timer) {

	    if (j->carried_by)
	       act("$p decays in your hands.", FALSE, j->carried_by, j, 0, TO_CHAR);
	    else if ((j->in_room != NOWHERE) && (world[j->in_room].people)) {
	       act("A quivering hoard of maggots consumes $p.",
	           TRUE, world[j->in_room].people, j, 0, TO_ROOM);
	       act("A quivering hoard of maggots consumes $p.",
	           TRUE, world[j->in_room].people, j, 0, TO_CHAR);
	    }

	    for (jj = j->contains; jj; jj = next_thing2) {
	       next_thing2 = jj->next_content; /* Next in inventory */
	       obj_from_obj(jj);

	       if (j->in_obj)
		  obj_to_obj(jj, j->in_obj);
	       else if (j->carried_by)
		  obj_to_room(jj, j->carried_by->in_room);
	       else if (j->in_room != NOWHERE)
		  obj_to_room(jj, j->in_room);
	       else
		  assert(FALSE);
	    }
	    extract_obj(j);
	 }
      }
   }
}