Crossfire Server, Trunk  R20608
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 static const char *const drain_msg[NUM_STATS] = {
140  "Oh no! You are weakened!",
141  "You're feeling clumsy!",
142  "Your health deteriorates!",
143  "You suddenly begin to lose your memory!",
144  "Your face gets distorted!",
145  "Watch out, your mind is going!",
146  "Your spirit feels drained!"
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 memory return.",
157  "You feel your spirits 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 lose some of your memory!",
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++;
772  }
773  }
774 
775  object_remove(depl);
777  fix_object(op);
778 
779  return (count == 0) ? 0 : 1;
780 }
781 
791 void change_luck(object *op, int value) {
792  object *tmp;
793  archetype *at;
794  int new_luck;
795 
796  at = find_archetype("luck");
797  if (!at)
798  ;
799  else {
800  tmp = arch_present_in_ob(at, op);
801  if (!tmp) {
802  if (!value)
803  return;
804  tmp = arch_to_object(at);
805  tmp = object_insert_in_ob(tmp, op);
806  SET_FLAG(tmp, FLAG_APPLIED);
807  }
808  if (value) {
809  /* Limit the luck value of the bad luck object to +/-100. This
810  * (arbitrary) value prevents overflows (both in the bad luck object and
811  * in op itself).
812  */
813  new_luck = tmp->stats.luck+value;
814  if (new_luck >= -100 && new_luck <= 100) {
815  op->stats.luck += value;
816  tmp->stats.luck = new_luck;
817  }
818  } else {
819  if (!tmp->stats.luck) {
820  return;
821  }
822  /* Randomly change the players luck. Basically, we move it
823  * back neutral (if greater>0, subtract, otherwise add)
824  */
825  if (RANDOM()%(FABS(tmp->stats.luck)) >= RANDOM()%30) {
826  int diff = tmp->stats.luck > 0 ? -1 : 1;
827  op->stats.luck += diff;
828  tmp->stats.luck += diff;
829  }
830  }
831  }
832 }
833 
840 void remove_statbonus(object *op) {
841  op->stats.Str -= op->arch->clone.stats.Str;
842  op->stats.Dex -= op->arch->clone.stats.Dex;
843  op->stats.Con -= op->arch->clone.stats.Con;
844  op->stats.Wis -= op->arch->clone.stats.Wis;
845  op->stats.Pow -= op->arch->clone.stats.Pow;
846  op->stats.Cha -= op->arch->clone.stats.Cha;
847  op->stats.Int -= op->arch->clone.stats.Int;
848  op->contr->orig_stats.Str -= op->arch->clone.stats.Str;
849  op->contr->orig_stats.Dex -= op->arch->clone.stats.Dex;
850  op->contr->orig_stats.Con -= op->arch->clone.stats.Con;
851  op->contr->orig_stats.Wis -= op->arch->clone.stats.Wis;
852  op->contr->orig_stats.Pow -= op->arch->clone.stats.Pow;
853  op->contr->orig_stats.Cha -= op->arch->clone.stats.Cha;
854  op->contr->orig_stats.Int -= op->arch->clone.stats.Int;
855 }
856 
863 void add_statbonus(object *op) {
864  op->stats.Str += op->arch->clone.stats.Str;
865  op->stats.Dex += op->arch->clone.stats.Dex;
866  op->stats.Con += op->arch->clone.stats.Con;
867  op->stats.Wis += op->arch->clone.stats.Wis;
868  op->stats.Pow += op->arch->clone.stats.Pow;
869  op->stats.Cha += op->arch->clone.stats.Cha;
870  op->stats.Int += op->arch->clone.stats.Int;
871  op->contr->orig_stats.Str += op->arch->clone.stats.Str;
872  op->contr->orig_stats.Dex += op->arch->clone.stats.Dex;
873  op->contr->orig_stats.Con += op->arch->clone.stats.Con;
874  op->contr->orig_stats.Wis += op->arch->clone.stats.Wis;
875  op->contr->orig_stats.Pow += op->arch->clone.stats.Pow;
876  op->contr->orig_stats.Cha += op->arch->clone.stats.Cha;
877  op->contr->orig_stats.Int += op->arch->clone.stats.Int;
878 }
879 
892 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)
893 {
894  int pl_level, i;
895  float character_load = 0.0, maxhp, tmpf;
896 
897  if (op->type != PLAYER)
898  return;
899 
901  pl_level = op->level;
902 
903  if (pl_level < 1)
904  pl_level = 1; /* safety, we should always get 1 levels worth of hp! */
905 
906  /*
907  * We store maxhp as a float to hold any fractional hp bonuses,
908  * (eg, 2.5 con bonus). While it may seem simpler to just
909  * do a get_con_bonus() * min(level,10), there is also a 1 hp/level
910  * minimum (including bonus), se we have to do the logic on a
911  * level basis.
912  */
913  maxhp = 0.0;
914  for (i = 1, op->stats.maxhp = 0; i <= pl_level && i <= 10; i++) {
915 
916  tmpf = op->contr->levhp[i]+get_con_bonus(op->stats.Con);
917 
918  /* always get at least 1 hp/level */
919  if (tmpf < 1.0) tmpf = 1.0;
920 
921  maxhp += tmpf;
922  }
923 
924  /* Add 0.5 so that this rounds normally - the cast just drops the
925  * fraction, so 1.5 becomes 1.
926  */
927  op->stats.maxhp = (int)(maxhp + 0.5);
928  if (op->level > 10)
929  op->stats.maxhp += 2 * (op->level - 10);
930 
931  op->stats.maxhp += op->arch->clone.stats.maxhp;
932 
933  if (op->stats.hp > op->stats.maxhp)
934  op->stats.hp = op->stats.maxhp;
935 
936  /* Sp gain is controlled by the level of the player's
937  * relevant experience object (mana_obj, see above) */
938 
939  /* set maxsp */
940  if (!mana_obj || !mana_obj->level) {
941  op->stats.maxsp = 1;
942  } else {
943  float sp_tmp = 0.0, mana_bonus;
944  int mana_lvl_max;
945 
946  mana_lvl_max = (mana_obj->level >10 ? 10: mana_obj->level);
947  mana_bonus = (2.0*get_sp_bonus(op->stats.Pow)+get_sp_bonus(op->stats.Int)) / 3.0;
948 
949  for (i = 1; i <= mana_lvl_max; i++) {
950  float stmp;
951 
952  stmp = op->contr->levsp[i] + mana_bonus;
953 
954  /* Got some extra bonus at first level */
955  if (i == 1) stmp += mana_bonus;
956 
957  if (stmp < 1.0)
958  stmp = 1.0;
959 
960  sp_tmp += stmp;
961  }
962 
963  op->stats.maxsp = (int)sp_tmp+op->arch->clone.stats.maxsp;
964 
965  if (mana_obj->level > 10)
966  op->stats.maxsp += 2 * (mana_obj->level - 10);
967  }
968 
969  /* Characters can get their sp supercharged via rune of transferrance */
970  if (op->stats.sp > op->stats.maxsp*2)
971  op->stats.sp = op->stats.maxsp*2;
972 
973  /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */
974  if (!grace_obj || !grace_obj->level) {
975  op->stats.maxgrace = 1;
976  } else {
977  /* store grace in a float - this way, the divisions below don't create
978  * big jumps when you go from level to level - with int's, it then
979  * becomes big jumps when the sums of the bonuses jump to the next
980  * step of 8 - with floats, even fractional ones are useful.
981  */
982  float sp_tmp = 0.0, grace_bonus;
983 
984  grace_bonus = (get_grace_bonus(op->stats.Pow)+2.0*get_grace_bonus(op->stats.Wis)) / 3.0;
985 
986  for (i = 1; i <= grace_obj->level && i <= 10; i++) {
987  float grace_tmp = op->contr->levgrace[i] + grace_bonus;
988 
989  /* Got some extra bonus at first level */
990  if (i == 1)
991  grace_tmp += grace_bonus;
992 
993  if (grace_tmp < 1.0)
994  grace_tmp = 1.0;
995  sp_tmp += grace_tmp;
996  }
997  op->stats.maxgrace = (int)sp_tmp+op->arch->clone.stats.maxgrace;
998 
999  /* two grace points per level after 11 */
1000  if (grace_obj->level > 10)
1001  op->stats.maxgrace += 2 * (grace_obj->level - 10);
1002  }
1003  /* No limit on grace vs maxgrace */
1004 
1005  if (op->contr->braced) {
1006  (*ac) += 2;
1007  (*wc) += 4;
1008  } else
1009  (*ac) -= get_dex_bonus(op->stats.Dex);
1010 
1011  /* In new exp/skills system, wc bonuses are related to
1012  * the players level in a relevant exp object (wc_obj)
1013  * not the general player level -b.t.
1014  * I changed this slightly so that wc bonuses are better
1015  * than before. This is to balance out the fact that
1016  * the player no longer gets a personal weapon w/ 1
1017  * improvement every level, now its fighterlevel/5. So
1018  * we give the player a bonus here in wc and dam
1019  * to make up for the change. Note that I left the
1020  * monster bonus the same as before. -b.t.
1021  */
1022 
1023  if (wc_obj && wc_obj->level >= 1) {
1024  const char *wc_in = object_get_value(wc_obj, "wc_increase_rate");
1025  int wc_increase_rate;
1026 
1027  wc_increase_rate = wc_in?atoi(wc_in):5;
1028  assert(wc_increase_rate != 0);
1029 
1030  (*wc) -= get_thaco_bonus(op->stats.Str);
1031  (*wc) -= (wc_obj->level-1)/wc_increase_rate;
1032  op->stats.dam += (wc_obj->level-1)/4;
1033  } else {
1034  (*wc) -= (((op->level-1)/5)+get_thaco_bonus(op->stats.Str));
1035  }
1036  op->stats.dam += get_dam_bonus(op->stats.Str);
1037 
1038  if (op->stats.dam < 1)
1039  op->stats.dam = 1;
1040 
1042 
1043  if (settings.search_items && op->contr->search_str[0])
1044  op->speed -= 0.25;
1045 
1046  if (op->attacktype == 0)
1047  op->attacktype = op->arch->clone.attacktype;
1048 
1049 
1050  /* First block is for encumbrance of the player */
1051 
1052  /* The check for FREE_PLAYER_LOAD_PERCENT < 1.0 is really a safety. One would
1053  * think that it should never be the case if that is set to 1.0, that carrying
1054  * would be above the limit. But it could be if character is weakened and
1055  * was otherwise at limit. Without that safety, could get divide by zeros.
1056  */
1058  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
1059  int extra_weight = op->carrying-get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT;
1060 
1061  character_load = (float)extra_weight/(float)(get_weight_limit(op->stats.Str)*(1.0-FREE_PLAYER_LOAD_PERCENT));
1062 
1063  /* character_load is used for weapon speed below, so sanitize value */
1064  if (character_load >= 1.0)
1065  character_load = 1.0;
1066 
1067  /* If a character is fully loaded, they will always get cut down to min
1068  * speed no matter what their dex. Note that magic is below, so
1069  * still helps out.
1070  */
1071  if (op->speed > MAX_PLAYER_SPEED)
1072  op->speed -= character_load*(op->speed-MIN_PLAYER_SPEED);
1073  else
1074  op->speed -= character_load*(MAX_PLAYER_SPEED-MIN_PLAYER_SPEED);
1075  }
1076 
1077  /* This block is for weapon speed */
1078  op->weapon_speed = BASE_WEAPON_SPEED+get_speed_bonus(op->stats.Dex)-weapon_speed/20.0+added_speed/10.0;
1079  if (wc_obj) {
1080  op->weapon_speed += 0.005*wc_obj->level;
1081  } else
1082  op->weapon_speed += 0.005*op->level;
1083 
1084  /* character_load=1.0 means character is fully loaded, 0.0 is unloaded. Multiplying
1085  * by 0.2 for penalty is purely arbitrary, but slows things down without completely
1086  * stopping them.
1087  */
1088  op->weapon_speed -= character_load*0.2;
1089 
1090  if (op->weapon_speed < 0.05)
1091  op->weapon_speed = 0.05;
1092 
1093  /* It is quite possible that a player's spell costing might have changed,
1094  * so we will check that now.
1095  */
1097 }
1118 void fix_object(object *op) {
1119  int i;
1120  float max = 9, added_speed = 0, speed_reduce_from_disease = 1;
1121  int weapon_speed = 0;
1122  int best_wc = 0, best_ac = 0, wc = 0, ac = 0;
1123  int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS];
1124  const object *grace_obj = NULL, *mana_obj = NULL, *wc_obj = NULL;
1125 
1126  /* First task is to clear all the values back to their original values */
1127  if (op->type == PLAYER) {
1128  for (i = 0; i < NUM_STATS; i++) {
1129  set_attr_value(&(op->stats), i, get_attr_value(&(op->contr->orig_stats), i));
1130  set_attr_value(&(op->contr->applied_stats), i, 0);
1131  }
1133  op->contr->encumbrance = 0;
1134 
1135  op->attacktype = 0;
1136  op->contr->digestion = 0;
1137  op->contr->gen_hp = 0;
1138  op->contr->gen_sp = 0;
1139  op->contr->gen_grace = 0;
1140  op->contr->gen_sp_armour = 10;
1141  op->contr->item_power = 0;
1142 
1143  /* Don't clobber all the range_ values. range_golem otherwise
1144  * gets reset for no good reason, and we don't want to reset
1145  * range_magic (what spell is readied). These three below
1146  * well get filled in based on what the player has equipped.
1147  */
1148  op->contr->ranges[range_bow] = NULL;
1149  op->contr->ranges[range_misc] = NULL;
1150  op->contr->ranges[range_skill] = NULL;
1151  } /* If player */
1152  memcpy(op->body_used, op->body_info, sizeof(op->body_info));
1153 
1154  if (op->slaying != NULL) {
1155  free_string(op->slaying);
1156  op->slaying = NULL;
1157  }
1158  if (!QUERY_FLAG(op, FLAG_WIZ)) {
1159  CLEAR_FLAG(op, FLAG_XRAYS);
1161  }
1162 
1164  CLEAR_FLAG(op, FLAG_STEALTH);
1165  CLEAR_FLAG(op, FLAG_BLIND);
1166  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_SPELL))
1168  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_MISSILE))
1170  if (!QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
1171  CLEAR_FLAG(op, FLAG_UNDEAD);
1172  if (!QUERY_FLAG(&op->arch->clone, FLAG_SEE_IN_DARK))
1174  CLEAR_FLAG(op, FLAG_PROBE);
1175 
1176  op->path_attuned = op->arch->clone.path_attuned;
1178  op->path_denied = op->arch->clone.path_denied;
1179  op->glow_radius = op->arch->clone.glow_radius;
1180  op->move_type = op->arch->clone.move_type;
1181  op->chosen_skill = NULL;
1182 
1183  /* initializing resistances from the values in player/monster's
1184  * archetype clone
1185  */
1186  memcpy(&op->resist, &op->arch->clone.resist, sizeof(op->resist));
1187 
1188  for (i = 0; i < NROFATTACKS; i++) {
1189  if (op->resist[i] > 0)
1190  prot[i] = op->resist[i], vuln[i] = 0;
1191  else
1192  vuln[i] = -(op->resist[i]), prot[i] = 0;
1193  potion_resist[i] = 0;
1194  }
1195 
1196  wc = op->arch->clone.stats.wc;
1197  op->stats.dam = op->arch->clone.stats.dam;
1198 
1199  /* for players which cannot use armour, they gain AC -1 per 3 levels,
1200  * plus a small amount of physical resist, those poor suckers. ;)
1201  * the fact that maxlevel is factored in could be considered sort of bogus -
1202  * we should probably give them some bonus and cap it off - otherwise,
1203  * basically, if a server updates its max level, these playes may find
1204  * that their protection from physical goes down
1205  */
1206  if (!QUERY_FLAG(op, FLAG_USE_ARMOUR) && op->type == PLAYER) {
1207  ac = MAX(-10, op->arch->clone.stats.ac-op->level/3);
1208  prot[ATNR_PHYSICAL] += ((100-prot[AT_PHYSICAL])*(80*op->level/settings.max_level))/100;
1209  } else
1210  ac = op->arch->clone.stats.ac;
1211 
1212  op->stats.luck = op->arch->clone.stats.luck;
1213  op->speed = op->arch->clone.speed;
1214 
1215  /* OK - we've reset most all the objects attributes to sane values.
1216  * now go through and make adjustments for what the player has equipped.
1217  */
1218 
1219  FOR_INV_PREPARE(op, tmp) {
1220  /* See note in map.c:update_position about making this additive
1221  * since light sources are never applied, need to put check here.
1222  */
1223  if (tmp->glow_radius > op->glow_radius)
1224  op->glow_radius = tmp->glow_radius;
1225 
1226  /* This happens because apply_potion calls change_abil with the potion
1227  * applied so we can tell the player what chagned. But change_abil
1228  * then calls this function.
1229  */
1230  if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION)
1231  continue;
1232 
1233  /* For some things, we don't care what is equipped */
1234  if (tmp->type == SKILL) {
1235  /* Want to take the highest skill here. */
1236  if (IS_MANA_SKILL(tmp->subtype)) {
1237  if (!mana_obj)
1238  mana_obj = tmp;
1239  else if (tmp->level > mana_obj->level)
1240  mana_obj = tmp;
1241  }
1242  if (IS_GRACE_SKILL(tmp->subtype)) {
1243  if (!grace_obj)
1244  grace_obj = tmp;
1245  else if (tmp->level > grace_obj->level)
1246  grace_obj = tmp;
1247  }
1248  }
1249 
1250  /* Container objects are not meant to adjust a players, but other applied
1251  * objects need to make adjustments.
1252  * This block should handle all player specific changes
1253  * The check for Praying is a bit of a hack - god given bonuses are put
1254  * in the praying skill, and the player should always get those.
1255  * It also means we need to put in additional checks for applied below,
1256  * because the skill shouldn't count against body positions being used
1257  * up, etc.
1258  */
1259  if ((QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type != CONTAINER && tmp->type != CLOSE_CON)
1260  || (tmp->type == SKILL && tmp->subtype == SK_PRAYING)) {
1261  if (op->type == PLAYER) {
1262  if (tmp->type == BOW)
1263  op->contr->ranges[range_bow] = tmp;
1264 
1265  if (tmp->type == WAND || tmp->type == ROD)
1266  op->contr->ranges[range_misc] = tmp;
1267 
1268  for (i = 0; i < NUM_STATS; i++) {
1269  int8_t value;
1270 
1271  value = get_attr_value(&(tmp->stats), i);
1272  change_attr_value(&(op->stats), i, value);
1273  if (strcmp(tmp->arch->clone.name, ARCH_DEPLETION) != 0)
1274  change_attr_value(&(op->contr->applied_stats), i, value);
1275  }
1276 
1277  /* For this temporary calculation, allow wider range of stat - if we have
1278  * having that gives +5 and different object that gives -5 and stat
1279  * is maxed, we don't want different results based on order of
1280  * inventory.
1281  */
1282  check_stat_bounds(&(tmp->stats), -settings.max_stat, 2*settings.max_stat);
1283 
1284  /* these are the items that currently can change digestion, regeneration,
1285  * spell point recovery and mana point recovery. Seems sort of an arbitary
1286  * list, but other items store other info into stats array.
1287  */
1288  switch (tmp->type)
1289  {
1290  case WEAPON:
1291  case ARMOUR:
1292  case HELMET:
1293  case SHIELD:
1294  case RING:
1295  case BOOTS:
1296  case GLOVES:
1297  case AMULET:
1298  case GIRDLE:
1299  case BRACERS:
1300  case CLOAK:
1301  case DISEASE:
1302  case FORCE:
1303  case SKILL:
1304  op->contr->digestion += tmp->stats.food;
1305  op->contr->gen_hp += tmp->stats.hp;
1306  op->contr->gen_sp += tmp->stats.sp;
1307  op->contr->gen_grace += tmp->stats.grace;
1308  op->contr->gen_sp_armour += tmp->gen_sp_armour;
1309  /*FALLTHROUGH*/
1310 
1311  /* Bow and skill utils need to update item_power specifically.
1312  * This should fix bug #648
1313  * Daniel Hawkins 2017-08-09
1314  */
1315  case BOW:
1316  case SKILL_TOOL:
1317  op->contr->item_power += tmp->item_power;
1318  break;
1319  }
1320  } /* if this is a player */
1321 
1322  /* Update slots used for items */
1323  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1324  for (i = 0; i < NUM_BODY_LOCATIONS; i++)
1325  op->body_used[i] += tmp->body_info[i];
1326  }
1327 
1328  if (tmp->type == SYMPTOM && tmp->last_sp) {
1329  /* Should take the worst disease of the bunch */
1330  if (((float)tmp->last_sp/100.0) < speed_reduce_from_disease)
1331  speed_reduce_from_disease = (float)tmp->last_sp/100.0;
1332  }
1333 
1334  /* Pos. and neg. protections are counted seperate (-> pro/vuln).
1335  * (Negative protections are calculated extactly like positive.)
1336  * Resistance from potions are treated special as well. If there's
1337  * more than one potion-effect, the bigger prot.-value is taken.
1338  */
1339  if (tmp->type != POTION) {
1340  for (i = 0; i < NROFATTACKS; i++) {
1341  /* Potential for cursed potions, in which case we just can use
1342  * a straight MAX, as potion_resist is initialized to zero.
1343  */
1344  if (tmp->type == POTION_RESIST_EFFECT) {
1345  if (potion_resist[i])
1346  potion_resist[i] = MAX(potion_resist[i], tmp->resist[i]);
1347  else
1348  potion_resist[i] = tmp->resist[i];
1349  } else if (tmp->resist[i] > 0)
1350  prot[i] += ((100-prot[i])*tmp->resist[i])/100;
1351  else if (tmp->resist[i] < 0)
1352  vuln[i] += ((100-vuln[i])*(-tmp->resist[i]))/100;
1353  }
1354  }
1355 
1356  /* There may be other things that should not adjust the attacktype */
1357  if (tmp->type != BOW && tmp->type != SYMPTOM)
1358  op->attacktype |= tmp->attacktype;
1359 
1360  op->path_attuned |= tmp->path_attuned;
1361  op->path_repelled |= tmp->path_repelled;
1362  op->path_denied |= tmp->path_denied;
1363  op->stats.luck += tmp->stats.luck;
1364  op->move_type |= tmp->move_type;
1365 
1366  if (QUERY_FLAG(tmp, FLAG_LIFESAVE))
1367  SET_FLAG(op, FLAG_LIFESAVE);
1368  if (QUERY_FLAG(tmp, FLAG_REFL_SPELL))
1370  if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE))
1372  if (QUERY_FLAG(tmp, FLAG_STEALTH))
1373  SET_FLAG(op, FLAG_STEALTH);
1374  if (QUERY_FLAG(tmp, FLAG_XRAYS))
1375  SET_FLAG(op, FLAG_XRAYS);
1376  if (QUERY_FLAG(tmp, FLAG_BLIND))
1377  SET_FLAG(op, FLAG_BLIND);
1378  if (QUERY_FLAG(tmp, FLAG_SEE_IN_DARK))
1380  if (QUERY_FLAG(tmp, FLAG_PROBE))
1381  SET_FLAG(op, FLAG_PROBE);
1382 
1383  // Items can make the wielder confused.
1384  if (QUERY_FLAG(tmp, FLAG_CONFUSED))
1385  SET_FLAG(op, FLAG_CONFUSED);
1386 
1387  if (QUERY_FLAG(tmp, FLAG_UNDEAD) && !QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
1388  SET_FLAG(op, FLAG_UNDEAD);
1389 
1390  if (QUERY_FLAG(tmp, FLAG_MAKE_INVIS)) {
1392  op->invisible = 1;
1393  }
1394 
1395  if (tmp->stats.exp && tmp->type != SKILL) {
1396  added_speed += (float)tmp->stats.exp/3.0;
1397  }
1398 
1399  switch (tmp->type) {
1400  /* skills modifying the character -b.t. */
1401  /* for all skills and skill granting objects */
1402  case SKILL:
1403  if (!QUERY_FLAG(tmp, FLAG_APPLIED))
1404  break;
1405 
1406  if (IS_COMBAT_SKILL(tmp->subtype))
1407  wc_obj = tmp;
1408 
1409  if (op->chosen_skill) {
1410  LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name);
1411  }
1412  op->chosen_skill = tmp;
1413  if (tmp->stats.dam > 0) { /* skill is a 'weapon' */
1414  if (!QUERY_FLAG(op, FLAG_READY_WEAPON))
1415  weapon_speed = (int)WEAPON_SPEED(tmp);
1416  if (weapon_speed < 0)
1417  weapon_speed = 0;
1418  op->stats.dam += tmp->stats.dam*(1+(op->chosen_skill->level/9));
1419  op->stats.dam += tmp->magic;
1420  }
1421  if (tmp->stats.wc)
1422  wc -= (tmp->stats.wc+tmp->magic);
1423 
1424  if (tmp->slaying != NULL) {
1425  if (op->slaying != NULL)
1426  free_string(op->slaying);
1427  add_refcount(op->slaying = tmp->slaying);
1428  }
1429 
1430  if (tmp->stats.ac)
1431  ac -= (tmp->stats.ac+tmp->magic);
1432  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1433  op->contr->encumbrance += (int)3*tmp->weight/1000;
1434  if (op->type == PLAYER)
1435  op->contr->ranges[range_skill] = op;
1436  break;
1437 
1438  case SKILL_TOOL:
1439  if (op->chosen_skill) {
1440  LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name);
1441  }
1442  op->chosen_skill = tmp;
1443  if (op->type == PLAYER)
1444  op->contr->ranges[range_skill] = op;
1445  break;
1446 
1447  case SHIELD:
1448  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1449  op->contr->encumbrance += (int)tmp->weight/2000;
1450  case RING:
1451  case AMULET:
1452  case GIRDLE:
1453  case HELMET:
1454  case BOOTS:
1455  case GLOVES:
1456  case CLOAK:
1457  if (tmp->stats.wc)
1458  wc -= tmp->stats.wc;
1459  if (tmp->stats.dam)
1460  op->stats.dam += (tmp->stats.dam+tmp->magic);
1461  if (tmp->stats.ac)
1462  ac -= (tmp->stats.ac+tmp->magic);
1463  break;
1464 
1465  case WEAPON:
1466  wc -= tmp->stats.wc;
1467  if (tmp->stats.ac && tmp->stats.ac+tmp->magic > 0)
1468  ac -= tmp->stats.ac+tmp->magic;
1469  op->stats.dam += (tmp->stats.dam+tmp->magic);
1470  weapon_speed = ((int)WEAPON_SPEED(tmp)*2-tmp->magic)/2;
1471  if (weapon_speed < 0)
1472  weapon_speed = 0;
1473  if (tmp->slaying != NULL) {
1474  if (op->slaying != NULL)
1475  free_string(op->slaying);
1476  add_refcount(op->slaying = tmp->slaying);
1477  }
1478  /* If there is desire that two handed weapons should do
1479  * extra strength damage, this is where the code should
1480  * go.
1481  */
1482  op->current_weapon = tmp;
1483  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1484  op->contr->encumbrance += (int)3*tmp->weight/1000;
1485  break;
1486 
1487  case ARMOUR: /* Only the best of these three are used: */
1488  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1489  op->contr->encumbrance += (int)tmp->weight/1000;
1490 
1491  /* ARMOUR falls through to here */
1492 
1493  case BRACERS:
1494  case FORCE:
1495  // Code simplification to reduce branching -- we don't need to sub/add ac and wc all the time.
1496  // Daniel Hawkins 2018-05-28
1497  if (tmp->stats.wc) {
1498  // Since we are alreay here, make sure wc stacking also occurs for serpentman players.
1499  if (tmp->type == BRACERS && op->type == PLAYER && op->arch->name && strcmp(op->arch->name, "serpentman_player") == 0)
1500  {
1501  // Apply ac from bracers directly.
1502  // This also grants the side effect that armor and bracer ac are separate for serpentmen,
1503  // But that should be better than the extra bracers being mostly useless.
1504  wc -= tmp->stats.wc;
1505  }
1506  else if (best_wc < tmp->stats.wc) {
1507  wc += best_wc;
1508  best_wc = tmp->stats.wc;
1509  wc -= tmp->stats.wc;
1510  }
1511  }
1512  if (tmp->stats.ac) {
1513  /*
1514  * If we have a serpentman player, then we do bracers differently
1515  * to allow for both bracers they equip to apply to ac, instead of only the best.
1516  *
1517  * Daniel Hawkins 2018-05-28
1518  */
1519  if (tmp->type == BRACERS && op->type == PLAYER && op->arch->name && strcmp(op->arch->name, "serpentman_player") == 0)
1520  {
1521  // Apply ac from bracers directly.
1522  // This also grants the side effect that armor and bracer ac are separate for serpentmen,
1523  // But that should be better than the extra bracers being mostly useless.
1524  ac -= tmp->stats.ac+tmp->magic;
1525  }
1526  else if (best_ac < tmp->stats.ac+tmp->magic) {
1527  ac += best_ac; /* Remove last bonus */
1528  best_ac = tmp->stats.ac+tmp->magic;
1529  ac -= (tmp->stats.ac+tmp->magic);
1530  }
1531  }
1532  if (tmp->stats.dam && tmp->type == BRACERS)
1533  op->stats.dam += (tmp->stats.dam+tmp->magic);
1534  if (ARMOUR_SPEED(tmp) && ARMOUR_SPEED(tmp)/10.0 < max)
1535  max = ARMOUR_SPEED(tmp)/10.0;
1536  break;
1537  } /* switch tmp->type */
1538  } /* item is equipped */
1539  } FOR_INV_FINISH(); /* for loop of items */
1540 
1541  /* We've gone through all the objects the player has equipped. For many things, we
1542  * have generated intermediate values which we now need to assign.
1543  */
1544 
1545  /* 'total resistance = total protections - total vulnerabilities'.
1546  * If there is an uncursed potion in effect, granting more protection
1547  * than that, we take: 'total resistance = resistance from potion'.
1548  * If there is a cursed (and no uncursed) potion in effect, we take
1549  * 'total resistance = vulnerability from cursed potion'.
1550  */
1551  for (i = 0; i < NROFATTACKS; i++) {
1552  op->resist[i] = prot[i]-vuln[i];
1553  if (potion_resist[i]
1554  && ((potion_resist[i] > op->resist[i]) || (potion_resist[i] < 0)))
1555  op->resist[i] = potion_resist[i];
1556  }
1557 
1558  fix_player(op, &ac, &wc, grace_obj, mana_obj, wc_obj, weapon_speed, added_speed);
1559 
1560  op->speed = op->speed*speed_reduce_from_disease;
1561 
1562  /* Max is determined by armour */
1563  if (op->speed > max)
1564  op->speed = max;
1565 
1566  op->speed += added_speed/10.0;
1567 
1568 
1569  /* Put a lower limit on speed. Note with this speed, you move once every
1570  * 20 ticks or so. This amounts to once every 3 seconds of realtime.
1571  */
1572  if (op->speed < 0.05 && op->type == PLAYER)
1573  op->speed = 0.05;
1574 
1575  /* I want to limit the power of small monsters with big weapons: */
1576  if (op->type != PLAYER
1577  && op->arch != NULL
1578  && op->stats.dam > op->arch->clone.stats.dam*3)
1579  op->stats.dam = op->arch->clone.stats.dam*3;
1580 
1581  /* Prevent overflows of wc - best you can get is ABS(120) - this
1582  * should be more than enough - remember, AC is also in 8 bits,
1583  * so its value is the same.
1584  */
1585  if (wc > 120)
1586  wc = 120;
1587  else if (wc < -120)
1588  wc = -120;
1589  op->stats.wc = wc;
1590 
1591  if (ac > 120)
1592  ac = 120;
1593  else if (ac < -120)
1594  ac = -120;
1595  op->stats.ac = ac;
1596 
1597  /* if for some reason the creature doesn't have any move type,
1598  * give them walking as a default.
1599  * The second case is a special case - to more closely mimic the
1600  * old behaviour - if your flying, your not walking - just
1601  * one or the other.
1602  */
1603  if (op->move_type == 0)
1604  op->move_type = MOVE_WALK;
1605  else if (op->move_type&(MOVE_FLY_LOW|MOVE_FLY_HIGH))
1606  op->move_type &= ~MOVE_WALK;
1607 
1608  object_update_speed(op);
1609 }
1610 
1623 int allowed_class(const object *op) {
1624  return op->stats.Dex > 0
1625  && op->stats.Str > 0
1626  && op->stats.Con > 0
1627  && op->stats.Int > 0
1628  && op->stats.Wis > 0
1629  && op->stats.Pow > 0
1630  && op->stats.Cha > 0;
1631 }
1632 
1650 void set_dragon_name(object *pl, const object *abil, const object *skin) {
1651  int atnr = -1; /* attacknumber of highest level */
1652  int level = 0; /* highest level */
1653  int i;
1654 
1655  /* Perhaps do something more clever? */
1656  if (!abil || !skin)
1657  return;
1658 
1659  /* first, look for the highest level */
1660  for (i = 0; i < NROFATTACKS; i++) {
1661  if (atnr_is_dragon_enabled(i)
1662  && (atnr == -1 || abil->resist[i] > abil->resist[atnr])) {
1663  level = abil->resist[i];
1664  atnr = i;
1665  }
1666  }
1667 
1668  /* now if there are equals at highest level, pick the one with focus,
1669  or else at random */
1670  if (atnr_is_dragon_enabled(abil->stats.exp)
1671  && abil->resist[abil->stats.exp] >= level)
1672  atnr = abil->stats.exp;
1673 
1674  level = (int)(level/5.);
1675 
1676  /* now set the new title */
1677  if (pl->contr != NULL) {
1678  player_set_dragon_title(pl->contr, level, attacks[atnr], skin->resist[atnr]);
1679  }
1680 }
1681 
1690 static void dragon_level_gain(object *who) {
1691  object *abil = NULL; /* pointer to dragon ability force*/
1692  object *skin = NULL; /* pointer to dragon skin force*/
1693 
1694  /* now grab the 'dragon_ability'-forces from the player's inventory */
1695  abil = object_find_by_type_and_arch_name(who, FORCE, "dragon_ability_force");
1696  skin = object_find_by_type_and_arch_name(who, FORCE, "dragon_skin_force");
1697  /* if the force is missing -> bail out */
1698  if (abil == NULL)
1699  return;
1700 
1701  /* The ability_force keeps track of maximum level ever achieved.
1702  * New abilties can only be gained by surpassing this max level
1703  */
1704  if (who->level > abil->level) {
1705  /* increase our focused ability */
1706  int lev = ++(abil->resist[abil->stats.exp]);
1707 
1708  if (lev > 0) {
1709  /* try to hand out a new ability-gift
1710  * if not the right level, this will handout nothing.
1711  */
1712  dragon_ability_gain(who, (int)abil->stats.exp, lev);
1713  }
1714 
1715  if (abil->last_eat > 0 && atnr_is_dragon_enabled(abil->last_eat)) {
1716  /* apply new ability focus */
1718  "Your metabolism now focuses on %s!",
1719  change_resist_msg[abil->last_eat]);
1720 
1721  abil->stats.exp = abil->last_eat;
1722  abil->last_eat = 0;
1723  }
1724 
1725  abil->level = who->level;
1726  }
1727 
1728  /* last but not least, set the new title for the dragon */
1729  set_dragon_name(who, abil, skin);
1730 }
1731 
1750 object *give_skill_by_name(object *op, const char *skill_name) {
1751  object *skill_obj;
1752  archetype *skill_arch;
1753 
1754  skill_arch = get_archetype_by_skill_name(skill_name, SKILL);
1755  if (!skill_arch) {
1756  LOG(llevError, "add_player_exp: couldn't find skill %s\n", skill_name);
1757  return NULL;
1758  }
1759  skill_obj = arch_to_object(skill_arch); /* never returns NULL. */
1760 
1761  /* clear the flag - exp goes into this bucket, but player
1762  * still doesn't know it.
1763  */
1764  CLEAR_FLAG(skill_obj, FLAG_CAN_USE_SKILL);
1765  skill_obj->stats.exp = 0;
1766  skill_obj->level = 1;
1767  object_insert_in_ob(skill_obj, op);
1768  if (op->contr) {
1769  op->contr->last_skill_ob[skill_obj->subtype] = skill_obj;
1770  op->contr->last_skill_exp[skill_obj->subtype] = -1;
1771  }
1772  return skill_obj;
1773 }
1774 
1791 void player_lvl_adj(object *who, object *op) {
1792  char buf[MAX_BUF];
1793 
1794  assert(who);
1795 
1796  if (!op) /* when rolling stats */
1797  op = who;
1798 
1799  if (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1, who->expmul)) {
1800  do{
1801  op->level++;
1802 
1803  if (op != NULL && op == who && op->stats.exp > 1 && is_dragon_pl(who))
1804  dragon_level_gain(who);
1805 
1806  /* Only roll these if it is the player (who) that gained the level */
1807  if (op == who && (who->level < 11) && who->type == PLAYER) {
1808  who->contr->levhp[who->level] = die_roll(2, 4, who, PREFER_HIGH)+1;
1809  who->contr->levsp[who->level] = die_roll(2, 3, who, PREFER_HIGH);
1810  who->contr->levgrace[who->level] = die_roll(2, 2, who, PREFER_HIGH)-1;
1811  }
1812 
1813  fix_object(who); // TODO: Call this only once per function call?
1814  if (op->level > 1) {
1815  if (op->type != PLAYER)
1816  snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.", op->level, op->name);
1817  else
1818  snprintf(buf, sizeof(buf), "You are now level %d.", op->level);
1819 
1821  }
1822  } while (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1, who->expmul)); /* To increase more levels */
1823  } else if (op->level > 1 && op->stats.exp < level_exp(op->level, who->expmul)) {
1824  do{
1825  op->level--;
1826  fix_object(who); // TODO: Call this only once per function call?
1827  if (op->type != PLAYER)
1828  snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.", op->level, op->name);
1829  else
1830  snprintf(buf, sizeof(buf), "You are now level %d.", op->level);
1831 
1833  } while (op->level > 1 && op->stats.exp < level_exp(op->level, who->expmul)); /* To decrease more levels */
1834  }
1835 }
1836 
1846 int64_t level_exp(int level, double expmul) {
1847  if (level > settings.max_level)
1848  return expmul*levels[settings.max_level];
1849  return expmul*levels[level];
1850 }
1851 
1862 void calc_perm_exp(object *op) {
1863  int64_t p_exp_min;
1864 
1865  /* Ensure that our permanent experience minimum is met.
1866  * permenent_exp_ratio is an integer percentage, we divide by 100
1867  * to get the fraction */
1868  p_exp_min = settings.permanent_exp_ratio*op->stats.exp/100;
1869 
1870  if (op->perm_exp < p_exp_min)
1871  op->perm_exp = p_exp_min;
1872 
1873  /* Cap permanent experience. */
1874  if (op->perm_exp < 0)
1875  op->perm_exp = 0;
1876  else if (op->perm_exp > MAX_EXPERIENCE)
1877  op->perm_exp = MAX_EXPERIENCE;
1878 }
1879 
1880 
1895 static void add_player_exp(object *op, int64_t exp, const char *skill_name, int flag) {
1896  object *skill_obj = NULL;
1897  int64_t limit, exp_to_add;
1898  int i;
1899 
1900  /* prevents some forms of abuse. */
1901  if (op->contr->braced)
1902  exp = exp/5;
1903 
1904  /* Try to find the matching skill.
1905  * We do a shortcut/time saving mechanism first - see if it matches
1906  * chosen_skill. This means we don't need to search through
1907  * the players inventory.
1908  */
1909  if (skill_name) {
1910  if (op->chosen_skill
1911  && op->chosen_skill->type == SKILL
1912  && !strcmp(skill_name, op->chosen_skill->skill))
1913  skill_obj = op->chosen_skill;
1914  else {
1915  for (i = 0; i < NUM_SKILLS; i++)
1916  if (op->contr->last_skill_ob[i]
1917  && !strcmp(op->contr->last_skill_ob[i]->skill, skill_name)) {
1918  skill_obj = op->contr->last_skill_ob[i];
1919  break;
1920  }
1921 
1922  /* Player doesn't have the skill. Check to see what to do, and give
1923  * it to the player if necessary
1924  */
1925  if (!skill_obj) {
1926  if (flag == SK_EXP_NONE)
1927  return;
1928  else if (flag == SK_EXP_ADD_SKILL)
1929  skill_obj = give_skill_by_name(op, skill_name);
1930  }
1931  }
1932  }
1933 
1934  /* Basically, you can never gain more experience in one shot
1935  * than half what you need to gain for next level.
1936  */
1937  exp_to_add = exp;
1938  /*
1939  * Make sure we aren't trying to go backwards when we hit maximum level,
1940  * but make sure we can still add to our permanent experience.
1941  *
1942  * -- Daniel Hawkins 2015-05-24
1943  */
1944  if (op->level == settings.max_level)
1945  limit = levels[op->level] / 2;
1946  else
1947  limit = (levels[op->level+1]-levels[op->level])/2;
1948 
1949  if (exp_to_add > limit)
1950  exp_to_add = limit;
1951 
1952  ADD_EXP(op->stats.exp, (float)exp_to_add*(skill_obj ? skill_obj->expmul : 1));
1954  ADD_EXP(op->perm_exp, (float)exp_to_add*PERM_EXP_GAIN_RATIO*(skill_obj ? skill_obj->expmul : 1));
1955  calc_perm_exp(op);
1956  }
1957 
1958  player_lvl_adj(op, NULL);
1959  if (skill_obj) {
1960  exp_to_add = exp;
1961  /*
1962  * Make sure we aren't trying to go backwards when we hit maximum level,
1963  * but make sure we can still add to our permanent experience.
1964  *
1965  * -- Daniel Hawkins 2015-05-24
1966  */
1967  if (skill_obj->level == settings.max_level)
1968  limit = levels[skill_obj->level] / 2;
1969  else
1970  limit = (levels[skill_obj->level+1]-levels[skill_obj->level])/2;
1971 
1972  if (exp_to_add > limit)
1973  exp_to_add = limit;
1974  ADD_EXP(skill_obj->stats.exp, exp_to_add);
1976  skill_obj->perm_exp += exp_to_add*PERM_EXP_GAIN_RATIO;
1977  calc_perm_exp(skill_obj);
1978  }
1979  player_lvl_adj(op, skill_obj);
1980  }
1981 }
1982 
1998 int64_t check_exp_loss(const object *op, int64_t exp) {
1999  int64_t del_exp;
2000 
2001  if (exp > op->stats.exp)
2002  exp = op->stats.exp;
2004  del_exp = (op->stats.exp-op->perm_exp)*PERM_EXP_MAX_LOSS_RATIO;
2005  if (del_exp < 0)
2006  del_exp = 0;
2007  if (exp > del_exp)
2008  exp = del_exp;
2009  }
2010  return exp;
2011 }
2012 
2023 int64_t check_exp_adjust(const object *op, int64_t exp) {
2024  if (exp < 0)
2025  return check_exp_loss(op, exp);
2026  else
2027  return MIN(exp, MAX_EXPERIENCE-op->stats.exp);
2028 }
2029 
2052 static void subtract_player_exp(object *op, int64_t exp, const char *skill, int flag) {
2053  float fraction = (float)exp/(float)op->stats.exp;
2054  int64_t del_exp;
2055 
2056  FOR_INV_PREPARE(op, tmp)
2057  if (tmp->type == SKILL && tmp->stats.exp) {
2058  if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp(tmp->skill, skill)) {
2059  del_exp = check_exp_loss(tmp, exp);
2060  tmp->stats.exp -= del_exp;
2061  player_lvl_adj(op, tmp);
2062  } else if (flag != SK_SUBTRACT_SKILL_EXP) {
2063  /* only want to process other skills if we are not trying
2064  * to match a specific skill.
2065  */
2066  del_exp = check_exp_loss(tmp, tmp->stats.exp*fraction);
2067  tmp->stats.exp -= del_exp;
2068  player_lvl_adj(op, tmp);
2069  }
2070  }
2071  FOR_INV_FINISH();
2072  if (flag != SK_SUBTRACT_SKILL_EXP) {
2073  del_exp = check_exp_loss(op, exp);
2074  op->stats.exp -= del_exp;
2075  player_lvl_adj(op, NULL);
2076  }
2077 }
2078 
2100 void change_exp(object *op, int64_t exp, const char *skill_name, int flag) {
2101  /* safety */
2102  if (!op) {
2103  LOG(llevError, "change_exp() called for null object!\n");
2104  return;
2105  }
2106 
2107  /* if no change in exp, just return - most of the below code
2108  * won't do anything if the value is 0 anyways.
2109  */
2110  if (exp == 0)
2111  return;
2112 
2113  /* Monsters are easy - we just adjust their exp - we
2114  * don't adjust level, since in most cases it is unrelated to
2115  * the exp they have - the monsters exp represents what its
2116  * worth.
2117  */
2118  if (op->type != PLAYER) {
2119  /* Sanity check */
2120  if (!QUERY_FLAG(op, FLAG_ALIVE))
2121  return;
2122 
2123  /* reset exp to max allowed value. We subtract from
2124  * MAX_EXPERIENCE to prevent overflows. If the player somehow has
2125  * more than max exp, just return.
2126  */
2127  if (exp > 0 && (op->stats.exp > (MAX_EXPERIENCE-exp))) {
2128  exp = MAX_EXPERIENCE-op->stats.exp;
2129  if (exp < 0)
2130  return;
2131  }
2132 
2133  op->stats.exp += exp;
2134  } else { /* Players only */
2135  if (exp > 0)
2136  add_player_exp(op, exp, skill_name, flag);
2137  else
2138  subtract_player_exp(op, FABS(exp), skill_name, flag);
2139  }
2140 }
2141 
2150 void apply_death_exp_penalty(object *op) {
2151  int64_t loss;
2152  int64_t percentage_loss; /* defined by the setting 'death_penalty_percent' */
2153  int64_t level_loss; /* defined by the setting 'death_penalty_levels */
2154 
2155  FOR_INV_PREPARE(op, tmp)
2156  if (tmp->type == SKILL && tmp->stats.exp) {
2157  percentage_loss = tmp->stats.exp*settings.death_penalty_ratio/100;
2158  level_loss = tmp->stats.exp-levels[MAX(0, tmp->level-settings.death_penalty_level)];
2159 
2160  /* With the revised exp system, you can get cases where
2161  * losing several levels would still require that you have more
2162  * exp than you currently have - this is true if the levels
2163  * tables is a lot harder.
2164  */
2165  if (level_loss < 0)
2166  level_loss = 0;
2167 
2168  loss = check_exp_loss(tmp, MIN(level_loss, percentage_loss));
2169 
2170  tmp->stats.exp -= loss;
2171  player_lvl_adj(op, tmp);
2172  }
2173  FOR_INV_FINISH();
2174 
2175  percentage_loss = op->stats.exp*settings.death_penalty_ratio/100;
2176  level_loss = op->stats.exp-levels[MAX(0, op->level-settings.death_penalty_level)];
2177  if (level_loss < 0)
2178  level_loss = 0;
2179  loss = check_exp_loss(op, MIN(level_loss, percentage_loss));
2180 
2181  op->stats.exp -= loss;
2182  player_lvl_adj(op, NULL);
2183 }
2184 
2199 int did_make_save(const object *op, int level, int bonus) {
2200  if (level > MAX_SAVE_LEVEL)
2201  level = MAX_SAVE_LEVEL;
2202 
2203  if ((random_roll(1, 20, op, PREFER_HIGH)+bonus) < savethrow[level])
2204  return 0;
2205  return 1;
2206 }
2207 
2229 void share_exp(object *op, int64_t exp, const char *skill, int flag) {
2230  int shares = 0, count = 0;
2231  player *pl;
2232  partylist *party;
2233 
2234  if (op->type != PLAYER || op->contr->party == NULL) {
2235  change_exp(op, exp, skill, 0);
2236  return;
2237  }
2238 
2239  party = op->contr->party;
2240 
2241  for (pl = first_player; pl != NULL; pl = pl->next) {
2242  if (party && pl->ob->contr->party == party && on_same_map(pl->ob, op)) {
2243  count++;
2244  shares += (pl->ob->level+4);
2245  }
2246  }
2247 
2248  assert(shares > 0);
2249 
2250  if (count == 1 || shares > exp)
2251  change_exp(op, exp, skill, flag);
2252  else {
2253  int64_t share = exp/shares, given = 0, nexp;
2254  for (pl = first_player; pl != NULL; pl = pl->next) {
2255  if (party && pl->ob->contr->party == party && on_same_map(pl->ob, op)) {
2256  nexp = (pl->ob->level+4)*share;
2257  change_exp(pl->ob, nexp, skill, SK_EXP_TOTAL);
2258  given += nexp;
2259  }
2260  }
2261  exp -= given;
2262  /* give any remainder to the player */
2263  change_exp(op, exp, skill, flag);
2264  }
2265 }
2266 
2267 int get_cha_bonus(int stat) {
2269 }
2270 
2271 int get_dex_bonus(int stat) {
2273 }
2274 
2275 int get_thaco_bonus(int stat) {
2277 }
2278 
2281 }
2282 
2283 int get_learn_spell(int stat) {
2285 }
2286 
2287 int get_cleric_chance(int stat) {
2289 }
2290 
2291 int get_turn_bonus(int stat) {
2293 }
2294 
2295 int get_dam_bonus(int stat) {
2297 }
2298 
2299 float get_speed_bonus(int stat) {
2301 }
2302 
2303 int get_fear_bonus(int stat) {
2305 }
2306 
2307 static float get_con_bonus(int stat) {
2309 }
2310 
2311 static float get_sp_bonus(int stat) {
2313 }
2314 
2315 static float get_grace_bonus(int stat) {
2317 }
2318 
2327 static size_t get_index(int stat, size_t max_index) {
2328  size_t index;
2329 
2330  if (stat < 0) {
2331  return 0;
2332  }
2333 
2334  index = (size_t)stat;
2335  return MIN(index, max_index);
2336 }
2337 
2355 static int load_table_int(int **bonuses, FILE *fp, char *bonus_name)
2356 {
2357  char buf[MAX_BUF], *cp;
2358  int on_stat = 0, tmp_bonus;
2359 
2360  *bonuses = calloc(settings.max_stat+1, sizeof(int));
2361 
2362  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2363  if (buf[0] == '#')
2364  continue;
2365 
2366  /* Skip over empty lines */
2367  if (buf[0] == '\n')
2368  continue;
2369 
2370  /* Do not care about opening brace */
2371  if (buf[0] == '{')
2372  continue;
2373 
2374  if (buf[0] == '}') {
2375  if ((on_stat-1) != settings.max_stat) {
2376  LOG(llevError,"Number of bonus does not match max stat (%d!=%d, bonus=%s)\n",
2377  on_stat, settings.max_stat, bonus_name);
2378  return 1;
2379  }
2380  else return 0;
2381  }
2382 
2383  /* If not any of the above values, must be the stat table,
2384  * or so we hope.
2385  */
2386  cp = buf;
2387  while (*cp != 0) {
2388  /* Skip over any non numbers */
2389  while (!isdigit(*cp) && *cp!='.' && *cp!='-' && *cp!='+' && *cp != 0)
2390  cp++;
2391 
2392  if (*cp == 0) break;
2393 
2394  tmp_bonus = atoi(cp);
2395 
2396  if (on_stat > settings.max_stat) {
2397  LOG(llevError,"Number of bonus entries exceed max stat (line=%s, bonus=%s)\n",
2398  buf, bonus_name);
2399  return 1;
2400  }
2401  (*bonuses)[on_stat] = tmp_bonus;
2402  on_stat++;
2403 
2404  /* Skip over any digits, as that is the number we just processed */
2405  while ((isdigit(*cp) || *cp=='-' || *cp=='+') && *cp != 0)
2406  cp++;
2407  }
2408  }
2409  /* This should never happen - we should always get the closing brace */
2410  LOG(llevError,"Reached end of file without getting close brace? bonus=%s\n", bonus_name);
2411  return 1;
2412 }
2413 
2431 static int load_table_float(float **bonuses, FILE *fp, char *bonus_name)
2432 {
2433  char buf[MAX_BUF], *cp;
2434  int on_stat = 0;
2435  float tmp_bonus;
2436 
2437  *bonuses = calloc(settings.max_stat+1, sizeof(float));
2438 
2439  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2440  if (buf[0] == '#')
2441  continue;
2442 
2443  /* Skip over empty lines */
2444  if (buf[0] == '\n')
2445  continue;
2446 
2447  /* Do not care about opening brace */
2448  if (buf[0] == '{')
2449  continue;
2450 
2451  if (buf[0] == '}') {
2452  if ((on_stat-1) != settings.max_stat) {
2453  LOG(llevError,"Number of bonus does not match max stat (%d!=%d, bonus=%s)\n",
2454  on_stat, settings.max_stat, bonus_name);
2455  return 1;
2456  }
2457  else return 0;
2458  }
2459 
2460  /* If not any of the above values, must be the stat table,
2461  * or so we hope.
2462  */
2463  cp = buf;
2464  while (*cp != 0) {
2465  /* Skip over any non numbers */
2466  while (!isdigit(*cp) && *cp!='.' && *cp!='-' && *cp!='+' && *cp != 0)
2467  cp++;
2468 
2469  if (*cp == 0) break;
2470 
2471  tmp_bonus = atof(cp);
2472 
2473  if (on_stat > settings.max_stat) {
2474  LOG(llevError,"Number of bonus entries exceed max stat (line=%s, bonus=%s)\n",
2475  buf, bonus_name);
2476  return 1;
2477  }
2478  (*bonuses)[on_stat] = tmp_bonus;
2479  on_stat++;
2480 
2481  /* Skip over any digits, as that is the number we just processed
2482  * since this is floats, also skip over any dots.
2483  */
2484  while ((isdigit(*cp) || *cp=='-' || *cp=='+' || *cp=='.') && *cp != 0)
2485  cp++;
2486  }
2487  }
2488  /* This should never happen - we should always get the closing brace */
2489  LOG(llevError,"Reached end of file without getting close brace? bonus=%s\n", bonus_name);
2490  return 1;
2491 }
2492 
2493 
2511 void init_stats(int reload) {
2512  char buf[MAX_BUF], *cp;
2513  int error=0, i, oldmax = settings.max_stat;
2514  FILE *fp;
2515  float *new_float_bonuses[NUM_FLOAT_BONUSES];
2516  int *new_int_bonuses[NUM_INT_BONUSES];
2517 
2518  snprintf(buf, sizeof(buf), "%s/stat_bonus", settings.confdir);
2519 
2520  memset(new_int_bonuses, 0, NUM_INT_BONUSES * sizeof(int));
2521  memset(new_float_bonuses, 0, NUM_FLOAT_BONUSES * sizeof(float));
2522 
2523  if ((fp = fopen(buf, "r")) == NULL) {
2524  LOG(llevError, "Fatal error: could not open experience table (%s)\n", buf);
2525  if (reload) return;
2526  else exit(1);
2527  }
2528  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2529  if (buf[0] == '#')
2530  continue;
2531 
2532  /* eliminate newline */
2533  if ((cp = strrchr(buf, '\n')) != NULL)
2534  *cp = '\0';
2535 
2536  /* Skip over empty lines */
2537  if (buf[0] == 0)
2538  continue;
2539 
2540  /* Skip over leading spaces */
2541  cp = buf;
2542  while (isspace(*cp) && *cp != 0)
2543  cp++;
2544 
2545  if (!strncasecmp(cp, "max_stat", 8)) {
2546  int newmax = atoi(cp+8);
2547 
2548  /* newmax must be at least MIN_STAT and we do not currently
2549  * cupport decrease max stat on the fly - why this might be possible,
2550  * bounds checking for all objects would be needed, potentionally resetting
2551  * them.
2552  * If this is a reload, then on error, we just return without doing work.
2553  * If this is initial load, having an invalid stat range is an error, so
2554  * exit the program.
2555  */
2556  if (newmax < MIN_STAT || newmax < settings.max_stat) {
2557  LOG(llevError, "Got invalid max_stat (%d) from stat_bonus file\n", newmax);
2558  fclose(fp);
2559  if (reload) return;
2560  else exit(1);
2561  }
2562  settings.max_stat = newmax;
2563  continue;
2564  }
2565  /* max_stat needs to be set before any of the bonus values - we
2566  * need to know how large to make the array.
2567  */
2568  if (settings.max_stat == 0) {
2569  LOG(llevError, "Got bonus line or otherwise unknown value before max stat! (%s)\n",
2570  buf);
2571  fclose(fp);
2572 
2573  if (reload) {
2574  return;
2575  } else {
2576  exit(1);
2577  }
2578  }
2579 
2580  for (i=0; i<NUM_INT_BONUSES; i++) {
2581  if (!strncasecmp(cp, int_bonus_names[i], strlen(int_bonus_names[i]))) {
2582  error = load_table_int(&new_int_bonuses[i], fp, cp);
2583  break;
2584  }
2585  }
2586  /* If we did not find a match in the int bonuses, check the
2587  * float bonuses now.
2588  */
2589  if (i == NUM_INT_BONUSES) {
2590  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2591  if (!strncasecmp(cp, float_bonus_names[i], strlen(float_bonus_names[i]))) {
2592  error = load_table_float(&new_float_bonuses[i], fp, cp);
2593  break;
2594  }
2595  }
2596  /* This may not actually be a critical error */
2597  if (i == NUM_FLOAT_BONUSES) {
2598  LOG(llevError,"Unknown line in stat_bonus file: %s\n", buf);
2599  }
2600  }
2601  if (error) break;
2602  }
2603  fclose(fp);
2604 
2605  /* Make sure that we have load tables for all the bonuses.
2606  * This is critical on initial load, but on reloads, it enusres that
2607  * all the bonus data matches.
2608  */
2609  for (i=0; i<NUM_INT_BONUSES; i++) {
2610  if (!new_int_bonuses[i]) {
2611  LOG(llevError,"No bonus loaded for %s\n", int_bonus_names[i]);
2612  error=2;
2613  }
2614  }
2615 
2616  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2617  if (!new_float_bonuses[i]) {
2618  LOG(llevError,"No bonus loaded for %s\n", float_bonus_names[i]);
2619  error=2;
2620  }
2621  }
2622 
2623  /* If we got an error, we just free up the data we read in and return/exit.
2624  * if no error, we make the tables we just read in into the default
2625  * tables.
2626  */
2627  if (error) {
2628  if (error==1)
2629  LOG(llevError,"Got error reading stat_bonus: %s\n", buf);
2630 
2631  if (reload) {
2632  for (i=0; i<NUM_INT_BONUSES; i++)
2633  if (new_int_bonuses[i]) FREE_AND_CLEAR(new_int_bonuses[i]);
2634  for (i=0; i<NUM_FLOAT_BONUSES; i++)
2635  if (new_float_bonuses[i]) FREE_AND_CLEAR(new_float_bonuses[i]);
2636  settings.max_stat = oldmax;
2637  } else {
2638  exit(1);
2639  }
2640  } else {
2641  /* Everything check out - now copy the data into
2642  * the live arrays.
2643  */
2644  for (i=0; i<NUM_INT_BONUSES; i++) {
2645  free(int_bonuses[i]);
2646  int_bonuses[i] = new_int_bonuses[i];
2647  new_int_bonuses[i] = NULL;
2648  }
2649 
2650  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2651  free(float_bonuses[i]);
2652  float_bonuses[i] = new_float_bonuses[i];
2653  new_float_bonuses[i] = NULL;
2654  }
2655  }
2656 }
#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:2307
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:218
int8_t Int
Definition: living.h:35
#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:482
int8_t ac
Definition: living.h:37
#define MOVE_WALK
Definition: define.h:407
int64_t check_exp_adjust(const object *op, int64_t exp)
Definition: living.c:2023
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:320
int64_t level_exp(int level, double expmul)
Definition: living.c:1846
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:2315
int get_dam_bonus(int stat)
Definition: living.c:2295
#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:288
int16_t max_level
Definition: global.h:298
int16_t maxgrace
Definition: living.h:44
void free_string(sstring str)
Definition: shstr.c:280
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4246
#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:2835
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:46
void change_luck(object *op, int value)
Definition: living.c:791
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:3061
Definition: living.h:34
int64_t check_exp_loss(const object *op, int64_t exp)
Definition: living.c:1998
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:41
uint32_t get_weight_limit(int stat)
Definition: living.c:2279
#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
static 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:42
#define MIN(x, y)
Definition: compat.h:17
int8_t Con
Definition: living.h:35
int16_t hp
Definition: living.h:39
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:1750
#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:892
int get_cha_bonus(int stat)
Definition: living.c:2267
struct obj * chosen_skill
Definition: object.h:386
void object_free_drop_inventory(object *ob)
Definition: object.c:1368
int change_abil(object *op, object *tmp)
Definition: living.c:394
#define NDI_RED
Definition: newclient.h:224
int16_t maxhp
Definition: living.h:40
#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:1791
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:2291
static int load_table_int(int **bonuses, FILE *fp, char *bonus_name)
Definition: living.c:2355
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2690
#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:2052
#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:35
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:579
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:712
int16_t dam
Definition: living.h:45
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.c:121
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:2303
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:480
int allowed_class(const object *op)
Definition: living.c:1623
#define INT_WEIGHT_LIMIT
Definition: living.c:66
static float get_sp_bonus(int stat)
Definition: living.c:2311
struct obj * current_weapon
Definition: object.h:370
#define PERM_EXP_GAIN_RATIO
Definition: config.h:287
#define MAX_PLAYER_SPEED
Definition: config.h:97
int8_t Cha
Definition: living.h:35
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:863
#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:1690
static float * float_bonuses[NUM_FLOAT_BONUSES]
Definition: living.c:90
int8_t luck
Definition: living.h:38
#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:2638
#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:2100
#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:36
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:35
void calc_perm_exp(object *op)
Definition: living.c:1862
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:1895
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:599
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:679
int16_t grace
Definition: living.h:43
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:35
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:2511
static size_t get_index(int stat, size_t max_index)
Definition: living.c:2327
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:2431
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:2150
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:35
#define NDI_UNIQUE
Definition: newclient.h:245
int get_cleric_chance(int stat)
Definition: living.c:2287
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:2283
int did_make_save(const object *op, int level, int bonus)
Definition: living.c:2199
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:1650
#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:2229
void remove_statbonus(object *op)
Definition: living.c:840
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:4168
#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:2271
int16_t level
Definition: object.h:351
#define FLOAT_GRACE_BONUS
Definition: living.c:88
void fix_object(object *op)
Definition: living.c:1118
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:2299
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:1129
int16_t digestion
Definition: player.h:112
const char * name
Definition: object.h:466
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:705
void object_remove(object *op)
Definition: object.c:1654
#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:47
Definition: object.h:224
int get_thaco_bonus(int stat)
Definition: living.c:2275
void check_stat_bounds(living *stats, int8_t min_stat, int8_t max_stat)
Definition: living.c:354