Crossfire Server, Trunk  R21041
living.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
22 #include "global.h"
23 
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "sproto.h"
30 #include "living.h"
31 
32 static float get_con_bonus(int stat);
33 static float get_sp_bonus(int stat);
34 static float get_grace_bonus(int stat);
35 static size_t get_index(int stat, size_t max_index);
36 
42 #define ADD_EXP(exptotal, exp) { exptotal += exp; if (exptotal > MAX_EXPERIENCE) exptotal = MAX_EXPERIENCE; }
43 
58 #define INT_FEAR_BONUS 0
59 #define INT_TURN_BONUS 1
60 #define INT_CLERIC_CHANCE 2
61 #define INT_LEARN_SPELL 3
62 #define INT_CHA_BONUS 4
63 #define INT_DEX_BONUS 5
64 #define INT_DAM_BONUS 6
65 #define INT_THAC0_BONUS 7
66 #define INT_WEIGHT_LIMIT 8
67 #define NUM_INT_BONUSES 9
68 
70 
75 static const char *int_bonus_names[NUM_INT_BONUSES] = {
76  "cha_fear_bonus", "wis_turn_bonus", "wis_cleric_chance", "int_wis_learn_spell",
77  "cha_shop_bonus", "dex_bonus", "str_damage_bonus", "str_hit_bonus",
78  "str_weight_limit",
79 };
80 
85 #define FLOAT_CON_BONUS 0
86 #define FLOAT_DEX_BONUS 1
87 #define FLOAT_SP_BONUS 2
88 #define FLOAT_GRACE_BONUS 3
89 #define NUM_FLOAT_BONUSES 4
91 static const char *float_bonus_names[NUM_FLOAT_BONUSES] = {
92  "con_hp_bonus", "dex_speed_bonus", "pow_int_sp_bonus", "wis_pow_grace_bonus"
93 };
94 
95 /*
96  * Since this is nowhere defined ...
97  * Both come in handy at least in function add_exp()
98  */
99 #define MAX_EXPERIENCE levels[settings.max_level]
100 
101 extern int64_t *levels;
102 
103 #define MAX_SAVE_LEVEL 110
104 
113 static const int savethrow[MAX_SAVE_LEVEL+1] = {
114  18,
115  18, 17, 16, 15, 14, 14, 13, 13, 12, 12,
116  12, 11, 11, 11, 11, 10, 10, 10, 10, 9,
117  9, 9, 9, 9, 8, 8, 8, 8, 8, 8,
118  7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
119  6, 6, 6, 6, 6, 5, 5, 5, 5, 5,
120  5, 5, 5, 5, 4, 4, 4, 4, 4, 4,
121  4, 4, 4, 4, 3, 3, 3, 3, 3, 3,
122  3, 3, 3, 3, 3, 2, 2, 2, 2, 2,
123  2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
124  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
125  1, 1, 1, 1, 1, 1, 1, 1, 1, 1
126 };
127 
129 const char *const attacks[NROFATTACKS] = {
130  "physical", "magical", "fire", "electricity", "cold",
131  "confusion", "acid", "drain", "weaponmagic", "ghosthit",
132  "poison", "slow", "paralyze", "turn undead", "fear",
133  "cancellation", "depletion", "death", "chaos", "counterspell",
134  "god power", "holy power", "blinding", "", "life stealing",
135  "disease"
136 };
137 
139 const char *const drain_msg[NUM_STATS] = {
140  "You feel drained of strength.",
141  "You feel drained of agility.",
142  "You feel drained of health.",
143  "You feel drained of wisdom.",
144  "You feel drained of charisma.",
145  "You feel drained of intelligence.",
146  "You feel drained of power."
147 };
148 
150 const char *const restore_msg[NUM_STATS] = {
151  "You feel your strength return.",
152  "You feel your agility return.",
153  "You feel your health return.",
154  "You feel your wisdom return.",
155  "You feel your charisma return.",
156  "You feel your intelligence return.",
157  "You feel your power return."
158 };
159 
161 const char *const gain_msg[NUM_STATS] = {
162  "You feel stronger.",
163  "You feel more agile.",
164  "You feel healthy.",
165  "You feel wiser.",
166  "You seem to look better.",
167  "You feel smarter.",
168  "You feel more potent."
169 };
170 
172 const char *const lose_msg[NUM_STATS] = {
173  "You feel weaker!",
174  "You feel clumsy!",
175  "You feel less healthy!",
176  "You feel foolish!",
177  "You look ugly!",
178  "You feel stupid!",
179  "You feel less potent!"
180 };
181 
183 const char *const statname[NUM_STATS] = {
184  "strength",
185  "dexterity",
186  "constitution",
187  "wisdom",
188  "charisma",
189  "intelligence",
190  "power"
191 };
192 
194 const char *const short_stat_name[NUM_STATS] = {
195  "Str",
196  "Dex",
197  "Con",
198  "Wis",
199  "Cha",
200  "Int",
201  "Pow"
202 };
203 
218 void set_attr_value(living *stats, int attr, int8_t value) {
219  switch (attr) {
220  case STRENGTH:
221  stats->Str = value;
222  break;
223 
224  case DEXTERITY:
225  stats->Dex = value;
226  break;
227 
228  case CONSTITUTION:
229  stats->Con = value;
230  break;
231 
232  case WISDOM:
233  stats->Wis = value;
234  break;
235 
236  case POWER:
237  stats->Pow = value;
238  break;
239 
240  case CHARISMA:
241  stats->Cha = value;
242  break;
243 
244  case INTELLIGENCE:
245  stats->Int = value;
246  break;
247  }
248 }
249 
264 void change_attr_value(living *stats, int attr, int8_t value) {
265  if (value == 0)
266  return;
267  switch (attr) {
268  case STRENGTH:
269  stats->Str += value;
270  break;
271 
272  case DEXTERITY:
273  stats->Dex += value;
274  break;
275 
276  case CONSTITUTION:
277  stats->Con += value;
278  break;
279 
280  case WISDOM:
281  stats->Wis += value;
282  break;
283 
284  case POWER:
285  stats->Pow += value;
286  break;
287 
288  case CHARISMA:
289  stats->Cha += value;
290  break;
291 
292  case INTELLIGENCE:
293  stats->Int += value;
294  break;
295 
296  default:
297  LOG(llevError, "Invalid attribute in change_attr_value: %d\n", attr);
298  }
299 }
300 
313 int8_t get_attr_value(const living *stats, int attr) {
314  switch (attr) {
315  case STRENGTH:
316  return(stats->Str);
317 
318  case DEXTERITY:
319  return(stats->Dex);
320 
321  case CONSTITUTION:
322  return(stats->Con);
323 
324  case WISDOM:
325  return(stats->Wis);
326 
327  case CHARISMA:
328  return(stats->Cha);
329 
330  case INTELLIGENCE:
331  return(stats->Int);
332 
333  case POWER:
334  return(stats->Pow);
335  }
336  return 0;
337 }
338 
354 void check_stat_bounds(living *stats, int8_t min_stat, int8_t max_stat) {
355  int i, v;
356  for (i = 0; i < NUM_STATS; i++)
357  if ((v = get_attr_value(stats, i)) > max_stat)
358  set_attr_value(stats, i, max_stat);
359  else if (v < min_stat)
360  set_attr_value(stats, i, min_stat);
361 }
362 
363 
369 #define DIFF_MSG(flag, subtype1, subtype2, msg1, msg2) \
370  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, (flag > 0) ? subtype1 : subtype2, (flag > 0) ? msg1 : msg2);
371 
394 int change_abil(object *op, object *tmp) {
395  int flag = QUERY_FLAG(tmp, FLAG_APPLIED) ? 1 : -1, i, j, success = 0;
396  object refop;
397  int potion_max = 0;
398 
399  /* remember what object was like before it was changed. note that
400  * refop is a local copy of op only to be used for detecting changes
401  * found by fix_object. refop is not a real object
402  */
403  memcpy(&refop, op, sizeof(object));
404 
405  if (op->type == PLAYER) {
406  if (tmp->type == POTION) {
407  potion_max = 1;
408  for (j = 0; j < NUM_STATS; j++) {
409  int nstat, ostat;
410 
411  ostat = get_attr_value(&(op->contr->orig_stats), j);
412  i = get_attr_value(&(tmp->stats), j);
413 
414  /* nstat is what the stat will be after use of the potion */
415  nstat = flag*i+ostat;
416 
417  /* Do some bounds checking. While I don't think any
418  * potions do so right now, there is the potential for potions
419  * that adjust that stat by more than one point, so we need
420  * to allow for that.
421  */
422  if (nstat < 1 && i*flag < 0)
423  nstat = 1;
424  else if (nstat > 20+get_attr_value(&(op->arch->clone.stats), j)) {
425  nstat = 20+get_attr_value(&(op->arch->clone.stats), j);
426  }
427  if (nstat != ostat) {
428  set_attr_value(&(op->contr->orig_stats), j, nstat);
429  potion_max = 0;
430  } else if (i) {
431  /* potion is useless - player has already hit the natural maximum */
432  potion_max = 1;
433  }
434  }
435  /* This section of code ups the characters normal stats also. I am not
436  * sure if this is strictly necessary, being that fix_object probably
437  * recalculates this anyway.
438  */
439  for (j = 0; j < NUM_STATS; j++)
440  change_attr_value(&(op->stats), j, flag*get_attr_value(&(tmp->stats), j));
442  } /* end of potion handling code */
443  }
444 
445  /* reset attributes that fix_object doesn't reset since it doesn't search
446  * everything to set
447  */
448  if (flag == -1) {
449  op->attacktype &= ~tmp->attacktype;
450  op->path_attuned &= ~tmp->path_attuned;
451  op->path_repelled &= ~tmp->path_repelled;
452  op->path_denied &= ~tmp->path_denied;
453  /* Presuming here that creatures only have move_type,
454  * and not the other move_ fields.
455  */
456  op->move_type &= ~tmp->move_type;
457  }
458 
459  /* call fix_object since op object could have whatever attribute due
460  * to multiple items. if fix_object always has to be called after
461  * change_ability then might as well call it from here
462  */
463  fix_object(op);
464 
465  /* Fix player won't add the bows ability to the player, so don't
466  * print out message if this is a bow.
467  */
468  if (tmp->attacktype&AT_CONFUSION && tmp->type != BOW) {
469  success = 1;
471  "Your hands begin to glow red.",
472  "Your hands stop glowing red.");
473  }
474  if (QUERY_FLAG(op, FLAG_LIFESAVE) != QUERY_FLAG(&refop, FLAG_LIFESAVE)) {
475  success = 1;
477  "You feel very protected.",
478  "You don't feel protected anymore.");
479  }
481  success = 1;
483  "A magic force shimmers around you.",
484  "The magic force fades away.");
485  }
486  if (QUERY_FLAG(op, FLAG_REFL_SPELL) != QUERY_FLAG(&refop, FLAG_REFL_SPELL)) {
487  success = 1;
489  "You feel more safe now, somehow.",
490  "Suddenly you feel less safe, somehow.");
491  }
492  /* movement type has changed. We don't care about cases where
493  * user has multiple items giving the same type appled like we
494  * used to - that is more work than what we gain, plus messages
495  * can be misleading (a little higher could be miscontrued from
496  * from fly high)
497  */
498  if (tmp->move_type && op->move_type != refop.move_type) {
499  success = 1;
500 
501  /* MOVE_FLY_HIGH trumps MOVE_FLY_LOW - changing your move_fly_low
502  * status doesn't make a difference if you are flying high
503  */
504  if (tmp->move_type&MOVE_FLY_LOW && !(op->move_type&MOVE_FLY_HIGH)) {
506  "You start to float in the air!",
507  "You float down to the ground.");
508  }
509 
510  if (tmp->move_type&MOVE_FLY_HIGH) {
511  /* double conditional - second case covers if you have move_fly_low -
512  * in that case, you don't actually land
513  */
515  "You soar into the air!.",
516  (op->move_type&MOVE_FLY_LOW ? "You glide closer to the ground.":
517  "You float down to the ground."));
518  }
519  if (tmp->move_type&MOVE_SWIM)
521  "You feel ready for a swim",
522  "You no longer feel like swimming");
523 
524  /* Changing move status may mean you are affected by things you weren't before */
525  object_check_move_on(op, op);
526  }
527 
528  /* becoming UNDEAD... a special treatment for this flag. Only those not
529  * originally undead may change their status
530  */
531  if (!QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
532  if (QUERY_FLAG(op, FLAG_UNDEAD) != QUERY_FLAG(&refop, FLAG_UNDEAD)) {
533  success = 1;
534  if (flag > 0) {
535  if (op->race)
536  free_string(op->race);
537  op->race = add_string("undead");
538  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE, "Your lifeforce drains away!");
539  } else {
540  if (op->race)
541  free_string(op->race);
542  if (op->arch->clone.race)
543  op->race = add_string(op->arch->clone.race);
544  else
545  op->race = NULL;
546  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE, "Your lifeforce returns!");
547  }
548  }
549 
550  if (QUERY_FLAG(op, FLAG_STEALTH) != QUERY_FLAG(&refop, FLAG_STEALTH)) {
551  success = 1;
553  "You walk more quietly.",
554  "You walk more noisily.");
555  }
556  if (QUERY_FLAG(op, FLAG_MAKE_INVIS) != QUERY_FLAG(&refop, FLAG_MAKE_INVIS)) {
557  success = 1;
559  "You become transparent.",
560  "You can see yourself.");
561  }
562  /* blinded you can tell if more blinded since blinded player has minimal
563  * vision
564  */
565  if (QUERY_FLAG(tmp, FLAG_BLIND)) {
566  success = 1;
567  if (flag > 0) {
568  if (QUERY_FLAG(op, FLAG_WIZ))
569  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START, "Your mortal self is blinded.");
570  else {
572  SET_FLAG(op, FLAG_BLIND);
573  if (op->type == PLAYER)
574  op->contr->do_los = 1;
575  }
576  } else {
577  if (QUERY_FLAG(op, FLAG_WIZ))
578  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END, "Your mortal self can now see again.");
579  else {
581  CLEAR_FLAG(op, FLAG_BLIND);
582  if (op->type == PLAYER)
583  op->contr->do_los = 1;
584  }
585  }
586  }
587 
589  success = 1;
590  if (op->type == PLAYER)
591  op->contr->do_los = 1;
593  "Your vision is better in the dark.",
594  "You see less well in the dark.");
595  }
596 
597  if (QUERY_FLAG(op, FLAG_XRAYS) != QUERY_FLAG(&refop, FLAG_XRAYS)) {
598  success = 1;
599  if (flag > 0) {
600  if (QUERY_FLAG(op, FLAG_WIZ))
601  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, "Your vision becomes a little clearer.");
602  else {
603  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, "Everything becomes transparent.");
604  if (op->type == PLAYER)
605  op->contr->do_los = 1;
606  }
607  } else {
608  if (QUERY_FLAG(op, FLAG_WIZ))
609  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, "Your vision becomes a bit out of focus.");
610  else {
611  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, "Everything suddenly looks very solid.");
612  if (op->type == PLAYER)
613  op->contr->do_los = 1;
614  }
615  }
616  }
617 
618  if (tmp->stats.luck) {
619  success = 1;
621  "You feel more lucky.",
622  "You feel less lucky.");
623  }
624 
625  if (tmp->stats.hp && op->type == PLAYER) {
626  success = 1;
628  "You feel much more healthy!",
629  "You feel much less healthy!");
630  }
631 
632  if (tmp->stats.sp && op->type == PLAYER && tmp->type != SKILL) {
633  success = 1;
635  "You feel one with the powers of magic!",
636  "You suddenly feel very mundane.");
637  }
638 
639  /* for the future when artifacts set this -b.t. */
640  if (tmp->stats.grace && op->type == PLAYER) {
641  success = 1;
643  "You feel closer to your god!",
644  "You suddenly feel less holy.");
645  }
646 
647  if (tmp->stats.wc && op->type == PLAYER) {
648  success = 1;
650  "You feel more confident in combat.",
651  "You feel less confident in combat.");
652  }
653 
654  if (tmp->stats.ac && op->type == PLAYER) {
655  success = 1;
657  "You feel more confident in your dodging skills.",
658  "You feel less confident in your dodging skills.");
659  }
660 
661  if (tmp->stats.exp && tmp->type != SKILL && op->type == PLAYER) {
662  success = 1;
664  "You feel like you're moving faster.",
665  "You feel like you're moving more slowly.");
666  }
667 
668  if (tmp->stats.food && op->type == PLAYER) {
669  success = 1;
671  "You feel your digestion slowing down.",
672  "You feel your digestion speeding up.");
673  }
674 
675  /* Messages for changed resistance */
676  for (i = 0; i < NROFATTACKS; i++) {
677  if (i == ATNR_PHYSICAL)
678  continue; /* Don't display about armour */
679 
680  if (op->resist[i] != refop.resist[i]) {
681  success = 1;
682  if (op->resist[i] > refop.resist[i])
684  "Your resistance to %s rises to %d%%.",
685  change_resist_msg[i], op->resist[i]);
686  else
688  "Your resistance to %s drops to %d%%.",
689  change_resist_msg[i], op->resist[i]);
690  }
691  }
692 
693  if (!potion_max) {
694  for (j = 0; j < NUM_STATS; j++) {
695  if ((i = get_attr_value(&(tmp->stats), j)) != 0) {
696  success = 1;
698  }
699  }
700  }
701  return success;
702 }
703 
712 void drain_stat(object *op) {
714 }
715 
724 void drain_specific_stat(object *op, int deplete_stats) {
725  object *tmp;
726  archetype *at;
727 
729  if (!at) {
730  return;
731  } else {
732  tmp = arch_present_in_ob(at, op);
733  if (!tmp) {
734  tmp = arch_to_object(at);
735  tmp = object_insert_in_ob(tmp, op);
736  SET_FLAG(tmp, FLAG_APPLIED);
737  }
738  }
739 
741  change_attr_value(&tmp->stats, deplete_stats, -1);
742  fix_object(op);
743 }
744 
751 int remove_depletion(object *op, int level) {
752  object *depl;
753  archetype *at;
754  int i, count = 0;
755 
756  if ((at = find_archetype(ARCH_DEPLETION)) == NULL) {
757  return 0;
758  }
759 
760  depl = arch_present_in_ob(at, op);
761 
762  if (depl == NULL)
763  return 0;
764 
765  if (level != -1 && level < op->level)
766  return 0;
767 
768  for (i = 0; i < NUM_STATS; i++) {
769  if (get_attr_value(&depl->stats, i)) {
770  count++;
773  }
774  }
775 
776  object_remove(depl);
778  fix_object(op);
779 
780  return (count == 0) ? 0 : 1;
781 }
782 
792 void change_luck(object *op, int value) {
793  object *tmp;
794  archetype *at;
795  int new_luck;
796 
797  at = find_archetype("luck");
798  if (!at)
799  ;
800  else {
801  tmp = arch_present_in_ob(at, op);
802  if (!tmp) {
803  if (!value)
804  return;
805  tmp = arch_to_object(at);
806  tmp = object_insert_in_ob(tmp, op);
807  SET_FLAG(tmp, FLAG_APPLIED);
808  }
809  if (value) {
810  /* Limit the luck value of the bad luck object to +/-100. This
811  * (arbitrary) value prevents overflows (both in the bad luck object and
812  * in op itself).
813  */
814  new_luck = tmp->stats.luck+value;
815  if (new_luck >= -100 && new_luck <= 100) {
816  op->stats.luck += value;
817  tmp->stats.luck = new_luck;
818  }
819  } else {
820  if (!tmp->stats.luck) {
821  return;
822  }
823  /* Randomly change the players luck. Basically, we move it
824  * back neutral (if greater>0, subtract, otherwise add)
825  */
826  if (RANDOM()%(FABS(tmp->stats.luck)) >= RANDOM()%30) {
827  int diff = tmp->stats.luck > 0 ? -1 : 1;
828  op->stats.luck += diff;
829  tmp->stats.luck += diff;
830  }
831  }
832  }
833 }
834 
841 void remove_statbonus(object *op) {
842  op->stats.Str -= op->arch->clone.stats.Str;
843  op->stats.Dex -= op->arch->clone.stats.Dex;
844  op->stats.Con -= op->arch->clone.stats.Con;
845  op->stats.Wis -= op->arch->clone.stats.Wis;
846  op->stats.Pow -= op->arch->clone.stats.Pow;
847  op->stats.Cha -= op->arch->clone.stats.Cha;
848  op->stats.Int -= op->arch->clone.stats.Int;
849  op->contr->orig_stats.Str -= op->arch->clone.stats.Str;
850  op->contr->orig_stats.Dex -= op->arch->clone.stats.Dex;
851  op->contr->orig_stats.Con -= op->arch->clone.stats.Con;
852  op->contr->orig_stats.Wis -= op->arch->clone.stats.Wis;
853  op->contr->orig_stats.Pow -= op->arch->clone.stats.Pow;
854  op->contr->orig_stats.Cha -= op->arch->clone.stats.Cha;
855  op->contr->orig_stats.Int -= op->arch->clone.stats.Int;
856 }
857 
864 void add_statbonus(object *op) {
865  op->stats.Str += op->arch->clone.stats.Str;
866  op->stats.Dex += op->arch->clone.stats.Dex;
867  op->stats.Con += op->arch->clone.stats.Con;
868  op->stats.Wis += op->arch->clone.stats.Wis;
869  op->stats.Pow += op->arch->clone.stats.Pow;
870  op->stats.Cha += op->arch->clone.stats.Cha;
871  op->stats.Int += op->arch->clone.stats.Int;
872  op->contr->orig_stats.Str += op->arch->clone.stats.Str;
873  op->contr->orig_stats.Dex += op->arch->clone.stats.Dex;
874  op->contr->orig_stats.Con += op->arch->clone.stats.Con;
875  op->contr->orig_stats.Wis += op->arch->clone.stats.Wis;
876  op->contr->orig_stats.Pow += op->arch->clone.stats.Pow;
877  op->contr->orig_stats.Cha += op->arch->clone.stats.Cha;
878  op->contr->orig_stats.Int += op->arch->clone.stats.Int;
879 }
880 
893 static void fix_player(object *op, int *ac, int *wc, const object *grace_obj, const object *mana_obj, const object *wc_obj, int weapon_speed, float added_speed)
894 {
895  int pl_level, i;
896  float character_load = 0.0, maxhp, tmpf;
897 
898  if (op->type != PLAYER)
899  return;
900 
902  pl_level = op->level;
903 
904  if (pl_level < 1)
905  pl_level = 1; /* safety, we should always get 1 levels worth of hp! */
906 
907  /*
908  * We store maxhp as a float to hold any fractional hp bonuses,
909  * (eg, 2.5 con bonus). While it may seem simpler to just
910  * do a get_con_bonus() * min(level,10), there is also a 1 hp/level
911  * minimum (including bonus), se we have to do the logic on a
912  * level basis.
913  */
914  maxhp = 0.0;
915  for (i = 1, op->stats.maxhp = 0; i <= pl_level && i <= 10; i++) {
916 
917  tmpf = op->contr->levhp[i]+get_con_bonus(op->stats.Con);
918 
919  /* always get at least 1 hp/level */
920  if (tmpf < 1.0) tmpf = 1.0;
921 
922  maxhp += tmpf;
923  }
924 
925  /* Add 0.5 so that this rounds normally - the cast just drops the
926  * fraction, so 1.5 becomes 1.
927  */
928  op->stats.maxhp = (int)(maxhp + 0.5);
929  if (op->level > 10)
930  op->stats.maxhp += 2 * (op->level - 10);
931 
932  op->stats.maxhp += op->arch->clone.stats.maxhp;
933 
934  if (op->stats.hp > op->stats.maxhp)
935  op->stats.hp = op->stats.maxhp;
936 
937  /* Sp gain is controlled by the level of the player's
938  * relevant experience object (mana_obj, see above) */
939 
940  /* set maxsp */
941  if (!mana_obj || !mana_obj->level) {
942  op->stats.maxsp = 1;
943  } else {
944  float sp_tmp = 0.0, mana_bonus;
945  int mana_lvl_max;
946 
947  mana_lvl_max = (mana_obj->level >10 ? 10: mana_obj->level);
948  mana_bonus = (2.0*get_sp_bonus(op->stats.Pow)+get_sp_bonus(op->stats.Int)) / 3.0;
949 
950  for (i = 1; i <= mana_lvl_max; i++) {
951  float stmp;
952 
953  stmp = op->contr->levsp[i] + mana_bonus;
954 
955  /* Got some extra bonus at first level */
956  if (i == 1) stmp += mana_bonus;
957 
958  if (stmp < 1.0)
959  stmp = 1.0;
960 
961  sp_tmp += stmp;
962  }
963 
964  op->stats.maxsp = (int)sp_tmp+op->arch->clone.stats.maxsp;
965 
966  if (mana_obj->level > 10)
967  op->stats.maxsp += 2 * (mana_obj->level - 10);
968  }
969 
970  /* Characters can get their sp supercharged via rune of transferrance */
971  if (op->stats.sp > op->stats.maxsp*2)
972  op->stats.sp = op->stats.maxsp*2;
973 
974  /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */
975  if (!grace_obj || !grace_obj->level) {
976  op->stats.maxgrace = 1;
977  } else {
978  /* store grace in a float - this way, the divisions below don't create
979  * big jumps when you go from level to level - with int's, it then
980  * becomes big jumps when the sums of the bonuses jump to the next
981  * step of 8 - with floats, even fractional ones are useful.
982  */
983  float sp_tmp = 0.0, grace_bonus;
984 
985  grace_bonus = (get_grace_bonus(op->stats.Pow)+2.0*get_grace_bonus(op->stats.Wis)) / 3.0;
986 
987  for (i = 1; i <= grace_obj->level && i <= 10; i++) {
988  float grace_tmp = op->contr->levgrace[i] + grace_bonus;
989 
990  /* Got some extra bonus at first level */
991  if (i == 1)
992  grace_tmp += grace_bonus;
993 
994  if (grace_tmp < 1.0)
995  grace_tmp = 1.0;
996  sp_tmp += grace_tmp;
997  }
998  op->stats.maxgrace = (int)sp_tmp+op->arch->clone.stats.maxgrace;
999 
1000  /* two grace points per level after 11 */
1001  if (grace_obj->level > 10)
1002  op->stats.maxgrace += 2 * (grace_obj->level - 10);
1003  }
1004  /* No limit on grace vs maxgrace */
1005 
1006  if (op->contr->braced) {
1007  (*ac) += 2;
1008  (*wc) += 4;
1009  } else
1010  (*ac) -= get_dex_bonus(op->stats.Dex);
1011 
1012  /* In new exp/skills system, wc bonuses are related to
1013  * the players level in a relevant exp object (wc_obj)
1014  * not the general player level -b.t.
1015  * I changed this slightly so that wc bonuses are better
1016  * than before. This is to balance out the fact that
1017  * the player no longer gets a personal weapon w/ 1
1018  * improvement every level, now its fighterlevel/5. So
1019  * we give the player a bonus here in wc and dam
1020  * to make up for the change. Note that I left the
1021  * monster bonus the same as before. -b.t.
1022  */
1023 
1024  if (wc_obj && wc_obj->level >= 1) {
1025  const char *wc_in = object_get_value(wc_obj, "wc_increase_rate");
1026  int wc_increase_rate;
1027 
1028  wc_increase_rate = wc_in?atoi(wc_in):5;
1029  assert(wc_increase_rate != 0);
1030 
1031  (*wc) -= get_thaco_bonus(op->stats.Str);
1032  (*wc) -= (wc_obj->level-1)/wc_increase_rate;
1033  op->stats.dam += (wc_obj->level-1)/4;
1034  } else {
1035  (*wc) -= (((op->level-1)/5)+get_thaco_bonus(op->stats.Str));
1036  }
1037  op->stats.dam += get_dam_bonus(op->stats.Str);
1038 
1039  if (op->stats.dam < 1)
1040  op->stats.dam = 1;
1041 
1043 
1044  if (settings.search_items && op->contr->search_str[0])
1045  op->speed -= 0.25;
1046 
1047  if (op->attacktype == 0)
1048  op->attacktype = op->arch->clone.attacktype;
1049 
1050 
1051  /* First block is for encumbrance of the player */
1052 
1053  /* The check for FREE_PLAYER_LOAD_PERCENT < 1.0 is really a safety. One would
1054  * think that it should never be the case if that is set to 1.0, that carrying
1055  * would be above the limit. But it could be if character is weakened and
1056  * was otherwise at limit. Without that safety, could get divide by zeros.
1057  */
1059  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
1060  int extra_weight = op->carrying-get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT;
1061 
1062  character_load = (float)extra_weight/(float)(get_weight_limit(op->stats.Str)*(1.0-FREE_PLAYER_LOAD_PERCENT));
1063 
1064  /* character_load is used for weapon speed below, so sanitize value */
1065  if (character_load >= 1.0)
1066  character_load = 1.0;
1067 
1068  /* If a character is fully loaded, they will always get cut down to min
1069  * speed no matter what their dex. Note that magic is below, so
1070  * still helps out.
1071  */
1072  if (op->speed > MAX_PLAYER_SPEED)
1073  op->speed -= character_load*(op->speed-MIN_PLAYER_SPEED);
1074  else
1075  op->speed -= character_load*(MAX_PLAYER_SPEED-MIN_PLAYER_SPEED);
1076  }
1077 
1078  /* This block is for weapon speed */
1079  op->weapon_speed = BASE_WEAPON_SPEED+get_speed_bonus(op->stats.Dex)-weapon_speed/20.0+added_speed/10.0;
1080  if (wc_obj) {
1081  op->weapon_speed += 0.005*wc_obj->level;
1082  } else
1083  op->weapon_speed += 0.005*op->level;
1084 
1085  /* character_load=1.0 means character is fully loaded, 0.0 is unloaded. Multiplying
1086  * by 0.2 for penalty is purely arbitrary, but slows things down without completely
1087  * stopping them.
1088  */
1089  op->weapon_speed -= character_load*0.2;
1090 
1091  if (op->weapon_speed < 0.05)
1092  op->weapon_speed = 0.05;
1093 
1094  /* It is quite possible that a player's spell costing might have changed,
1095  * so we will check that now.
1096  */
1098 }
1119 void fix_object(object *op) {
1120  int i;
1121  float max = 9, added_speed = 0, speed_reduce_from_disease = 1;
1122  int weapon_speed = 0;
1123  int best_wc = 0, best_ac = 0, wc = 0, ac = 0;
1124  int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS];
1125  const object *grace_obj = NULL, *mana_obj = NULL, *wc_obj = NULL;
1126 
1127  /* First task is to clear all the values back to their original values */
1128  if (op->type == PLAYER) {
1129  for (i = 0; i < NUM_STATS; i++) {
1130  set_attr_value(&(op->stats), i, get_attr_value(&(op->contr->orig_stats), i));
1131  set_attr_value(&(op->contr->applied_stats), i, 0);
1132  }
1134  op->contr->encumbrance = 0;
1135 
1136  op->attacktype = 0;
1137  op->contr->digestion = 0;
1138  op->contr->gen_hp = 0;
1139  op->contr->gen_sp = 0;
1140  op->contr->gen_grace = 0;
1141  op->contr->gen_sp_armour = 10;
1142  op->contr->item_power = 0;
1143 
1144  /* Don't clobber all the range_ values. range_golem otherwise
1145  * gets reset for no good reason, and we don't want to reset
1146  * range_magic (what spell is readied). These three below
1147  * well get filled in based on what the player has equipped.
1148  */
1149  op->contr->ranges[range_bow] = NULL;
1150  op->contr->ranges[range_misc] = NULL;
1151  op->contr->ranges[range_skill] = NULL;
1152  } /* If player */
1153  memcpy(op->body_used, op->body_info, sizeof(op->body_info));
1154 
1155  if (op->slaying != NULL) {
1156  free_string(op->slaying);
1157  op->slaying = NULL;
1158  }
1159  if (!QUERY_FLAG(op, FLAG_WIZ)) {
1160  CLEAR_FLAG(op, FLAG_XRAYS);
1162  }
1163 
1165  CLEAR_FLAG(op, FLAG_STEALTH);
1166  CLEAR_FLAG(op, FLAG_BLIND);
1168  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_SPELL))
1170  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_MISSILE))
1172  if (!QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
1173  CLEAR_FLAG(op, FLAG_UNDEAD);
1174  if (!QUERY_FLAG(&op->arch->clone, FLAG_SEE_IN_DARK))
1176  CLEAR_FLAG(op, FLAG_PROBE);
1177 
1178  op->path_attuned = op->arch->clone.path_attuned;
1180  op->path_denied = op->arch->clone.path_denied;
1181  op->glow_radius = op->arch->clone.glow_radius;
1182  op->move_type = op->arch->clone.move_type;
1183  op->chosen_skill = NULL;
1184 
1185  /* initializing resistances from the values in player/monster's
1186  * archetype clone
1187  */
1188  memcpy(&op->resist, &op->arch->clone.resist, sizeof(op->resist));
1189 
1190  for (i = 0; i < NROFATTACKS; i++) {
1191  if (op->resist[i] > 0)
1192  prot[i] = op->resist[i], vuln[i] = 0;
1193  else
1194  vuln[i] = -(op->resist[i]), prot[i] = 0;
1195  potion_resist[i] = 0;
1196  }
1197 
1198  wc = op->arch->clone.stats.wc;
1199  op->stats.dam = op->arch->clone.stats.dam;
1200 
1201  /* for players which cannot use armour, they gain AC -1 per 3 levels,
1202  * plus a small amount of physical resist, those poor suckers. ;)
1203  * the fact that maxlevel is factored in could be considered sort of bogus -
1204  * we should probably give them some bonus and cap it off - otherwise,
1205  * basically, if a server updates its max level, these playes may find
1206  * that their protection from physical goes down
1207  */
1208  if (!QUERY_FLAG(op, FLAG_USE_ARMOUR) && op->type == PLAYER) {
1209  ac = MAX(-10, op->arch->clone.stats.ac-op->level/3);
1210  prot[ATNR_PHYSICAL] += ((100-prot[AT_PHYSICAL])*(80*op->level/settings.max_level))/100;
1211  } else
1212  ac = op->arch->clone.stats.ac;
1213 
1214  op->stats.luck = op->arch->clone.stats.luck;
1215  op->speed = op->arch->clone.speed;
1216 
1217  /* OK - we've reset most all the objects attributes to sane values.
1218  * now go through and make adjustments for what the player has equipped.
1219  */
1220 
1221  FOR_INV_PREPARE(op, tmp) {
1222  /* See note in map.c:update_position about making this additive
1223  * since light sources are never applied, need to put check here.
1224  */
1225  if (tmp->glow_radius > op->glow_radius)
1226  op->glow_radius = tmp->glow_radius;
1227 
1228  /* This happens because apply_potion calls change_abil with the potion
1229  * applied so we can tell the player what chagned. But change_abil
1230  * then calls this function.
1231  */
1232  if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION)
1233  continue;
1234 
1235  /* For some things, we don't care what is equipped */
1236  if (tmp->type == SKILL) {
1237  /* Want to take the highest skill here. */
1238  if (IS_MANA_SKILL(tmp->subtype)) {
1239  if (!mana_obj)
1240  mana_obj = tmp;
1241  else if (tmp->level > mana_obj->level)
1242  mana_obj = tmp;
1243  }
1244  if (IS_GRACE_SKILL(tmp->subtype)) {
1245  if (!grace_obj)
1246  grace_obj = tmp;
1247  else if (tmp->level > grace_obj->level)
1248  grace_obj = tmp;
1249  }
1250  }
1251 
1252  /* Container objects are not meant to adjust a players, but other applied
1253  * objects need to make adjustments.
1254  * This block should handle all player specific changes
1255  * The check for Praying is a bit of a hack - god given bonuses are put
1256  * in the praying skill, and the player should always get those.
1257  * It also means we need to put in additional checks for applied below,
1258  * because the skill shouldn't count against body positions being used
1259  * up, etc.
1260  */
1261  if ((QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type != CONTAINER && tmp->type != CLOSE_CON)
1262  || (tmp->type == SKILL && tmp->subtype == SK_PRAYING)) {
1263  if (op->type == PLAYER) {
1264  if (tmp->type == BOW)
1265  op->contr->ranges[range_bow] = tmp;
1266 
1267  if (tmp->type == WAND || tmp->type == ROD)
1268  op->contr->ranges[range_misc] = tmp;
1269 
1270  for (i = 0; i < NUM_STATS; i++) {
1271  int8_t value;
1272 
1273  value = get_attr_value(&(tmp->stats), i);
1274  change_attr_value(&(op->stats), i, value);
1275  if (strcmp(tmp->arch->clone.name, ARCH_DEPLETION) != 0)
1276  change_attr_value(&(op->contr->applied_stats), i, value);
1277  }
1278 
1279  /* For this temporary calculation, allow wider range of stat - if we have
1280  * having that gives +5 and different object that gives -5 and stat
1281  * is maxed, we don't want different results based on order of
1282  * inventory.
1283  */
1284  check_stat_bounds(&(tmp->stats), -settings.max_stat, 2*settings.max_stat);
1285 
1286  /* these are the items that currently can change digestion, regeneration,
1287  * spell point recovery and mana point recovery. Seems sort of an arbitary
1288  * list, but other items store other info into stats array.
1289  */
1290  switch (tmp->type)
1291  {
1292  case WEAPON:
1293  case ARMOUR:
1294  case HELMET:
1295  case SHIELD:
1296  case RING:
1297  case BOOTS:
1298  case GLOVES:
1299  case AMULET:
1300  case GIRDLE:
1301  case BRACERS:
1302  case CLOAK:
1303  case DISEASE:
1304  case FORCE:
1305  case SKILL:
1306  op->contr->digestion += tmp->stats.food;
1307  op->contr->gen_hp += tmp->stats.hp;
1308  op->contr->gen_sp += tmp->stats.sp;
1309  op->contr->gen_grace += tmp->stats.grace;
1310  op->contr->gen_sp_armour += tmp->gen_sp_armour;
1311  /*FALLTHROUGH*/
1312 
1313  /* Bow and skill utils need to update item_power specifically.
1314  * This should fix bug #648
1315  * Daniel Hawkins 2017-08-09
1316  */
1317  case BOW:
1318  case SKILL_TOOL:
1319  op->contr->item_power += tmp->item_power;
1320  break;
1321  }
1322  } /* if this is a player */
1323 
1324  /* Update slots used for items */
1325  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1326  for (i = 0; i < NUM_BODY_LOCATIONS; i++)
1327  op->body_used[i] += tmp->body_info[i];
1328  }
1329 
1330  if (tmp->type == SYMPTOM && tmp->last_sp) {
1331  /* Should take the worst disease of the bunch */
1332  if (((float)tmp->last_sp/100.0) < speed_reduce_from_disease)
1333  speed_reduce_from_disease = (float)tmp->last_sp/100.0;
1334  }
1335 
1336  /* Pos. and neg. protections are counted seperate (-> pro/vuln).
1337  * (Negative protections are calculated extactly like positive.)
1338  * Resistance from potions are treated special as well. If there's
1339  * more than one potion-effect, the bigger prot.-value is taken.
1340  */
1341  if (tmp->type != POTION) {
1342  for (i = 0; i < NROFATTACKS; i++) {
1343  /* Potential for cursed potions, in which case we just can use
1344  * a straight MAX, as potion_resist is initialized to zero.
1345  */
1346  if (tmp->type == POTION_RESIST_EFFECT) {
1347  if (potion_resist[i])
1348  potion_resist[i] = MAX(potion_resist[i], tmp->resist[i]);
1349  else
1350  potion_resist[i] = tmp->resist[i];
1351  } else if (tmp->resist[i] > 0)
1352  prot[i] += ((100-prot[i])*tmp->resist[i])/100;
1353  else if (tmp->resist[i] < 0)
1354  vuln[i] += ((100-vuln[i])*(-tmp->resist[i]))/100;
1355  }
1356  }
1357 
1358  /* There may be other things that should not adjust the attacktype */
1359  if (tmp->type != BOW && tmp->type != SYMPTOM)
1360  op->attacktype |= tmp->attacktype;
1361 
1362  op->path_attuned |= tmp->path_attuned;
1363  op->path_repelled |= tmp->path_repelled;
1364  op->path_denied |= tmp->path_denied;
1365  op->stats.luck += tmp->stats.luck;
1366  op->move_type |= tmp->move_type;
1367 
1368  if (QUERY_FLAG(tmp, FLAG_LIFESAVE))
1369  SET_FLAG(op, FLAG_LIFESAVE);
1370  if (QUERY_FLAG(tmp, FLAG_REFL_SPELL))
1372  if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE))
1374  if (QUERY_FLAG(tmp, FLAG_STEALTH))
1375  SET_FLAG(op, FLAG_STEALTH);
1376  if (QUERY_FLAG(tmp, FLAG_XRAYS))
1377  SET_FLAG(op, FLAG_XRAYS);
1378  if (QUERY_FLAG(tmp, FLAG_BLIND))
1379  SET_FLAG(op, FLAG_BLIND);
1380  if (QUERY_FLAG(tmp, FLAG_SEE_IN_DARK))
1382  if (QUERY_FLAG(tmp, FLAG_PROBE))
1383  SET_FLAG(op, FLAG_PROBE);
1384 
1385  // Items can make the wielder confused.
1386  if (QUERY_FLAG(tmp, FLAG_CONFUSED))
1387  SET_FLAG(op, FLAG_CONFUSED);
1388 
1389  if (QUERY_FLAG(tmp, FLAG_UNDEAD) && !QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
1390  SET_FLAG(op, FLAG_UNDEAD);
1391 
1392  if (QUERY_FLAG(tmp, FLAG_MAKE_INVIS)) {
1394  op->invisible = 1;
1395  }
1396 
1397  if (tmp->stats.exp && tmp->type != SKILL) {
1398  added_speed += (float)tmp->stats.exp/3.0;
1399  }
1400 
1401  switch (tmp->type) {
1402  /* skills modifying the character -b.t. */
1403  /* for all skills and skill granting objects */
1404  case SKILL:
1405  if (!QUERY_FLAG(tmp, FLAG_APPLIED))
1406  break;
1407 
1408  if (IS_COMBAT_SKILL(tmp->subtype))
1409  wc_obj = tmp;
1410 
1411  if (op->chosen_skill) {
1412  LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name);
1413  }
1414  op->chosen_skill = tmp;
1415  if (tmp->stats.dam > 0) { /* skill is a 'weapon' */
1416  if (!QUERY_FLAG(op, FLAG_READY_WEAPON))
1417  weapon_speed = (int)WEAPON_SPEED(tmp);
1418  if (weapon_speed < 0)
1419  weapon_speed = 0;
1420  op->stats.dam += tmp->stats.dam*(1+(op->chosen_skill->level/9));
1421  op->stats.dam += tmp->magic;
1422  }
1423  if (tmp->stats.wc)
1424  wc -= (tmp->stats.wc+tmp->magic);
1425 
1426  if (tmp->slaying != NULL) {
1427  if (op->slaying != NULL)
1428  free_string(op->slaying);
1429  add_refcount(op->slaying = tmp->slaying);
1430  }
1431 
1432  if (tmp->stats.ac)
1433  ac -= (tmp->stats.ac+tmp->magic);
1434  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1435  op->contr->encumbrance += (int)3*tmp->weight/1000;
1436  if (op->type == PLAYER)
1437  op->contr->ranges[range_skill] = op;
1438  break;
1439 
1440  case SKILL_TOOL:
1441  if (op->chosen_skill) {
1442  LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name);
1443  }
1444  op->chosen_skill = tmp;
1445  if (op->type == PLAYER)
1446  op->contr->ranges[range_skill] = op;
1447  break;
1448 
1449  case SHIELD:
1450  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1451  op->contr->encumbrance += (int)tmp->weight/2000;
1452  case RING:
1453  case AMULET:
1454  case GIRDLE:
1455  case HELMET:
1456  case BOOTS:
1457  case GLOVES:
1458  case CLOAK:
1459  if (tmp->stats.wc)
1460  wc -= tmp->stats.wc;
1461  if (tmp->stats.dam)
1462  op->stats.dam += (tmp->stats.dam+tmp->magic);
1463  if (tmp->stats.ac)
1464  ac -= (tmp->stats.ac+tmp->magic);
1465  break;
1466 
1467  case WEAPON:
1468  wc -= tmp->stats.wc;
1469  if (tmp->stats.ac && tmp->stats.ac+tmp->magic > 0)
1470  ac -= tmp->stats.ac+tmp->magic;
1471  op->stats.dam += (tmp->stats.dam+tmp->magic);
1472  weapon_speed = ((int)WEAPON_SPEED(tmp)*2-tmp->magic)/2;
1473  if (weapon_speed < 0)
1474  weapon_speed = 0;
1475  if (tmp->slaying != NULL) {
1476  if (op->slaying != NULL)
1477  free_string(op->slaying);
1478  add_refcount(op->slaying = tmp->slaying);
1479  }
1480  /* If there is desire that two handed weapons should do
1481  * extra strength damage, this is where the code should
1482  * go.
1483  */
1484  op->current_weapon = tmp;
1485  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1486  op->contr->encumbrance += (int)3*tmp->weight/1000;
1487  break;
1488 
1489  case ARMOUR: /* Only the best of these three are used: */
1490  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1491  op->contr->encumbrance += (int)tmp->weight/1000;
1492 
1493  /* ARMOUR falls through to here */
1494 
1495  case BRACERS:
1496  case FORCE:
1497  // Code simplification to reduce branching -- we don't need to sub/add ac and wc all the time.
1498  // Daniel Hawkins 2018-05-28
1499  if (tmp->stats.wc) {
1500  // Since we are alreay here, make sure wc stacking also occurs for serpentman players.
1501  if (tmp->type == BRACERS && op->type == PLAYER && op->arch->name && strcmp(op->arch->name, "serpentman_player") == 0)
1502  {
1503  // Apply ac from bracers directly.
1504  // This also grants the side effect that armor and bracer ac are separate for serpentmen,
1505  // But that should be better than the extra bracers being mostly useless.
1506  wc -= tmp->stats.wc;
1507  }
1508  else if (best_wc < tmp->stats.wc) {
1509  wc += best_wc;
1510  best_wc = tmp->stats.wc;
1511  wc -= tmp->stats.wc;
1512  }
1513  }
1514  if (tmp->stats.ac) {
1515  /*
1516  * If we have a serpentman player, then we do bracers differently
1517  * to allow for both bracers they equip to apply to ac, instead of only the best.
1518  *
1519  * Daniel Hawkins 2018-05-28
1520  */
1521  if (tmp->type == BRACERS && op->type == PLAYER && op->arch->name && strcmp(op->arch->name, "serpentman_player") == 0)
1522  {
1523  // Apply ac from bracers directly.
1524  // This also grants the side effect that armor and bracer ac are separate for serpentmen,
1525  // But that should be better than the extra bracers being mostly useless.
1526  ac -= tmp->stats.ac+tmp->magic;
1527  }
1528  else if (best_ac < tmp->stats.ac+tmp->magic) {
1529  ac += best_ac; /* Remove last bonus */
1530  best_ac = tmp->stats.ac+tmp->magic;
1531  ac -= (tmp->stats.ac+tmp->magic);
1532  }
1533  }
1534  if (tmp->stats.dam && tmp->type == BRACERS)
1535  op->stats.dam += (tmp->stats.dam+tmp->magic);
1536  if (ARMOUR_SPEED(tmp) && ARMOUR_SPEED(tmp)/10.0 < max)
1537  max = ARMOUR_SPEED(tmp)/10.0;
1538  break;
1539  } /* switch tmp->type */
1540  } /* item is equipped */
1541  } FOR_INV_FINISH(); /* for loop of items */
1542 
1543  /* We've gone through all the objects the player has equipped. For many things, we
1544  * have generated intermediate values which we now need to assign.
1545  */
1546 
1547  /* 'total resistance = total protections - total vulnerabilities'.
1548  * If there is an uncursed potion in effect, granting more protection
1549  * than that, we take: 'total resistance = resistance from potion'.
1550  * If there is a cursed (and no uncursed) potion in effect, we take
1551  * 'total resistance = vulnerability from cursed potion'.
1552  */
1553  for (i = 0; i < NROFATTACKS; i++) {
1554  op->resist[i] = prot[i]-vuln[i];
1555  if (potion_resist[i]
1556  && ((potion_resist[i] > op->resist[i]) || (potion_resist[i] < 0)))
1557  op->resist[i] = potion_resist[i];
1558  }
1559 
1560  fix_player(op, &ac, &wc, grace_obj, mana_obj, wc_obj, weapon_speed, added_speed);
1561 
1562  op->speed = op->speed*speed_reduce_from_disease;
1563 
1564  /* Max is determined by armour */
1565  if (op->speed > max)
1566  op->speed = max;
1567 
1568  op->speed += added_speed/10.0;
1569 
1570 
1571  /* Put a lower limit on speed. Note with this speed, you move once every
1572  * 20 ticks or so. This amounts to once every 3 seconds of realtime.
1573  */
1574  if (op->speed < 0.05 && op->type == PLAYER)
1575  op->speed = 0.05;
1576 
1577  /* I want to limit the power of small monsters with big weapons: */
1578  if (op->type != PLAYER
1579  && op->arch != NULL
1580  && op->stats.dam > op->arch->clone.stats.dam*3)
1581  op->stats.dam = op->arch->clone.stats.dam*3;
1582 
1583  /* Prevent overflows of wc - best you can get is ABS(120) - this
1584  * should be more than enough - remember, AC is also in 8 bits,
1585  * so its value is the same.
1586  */
1587  if (wc > 120)
1588  wc = 120;
1589  else if (wc < -120)
1590  wc = -120;
1591  op->stats.wc = wc;
1592 
1593  if (ac > 120)
1594  ac = 120;
1595  else if (ac < -120)
1596  ac = -120;
1597  op->stats.ac = ac;
1598 
1599  /* if for some reason the creature doesn't have any move type,
1600  * give them walking as a default.
1601  * The second case is a special case - to more closely mimic the
1602  * old behaviour - if your flying, your not walking - just
1603  * one or the other.
1604  */
1605  if (op->move_type == 0)
1606  op->move_type = MOVE_WALK;
1607  else if (op->move_type&(MOVE_FLY_LOW|MOVE_FLY_HIGH))
1608  op->move_type &= ~MOVE_WALK;
1609 
1610  object_update_speed(op);
1611 }
1612 
1625 int allowed_class(const object *op) {
1626  return op->stats.Dex > 0
1627  && op->stats.Str > 0
1628  && op->stats.Con > 0
1629  && op->stats.Int > 0
1630  && op->stats.Wis > 0
1631  && op->stats.Pow > 0
1632  && op->stats.Cha > 0;
1633 }
1634 
1652 void set_dragon_name(object *pl, const object *abil, const object *skin) {
1653  int atnr = -1; /* attacknumber of highest level */
1654  int level = 0; /* highest level */
1655  int i;
1656 
1657  /* Perhaps do something more clever? */
1658  if (!abil || !skin)
1659  return;
1660 
1661  /* first, look for the highest level */
1662  for (i = 0; i < NROFATTACKS; i++) {
1663  if (atnr_is_dragon_enabled(i)
1664  && (atnr == -1 || abil->resist[i] > abil->resist[atnr])) {
1665  level = abil->resist[i];
1666  atnr = i;
1667  }
1668  }
1669 
1670  /* now if there are equals at highest level, pick the one with focus,
1671  or else at random */
1672  if (atnr_is_dragon_enabled(abil->stats.exp)
1673  && abil->resist[abil->stats.exp] >= level)
1674  atnr = abil->stats.exp;
1675 
1676  level = (int)(level/5.);
1677 
1678  /* now set the new title */
1679  if (pl->contr != NULL) {
1680  player_set_dragon_title(pl->contr, level, attacks[atnr], skin->resist[atnr]);
1681  }
1682 }
1683 
1692 static void dragon_level_gain(object *who) {
1693  object *abil = NULL; /* pointer to dragon ability force*/
1694  object *skin = NULL; /* pointer to dragon skin force*/
1695 
1696  /* now grab the 'dragon_ability'-forces from the player's inventory */
1697  abil = object_find_by_type_and_arch_name(who, FORCE, "dragon_ability_force");
1698  skin = object_find_by_type_and_arch_name(who, FORCE, "dragon_skin_force");
1699  /* if the force is missing -> bail out */
1700  if (abil == NULL)
1701  return;
1702 
1703  /* The ability_force keeps track of maximum level ever achieved.
1704  * New abilties can only be gained by surpassing this max level
1705  */
1706  if (who->level > abil->level) {
1707  /* increase our focused ability */
1708  int lev = ++(abil->resist[abil->stats.exp]);
1709 
1710  if (lev > 0) {
1711  /* try to hand out a new ability-gift
1712  * if not the right level, this will handout nothing.
1713  */
1714  dragon_ability_gain(who, (int)abil->stats.exp, lev);
1715  }
1716 
1717  if (abil->last_eat > 0 && atnr_is_dragon_enabled(abil->last_eat)) {
1718  /* apply new ability focus */
1720  "Your metabolism now focuses on %s!",
1721  change_resist_msg[abil->last_eat]);
1722 
1723  abil->stats.exp = abil->last_eat;
1724  abil->last_eat = 0;
1725  }
1726 
1727  abil->level = who->level;
1728  }
1729 
1730  /* last but not least, set the new title for the dragon */
1731  set_dragon_name(who, abil, skin);
1732 }
1733 
1752 object *give_skill_by_name(object *op, const char *skill_name) {
1753  object *skill_obj;
1754  archetype *skill_arch;
1755 
1756  skill_arch = get_archetype_by_skill_name(skill_name, SKILL);
1757  if (!skill_arch) {
1758  LOG(llevError, "add_player_exp: couldn't find skill %s\n", skill_name);
1759  return NULL;
1760  }
1761  skill_obj = arch_to_object(skill_arch); /* never returns NULL. */
1762 
1763  /* clear the flag - exp goes into this bucket, but player
1764  * still doesn't know it.
1765  */
1766  CLEAR_FLAG(skill_obj, FLAG_CAN_USE_SKILL);
1767  skill_obj->stats.exp = 0;
1768  skill_obj->level = 1;
1769  object_insert_in_ob(skill_obj, op);
1770  if (op->contr) {
1771  op->contr->last_skill_ob[skill_obj->subtype] = skill_obj;
1772  op->contr->last_skill_exp[skill_obj->subtype] = -1;
1773  }
1774  return skill_obj;
1775 }
1776 
1793 void player_lvl_adj(object *who, object *op) {
1794  char buf[MAX_BUF];
1795 
1796  assert(who);
1797 
1798  if (!op) /* when rolling stats */
1799  op = who;
1800 
1801  if (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1, who->expmul)) {
1802  do{
1803  op->level++;
1804 
1805  if (op != NULL && op == who && op->stats.exp > 1 && is_dragon_pl(who))
1806  dragon_level_gain(who);
1807 
1808  /* Only roll these if it is the player (who) that gained the level */
1809  if (op == who && (who->level < 11) && who->type == PLAYER) {
1810  who->contr->levhp[who->level] = die_roll(2, 4, who, PREFER_HIGH)+1;
1811  who->contr->levsp[who->level] = die_roll(2, 3, who, PREFER_HIGH);
1812  who->contr->levgrace[who->level] = die_roll(2, 2, who, PREFER_HIGH)-1;
1813  }
1814 
1815  fix_object(who); // TODO: Call this only once per function call?
1816  if (op->level > 1) {
1817  if (op->type != PLAYER)
1818  snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.", op->level, op->name);
1819  else
1820  snprintf(buf, sizeof(buf), "You are now level %d.", op->level);
1821 
1823  }
1824  } while (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1, who->expmul)); /* To increase more levels */
1825  } else if (op->level > 1 && op->stats.exp < level_exp(op->level, who->expmul)) {
1826  do{
1827  op->level--;
1828  fix_object(who); // TODO: Call this only once per function call?
1829  if (op->type != PLAYER)
1830  snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.", op->level, op->name);
1831  else
1832  snprintf(buf, sizeof(buf), "You are now level %d.", op->level);
1833 
1835  } while (op->level > 1 && op->stats.exp < level_exp(op->level, who->expmul)); /* To decrease more levels */
1836  }
1837 }
1838 
1848 int64_t level_exp(int level, double expmul) {
1849  if (level > settings.max_level)
1850  return expmul*levels[settings.max_level];
1851  return expmul*levels[level];
1852 }
1853 
1864 void calc_perm_exp(object *op) {
1865  int64_t p_exp_min;
1866 
1867  /* Ensure that our permanent experience minimum is met.
1868  * permenent_exp_ratio is an integer percentage, we divide by 100
1869  * to get the fraction */
1870  p_exp_min = settings.permanent_exp_ratio*op->stats.exp/100;
1871 
1872  if (op->perm_exp < p_exp_min)
1873  op->perm_exp = p_exp_min;
1874 
1875  /* Cap permanent experience. */
1876  if (op->perm_exp < 0)
1877  op->perm_exp = 0;
1878  else if (op->perm_exp > MAX_EXPERIENCE)
1879  op->perm_exp = MAX_EXPERIENCE;
1880 }
1881 
1882 
1897 static void add_player_exp(object *op, int64_t exp, const char *skill_name, int flag) {
1898  object *skill_obj = NULL;
1899  int64_t limit, exp_to_add;
1900  int i;
1901 
1902  /* prevents some forms of abuse. */
1903  if (op->contr->braced)
1904  exp = exp/5;
1905 
1906  /* Try to find the matching skill.
1907  * We do a shortcut/time saving mechanism first - see if it matches
1908  * chosen_skill. This means we don't need to search through
1909  * the players inventory.
1910  */
1911  if (skill_name) {
1912  if (op->chosen_skill
1913  && op->chosen_skill->type == SKILL
1914  && !strcmp(skill_name, op->chosen_skill->skill))
1915  skill_obj = op->chosen_skill;
1916  else {
1917  for (i = 0; i < NUM_SKILLS; i++)
1918  if (op->contr->last_skill_ob[i]
1919  && !strcmp(op->contr->last_skill_ob[i]->skill, skill_name)) {
1920  skill_obj = op->contr->last_skill_ob[i];
1921  break;
1922  }
1923 
1924  /* Player doesn't have the skill. Check to see what to do, and give
1925  * it to the player if necessary
1926  */
1927  if (!skill_obj) {
1928  if (flag == SK_EXP_NONE)
1929  return;
1930  else if (flag == SK_EXP_ADD_SKILL)
1931  skill_obj = give_skill_by_name(op, skill_name);
1932  }
1933  }
1934  }
1935 
1936  /* Basically, you can never gain more experience in one shot
1937  * than half what you need to gain for next level.
1938  */
1939  exp_to_add = exp;
1940  /*
1941  * Make sure we aren't trying to go backwards when we hit maximum level,
1942  * but make sure we can still add to our permanent experience.
1943  *
1944  * -- Daniel Hawkins 2015-05-24
1945  */
1946  if (op->level == settings.max_level)
1947  limit = levels[op->level] / 2;
1948  else
1949  limit = (levels[op->level+1]-levels[op->level])/2;
1950 
1951  if (exp_to_add > limit)
1952  exp_to_add = limit;
1953 
1954  ADD_EXP(op->stats.exp, (float)exp_to_add*(skill_obj ? skill_obj->expmul : 1));
1956  ADD_EXP(op->perm_exp, (float)exp_to_add*PERM_EXP_GAIN_RATIO*(skill_obj ? skill_obj->expmul : 1));
1957  calc_perm_exp(op);
1958  }
1959 
1960  player_lvl_adj(op, NULL);
1961  if (skill_obj) {
1962  exp_to_add = exp;
1963  /*
1964  * Make sure we aren't trying to go backwards when we hit maximum level,
1965  * but make sure we can still add to our permanent experience.
1966  *
1967  * -- Daniel Hawkins 2015-05-24
1968  */
1969  if (skill_obj->level == settings.max_level)
1970  limit = levels[skill_obj->level] / 2;
1971  else
1972  limit = (levels[skill_obj->level+1]-levels[skill_obj->level])/2;
1973 
1974  if (exp_to_add > limit)
1975  exp_to_add = limit;
1976  ADD_EXP(skill_obj->stats.exp, exp_to_add);
1978  skill_obj->perm_exp += exp_to_add*PERM_EXP_GAIN_RATIO;
1979  calc_perm_exp(skill_obj);
1980  }
1981  player_lvl_adj(op, skill_obj);
1982  }
1983 }
1984 
2000 int64_t check_exp_loss(const object *op, int64_t exp) {
2001  int64_t del_exp;
2002 
2003  if (exp > op->stats.exp)
2004  exp = op->stats.exp;
2006  del_exp = (op->stats.exp-op->perm_exp)*PERM_EXP_MAX_LOSS_RATIO;
2007  if (del_exp < 0)
2008  del_exp = 0;
2009  if (exp > del_exp)
2010  exp = del_exp;
2011  }
2012  return exp;
2013 }
2014 
2025 int64_t check_exp_adjust(const object *op, int64_t exp) {
2026  if (exp < 0)
2027  return check_exp_loss(op, exp);
2028  else
2029  return MIN(exp, MAX_EXPERIENCE-op->stats.exp);
2030 }
2031 
2054 static void subtract_player_exp(object *op, int64_t exp, const char *skill, int flag) {
2055  float fraction = (float)exp/(float)op->stats.exp;
2056  int64_t del_exp;
2057 
2058  FOR_INV_PREPARE(op, tmp)
2059  if (tmp->type == SKILL && tmp->stats.exp) {
2060  if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp(tmp->skill, skill)) {
2061  del_exp = check_exp_loss(tmp, exp);
2062  tmp->stats.exp -= del_exp;
2063  player_lvl_adj(op, tmp);
2064  } else if (flag != SK_SUBTRACT_SKILL_EXP) {
2065  /* only want to process other skills if we are not trying
2066  * to match a specific skill.
2067  */
2068  del_exp = check_exp_loss(tmp, tmp->stats.exp*fraction);
2069  tmp->stats.exp -= del_exp;
2070  player_lvl_adj(op, tmp);
2071  }
2072  }
2073  FOR_INV_FINISH();
2074  if (flag != SK_SUBTRACT_SKILL_EXP) {
2075  del_exp = check_exp_loss(op, exp);
2076  op->stats.exp -= del_exp;
2077  player_lvl_adj(op, NULL);
2078  }
2079 }
2080 
2102 void change_exp(object *op, int64_t exp, const char *skill_name, int flag) {
2103  /* safety */
2104  if (!op) {
2105  LOG(llevError, "change_exp() called for null object!\n");
2106  return;
2107  }
2108 
2109  /* if no change in exp, just return - most of the below code
2110  * won't do anything if the value is 0 anyways.
2111  */
2112  if (exp == 0)
2113  return;
2114 
2115  /* Monsters are easy - we just adjust their exp - we
2116  * don't adjust level, since in most cases it is unrelated to
2117  * the exp they have - the monsters exp represents what its
2118  * worth.
2119  */
2120  if (op->type != PLAYER) {
2121  /* Sanity check */
2122  if (!QUERY_FLAG(op, FLAG_ALIVE))
2123  return;
2124 
2125  /* reset exp to max allowed value. We subtract from
2126  * MAX_EXPERIENCE to prevent overflows. If the player somehow has
2127  * more than max exp, just return.
2128  */
2129  if (exp > 0 && (op->stats.exp > (MAX_EXPERIENCE-exp))) {
2130  exp = MAX_EXPERIENCE-op->stats.exp;
2131  if (exp < 0)
2132  return;
2133  }
2134 
2135  op->stats.exp += exp;
2136  } else { /* Players only */
2137  if (exp > 0)
2138  add_player_exp(op, exp, skill_name, flag);
2139  else
2140  subtract_player_exp(op, FABS(exp), skill_name, flag);
2141  }
2142 }
2143 
2152 void apply_death_exp_penalty(object *op) {
2153  int64_t loss;
2154  int64_t percentage_loss; /* defined by the setting 'death_penalty_percent' */
2155  int64_t level_loss; /* defined by the setting 'death_penalty_levels */
2156 
2157  FOR_INV_PREPARE(op, tmp)
2158  if (tmp->type == SKILL && tmp->stats.exp) {
2159  percentage_loss = tmp->stats.exp*settings.death_penalty_ratio/100;
2160  level_loss = tmp->stats.exp-levels[MAX(0, tmp->level-settings.death_penalty_level)];
2161 
2162  /* With the revised exp system, you can get cases where
2163  * losing several levels would still require that you have more
2164  * exp than you currently have - this is true if the levels
2165  * tables is a lot harder.
2166  */
2167  if (level_loss < 0)
2168  level_loss = 0;
2169 
2170  loss = check_exp_loss(tmp, MIN(level_loss, percentage_loss));
2171 
2172  tmp->stats.exp -= loss;
2173  player_lvl_adj(op, tmp);
2174  }
2175  FOR_INV_FINISH();
2176 
2177  percentage_loss = op->stats.exp*settings.death_penalty_ratio/100;
2178  level_loss = op->stats.exp-levels[MAX(0, op->level-settings.death_penalty_level)];
2179  if (level_loss < 0)
2180  level_loss = 0;
2181  loss = check_exp_loss(op, MIN(level_loss, percentage_loss));
2182 
2183  op->stats.exp -= loss;
2184  player_lvl_adj(op, NULL);
2185 }
2186 
2201 int did_make_save(const object *op, int level, int bonus) {
2202  if (level > MAX_SAVE_LEVEL)
2203  level = MAX_SAVE_LEVEL;
2204 
2205  if ((random_roll(1, 20, op, PREFER_HIGH)+bonus) < savethrow[level])
2206  return 0;
2207  return 1;
2208 }
2209 
2231 void share_exp(object *op, int64_t exp, const char *skill, int flag) {
2232  int shares = 0, count = 0;
2233  player *pl;
2234  partylist *party;
2235 
2236  if (op->type != PLAYER || op->contr->party == NULL) {
2237  change_exp(op, exp, skill, 0);
2238  return;
2239  }
2240 
2241  party = op->contr->party;
2242 
2243  for (pl = first_player; pl != NULL; pl = pl->next) {
2244  if (party && pl->ob->contr->party == party && on_same_map(pl->ob, op)) {
2245  count++;
2246  shares += (pl->ob->level+4);
2247  }
2248  }
2249 
2250  assert(shares > 0);
2251 
2252  if (count == 1 || shares > exp)
2253  change_exp(op, exp, skill, flag);
2254  else {
2255  int64_t share = exp/shares, given = 0, nexp;
2256  for (pl = first_player; pl != NULL; pl = pl->next) {
2257  if (party && pl->ob->contr->party == party && on_same_map(pl->ob, op)) {
2258  nexp = (pl->ob->level+4)*share;
2259  change_exp(pl->ob, nexp, skill, SK_EXP_TOTAL);
2260  given += nexp;
2261  }
2262  }
2263  exp -= given;
2264  /* give any remainder to the player */
2265  change_exp(op, exp, skill, flag);
2266  }
2267 }
2268 
2269 int get_cha_bonus(int stat) {
2271 }
2272 
2273 int get_dex_bonus(int stat) {
2275 }
2276 
2277 int get_thaco_bonus(int stat) {
2279 }
2280 
2283 }
2284 
2285 int get_learn_spell(int stat) {
2287 }
2288 
2289 int get_cleric_chance(int stat) {
2291 }
2292 
2293 int get_turn_bonus(int stat) {
2295 }
2296 
2297 int get_dam_bonus(int stat) {
2299 }
2300 
2301 float get_speed_bonus(int stat) {
2303 }
2304 
2305 int get_fear_bonus(int stat) {
2307 }
2308 
2309 static float get_con_bonus(int stat) {
2311 }
2312 
2313 static float get_sp_bonus(int stat) {
2315 }
2316 
2317 static float get_grace_bonus(int stat) {
2319 }
2320 
2329 static size_t get_index(int stat, size_t max_index) {
2330  size_t index;
2331 
2332  if (stat < 0) {
2333  return 0;
2334  }
2335 
2336  index = (size_t)stat;
2337  return MIN(index, max_index);
2338 }
2339 
2357 static int load_table_int(int **bonuses, FILE *fp, char *bonus_name)
2358 {
2359  char buf[MAX_BUF], *cp;
2360  int on_stat = 0, tmp_bonus;
2361 
2362  *bonuses = calloc(settings.max_stat+1, sizeof(int));
2363 
2364  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2365  if (buf[0] == '#')
2366  continue;
2367 
2368  /* Skip over empty lines */
2369  if (buf[0] == '\n')
2370  continue;
2371 
2372  /* Do not care about opening brace */
2373  if (buf[0] == '{')
2374  continue;
2375 
2376  if (buf[0] == '}') {
2377  if ((on_stat-1) != settings.max_stat) {
2378  LOG(llevError,"Number of bonus does not match max stat (%d!=%d, bonus=%s)\n",
2379  on_stat, settings.max_stat, bonus_name);
2380  return 1;
2381  }
2382  else return 0;
2383  }
2384 
2385  /* If not any of the above values, must be the stat table,
2386  * or so we hope.
2387  */
2388  cp = buf;
2389  while (*cp != 0) {
2390  /* Skip over any non numbers */
2391  while (!isdigit(*cp) && *cp!='.' && *cp!='-' && *cp!='+' && *cp != 0)
2392  cp++;
2393 
2394  if (*cp == 0) break;
2395 
2396  tmp_bonus = atoi(cp);
2397 
2398  if (on_stat > settings.max_stat) {
2399  LOG(llevError,"Number of bonus entries exceed max stat (line=%s, bonus=%s)\n",
2400  buf, bonus_name);
2401  return 1;
2402  }
2403  (*bonuses)[on_stat] = tmp_bonus;
2404  on_stat++;
2405 
2406  /* Skip over any digits, as that is the number we just processed */
2407  while ((isdigit(*cp) || *cp=='-' || *cp=='+') && *cp != 0)
2408  cp++;
2409  }
2410  }
2411  /* This should never happen - we should always get the closing brace */
2412  LOG(llevError,"Reached end of file without getting close brace? bonus=%s\n", bonus_name);
2413  return 1;
2414 }
2415 
2433 static int load_table_float(float **bonuses, FILE *fp, char *bonus_name)
2434 {
2435  char buf[MAX_BUF], *cp;
2436  int on_stat = 0;
2437  float tmp_bonus;
2438 
2439  *bonuses = calloc(settings.max_stat+1, sizeof(float));
2440 
2441  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2442  if (buf[0] == '#')
2443  continue;
2444 
2445  /* Skip over empty lines */
2446  if (buf[0] == '\n')
2447  continue;
2448 
2449  /* Do not care about opening brace */
2450  if (buf[0] == '{')
2451  continue;
2452 
2453  if (buf[0] == '}') {
2454  if ((on_stat-1) != settings.max_stat) {
2455  LOG(llevError,"Number of bonus does not match max stat (%d!=%d, bonus=%s)\n",
2456  on_stat, settings.max_stat, bonus_name);
2457  return 1;
2458  }
2459  else return 0;
2460  }
2461 
2462  /* If not any of the above values, must be the stat table,
2463  * or so we hope.
2464  */
2465  cp = buf;
2466  while (*cp != 0) {
2467  /* Skip over any non numbers */
2468  while (!isdigit(*cp) && *cp!='.' && *cp!='-' && *cp!='+' && *cp != 0)
2469  cp++;
2470 
2471  if (*cp == 0) break;
2472 
2473  tmp_bonus = atof(cp);
2474 
2475  if (on_stat > settings.max_stat) {
2476  LOG(llevError,"Number of bonus entries exceed max stat (line=%s, bonus=%s)\n",
2477  buf, bonus_name);
2478  return 1;
2479  }
2480  (*bonuses)[on_stat] = tmp_bonus;
2481  on_stat++;
2482 
2483  /* Skip over any digits, as that is the number we just processed
2484  * since this is floats, also skip over any dots.
2485  */
2486  while ((isdigit(*cp) || *cp=='-' || *cp=='+' || *cp=='.') && *cp != 0)
2487  cp++;
2488  }
2489  }
2490  /* This should never happen - we should always get the closing brace */
2491  LOG(llevError,"Reached end of file without getting close brace? bonus=%s\n", bonus_name);
2492  return 1;
2493 }
2494 
2495 
2513 void init_stats(int reload) {
2514  char buf[MAX_BUF], *cp;
2515  int error=0, i, oldmax = settings.max_stat;
2516  FILE *fp;
2517  float *new_float_bonuses[NUM_FLOAT_BONUSES];
2518  int *new_int_bonuses[NUM_INT_BONUSES];
2519 
2520  snprintf(buf, sizeof(buf), "%s/stat_bonus", settings.confdir);
2521 
2522  memset(new_int_bonuses, 0, NUM_INT_BONUSES * sizeof(int));
2523  memset(new_float_bonuses, 0, NUM_FLOAT_BONUSES * sizeof(float));
2524 
2525  if ((fp = fopen(buf, "r")) == NULL) {
2526  LOG(llevError, "Fatal error: could not open experience table (%s)\n", buf);
2527  if (reload) return;
2528  else exit(1);
2529  }
2530  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2531  if (buf[0] == '#')
2532  continue;
2533 
2534  /* eliminate newline */
2535  if ((cp = strrchr(buf, '\n')) != NULL)
2536  *cp = '\0';
2537 
2538  /* Skip over empty lines */
2539  if (buf[0] == 0)
2540  continue;
2541 
2542  /* Skip over leading spaces */
2543  cp = buf;
2544  while (isspace(*cp) && *cp != 0)
2545  cp++;
2546 
2547  if (!strncasecmp(cp, "max_stat", 8)) {
2548  int newmax = atoi(cp+8);
2549 
2550  /* newmax must be at least MIN_STAT and we do not currently
2551  * cupport decrease max stat on the fly - why this might be possible,
2552  * bounds checking for all objects would be needed, potentionally resetting
2553  * them.
2554  * If this is a reload, then on error, we just return without doing work.
2555  * If this is initial load, having an invalid stat range is an error, so
2556  * exit the program.
2557  */
2558  if (newmax < MIN_STAT || newmax < settings.max_stat) {
2559  LOG(llevError, "Got invalid max_stat (%d) from stat_bonus file\n", newmax);
2560  fclose(fp);
2561  if (reload) return;
2562  else exit(1);
2563  }
2564  settings.max_stat = newmax;
2565  continue;
2566  }
2567  /* max_stat needs to be set before any of the bonus values - we
2568  * need to know how large to make the array.
2569  */
2570  if (settings.max_stat == 0) {
2571  LOG(llevError, "Got bonus line or otherwise unknown value before max stat! (%s)\n",
2572  buf);
2573  fclose(fp);
2574 
2575  if (reload) {
2576  return;
2577  } else {
2578  exit(1);
2579  }
2580  }
2581 
2582  for (i=0; i<NUM_INT_BONUSES; i++) {
2583  if (!strncasecmp(cp, int_bonus_names[i], strlen(int_bonus_names[i]))) {
2584  error = load_table_int(&new_int_bonuses[i], fp, cp);
2585  break;
2586  }
2587  }
2588  /* If we did not find a match in the int bonuses, check the
2589  * float bonuses now.
2590  */
2591  if (i == NUM_INT_BONUSES) {
2592  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2593  if (!strncasecmp(cp, float_bonus_names[i], strlen(float_bonus_names[i]))) {
2594  error = load_table_float(&new_float_bonuses[i], fp, cp);
2595  break;
2596  }
2597  }
2598  /* This may not actually be a critical error */
2599  if (i == NUM_FLOAT_BONUSES) {
2600  LOG(llevError,"Unknown line in stat_bonus file: %s\n", buf);
2601  }
2602  }
2603  if (error) break;
2604  }
2605  fclose(fp);
2606 
2607  /* Make sure that we have load tables for all the bonuses.
2608  * This is critical on initial load, but on reloads, it enusres that
2609  * all the bonus data matches.
2610  */
2611  for (i=0; i<NUM_INT_BONUSES; i++) {
2612  if (!new_int_bonuses[i]) {
2613  LOG(llevError,"No bonus loaded for %s\n", int_bonus_names[i]);
2614  error=2;
2615  }
2616  }
2617 
2618  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2619  if (!new_float_bonuses[i]) {
2620  LOG(llevError,"No bonus loaded for %s\n", float_bonus_names[i]);
2621  error=2;
2622  }
2623  }
2624 
2625  /* If we got an error, we just free up the data we read in and return/exit.
2626  * if no error, we make the tables we just read in into the default
2627  * tables.
2628  */
2629  if (error) {
2630  if (error==1)
2631  LOG(llevError,"Got error reading stat_bonus: %s\n", buf);
2632 
2633  if (reload) {
2634  for (i=0; i<NUM_INT_BONUSES; i++)
2635  if (new_int_bonuses[i]) FREE_AND_CLEAR(new_int_bonuses[i]);
2636  for (i=0; i<NUM_FLOAT_BONUSES; i++)
2637  if (new_float_bonuses[i]) FREE_AND_CLEAR(new_float_bonuses[i]);
2638  settings.max_stat = oldmax;
2639  } else {
2640  exit(1);
2641  }
2642  } else {
2643  /* Everything check out - now copy the data into
2644  * the live arrays.
2645  */
2646  for (i=0; i<NUM_INT_BONUSES; i++) {
2647  free(int_bonuses[i]);
2648  int_bonuses[i] = new_int_bonuses[i];
2649  new_int_bonuses[i] = NULL;
2650  }
2651 
2652  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2653  free(float_bonuses[i]);
2654  float_bonuses[i] = new_float_bonuses[i];
2655  new_float_bonuses[i] = NULL;
2656  }
2657  }
2658 }
#define INT_THAC0_BONUS
Definition: living.c:65
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:315
static float get_con_bonus(int stat)
Definition: living.c:2309
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:218
int8_t Int
Definition: living.h:36
#define NUM_BODY_LOCATIONS
Definition: object.h:13
Definition: player.h:92
#define FLAG_SEE_IN_DARK
Definition: define.h:338
archetype * find_archetype(const char *name)
Definition: arch.c:692
#define WEAPON_SPEED(xyz)
Definition: define.h:484
int8_t ac
Definition: living.h:38
#define MOVE_WALK
Definition: define.h:407
int64_t check_exp_adjust(const object *op, int64_t exp)
Definition: living.c:2025
MoveType move_type
Definition: object.h:424
#define FLOAT_CON_BONUS
Definition: living.c:85
void drain_stat(object *op)
Definition: living.c:712
int16_t gen_hp
Definition: player.h:113
Definition: object.h:185
uint8_t max_stat
Definition: global.h:321
int64_t level_exp(int level, double expmul)
Definition: living.c:1848
const char * race
Definition: object.h:318
const char *const restore_msg[NUM_STATS]
Definition: living.c:150
uint8_t spell_encumbrance
Definition: global.h:264
#define SET_FLAG(xyz, p)
Definition: define.h:223
sstring add_refcount(sstring str)
Definition: shstr.c:210
#define FABS(x)
Definition: define.h:22
uint32_t braced
Definition: player.h:124
static float get_grace_bonus(int stat)
Definition: living.c:2317
int get_dam_bonus(int stat)
Definition: living.c:2297
#define INT_TURN_BONUS
Definition: living.c:59
#define MSG_TYPE_ATTRIBUTE_ATTACKTYPE_LOSS
Definition: newclient.h:533
uint8_t death_penalty_level
Definition: global.h:256
#define FLAG_USE_ARMOUR
Definition: define.h:296
Definition: object.h:204
#define ADD_EXP(exptotal, exp)
Definition: living.c:42
Definition: living.h:14
int16_t gen_grace
Definition: player.h:116
#define PERM_EXP_MAX_LOSS_RATIO
Definition: config.h:289
int16_t max_level
Definition: global.h:299
int16_t maxgrace
Definition: living.h:45
void free_string(sstring str)
Definition: shstr.c:280
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4264
#define NDI_BLUE
Definition: newclient.h:226
int8_t levsp[11]
Definition: player.h:168
object clone
Definition: object.h:470
int16_t invisible
Definition: object.h:360
Definition: living.h:17
Definition: object.h:119
Definition: object.h:136
const char * slaying
Definition: object.h:319
#define FLAG_CONFUSED
Definition: define.h:312
#define FLOAT_DEX_BONUS
Definition: living.c:86
#define MSG_TYPE_ATTRIBUTE_ATTACKTYPE_GAIN
Definition: newclient.h:532
#define FLAG_STEALTH
Definition: define.h:313
int object_check_move_on(object *op, object *originator)
Definition: object.c:2850
object * ranges[range_size]
Definition: player.h:103
uint8_t subtype
Definition: object.h:339
Definition: object.h:109
int64_t exp
Definition: living.h:47
void change_luck(object *op, int value)
Definition: living.c:792
double expmul
Definition: object.h:395
#define TRUE
Definition: compat.h:10
Definition: object.h:223
object * arch_present_in_ob(const archetype *at, const object *op)
Definition: object.c:3076
Definition: living.h:35
int64_t check_exp_loss(const object *op, int64_t exp)
Definition: living.c:2000
Definition: object.h:139
int8_t get_attr_value(const living *stats, int attr)
Definition: living.c:313
uint32_t path_attuned
Definition: object.h:343
#define MAX(x, y)
Definition: compat.h:20
uint8_t death_penalty_ratio
Definition: global.h:255
int16_t sp
Definition: living.h:42
uint32_t get_weight_limit(int stat)
Definition: living.c:2281
#define MAX_EXPERIENCE
Definition: living.c:99
uint32_t path_repelled
Definition: object.h:344
Definition: object.h:212
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:310
const char *const drain_msg[NUM_STATS]
Definition: living.c:139
Definition: object.h:465
Definition: object.h:220
int16_t maxsp
Definition: living.h:43
#define MIN(x, y)
Definition: compat.h:17
int8_t Con
Definition: living.h:36
int16_t hp
Definition: living.h:40
partylist * party
Definition: player.h:186
#define IS_MANA_SKILL(num)
Definition: skills.h:107
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1752
#define MIN_STAT
Definition: define.h:33
#define FREE_PLAYER_LOAD_PERCENT
Definition: config.h:98
#define FLAG_UNDEAD
Definition: define.h:270
static void fix_player(object *op, int *ac, int *wc, const object *grace_obj, const object *mana_obj, const object *wc_obj, int weapon_speed, float added_speed)
Definition: living.c:893
int get_cha_bonus(int stat)
Definition: living.c:2269
struct obj * chosen_skill
Definition: object.h:386
void object_free_drop_inventory(object *ob)
Definition: object.c:1389
int change_abil(object *op, object *tmp)
Definition: living.c:394
#define NDI_RED
Definition: newclient.h:224
int16_t maxhp
Definition: living.h:41
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END
Definition: newclient.h:561
living applied_stats
Definition: player.h:152
#define NUM_FLOAT_BONUSES
Definition: living.c:89
void player_lvl_adj(object *who, object *op)
Definition: living.c:1793
Definition: object.h:118
uint32_t path_denied
Definition: object.h:345
#define MSG_TYPE_ATTRIBUTE_MOVE
Definition: newclient.h:554
#define FLAG_ALIVE
Definition: define.h:230
#define FLAG_REFL_SPELL
Definition: define.h:275
const char *const gain_msg[NUM_STATS]
Definition: living.c:161
static const int savethrow[MAX_SAVE_LEVEL+1]
Definition: living.c:113
int get_turn_bonus(int stat)
Definition: living.c:2293
static int load_table_int(int **bonuses, FILE *fp, char *bonus_name)
Definition: living.c:2357
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2705
#define SK_EXP_TOTAL
Definition: skills.h:80
int16_t gen_sp
Definition: player.h:114
#define MOVE_SWIM
Definition: define.h:411
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Definition: newclient.h:542
uint8_t search_items
Definition: global.h:263
static void subtract_player_exp(object *op, int64_t exp, const char *skill, int flag)
Definition: living.c:2054
#define FLAG_CAN_USE_SKILL
Definition: define.h:322
#define MOVE_FLY_LOW
Definition: define.h:408
const char *const statname[NUM_STATS]
Definition: living.c:183
int8_t Wis
Definition: living.h:36
int is_dragon_pl(const object *op)
Definition: player.c:114
char search_str[MAX_BUF]
Definition: player.h:192
#define snprintf
Definition: win32.h:46
#define ARCH_DEPLETION
Definition: object.h:577
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.c:724
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:380
#define FOR_INV_FINISH()
Definition: define.h:714
int16_t dam
Definition: living.h:46
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.c:122
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:264
#define FLAG_PROBE
Definition: define.h:257
int32_t carrying
Definition: object.h:367
archetype * get_archetype_by_skill_name(const char *skill, int type)
Definition: arch.c:107
int get_fear_bonus(int stat)
Definition: living.c:2305
const char * name
Definition: object.h:311
living orig_stats
Definition: player.h:148
int64_t perm_exp
Definition: object.h:369
#define ARMOUR_SPEED(xyz)
Definition: define.h:482
int allowed_class(const object *op)
Definition: living.c:1625
#define INT_WEIGHT_LIMIT
Definition: living.c:66
static float get_sp_bonus(int stat)
Definition: living.c:2313
struct obj * current_weapon
Definition: object.h:370
#define PERM_EXP_GAIN_RATIO
Definition: config.h:288
#define MAX_PLAYER_SPEED
Definition: config.h:97
int8_t Cha
Definition: living.h:36
EXTERN const char *const change_resist_msg[NROFATTACKS]
Definition: attack.h:135
Definition: object.h:111
struct pl * contr
Definition: object.h:276
void add_statbonus(object *op)
Definition: living.c:864
#define MIN_PLAYER_SPEED
Definition: config.h:96
#define FLAG_XRAYS
Definition: define.h:301
#define INT_DAM_BONUS
Definition: living.c:64
#define MSG_TYPE_ATTRIBUTE_LEVEL_GAIN
Definition: newclient.h:564
static const char * float_bonus_names[NUM_FLOAT_BONUSES]
Definition: living.c:91
#define ATNR_PHYSICAL
Definition: attack.h:49
static void dragon_level_gain(object *who)
Definition: living.c:1692
static float * float_bonuses[NUM_FLOAT_BONUSES]
Definition: living.c:90
int8_t luck
Definition: living.h:39
#define AT_PHYSICAL
Definition: attack.h:76
float speed
Definition: object.h:328
int on_same_map(const object *op1, const object *op2)
Definition: map.c:2650
#define NUM_INT_BONUSES
Definition: living.c:67
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define BASE_WEAPON_SPEED
Definition: config.h:108
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
#define INT_LEARN_SPELL
Definition: living.c:61
static int * int_bonuses[NUM_INT_BONUSES]
Definition: living.c:69
#define FLAG_WIZ
Definition: define.h:231
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:2102
#define INT_FEAR_BONUS
Definition: living.c:58
#define MAX_BUF
Definition: define.h:35
#define FLOAT_SP_BONUS
Definition: living.c:87
int strncasecmp(const char *s1, const char *s2, int n)
Definition: porting.c:224
int16_t encumbrance
Definition: player.h:179
int8_t levhp[11]
Definition: player.h:167
#define MSG_TYPE_ATTRIBUTE_STAT_LOSS
Definition: newclient.h:563
const char * skill
Definition: object.h:321
int32_t last_eat
Definition: object.h:356
#define NUM_SKILLS
Definition: skills.h:71
int8_t wc
Definition: living.h:37
const char * confdir
Definition: global.h:241
signed __int64 int64_t
Definition: win32.h:168
#define FLAG_READY_WEAPON
Definition: define.h:335
#define DIFF_MSG(flag, subtype1, subtype2, msg1, msg2)
Definition: living.c:369
int8_t Str
Definition: living.h:36
void calc_perm_exp(object *op)
Definition: living.c:1864
int16_t resist[NROFATTACKS]
Definition: object.h:341
object * ob
Definition: player.h:158
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START
Definition: newclient.h:566
const char *const lose_msg[NUM_STATS]
Definition: living.c:172
uint8_t permanent_exp_ratio
Definition: global.h:254
#define INT_DEX_BONUS
Definition: living.c:63
unsigned int uint32_t
Definition: win32.h:162
Definition: object.h:107
static void add_player_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:1897
Definition: object.h:135
#define INT_CLERIC_CHANCE
Definition: living.c:60
int8_t body_info[NUM_BODY_LOCATIONS]
Definition: object.h:372
#define PREFER_HIGH
Definition: define.h:601
uint32_t attacktype
Definition: object.h:342
int64_t * levels
Definition: exp.c:26
#define FLAG_BLIND
Definition: define.h:337
#define RANDOM()
Definition: define.h:681
int16_t grace
Definition: living.h:44
signed char int8_t
Definition: win32.h:158
Definition: object.h:143
#define INT_CHA_BONUS
Definition: living.c:62
void player_set_dragon_title(struct pl *pl, int level, const char *attack, int skin_resist)
Definition: player.c:194
living stats
Definition: object.h:368
#define MSG_TYPE_ATTRIBUTE_STAT_GAIN
Definition: newclient.h:562
int8_t Dex
Definition: living.h:36
struct archt * arch
Definition: object.h:412
#define MSG_TYPE_ATTRIBUTE_LEVEL_LOSS
Definition: newclient.h:565
void init_stats(int reload)
Definition: living.c:2513
static size_t get_index(int stat, size_t max_index)
Definition: living.c:2329
uint8_t type
Definition: object.h:338
uint32_t do_los
Definition: player.h:126
struct Settings settings
Definition: init.c:40
static int load_table_float(float **bonuses, FILE *fp, char *bonus_name)
Definition: living.c:2433
int remove_depletion(object *op, int level)
Definition: living.c:751
#define SK_EXP_ADD_SKILL
Definition: skills.h:79
#define SK_EXP_NONE
Definition: skills.h:81
#define FLAG_APPLIED
Definition: define.h:235
#define NROFATTACKS
Definition: attack.h:17
#define FLAG_LIFESAVE
Definition: define.h:306
int64_t last_skill_exp[NUM_SKILLS]
Definition: player.h:138
#define FLAG_MAKE_INVIS
Definition: define.h:329
void apply_death_exp_penalty(object *op)
Definition: living.c:2152
int16_t gen_sp_armour
Definition: player.h:115
#define MAX_SAVE_LEVEL
Definition: living.c:103
sstring add_string(const char *str)
Definition: shstr.c:124
EXTERN player * first_player
Definition: global.h:117
struct pl * next
Definition: player.h:93
int8_t glow_radius
Definition: object.h:364
int8_t Pow
Definition: living.h:36
#define NDI_UNIQUE
Definition: newclient.h:245
int get_cleric_chance(int stat)
Definition: living.c:2289
Definition: object.h:213
#define MOVE_FLY_HIGH
Definition: define.h:409
void dragon_ability_gain(object *who, int atnr, int level)
Definition: main.c:345
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
int16_t item_power
Definition: player.h:117
int get_learn_spell(int stat)
Definition: living.c:2285
int did_make_save(const object *op, int level, int bonus)
Definition: living.c:2201
object * last_skill_ob[NUM_SKILLS]
Definition: player.h:137
#define MSG_TYPE_ATTRIBUTE_RACE
Definition: newclient.h:557
void set_dragon_name(object *pl, const object *abil, const object *skin)
Definition: living.c:1652
#define MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS
Definition: newclient.h:543
#define SK_SUBTRACT_SKILL_EXP
Definition: skills.h:82
void share_exp(object *op, int64_t exp, const char *skill, int flag)
Definition: living.c:2231
void remove_statbonus(object *op)
Definition: living.c:841
void esrv_update_spells(player *pl)
Definition: main.c:358
#define IS_COMBAT_SKILL(num)
Definition: skills.h:92
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
#define AT_CONFUSION
Definition: attack.h:81
#define FREE_AND_CLEAR(xyz)
Definition: global.h:201
int8_t levgrace[11]
Definition: player.h:169
object * object_find_by_type_and_arch_name(const object *who, int type, const char *name)
Definition: object.c:4186
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
Definition: newclient.h:569
Definition: object.h:120
const char *const short_stat_name[NUM_STATS]
Definition: living.c:194
#define FLAG_REFL_MISSILE
Definition: define.h:273
int get_dex_bonus(int stat)
Definition: living.c:2273
int16_t level
Definition: object.h:351
#define FLOAT_GRACE_BONUS
Definition: living.c:88
void fix_object(object *op)
Definition: living.c:1119
float weapon_speed
Definition: object.h:330
#define IS_GRACE_SKILL(num)
Definition: skills.h:121
float get_speed_bonus(int stat)
Definition: living.c:2301
static const char * int_bonus_names[NUM_INT_BONUSES]
Definition: living.c:75
object * arch_to_object(archetype *at)
Definition: arch.c:569
void object_update_speed(object *op)
Definition: object.c:1150
int16_t digestion
Definition: player.h:112
const char * name
Definition: object.h:466
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:707
void object_remove(object *op)
Definition: object.c:1669
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Definition: newclient.h:558
const char *const attacks[NROFATTACKS]
Definition: living.c:129
int8_t body_used[NUM_BODY_LOCATIONS]
Definition: object.h:373
int atnr_is_dragon_enabled(int attacknr)
Definition: player.c:95
int32_t food
Definition: living.h:48
Definition: object.h:224
int get_thaco_bonus(int stat)
Definition: living.c:2277
void check_stat_bounds(living *stats, int8_t min_stat, int8_t max_stat)
Definition: living.c:354