/* ************************************************************************ * 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 <assert.h> #include "structs.h" #include "limits.h" #include "utils.h" #include "spells.h" #include "comm.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][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)); */ max = MAX(ch->points.max_mana, max); } 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); max = MAX(ch->points.max_move, max); } 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) ||IS_SET(ch->specials.act,PLR_ISMULTIMU)||IS_SET(ch->specials.act,PLR_ISMULTICL)) 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))&& (!IS_SET(ch->specials.act,PLR_ISMULTIWA)&&!IS_SET(ch->specials.act,PLR_ISMULTITH))) 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; 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; } if (IS_SET(ch->specials.act,PLR_ISMULTITH)|| IS_SET(ch->specials.act,PLR_ISMULTICL)|| IS_SET(ch->specials.act,PLR_ISMULTIWA)|| IS_SET(ch->specials.act,PLR_ISMULTIMU)){ add_hp /= 2; } /* Dual classed chars only gain 1/2 hp */ 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) 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 (IS_TRUSTED(ch)){ for (i = 0; i < 3; i++) ch->specials.conditions[i] = -1; } else { /* Temporary kludge since I inadvertently gave mortals * immortals' special hunger/thirst/drunk conditions. -Swift */ 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) || (!IS_TRUSTED(ch) && GET_LEVEL(ch) > 0 && GET_LEVEL(ch)<LV_IMMORTAL)) { if (gain > 0) { gain = MIN(100000, gain); if (!IS_NPC(ch)){ /* Dual class gain 1/2 exp */ if (IS_SET(ch->specials.act,PLR_ISMULTITH)|| IS_SET(ch->specials.act,PLR_ISMULTIMU)|| IS_SET(ch->specials.act,PLR_ISMULTIWA)|| IS_SET(ch->specials.act,PLR_ISMULTICL)){ gain /= 3; } } 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 */ /* Swiftest: I added the following two lines */ save_char(ch,NOWHERE); save_obj2(ch); } else if (ch->specials.timer > 48) { if (ch->in_room != NOWHERE) char_from_room(ch); /* Swiftest: want to put all link deads in limbo instead of extract... if (ch->specials.was_in_room != NOWHERE) char_to_room(ch, ch->specials.was_in_room); else char_to_room(ch, 1); */ char_to_room(ch,1); if (ch->desc) close_socket(ch->desc); ch->desc = 0; /* Swiftest: Want to save their equipment.. extract_char(ch); So I added the following instead. Hope it works... */ save_char(ch,NOWHERE); save_obj2(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) <= LV_DEMIGOD) 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); } } } }