/* ************************************************************************
* 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 "os.h"
#include "structs.h"
#include "limits.h"
#include "utils.h"
#include "spells.h"
#include "comm.h"
#include "handler.h"
#include "prototypes.h"
#define READ_TITLE(ch) \
( GET_SEX(ch) == SEX_MALE ? \
titles[(int)GET_CLASS(ch)-1][(int)GET_LEVEL(ch)].title_m : \
titles[(int)GET_CLASS(ch)-1][(int)GET_LEVEL(ch)].title_f )
extern struct char_data *character_list;
extern struct obj_data *object_list;
extern struct title_type titles[4][25];
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)); */
else
max = 100;
return (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);
/* Class/Level calculations */
/* Skill/Spell calculations */
return (max);
}
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);
else
max = ch->points.max_move;
/* 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 = 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 += gain;
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 += gain;
}
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, 2, 5, 10, 18, 6, 4, 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, 12, 18, 22, 21, 14, 10, 6);
/* 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;
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);
}
break;
case CLASS_CLERIC:{
add_hp += number (5, 10);
}
break;
case CLASS_THIEF:{
add_hp += number (7, 13);
}
break;
case CLASS_WARRIOR:{
add_hp += number (10, 15);
}
break;
}
ch->points.max_hit += MAX (1, add_hp);
if (GET_CLASS (ch) == CLASS_MAGIC_USER || GET_CLASS (ch) == CLASS_CLERIC)
ch->specials.spells_to_learn += MAX (2, wis_app[GET_WIS (ch)].bonus);
else
ch->specials.spells_to_learn +=
MIN (2, MAX (1, wis_app[GET_WIS (ch)].bonus));
if (GET_LEVEL (ch) > 20)
for (i = 0; i < 3; i++)
ch->specials.conditions[i] = -1;
}
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 gain_exp (struct char_data *ch, int gain)
{
int i;
bool is_altered = FALSE;
if (IS_NPC (ch) || ((GET_LEVEL (ch) < 21) && (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 raise 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);
}
}
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 < 25) && (titles[GET_CLASS (ch) - 1][i].exp <= GET_EXP (ch));
i++) {
if (i > GET_LEVEL (ch)) {
send_to_char ("You raise 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);
}
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))
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);
char_from_room (ch);
char_to_room (ch, 1); /* Into room number 0 */
} else if (ch->specials.timer > 48) {
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 (ch->desc)
close_socket (ch->desc);
ch->desc = 0;
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) < 22)
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);
}
}
}
}