/ circle3.1 / src / limits.c
limits.c
  1  /* ************************************************************************
  2  *   File: limits.c                                      Part of CircleMUD *
  3  *  Usage: limits & gain funcs for HMV, exp, hunger/thirst, idle time      *
  4  *                                                                         *
  5  *  All rights reserved.  See license.doc for complete information.        *
  6  *                                                                         *
  7  *  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
  8  *  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
  9  ************************************************************************ */
 10  
 11  #include "conf.h"
 12  #include "sysdep.h"
 13  
 14  #include "structs.h"
 15  #include "utils.h"
 16  #include "spells.h"
 17  #include "comm.h"
 18  #include "db.h"
 19  #include "handler.h"
 20  #include "interpreter.h"
 21  
 22  
 23  /* external variables */
 24  extern int max_exp_gain;
 25  extern int max_exp_loss;
 26  extern int idle_rent_time;
 27  extern int idle_max_level;
 28  extern int idle_void;
 29  extern int immort_level_ok;
 30  extern int use_autowiz;
 31  extern int min_wizlist_lev;
 32  extern int free_rent;
 33  
 34  /* local functions */
 35  int graf(int grafage, int p0, int p1, int p2, int p3, int p4, int p5, int p6);
 36  void run_autowiz(void);
 37  
 38  void Crash_rentsave(struct char_data *ch, int cost);
 39  int level_exp(int chclass, int level);
 40  char *title_male(int chclass, int level);
 41  char *title_female(int chclass, int level);
 42  void update_char_objects(struct char_data *ch);	/* handler.c */
 43  void reboot_wizlists(void);
 44  
 45  /* When age < 15 return the value p0 */
 46  /* When age in 15..29 calculate the line between p1 & p2 */
 47  /* When age in 30..44 calculate the line between p2 & p3 */
 48  /* When age in 45..59 calculate the line between p3 & p4 */
 49  /* When age in 60..79 calculate the line between p4 & p5 */
 50  /* When age >= 80 return the value p6 */
 51  int graf(int grafage, int p0, int p1, int p2, int p3, int p4, int p5, int p6)
 52  {
 53  
 54    if (grafage < 15)
 55      return (p0);					/* < 15   */
 56    else if (grafage <= 29)
 57      return (p1 + (((grafage - 15) * (p2 - p1)) / 15));	/* 15..29 */
 58    else if (grafage <= 44)
 59      return (p2 + (((grafage - 30) * (p3 - p2)) / 15));	/* 30..44 */
 60    else if (grafage <= 59)
 61      return (p3 + (((grafage - 45) * (p4 - p3)) / 15));	/* 45..59 */
 62    else if (grafage <= 79)
 63      return (p4 + (((grafage - 60) * (p5 - p4)) / 20));	/* 60..79 */
 64    else
 65      return (p6);					/* >= 80 */
 66  }
 67  
 68  
 69  /*
 70   * The hit_limit, mana_limit, and move_limit functions are gone.  They
 71   * added an unnecessary level of complexity to the internal structure,
 72   * weren't particularly useful, and led to some annoying bugs.  From the
 73   * players' point of view, the only difference the removal of these
 74   * functions will make is that a character's age will now only affect
 75   * the HMV gain per tick, and _not_ the HMV maximums.
 76   */
 77  
 78  /* manapoint gain pr. game hour */
 79  int mana_gain(struct char_data *ch)
 80  {
 81    int gain;
 82  
 83    if (IS_NPC(ch)) {
 84      /* Neat and fast */
 85      gain = GET_LEVEL(ch);
 86    } else {
 87      gain = graf(age(ch)->year, 4, 8, 12, 16, 12, 10, 8);
 88  
 89      /* Class calculations */
 90  
 91      /* Skill/Spell calculations */
 92  
 93      /* Position calculations    */
 94      switch (GET_POS(ch)) {
 95      case POS_SLEEPING:
 96        gain *= 2;
 97        break;
 98      case POS_RESTING:
 99        gain += (gain / 2);	/* Divide by 2 */
100        break;
101      case POS_SITTING:
102        gain += (gain / 4);	/* Divide by 4 */
103        break;
104      }
105  
106      if (IS_MAGIC_USER(ch) || IS_CLERIC(ch))
107        gain *= 2;
108  
109      if ((GET_COND(ch, FULL) == 0) || (GET_COND(ch, THIRST) == 0))
110        gain /= 4;
111    }
112  
113    if (AFF_FLAGGED(ch, AFF_POISON))
114      gain /= 4;
115  
116    return (gain);
117  }
118  
119  
120  /* Hitpoint gain pr. game hour */
121  int hit_gain(struct char_data *ch)
122  {
123    int gain;
124  
125    if (IS_NPC(ch)) {
126      /* Neat and fast */
127      gain = GET_LEVEL(ch);
128    } else {
129  
130      gain = graf(age(ch)->year, 8, 12, 20, 32, 16, 10, 4);
131  
132      /* Class/Level calculations */
133  
134      /* Skill/Spell calculations */
135  
136      /* Position calculations    */
137  
138      switch (GET_POS(ch)) {
139      case POS_SLEEPING:
140        gain += (gain / 2);	/* Divide by 2 */
141        break;
142      case POS_RESTING:
143        gain += (gain / 4);	/* Divide by 4 */
144        break;
145      case POS_SITTING:
146        gain += (gain / 8);	/* Divide by 8 */
147        break;
148      }
149  
150      if (IS_MAGIC_USER(ch) || IS_CLERIC(ch))
151        gain /= 2;	/* Ouch. */
152  
153      if ((GET_COND(ch, FULL) == 0) || (GET_COND(ch, THIRST) == 0))
154        gain /= 4;
155    }
156  
157    if (AFF_FLAGGED(ch, AFF_POISON))
158      gain /= 4;
159  
160    return (gain);
161  }
162  
163  
164  
165  /* move gain pr. game hour */
166  int move_gain(struct char_data *ch)
167  {
168    int gain;
169  
170    if (IS_NPC(ch)) {
171      /* Neat and fast */
172      gain = GET_LEVEL(ch);
173    } else {
174      gain = graf(age(ch)->year, 16, 20, 24, 20, 16, 12, 10);
175  
176      /* Class/Level calculations */
177  
178      /* Skill/Spell calculations */
179  
180  
181      /* Position calculations    */
182      switch (GET_POS(ch)) {
183      case POS_SLEEPING:
184        gain += (gain / 2);	/* Divide by 2 */
185        break;
186      case POS_RESTING:
187        gain += (gain / 4);	/* Divide by 4 */
188        break;
189      case POS_SITTING:
190        gain += (gain / 8);	/* Divide by 8 */
191        break;
192      }
193  
194      if ((GET_COND(ch, FULL) == 0) || (GET_COND(ch, THIRST) == 0))
195        gain /= 4;
196    }
197  
198    if (AFF_FLAGGED(ch, AFF_POISON))
199      gain /= 4;
200  
201    return (gain);
202  }
203  
204  
205  
206  void set_title(struct char_data *ch, char *title)
207  {
208    if (title == NULL) {
209      if (GET_SEX(ch) == SEX_FEMALE)
210        title = title_female(GET_CLASS(ch), GET_LEVEL(ch));
211      else
212        title = title_male(GET_CLASS(ch), GET_LEVEL(ch));
213    }
214  
215    if (strlen(title) > MAX_TITLE_LENGTH)
216      title[MAX_TITLE_LENGTH] = '\0';
217  
218    if (GET_TITLE(ch) != NULL)
219      free(GET_TITLE(ch));
220  
221    GET_TITLE(ch) = strdup(title);
222  }
223  
224  
225  void run_autowiz(void)
226  {
227  #if defined(CIRCLE_UNIX) || defined(CIRCLE_WINDOWS)
228    if (use_autowiz) {
229      size_t res;
230      char buf[256];
231  
232  #if defined(CIRCLE_UNIX)
233      res = snprintf(buf, sizeof(buf), "nice ../bin/autowiz %d %s %d %s %d &",
234  	min_wizlist_lev, WIZLIST_FILE, LVL_IMMORT, IMMLIST_FILE, (int) getpid());
235  #elif defined(CIRCLE_WINDOWS)
236      res = snprintf(buf, sizeof(buf), "autowiz %d %s %d %s",
237  	min_wizlist_lev, WIZLIST_FILE, LVL_IMMORT, IMMLIST_FILE);
238  #endif /* CIRCLE_WINDOWS */
239  
240      /* Abusing signed -> unsigned conversion to avoid '-1' check. */
241      if (res < sizeof(buf)) {
242        mudlog(CMP, LVL_IMMORT, FALSE, "Initiating autowiz.");
243        system(buf);
244        reboot_wizlists();
245      } else
246        log("Cannot run autowiz: command-line doesn't fit in buffer.");
247    }
248  #endif /* CIRCLE_UNIX || CIRCLE_WINDOWS */
249  }
250  
251  
252  
253  void gain_exp(struct char_data *ch, int gain)
254  {
255    int is_altered = FALSE;
256    int num_levels = 0;
257  
258    if (!IS_NPC(ch) && ((GET_LEVEL(ch) < 1 || GET_LEVEL(ch) >= LVL_IMMORT)))
259      return;
260  
261    if (IS_NPC(ch)) {
262      GET_EXP(ch) += gain;
263      return;
264    }
265    if (gain > 0) {
266      gain = MIN(max_exp_gain, gain);	/* put a cap on the max gain per kill */
267      GET_EXP(ch) += gain;
268      while (GET_LEVEL(ch) < LVL_IMMORT - immort_level_ok &&
269  	GET_EXP(ch) >= level_exp(GET_CLASS(ch), GET_LEVEL(ch) + 1)) {
270        GET_LEVEL(ch) += 1;
271        num_levels++;
272        advance_level(ch);
273        is_altered = TRUE;
274      }
275  
276      if (is_altered) {
277        mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s advanced %d level%s to level %d.",
278  		GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch));
279        if (num_levels == 1)
280          send_to_char(ch, "You rise a level!\r\n");
281        else
282  	send_to_char(ch, "You rise %d levels!\r\n", num_levels);
283        set_title(ch, NULL);
284        if (GET_LEVEL(ch) >= LVL_IMMORT)
285          run_autowiz();
286      }
287    } else if (gain < 0) {
288      gain = MAX(-max_exp_loss, gain);	/* Cap max exp lost per death */
289      GET_EXP(ch) += gain;
290      if (GET_EXP(ch) < 0)
291        GET_EXP(ch) = 0;
292    }
293  }
294  
295  
296  void gain_exp_regardless(struct char_data *ch, int gain)
297  {
298    int is_altered = FALSE;
299    int num_levels = 0;
300  
301    GET_EXP(ch) += gain;
302    if (GET_EXP(ch) < 0)
303      GET_EXP(ch) = 0;
304  
305    if (!IS_NPC(ch)) {
306      while (GET_LEVEL(ch) < LVL_IMPL &&
307  	GET_EXP(ch) >= level_exp(GET_CLASS(ch), GET_LEVEL(ch) + 1)) {
308        GET_LEVEL(ch) += 1;
309        num_levels++;
310        advance_level(ch);
311        is_altered = TRUE;
312      }
313  
314      if (is_altered) {
315        mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s advanced %d level%s to level %d.",
316  		GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch));
317        if (num_levels == 1)
318          send_to_char(ch, "You rise a level!\r\n");
319        else
320  	send_to_char(ch, "You rise %d levels!\r\n", num_levels);
321        set_title(ch, NULL);
322        if (GET_LEVEL(ch) >= LVL_IMMORT)
323          run_autowiz();
324      }
325    }
326  }
327  
328  
329  void gain_condition(struct char_data *ch, int condition, int value)
330  {
331    bool intoxicated;
332  
333    if (IS_NPC(ch) || GET_COND(ch, condition) == -1)	/* No change */
334      return;
335  
336    intoxicated = (GET_COND(ch, DRUNK) > 0);
337  
338    GET_COND(ch, condition) += value;
339  
340    GET_COND(ch, condition) = MAX(0, GET_COND(ch, condition));
341    GET_COND(ch, condition) = MIN(24, GET_COND(ch, condition));
342  
343    if (GET_COND(ch, condition) || PLR_FLAGGED(ch, PLR_WRITING))
344      return;
345  
346    switch (condition) {
347    case FULL:
348      send_to_char(ch, "You are hungry.\r\n");
349      break;
350    case THIRST:
351      send_to_char(ch, "You are thirsty.\r\n");
352      break;
353    case DRUNK:
354      if (intoxicated)
355        send_to_char(ch, "You are now sober.\r\n");
356      break;
357    default:
358      break;
359    }
360  
361  }
362  
363  
364  void check_idling(struct char_data *ch)
365  {
366    if (++(ch->char_specials.timer) > idle_void) {
367      if (GET_WAS_IN(ch) == NOWHERE && IN_ROOM(ch) != NOWHERE) {
368        GET_WAS_IN(ch) = IN_ROOM(ch);
369        if (FIGHTING(ch)) {
370  	stop_fighting(FIGHTING(ch));
371  	stop_fighting(ch);
372        }
373        act("$n disappears into the void.", TRUE, ch, 0, 0, TO_ROOM);
374        send_to_char(ch, "You have been idle, and are pulled into a void.\r\n");
375        save_char(ch);
376        Crash_crashsave(ch);
377        char_from_room(ch);
378        char_to_room(ch, 1);
379      } else if (ch->char_specials.timer > idle_rent_time) {
380        if (IN_ROOM(ch) != NOWHERE)
381  	char_from_room(ch);
382        char_to_room(ch, 3);
383        if (ch->desc) {
384  	STATE(ch->desc) = CON_DISCONNECT;
385  	/*
386  	 * For the 'if (d->character)' test in close_socket().
387  	 * -gg 3/1/98 (Happy anniversary.)
388  	 */
389  	ch->desc->character = NULL;
390  	ch->desc = NULL;
391        }
392        if (free_rent)
393  	Crash_rentsave(ch, 0);
394        else
395  	Crash_idlesave(ch);
396        mudlog(CMP, LVL_GOD, TRUE, "%s force-rented and extracted (idle).", GET_NAME(ch));
397        extract_char(ch);
398      }
399    }
400  }
401  
402  
403  
404  /* Update PCs, NPCs, and objects */
405  void point_update(void)
406  {
407    struct char_data *i, *next_char;
408    struct obj_data *j, *next_thing, *jj, *next_thing2;
409  
410    /* characters */
411    for (i = character_list; i; i = next_char) {
412      next_char = i->next;
413  	
414      gain_condition(i, FULL, -1);
415      gain_condition(i, DRUNK, -1);
416      gain_condition(i, THIRST, -1);
417  	
418      if (GET_POS(i) >= POS_STUNNED) {
419        GET_HIT(i) = MIN(GET_HIT(i) + hit_gain(i), GET_MAX_HIT(i));
420        GET_MANA(i) = MIN(GET_MANA(i) + mana_gain(i), GET_MAX_MANA(i));
421        GET_MOVE(i) = MIN(GET_MOVE(i) + move_gain(i), GET_MAX_MOVE(i));
422        if (AFF_FLAGGED(i, AFF_POISON))
423  	if (damage(i, i, 2, SPELL_POISON) == -1)
424  	  continue;	/* Oops, they died. -gg 6/24/98 */
425        if (GET_POS(i) <= POS_STUNNED)
426  	update_pos(i);
427      } else if (GET_POS(i) == POS_INCAP) {
428        if (damage(i, i, 1, TYPE_SUFFERING) == -1)
429  	continue;
430      } else if (GET_POS(i) == POS_MORTALLYW) {
431        if (damage(i, i, 2, TYPE_SUFFERING) == -1)
432  	continue;
433      }
434      if (!IS_NPC(i)) {
435        update_char_objects(i);
436        if (GET_LEVEL(i) < idle_max_level)
437  	check_idling(i);
438      }
439    }
440  
441    /* objects */
442    for (j = object_list; j; j = next_thing) {
443      next_thing = j->next;	/* Next in object list */
444  
445      /* If this is a corpse */
446      if (IS_CORPSE(j)) {
447        /* timer count down */
448        if (GET_OBJ_TIMER(j) > 0)
449  	GET_OBJ_TIMER(j)--;
450  
451        if (!GET_OBJ_TIMER(j)) {
452  
453  	if (j->carried_by)
454  	  act("$p decays in your hands.", FALSE, j->carried_by, j, 0, TO_CHAR);
455  	else if ((IN_ROOM(j) != NOWHERE) && (world[IN_ROOM(j)].people)) {
456  	  act("A quivering horde of maggots consumes $p.",
457  	      TRUE, world[IN_ROOM(j)].people, j, 0, TO_ROOM);
458  	  act("A quivering horde of maggots consumes $p.",
459  	      TRUE, world[IN_ROOM(j)].people, j, 0, TO_CHAR);
460  	}
461  	for (jj = j->contains; jj; jj = next_thing2) {
462  	  next_thing2 = jj->next_content;	/* Next in inventory */
463  	  obj_from_obj(jj);
464  
465  	  if (j->in_obj)
466  	    obj_to_obj(jj, j->in_obj);
467  	  else if (j->carried_by)
468  	    obj_to_room(jj, IN_ROOM(j->carried_by));
469  	  else if (IN_ROOM(j) != NOWHERE)
470  	    obj_to_room(jj, IN_ROOM(j));
471  	  else
472  	    core_dump();
473  	}
474  	extract_obj(j);
475        }
476      }
477    }
478  }