/
Crimson/
Crimson/lib/PFILE-BACKUPS/
Crimson/lib/areas/
Crimson/lib/boards/
Crimson/lib/rentfiles/A-E/
Crimson/lib/rentfiles/F-J/
Crimson/lib/rentfiles/P-T/
/* ************************************************************************
*  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 "utility.h"
#include "comm.h"
#include "db.h"
#include "handler.h"
#include "spells.h"
#include "constants.h"
#include "fight.h"
#include "act.h"
#include "ansi.h"
#include "limits.h"

/* should be in config.sys */
#define CANT_IDLE_IN_FIGHT 1
#define IDLE_TIMEOUT 12  /* in ticks */
#define DROPLINK_TIMEOUT 48 /* in ticks */


/* 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) && GET_LEVEL(ch) < IMO_LEV3) {
      max = (ch->points.max_mana);
      switch  (GET_CLASS(ch)) {
         case CLASS_MAGIC_USER:
         case CLASS_BARD:
	 case CLASS_EKNIGHT:
         case CLASS_DKNIGHT:
            max += (GET_LEVEL(ch)/2)*(con_app[GET_INT(ch)].hitp);
	    break;
         
	 case CLASS_CLERIC:
         case CLASS_KAI:
         case CLASS_DRAKKHAR:
            max += (GET_LEVEL(ch)/2)*(con_app[GET_WIS(ch)].hitp);
            break;
      }
   } else 
      max = 99;

   return(max);
}


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

   if (!IS_NPC(ch) && (GET_LEVEL(ch) < IMO_LEV3))
      max = (ch->points.max_hit) +
            (GET_LEVEL(ch)*con_app[GET_CON(ch)].hitp);
   else 
      max = (ch->points.max_hit);


/* Class/Level calculations */
/* Skill/Spell calculations */
   
  return (max);
}


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

   max = ch->points.max_move;
   if(!IS_NPC(ch) && GET_LEVEL(ch) < IMO_LEV3)
   max += (GET_LEVEL(ch)/2)*(con_app[GET_DEX(ch)].hitp);

/* Class/Level calculations */
/* Skill/Spell calculations */

  return (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 = 8;
      gain += age(ch).year/20;

      /* Class calculations */

      /* Skill/Spell calculations */
      switch  (GET_CLASS(ch)) {
         case CLASS_MAGIC_USER:
            gain += gain;
            break;
         case CLASS_CLERIC:
            gain += gain;
            break;
         case CLASS_BARD:
            gain += gain >> 2;
            break;
         case CLASS_KAI:
            gain = MINV(12,(GET_ALIGNMENT(ch) - 200) / 50);
            break;
         case CLASS_DRAKKHAR:
            gain = MINV(12,(-1 * GET_ALIGNMENT(ch) - 200) / 50);
            break;
         case CLASS_EKNIGHT:
            gain = MINV(10,(GET_ALIGNMENT(ch) - 200) / 50);
            break;
         case CLASS_DKNIGHT:
            gain = MINV(10,(-1 * GET_ALIGNMENT(ch) - 200) / 50);
            break;
      }
      /* 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 (IS_AFFECTED(ch,AFF_REGENERATION))
      gain += (gain>>1);

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

   if((GET_COND(ch,FULL)==0)||(GET_COND(ch,THIRST)==0))
      gain >>= 2;
 
   if (world[ch->in_room].room_flags == TUNNEL) 
     gain += gain;     /* temporarily TUNNEL is for fast healing rooms */

  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 {
      if ((world[ch->in_room].sector_type ==SECT_UNDERWATER)
       && GET_LEVEL(ch) < IMO_LEV && !IS_AFFECTED(ch,AFF_BREATHWATER)) {
	ansi_act("You're starting to drown!",FALSE, ch, 0, 0, TO_CHAR,CLR_DAM);
	gain = -1*number(1,4);
	if (IS_AFFECTED(ch,AFF_POISON)) {
	  gain >>= 2;
	  DAMAGE(ch,ch,2,SPELL_POISON);
         }
      }

      /* gain = graf(age(ch).year, 2,5,10,18,6,4,2); */
      gain = 16;
      gain -= age(ch).year/10;

      /* Class/Level calculations */
      switch  (GET_CLASS(ch)) {
         case CLASS_MAGIC_USER:
            gain = gain >> 1;
         case CLASS_WARRIOR:
            gain += gain >> 1;
            break;
         case CLASS_THIEF:
            gain += gain >> 2;
            break;
         case CLASS_DRAGONW:
            gain += gain;
            break;
         case CLASS_KAI:
            gain = MINV(18,(GET_ALIGNMENT(ch) - 200) / 50);
            gain = MAXV(gain,-5);
            break;
         case CLASS_DRAKKHAR:
            gain = MINV(18,(-1 * GET_ALIGNMENT(ch) - 200) / 50);
            gain = MAXV(gain,-5);
            break;
         case CLASS_EKNIGHT:
            gain = MINV(18,(GET_ALIGNMENT(ch) - 200) / 50);
            gain = MAXV(gain,0);
            break;
         case CLASS_DKNIGHT:
            gain = MINV(18,(-1 * GET_ALIGNMENT(ch) - 200) / 50);
            gain = MAXV(gain,0);
            break;
      }

      /* 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_REGENERATION))
      gain += 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;

   if (world[ch->in_room].room_flags == TUNNEL) 
     gain += gain;    /* temporarily TUNNEL is for fast healing rooms */

   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, 12,18,22,21,14,10,6) */;
      gain = 21;
      gain -= age(ch).year/10;

      /* Class/Level calculations */
      switch  (GET_CLASS(ch)) {
         case CLASS_THIEF:
            gain += gain;
            break;
         case CLASS_BARD:
            gain += gain >> 1;
            break;
         case CLASS_DRAGONW:
            gain -= gain>>2;
            break;
      }

      /* 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_REGENERATION))
      gain += gain>>1;
   if (IS_AFFECTED(ch,AFF_POISON))
      gain >>= 2;
   if((GET_COND(ch,FULL)==0)||(GET_COND(ch,THIRST)==0))
      gain >>= 2;

   if (world[ch->in_room].room_flags == TUNNEL) 
     gain += gain;       /* temporarily TUNNEL is for fast healing rooms */

   return (gain);
}

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

   add_hp = 0;
   add_move = 0;
   add_mana = 0;

   switch(GET_CLASS(ch)) {
      case CLASS_MAGIC_USER : 
         add_hp += number(4, 8);
         add_move += number(1,8);
         add_mana += number(1,5);
         break;
      case CLASS_CLERIC : 
         add_hp += number(5, 10);
         add_move += number(1,8);
         add_mana += number(2,6);
         break;
      case CLASS_THIEF : 
         add_hp += number(6,13);
         add_move += number(5,8);
         break;
      case CLASS_WARRIOR : 
         add_hp += number(9,15);
         add_move += number(1,8);
         break;
      case CLASS_BARD : 
         add_hp += number(5, 13);
         add_move += number(3,8);
         add_mana += number(0,3);
         break;
      case CLASS_KAI : 
         add_hp  += number(8, 14);
         add_move += number(1,8);
         add_mana += number(0,3);
         break;
      case CLASS_DRAKKHAR : 
         add_hp += number(8,14);
         add_move += number(1,8);
         add_mana += number(0,3);
         break;
      case CLASS_EKNIGHT : 
         add_hp += number(7,13);
         add_move += number(1,8);
         add_mana += number(0,3);
         break;
      case CLASS_DKNIGHT : 
         add_hp += number(7, 13);
         add_move += number(1,8);
         add_mana += number(0,3);
         break;
      case CLASS_DRAGONW : 
         add_hp += number(10, 17);
         add_move += number(1,6);
         break;
   }

   ch->points.max_hit += MAXV(1, add_hp);
   ch->points.max_mana += MAXV(1, add_mana);
   ch->points.max_move += MAXV(1, add_move);

   if (GET_CLASS(ch) == CLASS_MAGIC_USER || GET_CLASS(ch) == CLASS_CLERIC)
      ch->specials.spells_to_learn += MAXV(2,number(0, wis_app[GET_WIS(ch)].bonus)+1);
   else if (GET_CLASS(ch) == CLASS_WARRIOR || GET_CLASS(ch) == CLASS_THIEF || GET_CLASS(ch) == CLASS_DRAGONW)
      ch->specials.spells_to_learn += number(0, wis_app[GET_WIS(ch)].bonus)/2+1;
   else
      ch->specials.spells_to_learn += number(0, wis_app[GET_WIS(ch)].bonus+1)/2+2;

   if (GET_LEVEL(ch) >= IMO_LEV )
      for (i = 0; i < 3; i++)
         ch->specials.conditions[i] = -1;
   if (GET_LEVEL(ch) == 3 ||
   GET_LEVEL(ch) == 5 || GET_LEVEL(ch) == 8) roll_abilities(ch);

}  


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))+1);
   strcpy(GET_TITLE(ch), READ_TITLE(ch));
}



void gain_exp(struct char_data *ch, int gain)
{
   int i;
   char buf[128];
   bool is_altered = FALSE;

   if (IS_SET(zone_table[world[ch->in_room].zone].flags, ZONE_TESTING)) {
     ansi_act("Warning: This zone is till being tester.", FALSE, ch, 0, 0, TO_CHAR,CLR_ERROR);
   }
   if (IS_SET(zone_table[world[ch->in_room].zone].flags, ZONE_NOENTER)) {
     ansi_act("Warning: This zone is closed - why are you here?", FALSE, ch, 0, 0, TO_CHAR,CLR_ERROR);
   }
   if (IS_NPC(ch) || ((GET_LEVEL(ch) < IMO_LEV) && (GET_LEVEL(ch) > 0))) {
      if (gain > 0) {
         GET_EXP(ch) += gain;
         if ((!IS_NPC(ch))&&(GET_LEVEL(ch) < IMO_LEV) ) {
            gain = MAXV(gain, 
              (titles[GET_CLASS(ch)-1][GET_LEVEL(ch)+1].exp
              - titles[GET_CLASS(ch)-1][GET_LEVEL(ch)].exp)/4
            );
            gain = MAXV(gain, 200000);
            for (i = 0; titles[GET_CLASS(ch)-1][i].exp <= GET_EXP(ch) && GET_LEVEL(ch) < IMO_LEV; i++) {
               if (i > GET_LEVEL(ch)) {
		 ansi_act("Congratulations! You raise a level.", FALSE, ch, 0, 0, TO_CHAR,WHITE);
		 GET_LEVEL(ch) = i;
		 advance_level(ch);
		 sprintf(buf, "SYS: %s has raised a level!", GET_NAME(ch));
		 do_sys(buf, 1);
		 is_altered = TRUE;
		 sprintf(buf,"Grats: [The Mayor of Midgaard] I am proud of you, %s!\n\r", GET_NAME(ch));
		 send_to_all(buf);
               }
            }
         }
      }

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

      if (is_altered)
         set_title(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<MAX_LEV) && (titles[GET_CLASS(ch)-1][i].exp <= GET_EXP(ch)); i++) {
            if (i > GET_LEVEL(ch)) {
	      ansi_act("Congratulations! You raise a level.", FALSE, ch, 0, 0, TO_CHAR,WHITE);
	      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);
}

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) = MAXV(0,GET_COND(ch,condition));
   GET_COND(ch,condition) = MINV(24,GET_COND(ch,condition));

   if(GET_COND(ch,condition))
      return;
   switch(condition){
      case FULL :{
         ansi_act("You are hungry.", FALSE, ch, 0, 0, TO_CHAR,CLR_ACTION);
         return;
      }
      case THIRST :{
         ansi_act("You are thirsty.", FALSE, ch, 0, 0, TO_CHAR,CLR_ACTION);
         return;
      }
      case DRUNK :{
         if(intoxicated) {
         ansi_act("You are now sober.", FALSE, ch, 0, 0, TO_CHAR,CLR_ACTION);
         return;
       }
      }
      default : break;
   }
}


void check_idling(struct char_data *ch)
{
   char buf[MAX_INPUT_LENGTH];

   if (++(ch->specials.timer) > IDLE_TIMEOUT)
      if (ch->specials.was_in_room == NOWHERE && ch->in_room != NOWHERE)
      {
         if (ch->specials.fighting)
         {
            if (CANT_IDLE_IN_FIGHT) {
              return; /* keep fighting till death */
            } else {
              stop_fighting(ch->specials.fighting);
              stop_fighting(ch);
            }
         }
         ch->specials.was_in_room = ch->in_room;
         ansi_act("$n disappears into the void.", TRUE, ch, 0, 0, TO_ROOM,CLR_ACTION);
         act("You have been idle, and are pulled into a void.", FALSE, ch, 0, 0, TO_CHAR);
         char_from_room(ch);
         char_to_room(ch, 1);  /* Into room number 0 */
      }
      else if (ch->specials.timer > DROPLINK_TIMEOUT)
      {
         int save_room;
         struct obj_cost cost;
         int recep_offer(struct char_data *ch, struct char_data *receptionist, struct obj_cost *cost);
         int save_obj(struct char_data *ch, struct obj_cost *cost);
         int del_objs(struct char_data *ch); /* inventory & equip */
         int del_char_objs(struct char_data *ch); /* del rent file */

         if (ch->in_room != NOWHERE)
            char_from_room(ch);
         if (ch->specials.was_in_room != NOWHERE)
            char_to_room(ch, ch->specials.was_in_room);
         else
            char_to_room(ch, 1);

         /* if we got the money rent us out */
         if (recep_offer(ch, ch, &cost)) {
           sprintf(buf, "SYSTEM: %s has just been auto-rented.", GET_NAME(ch));
           log(buf); 
           obj_to_char(read_object(3431, VIRTUAL),ch);  /* give autorent 
                                                           notice */
           save_obj(ch, &cost);
           del_objs(ch);
           save_room = ch->in_room;
           extract_char(ch);
           ch->in_room = world[save_room].number;
           save_char(ch, ch->in_room);
         } else {
           sprintf(buf, "SYSTEM: %s hasn't got enough coins for auto-rent.", GET_NAME(ch));
           log(buf); 
           save_room = ch->in_room;
           extract_char(ch);
	   del_char_objs(ch);
           ch->in_room = world[save_room].number;
           save_char(ch, ch->in_room);
	 }

         /* close out the link the player */
         if (ch->desc)
            close_socket(ch->desc);
         ch->desc = 0;
      }
}





/* 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;
    GET_HIT(i)  = MINV(GET_HIT(i)  + hit_gain(i),  hit_limit(i));
    GET_MANA(i) = MINV(GET_MANA(i) + mana_gain(i), mana_limit(i));
    GET_MOVE(i) = MINV(GET_MOVE(i) + move_gain(i), move_limit(i));
    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);
    } else if (GET_POS(i) == POSITION_STUNNED || GET_HIT(i) <= 0) 
      update_pos( i );
    if (!IS_NPC(i)) {
      update_char_objects(i);
      if (GET_LEVEL(i) <= IMO_LEV2)
	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 decay 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 consume $p.",
	      TRUE, world[j->in_room].people, j, 0, TO_ROOM);
	  act("A quivering hoard of maggots consume $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);
      }
    }
  }
}