/* ************************************************************************ * File: limits.c Part of CircleMUD * * Usage: limits & gain funcs for HMV, exp, hunger/thirst, idle time * * * * All rights reserved. See license.doc for complete information. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "spells.h" #include "comm.h" #include "db.h" #include "handler.h" #include "dg_scripts.h" extern struct char_data *character_list; extern struct obj_data *object_list; extern struct index_data *obj_index; extern struct room_data *world; extern int max_exp_gain; extern int max_exp_loss; extern int exp_to_level(); extern int arena_flee_timeout; extern long r_immort_start_room; void do_oldbie (struct char_data *vict); void die(struct char_data * ch, struct char_data * killer); char *title_male (int class, int level); char *title_female (int class, int level); extern int has_boat(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 hit_limit, mana_limit, and move_limit functions are gone. They * added an unnecessary level of complexity to the internal structure, * weren't particularly useful, and led to some annoying bugs. From the * players' point of view, the only difference the removal of these * functions will make is that a character's age will now only affect * the HMV gain per tick, and _not_ the HMV maximums. */ /* manapoint gain pr. game hour */ int mana_gain (struct char_data *ch) { int gain; int i; if (IS_NPC (ch)) { /* Neat and fast */ gain = GET_LEVEL (ch); } else { gain = graf (age (ch).year, 4, 8, 12, 16, 12, 10, 8); for (i=0; i< NUM_WEARS; i++) { if(GET_EQ(ch, i)) { if(GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_MP_REGEN) { gain += 10; /* this can be played around with to your liking */ } } } /* Class calculations */ /* Skill/Spell calculations */ /* Position calculations */ switch (GET_POS (ch)) { case POS_SLEEPING: gain <<= 1; /* mult by 2 */ break; case POS_MEDITATING: gain <<= 3; /* mult by 8 */ break; case POS_RESTING: gain += (gain >> 1); /* Divide by 2 */ break; case POS_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; if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_GOOD_REGEN)) gain += (gain * 2); if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_BAD_REGEN)) gain=0; return (gain); } int hit_gain (struct char_data *ch) /* Hitpoint gain pr. game hour */ { int gain; int i; if (IS_NPC (ch)) { gain = GET_LEVEL (ch); /* Neat and fast */ } else { gain = graf (age (ch).year, 8, 12, 20, 32, 16, 10, 4); for (i=0; i< NUM_WEARS; i++) { if(GET_EQ(ch, i)) { if(GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_HP_REGEN) { gain += 10; /* this can be played around with to your liking */ } } } /* Class/Level calculations */ /* Skill/Spell calculations */ /* Position calculations */ switch (GET_POS (ch)) { case POS_SLEEPING: gain += (gain >> 1); /* Divide by 2 */ break; case POS_MEDITATING: gain += (gain >> 1); /* Divide by 2 */ break; case POS_RESTING: gain += (gain >> 2); /* Divide by 4 */ break; case POS_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) || IS_AFFECTED (ch, AFF_PLAGUED)) gain >>= 2; if ((GET_COND (ch, FULL) == 0) || (GET_COND (ch, THIRST) == 0)) gain >>= 2; if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_GOOD_REGEN)) gain += (gain * 2); if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_BAD_REGEN)) gain=0; return (gain); } int move_gain (struct char_data *ch) /* move gain pr. game hour */ { int gain; int i; if (IS_NPC (ch)) { return (GET_LEVEL (ch)); /* Neat and fast */ } else { gain = graf (age (ch).year, 16, 20, 24, 20, 16, 12, 10); for (i=0; i< NUM_WEARS; i++) { if(GET_EQ(ch, i)) { if(GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_MV_REGEN) { gain += 10; /* this can be played around with to your liking */ } } } /* Class/Level calculations */ /* Skill/Spell calculations */ /* Position calculations */ switch (GET_POS (ch)) { case POS_SLEEPING: gain += (gain >> 1); /* Divide by 2 */ break; case POS_MEDITATING: gain += (gain >> 1); /* Divide by 2 */ break; case POS_RESTING: gain += (gain >> 2); /* Divide by 4 */ break; case POS_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; if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_GOOD_REGEN)) gain += (gain * 2); if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_BAD_REGEN)) gain=0; return gain; } } void set_title (struct char_data *ch, char *title) { if (title == NULL) { if (GET_SEX (ch) == SEX_FEMALE) title = "the Woman"; else title = "the Man"; } if (strlen (title) > MAX_TITLE_LENGTH) title[MAX_TITLE_LENGTH] = '\0'; if (GET_TITLE (ch) != NULL) free (GET_TITLE (ch)); GET_TITLE (ch) = str_dup (title); } void check_autowiz (struct char_data *ch) { #ifndef CIRCLE_UNIX return; #else char buf[100]; extern int use_autowiz; extern int min_wizlist_lev; if (use_autowiz && GET_LEVEL (ch) >= LVL_HERO) { sprintf (buf, "nice ../bin/autowiz %d %s %d %s %d &", min_wizlist_lev, WIZLIST_FILE, LVL_HERO, IMMLIST_FILE, (int) getpid ()); mudlog ("Initiating autowiz.", PFT, LVL_IMMORT, FALSE); system (buf); } #endif /* CIRCLE_UNIX */ } void gain_exp (struct char_data *ch, int gain) { int is_altered = FALSE; // int i; // struct obj_data *obj; int num_levels = 0; char buf[128]; if (IS_NPC(ch)) return; if ((!IS_NPC (ch) && ((GET_LEVEL (ch) < 1 || GET_LEVEL (ch) >= LVL_HERO))) || PRF2_FLAGGED(ch, PRF2_INTANGIBLE)) return; if (IS_NPC (ch)) { GET_EXP (ch) += gain; return; } if (IS_ARENACOMBATANT(ch)){ return; } if (gain > 0) { gain = MIN (max_exp_gain, gain); /* put a cap on the max gain per kill */ GET_EXP (ch) += gain; while (GET_LEVEL (ch) < LVL_HERO && GET_EXP (ch) >= exp_to_level (GET_LEVEL(ch))) { GET_LEVEL (ch) += 1; num_levels++; advance_level (ch); is_altered = TRUE; } if (is_altered) { if (num_levels == 1) send_to_char ("You rise a level!\r\n", ch); else { sprintf (buf, "You rise %d levels!\r\n", num_levels); send_to_char (buf, ch); } check_autowiz (ch); sprintf (buf, "&m[&YINFO&m]&n %s has advanced to level %d!\r\n", GET_NAME (ch), GET_LEVEL (ch)); send_to_all (buf); // if (GET_LEVEL (ch) == LVL_GETSTUFF) // { // do_oldbie (ch); // send_to_char ("&YThe gods have rewarded you for getting to level 3!&n\r\nTwo gold bricks fall from the sky into your hands.&n\r\n", ch); // } if (GET_LEVEL (ch) == 10) send_to_char("Congratulations on achieving level 10! Be warned, that you are no longer\r\nconsidered a newbie and can now be attacked by other players. You will\r\nnow lose experience when fleeing from combat.\r\n", ch); if (GET_LEVEL (ch) == LVL_HERO) { sprintf (buf, "&m[&YINFO&m]&n %s has become a &YHERO&n!\r\n", GET_NAME (ch)); send_to_all (buf); set_title(ch, "the Hero"); send_to_char ("You shall forever be known as a hero throughout the land of Deltania!\r\nYou have reached level 100, for more information please type 'help hero'.\r\n", ch); /* a hero sees all that is seen and unseen */ SET_BIT (PRF_FLAGS (ch), PRF_HOLYLIGHT); save_char (ch, NOWHERE); } } } else if (gain < 0) { gain = MAX (-max_exp_loss, gain); /* Cap max exp lost per death */ GET_EXP (ch) += gain; if (GET_EXP (ch) < 0) GET_EXP (ch) = 0; } } void gain_exp_regardless (struct char_data *ch, int gain) { int is_altered = FALSE; int num_levels = 0; if (IS_NPC(ch)) return; GET_EXP (ch) += gain; if (GET_EXP (ch) < 0) GET_EXP (ch) = 0; if (!IS_NPC (ch)) { while (GET_LEVEL (ch) < LVL_IMPL && GET_EXP (ch) >= exp_to_level (GET_LEVEL(ch))) { GET_LEVEL (ch) += 1; num_levels++; advance_level (ch); is_altered = TRUE; } if (is_altered) { if (num_levels == 1) send_to_char ("You rise a level!\r\n", ch); else { sprintf (buf, "You rise %d levels!\r\n", num_levels); send_to_char (buf, ch); } check_autowiz (ch); sprintf (buf, "&m[&YINFO&m]&n %s has advanced to level %d!\r\n", GET_NAME (ch), GET_LEVEL (ch)); send_to_all (buf); if (GET_LEVEL (ch) == LVL_GETSTUFF) { do_oldbie (ch); send_to_char ("&YThe gods have rewarded you for getting to level 3!&n\r\nTwo gold bricks fall from the sky into your hands.&n\r\n", ch); } } } } void gain_condition (struct char_data *ch, int condition, int value) { bool intoxicated; if (GET_COND (ch, condition) == -100) /* No change */ return; intoxicated = (GET_COND (ch, DRUNK) > 4); GET_COND (ch, condition) += value; if ((condition == DRUNK)) GET_COND (ch, condition) = MAX (0, GET_COND (ch, condition)); else GET_COND (ch, condition) = MAX (-72, 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.\r\n", ch); return; case THIRST: send_to_char ("You are thirsty.\r\n", ch); return; case DRUNK: if (intoxicated) send_to_char ("You are now sober.\r\n", ch); return; default: break; } } void check_idling (struct char_data *ch) { extern int free_rent; void Crash_rentsave (struct char_data *ch, int cost); void command_interpreter (struct char_data *ch, char *argument); if (PRF2_FLAGGED(ch, PRF2_LOCKOUT)) ch->char_specials.timer = 0; if (++(ch->char_specials.timer) > 5 && PRF2_FLAGGED(ch, PRF2_MBUILDING)) { send_to_char("Build mode, my friend, was not made for you to idle in.\r\n", ch); command_interpreter(ch, "build off"); return; } if (ch->char_specials.timer > 8) { if (GET_WAS_IN (ch) == NOWHERE && ch->in_room != NOWHERE) { GET_WAS_IN (ch) = ch->in_room; if (FIGHTING (ch)) { stop_fighting (FIGHTING (ch)); 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.\r\n", ch); save_char (ch, NOWHERE); Crash_crashsave (ch); char_from_room (ch); char_to_room (ch, 1); } else if (ch->char_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 = NULL; if (free_rent || (GET_LEVEL(ch) >= LVL_IMMORT)) Crash_rentsave (ch, 0); else Crash_idlesave (ch); sprintf (buf, "%s force-rented and extracted (idle).", GET_NAME (ch)); mudlog (buf, PFT, LVL_GOD, TRUE); extract_char (ch); } } } /* Update PCs, NPCs, 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 */ void do_extract_char (struct char_data *ch, int type); /* handler.c */ struct descriptor_data *d; struct char_data *i, *next_char; struct obj_data *j, *next_thing, *jj, *next_thing2; char mybuf[1024]; int enter_player_game (struct descriptor_data *d); /* characters */ for (i = character_list; i; i = next_char) { next_char=i->next; if (!IS_NPC(i) && IS_SET(PRF2_FLAGS(i), PRF2_INTANGIBLE) && GET_LEVEL(i) < LVL_IMMORT) { if (PRF2_FLAGGED(i, PRF2_MBUILDING)) check_idling (i); else if (i->player_specials->saved.death_timer > 0) { i->player_specials->saved.death_timer--; if (!i->player_specials->saved.death_timer) { send_to_char("Death makes a cryptic gesture and you find yourself englufed in light!\r\n", i); act("Death makes a cryptic gesture and $n dissapears in a bright light!\r\n", FALSE, i, NULL, NULL, TO_ROOM); REMOVE_BIT(PRF2_FLAGS(i), PRF2_INTANGIBLE); d=i->desc; do_extract_char(i, 2); enter_player_game(d); look_at_room(d->character, 1); act("A white mist appears and $n steps out.\r\n", FALSE, i, NULL, NULL, TO_ROOM); } } continue; } gain_condition (i, FULL, -1); gain_condition (i, DRUNK, -1); gain_condition (i, THIRST, -1); if (GET_POS (i) >= POS_STUNNED) { GET_HIT (i) = MIN (GET_HIT (i) + hit_gain (i), GET_MAX_HIT (i)); GET_MANA (i) = MIN (GET_MANA (i) + mana_gain (i), GET_MAX_MANA (i)); GET_MOVE (i) = MIN (GET_MOVE (i) + move_gain (i), GET_MAX_MOVE (i)); /* This is the flee-protection code for arena */ /* A person who flee's from combat must wait for the flee_timer */ /* to expire before recalling, or else it's considered as */ /* concedeing a lost match. -Thargor- */ if (IS_ARENACOMBATANT(i)){ if (GET_ARENAFLEETIMER(i) >= (1 + arena_flee_timeout)){ send_to_char("Flee-Recall timer expired. You may now" "recall without conceding the match.\r\n", i); GET_ARENAFLEETIMER(i) = 0; }else if (GET_ARENAFLEETIMER(i) >= 1){ GET_ARENAFLEETIMER(i) += 1; sprintf(mybuf, "Flee-Recall timer in tic #%d. " "%d tic(s) to go.\r\n", (GET_ARENAFLEETIMER(i)-1), (arena_flee_timeout+1)-GET_ARENAFLEETIMER(i)); send_to_char(mybuf, i); } } if (IS_AFFECTED (i, AFF_POISON)) damage (i, i, 2, SPELL_POISON); if (GET_POS (i) <= POS_STUNNED) update_pos (i); if (IS_AFFECTED (i, AFF_PLAGUED)) send_to_char("&RYou feel the effects of a deadly plague ravage through your body.&n", i); } else if (GET_POS (i) == POS_INCAP) damage (i, i, 1, TYPE_SUFFERING); else if (GET_POS (i) == POS_MORTALLYW) damage (i, i, 2, TYPE_SUFFERING); if (!IS_NPC (i)) { update_char_objects (i); if (GET_LEVEL (i) < LVL_IMMORT) check_idling (i); if (GET_LEVEL(i) < LVL_IMMORT) if (SECT(i->in_room) == SECT_WATER_NOSWIM && !has_boat(i)) { act("$n thrashes about in the water straining to stay afloat.", FALSE, i, 0, 0, TO_ROOM); send_to_char("You are drowning!\r\n", i); damage (i, i, GET_MAX_HIT(i) / 5, TYPE_DROWNING); /* TYPE_DROWNING ? */ } if (GET_LEVEL(i) < LVL_IMMORT) { if ((GET_COND(i, THIRST) < 0) && (GET_COND(i, THIRST) > -12)) send_to_char("You are extremely thirsty.\r\n", i); else if ((GET_COND(i, THIRST) <= -12) && (GET_COND(i, THIRST) >= -20)) send_to_char("You are suffering from dehydration and must get something to drink.\r\n", i); else if ((GET_COND(i, THIRST) < -20) && (GET_COND(i, THIRST) > -24)) { send_to_char("You are now dying of thirst! You must get something to drink quickly.\r\n", i); damage (i, i, GET_MAX_HIT(i) / 4, TYPE_STARVING); update_pos(i); } else if ((GET_COND(i, THIRST) <= -24) && (GET_COND(i, THIRST) > -100)) { send_to_char("&RYou have died of extreme thirst!&n\r\n", i); die(i, NULL); } if ((GET_COND(i, FULL) < 0) && (GET_COND(i, FULL) >= -23)) send_to_char("You are very hungry.\r\n", i); if ((GET_COND(i, FULL) < -23) && (GET_COND(i, FULL) > -36)) send_to_char("You are extremely hungry.\r\n", i); else if ((GET_COND(i, FULL) <= -36) && (GET_COND(i, FULL) >= -48)) send_to_char("You are suffering from starvation and must get something to eat.\r\n", i); else if ((GET_COND(i, FULL) < -48) && (GET_COND(i, FULL) > -60)) { send_to_char("You are now dying of hunger! You must get something to eat soon.\r\n", i); damage (i, i, GET_MAX_HIT(i) / 8, TYPE_STARVING); update_pos(i); } else if ((GET_COND(i, FULL) <= -60) && (GET_COND(i, FULL) > -100)) { send_to_char("&RYou have died of extreme hunger!&n\r\n", i); die(i, NULL); } } } } /* objects */ for (j = object_list; j; j = next_thing) { next_thing = j->next; /* Next in object list */ /* If this object is in water. */ if (j->in_room != NOWHERE && (SECT(j->in_room) == SECT_WATER_NOSWIM || SECT(j->in_room) == SECT_WATER_SWIM)) { /* Give everything a random chance of sinking, some may never. */ if (GET_OBJ_TYPE(j) != ITEM_BOAT && number(0, GET_OBJ_WEIGHT(j)) > 0) { act("$p sinks into the murky depths.", FALSE, 0, j, 0, TO_ROOM); extract_obj(j); continue; } else act("$p floats unsteadily in the area.", FALSE, 0, j, 0, TO_ROOM); } /* Portals */ if (GET_OBJ_TYPE(j) == ITEM_PORTAL && !j->carried_by && !j->in_obj) { // Ignore portals in people's inv or in other objects. if (GET_OBJ_TIMER(j)>0) GET_OBJ_TIMER(j)--; if (GET_OBJ_TIMER(j)==0) { act ("$p dissapears in a puff of smoke!", FALSE, 0, j, 0, TO_ROOM); extract_obj(j); continue; } } /* If this is a corpse */ if ((GET_OBJ_TYPE (j) == ITEM_CONTAINER) && GET_OBJ_VAL (j, 3)) { /* timer count down */ if (GET_OBJ_TIMER (j) > 0) GET_OBJ_TIMER (j)--; if (!GET_OBJ_TIMER (j)) { 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 horde of maggots consumes $p.", TRUE, world[j->in_room].people, j, 0, TO_ROOM); act ("A quivering horde 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); } } /* If the timer is set, count it down and at 0, try the trigger */ /* note to .rej hand-patchers: make this last in your point-update() */ else if (GET_OBJ_TIMER(j)>0) { GET_OBJ_TIMER(j)--; if (!GET_OBJ_TIMER(j)) timer_otrigger(j); } } }