Crossfire Server, Trunk  1.75.0
living.cpp
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 <assert.h>
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "global.h"
28 #include "sproto.h"
29 #include "living.h"
30 
31 static float get_con_bonus(int stat);
32 static float get_sp_bonus(int stat);
33 static float get_grace_bonus(int stat);
34 static size_t get_index(int stat, size_t max_index);
35 
41 #define ADD_EXP(exptotal, exp) do { exptotal += exp; if (exptotal > MAX_EXPERIENCE) exptotal = MAX_EXPERIENCE; } while (0)
42 #define ADD_TOTALEXP(exptotal, exp) do { exptotal += exp; if (exptotal > MAX_TOTAL_EXPERIENCE) exptotal = MAX_TOTAL_EXPERIENCE; } while(0)
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  /* Check that the attribute value isn't already min or max value. */
697  int curr_val = get_attr_value(&op->stats, j);
698  if ((curr_val > MIN_STAT && i < 0) || (curr_val < settings.max_stat && i > 0)) {
699  success = 1;
701  }
702  }
703  }
704  }
705  return success;
706 }
707 
716 void drain_stat(object *op) {
718 }
719 
728 void drain_specific_stat(object *op, int deplete_stats) {
729  object *tmp;
730  archetype *at;
731 
733  if (!at) {
734  return;
735  } else {
736  tmp = arch_present_in_ob(at, op);
737  if (!tmp) {
738  tmp = arch_to_object(at);
739  tmp = object_insert_in_ob(tmp, op);
740  SET_FLAG(tmp, FLAG_APPLIED);
741  }
742  }
743 
745  change_attr_value(&tmp->stats, deplete_stats, -1);
746  fix_object(op);
747 }
748 
755 int remove_depletion(object *op, int level) {
756  object *depl;
757  archetype *at;
758  int i, count = 0;
759 
760  if ((at = find_archetype(ARCH_DEPLETION)) == NULL) {
761  return 0;
762  }
763 
764  depl = arch_present_in_ob(at, op);
765 
766  if (depl == NULL)
767  return 0;
768 
769  if (level != -1 && level < op->level)
770  return 0;
771 
772  for (i = 0; i < NUM_STATS; i++) {
773  if (get_attr_value(&depl->stats, i)) {
774  count++;
777  }
778  }
779 
780  object_remove(depl);
782  fix_object(op);
783 
784  return (count == 0) ? 0 : 1;
785 }
786 
796 void change_luck(object *op, int value) {
797  object *tmp;
798  archetype *at;
799  int new_luck;
800 
801  at = find_archetype("luck");
802  if (!at)
803  ;
804  else {
805  tmp = arch_present_in_ob(at, op);
806  if (!tmp) {
807  if (!value)
808  return;
809  tmp = arch_to_object(at);
810  tmp = object_insert_in_ob(tmp, op);
811  SET_FLAG(tmp, FLAG_APPLIED);
812  }
813  if (value) {
814  /* Limit the luck value of the bad luck object to +/-100. This
815  * (arbitrary) value prevents overflows (both in the bad luck object and
816  * in op itself).
817  */
818  new_luck = tmp->stats.luck+value;
819  if (new_luck >= -100 && new_luck <= 100) {
820  op->stats.luck += value;
821  tmp->stats.luck = new_luck;
822  }
823  } else {
824  if (!tmp->stats.luck) {
825  return;
826  }
827  /* Randomly change the players luck. Basically, we move it
828  * back neutral (if greater>0, subtract, otherwise add)
829  */
830  if (RANDOM()%(FABS(tmp->stats.luck)) >= RANDOM()%30) {
831  int diff = tmp->stats.luck > 0 ? -1 : 1;
832  op->stats.luck += diff;
833  tmp->stats.luck += diff;
834  }
835  }
836  }
837 }
838 
845 void remove_statbonus(object *op) {
846  op->stats.Str -= op->arch->clone.stats.Str;
847  op->stats.Dex -= op->arch->clone.stats.Dex;
848  op->stats.Con -= op->arch->clone.stats.Con;
849  op->stats.Wis -= op->arch->clone.stats.Wis;
850  op->stats.Pow -= op->arch->clone.stats.Pow;
851  op->stats.Cha -= op->arch->clone.stats.Cha;
852  op->stats.Int -= op->arch->clone.stats.Int;
853  op->contr->orig_stats.Str -= op->arch->clone.stats.Str;
854  op->contr->orig_stats.Dex -= op->arch->clone.stats.Dex;
855  op->contr->orig_stats.Con -= op->arch->clone.stats.Con;
856  op->contr->orig_stats.Wis -= op->arch->clone.stats.Wis;
857  op->contr->orig_stats.Pow -= op->arch->clone.stats.Pow;
858  op->contr->orig_stats.Cha -= op->arch->clone.stats.Cha;
859  op->contr->orig_stats.Int -= op->arch->clone.stats.Int;
860 }
861 
868 void add_statbonus(object *op) {
869  op->stats.Str += op->arch->clone.stats.Str;
870  op->stats.Dex += op->arch->clone.stats.Dex;
871  op->stats.Con += op->arch->clone.stats.Con;
872  op->stats.Wis += op->arch->clone.stats.Wis;
873  op->stats.Pow += op->arch->clone.stats.Pow;
874  op->stats.Cha += op->arch->clone.stats.Cha;
875  op->stats.Int += op->arch->clone.stats.Int;
876  op->contr->orig_stats.Str += op->arch->clone.stats.Str;
877  op->contr->orig_stats.Dex += op->arch->clone.stats.Dex;
878  op->contr->orig_stats.Con += op->arch->clone.stats.Con;
879  op->contr->orig_stats.Wis += op->arch->clone.stats.Wis;
880  op->contr->orig_stats.Pow += op->arch->clone.stats.Pow;
881  op->contr->orig_stats.Cha += op->arch->clone.stats.Cha;
882  op->contr->orig_stats.Int += op->arch->clone.stats.Int;
883 }
884 
897 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)
898 {
899  int pl_level, i;
900  float maxhp, tmpf;
901 
902  if (op->type != PLAYER)
903  return;
904 
906  pl_level = op->level;
907 
908  if (pl_level < 1)
909  pl_level = 1; /* safety, we should always get 1 levels worth of hp! */
910 
911  /*
912  * We store maxhp as a float to hold any fractional hp bonuses,
913  * (eg, 2.5 con bonus). While it may seem simpler to just
914  * do a get_con_bonus() * min(level,10), there is also a 1 hp/level
915  * minimum (including bonus), se we have to do the logic on a
916  * level basis.
917  */
918  maxhp = 0.0;
919  for (i = 1, op->stats.maxhp = 0; i <= pl_level && i <= 10; i++) {
920 
921  tmpf = op->contr->levhp[i]+get_con_bonus(op->stats.Con);
922 
923  /* always get at least 1 hp/level */
924  if (tmpf < 1.0) tmpf = 1.0;
925 
926  maxhp += tmpf;
927  }
928 
929  /* Add 0.5 so that this rounds normally - the cast just drops the
930  * fraction, so 1.5 becomes 1.
931  */
932  op->stats.maxhp = (int)(maxhp + 0.5);
933  if (op->level > 10)
934  op->stats.maxhp += 2 * (op->level - 10);
935 
936  op->stats.maxhp += op->arch->clone.stats.maxhp;
937 
938  if (op->stats.hp > op->stats.maxhp)
939  op->stats.hp = op->stats.maxhp;
940 
941  /* Sp gain is controlled by the level of the player's
942  * relevant experience object (mana_obj, see above) */
943 
944  /* set maxsp */
945  if (!mana_obj || !mana_obj->level) {
946  op->stats.maxsp = 1;
947  } else {
948  float sp_tmp = 0.0, mana_bonus;
949  int mana_lvl_max;
950 
951  mana_lvl_max = (mana_obj->level >10 ? 10: mana_obj->level);
952  mana_bonus = (2.0*get_sp_bonus(op->stats.Pow)+get_sp_bonus(op->stats.Int)) / 3.0;
953 
954  for (i = 1; i <= mana_lvl_max; i++) {
955  float stmp;
956 
957  stmp = op->contr->levsp[i] + mana_bonus;
958 
959  /* Got some extra bonus at first level */
960  if (i == 1) stmp += mana_bonus;
961 
962  if (stmp < 1.0)
963  stmp = 1.0;
964 
965  sp_tmp += stmp;
966  }
967 
968  op->stats.maxsp = (int)sp_tmp+op->arch->clone.stats.maxsp;
969 
970  if (mana_obj->level > 10)
971  op->stats.maxsp += 2 * (mana_obj->level - 10);
972  }
973 
974  /* Characters can get their sp supercharged via rune of transferrance */
975  if (op->stats.sp > op->stats.maxsp*2)
976  op->stats.sp = op->stats.maxsp*2;
977 
978  /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */
979  if (!grace_obj || !grace_obj->level) {
980  op->stats.maxgrace = 1;
981  } else {
982  /* store grace in a float - this way, the divisions below don't create
983  * big jumps when you go from level to level - with int's, it then
984  * becomes big jumps when the sums of the bonuses jump to the next
985  * step of 8 - with floats, even fractional ones are useful.
986  */
987  float sp_tmp = 0.0, grace_bonus;
988 
989  grace_bonus = (get_grace_bonus(op->stats.Pow)+2.0*get_grace_bonus(op->stats.Wis)) / 3.0;
990 
991  for (i = 1; i <= grace_obj->level && i <= 10; i++) {
992  float grace_tmp = op->contr->levgrace[i] + grace_bonus;
993 
994  /* Got some extra bonus at first level */
995  if (i == 1)
996  grace_tmp += grace_bonus;
997 
998  if (grace_tmp < 1.0)
999  grace_tmp = 1.0;
1000  sp_tmp += grace_tmp;
1001  }
1002  op->stats.maxgrace = (int)sp_tmp+op->arch->clone.stats.maxgrace;
1003 
1004  /* two grace points per level after 11 */
1005  if (grace_obj->level > 10)
1006  op->stats.maxgrace += 2 * (grace_obj->level - 10);
1007  }
1008  /* No limit on grace vs maxgrace */
1009 
1010  if (op->contr->braced) {
1011  (*ac) += 2;
1012  (*wc) += 4;
1013  } else
1014  (*ac) -= get_dex_bonus(op->stats.Dex);
1015 
1016  /* In new exp/skills system, wc bonuses are related to
1017  * the players level in a relevant exp object (wc_obj)
1018  * not the general player level -b.t.
1019  * I changed this slightly so that wc bonuses are better
1020  * than before. This is to balance out the fact that
1021  * the player no longer gets a personal weapon w/ 1
1022  * improvement every level, now its fighterlevel/5. So
1023  * we give the player a bonus here in wc and dam
1024  * to make up for the change. Note that I left the
1025  * monster bonus the same as before. -b.t.
1026  */
1027 
1028  if (wc_obj && wc_obj->level >= 1) {
1029  const char *wc_in = object_get_value(wc_obj, "wc_increase_rate");
1030  int wc_increase_rate;
1031 
1032  wc_increase_rate = wc_in?atoi(wc_in):5;
1033  assert(wc_increase_rate != 0);
1034 
1035  (*wc) -= get_thaco_bonus(op->stats.Str);
1036  (*wc) -= (wc_obj->level-1)/wc_increase_rate;
1037  op->stats.dam += (wc_obj->level-1)/4;
1038  } else {
1039  (*wc) -= (((op->level-1)/5)+get_thaco_bonus(op->stats.Str));
1040  }
1041  op->stats.dam += get_dam_bonus(op->stats.Str);
1042 
1043  if (op->stats.dam < 1)
1044  op->stats.dam = 1;
1045 
1046  /* Player speed is nominally 0.75, plus a bonus read in from the file
1047  * lib/config/stat_bonus. Currently, this is roughly:
1048  * -0.1 for Dex 1
1049  * +0 for Dex 8 through 11
1050  * +0.25 for Dex 30.
1051  */
1052 
1054 
1055  if (settings.search_items && op->contr->search_str[0])
1056  op->speed -= 0.25;
1057 
1058  if (op->attacktype == 0)
1059  op->attacktype = op->arch->clone.attacktype;
1060 
1061 
1062  /* First block is for encumbrance of the player */
1063 
1064  /* The check for FREE_PLAYER_LOAD_PERCENT < 1.0 is really a safety. One would
1065  * think that it should never be the case if that is set to 1.0, that carrying
1066  * would be above the limit. But it could be if character is weakened and
1067  * was otherwise at limit. Without that safety, could get divide by zeros.
1068  */
1070  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
1071  int extra_weight = op->carrying-get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT;
1072 
1073  op->contr->character_load = (float)extra_weight/(float)(get_weight_limit(op->stats.Str)*(1.0-FREE_PLAYER_LOAD_PERCENT));
1074 
1075  /* character_load is used for weapon speed below, so sanitize value */
1076  if (op->contr->character_load >= 1.0)
1077  op->contr->character_load = 1.0;
1078 
1079  /* If a character is fully loaded, they will always get cut down to min
1080  * speed no matter what their dex. Note that magic is below, so
1081  * still helps out.
1082  */
1083  if (op->speed > MAX_PLAYER_SPEED)
1084  op->speed -= op->contr->character_load*(op->speed-MIN_PLAYER_SPEED);
1085  else
1087  } else {
1088  op->contr->character_load = 0;
1089  }
1090 
1091  /* This block is for weapon speed */
1092  op->weapon_speed = BASE_WEAPON_SPEED+get_speed_bonus(op->stats.Dex)-weapon_speed/20.0+added_speed/10.0;
1093  if (wc_obj) {
1094  op->weapon_speed += 0.005*wc_obj->level;
1095  } else
1096  op->weapon_speed += 0.005*op->level;
1097 
1098  /* character_load=1.0 means character is fully loaded, 0.0 is unloaded. Multiplying
1099  * by 0.2 for penalty is purely arbitrary, but slows things down without completely
1100  * stopping them.
1101  */
1102  op->weapon_speed -= op->contr->character_load*0.2;
1103 
1104  if (op->weapon_speed < 0.05)
1105  op->weapon_speed = 0.05;
1106 
1107  /* It is quite possible that a player's spell costing might have changed,
1108  * so we will check that now.
1109  */
1111 }
1132 void fix_object(object *op) {
1133  int i;
1134  float max = 9, added_speed = 0, speed_reduce_from_disease = 1;
1135  int weapon_speed = 0;
1136  int best_wc = 0, best_ac = 0, wc = 0, ac = 0;
1137  int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS];
1138  const object *grace_obj = NULL, *mana_obj = NULL, *wc_obj = NULL;
1139 
1140  /* First task is to clear all the values back to their original values */
1141  if (op->type == PLAYER) {
1142  for (i = 0; i < NUM_STATS; i++) {
1143  set_attr_value(&(op->stats), i, get_attr_value(&(op->contr->orig_stats), i));
1144  set_attr_value(&(op->contr->applied_stats), i, 0);
1145  }
1147  op->contr->encumbrance = 0;
1148 
1149  op->attacktype = 0;
1150  op->contr->digestion = 0;
1151  op->contr->gen_hp = 0;
1152  op->contr->gen_sp = 0;
1153  op->contr->gen_grace = 0;
1154  op->contr->gen_sp_armour = 10;
1155  op->contr->item_power = 0;
1156 
1157  /* Don't clobber all the range_ values. range_golem otherwise
1158  * gets reset for no good reason, and we don't want to reset
1159  * range_magic (what spell is readied). These three below
1160  * well get filled in based on what the player has equipped.
1161  */
1162  op->contr->ranges[range_bow] = NULL;
1163  op->contr->ranges[range_builder] = NULL;
1164  op->contr->ranges[range_misc] = NULL;
1165  op->contr->ranges[range_skill] = NULL;
1166  } /* If player */
1167  memcpy(op->body_used, op->body_info, sizeof(op->body_info));
1168 
1169  if (op->slaying != NULL) {
1170  free_string(op->slaying);
1171  op->slaying = NULL;
1172  }
1173  if (!QUERY_FLAG(op, FLAG_WIZ)) {
1174  CLEAR_FLAG(op, FLAG_XRAYS);
1176  }
1177 
1179  CLEAR_FLAG(op, FLAG_STEALTH);
1180  CLEAR_FLAG(op, FLAG_BLIND);
1182  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_SPELL))
1184  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_MISSILE))
1186  if (!QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
1187  CLEAR_FLAG(op, FLAG_UNDEAD);
1188  if (!QUERY_FLAG(&op->arch->clone, FLAG_SEE_IN_DARK))
1190  CLEAR_FLAG(op, FLAG_PROBE);
1191 
1192  op->path_attuned = op->arch->clone.path_attuned;
1194  op->path_denied = op->arch->clone.path_denied;
1195  op->glow_radius = op->arch->clone.glow_radius;
1196  op->move_type = op->arch->clone.move_type;
1197  op->chosen_skill = NULL;
1198 
1199  /* initializing resistances from the values in player/monster's
1200  * archetype clone
1201  */
1202  memcpy(&op->resist, &op->arch->clone.resist, sizeof(op->resist));
1203 
1204  for (i = 0; i < NROFATTACKS; i++) {
1205  if (op->resist[i] > 0)
1206  prot[i] = op->resist[i], vuln[i] = 0;
1207  else
1208  vuln[i] = -(op->resist[i]), prot[i] = 0;
1209  potion_resist[i] = 0;
1210  }
1211 
1212  wc = op->arch->clone.stats.wc;
1213  op->stats.dam = op->arch->clone.stats.dam;
1214 
1215  /* for players which cannot use armour, they gain AC -1 per 3 levels,
1216  * plus a small amount of physical resist, those poor suckers. ;)
1217  * the fact that maxlevel is factored in could be considered sort of bogus -
1218  * we should probably give them some bonus and cap it off - otherwise,
1219  * basically, if a server updates its max level, these playes may find
1220  * that their protection from physical goes down
1221  */
1222  if (!QUERY_FLAG(op, FLAG_USE_ARMOUR) && op->type == PLAYER) {
1223  ac = MAX(-10, op->arch->clone.stats.ac-op->level/3);
1224  prot[ATNR_PHYSICAL] += ((100-prot[AT_PHYSICAL])*(80*op->level/settings.max_level))/100;
1225  } else
1226  ac = op->arch->clone.stats.ac;
1227 
1228  op->stats.luck = op->arch->clone.stats.luck;
1229  op->speed = op->arch->clone.speed;
1230 
1231  /* OK - we've reset most all the objects attributes to sane values.
1232  * now go through and make adjustments for what the player has equipped.
1233  */
1234 
1235  FOR_INV_PREPARE(op, tmp) {
1236  /* See note in map.c:update_position about making this additive
1237  * since light sources are never applied, need to put check here.
1238  */
1239  if (tmp->glow_radius > op->glow_radius)
1240  op->glow_radius = tmp->glow_radius;
1241 
1242  /* This happens because apply_potion calls change_abil with the potion
1243  * applied so we can tell the player what chagned. But change_abil
1244  * then calls this function.
1245  */
1246  if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION)
1247  continue;
1248 
1249  /* For some things, we don't care what is equipped */
1250  if (tmp->type == SKILL) {
1251  /* Want to take the highest skill here. */
1252  if (IS_MANA_SKILL(tmp->subtype)) {
1253  if (!mana_obj)
1254  mana_obj = tmp;
1255  else if (tmp->level > mana_obj->level)
1256  mana_obj = tmp;
1257  }
1258  if (IS_GRACE_SKILL(tmp->subtype)) {
1259  if (!grace_obj)
1260  grace_obj = tmp;
1261  else if (tmp->level > grace_obj->level)
1262  grace_obj = tmp;
1263  }
1264  }
1265 
1266  /* Container objects are not meant to adjust a players, but other applied
1267  * objects need to make adjustments.
1268  * This block should handle all player specific changes
1269  * The check for Praying is a bit of a hack - god given bonuses are put
1270  * in the praying skill, and the player should always get those.
1271  * It also means we need to put in additional checks for applied below,
1272  * because the skill shouldn't count against body positions being used
1273  * up, etc.
1274  */
1275  if ((QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type != CONTAINER && tmp->type != CLOSE_CON)
1276  || (tmp->type == SKILL && tmp->subtype == SK_PRAYING)) {
1277  if (op->type == PLAYER) {
1278  if (tmp->type == BOW)
1279  op->contr->ranges[range_bow] = tmp;
1280 
1281  if (tmp->type == WAND || tmp->type == ROD)
1282  op->contr->ranges[range_misc] = tmp;
1283 
1284  if (tmp->type == BUILDER)
1285  op->contr->ranges[range_builder] = tmp;
1286 
1287  for (i = 0; i < NUM_STATS; i++) {
1288  int8_t value;
1289 
1290  value = get_attr_value(&(tmp->stats), i);
1291  change_attr_value(&(op->stats), i, value);
1292  if (strcmp(tmp->arch->clone.name, ARCH_DEPLETION) != 0)
1293  change_attr_value(&(op->contr->applied_stats), i, value);
1294  }
1295 
1296  /* For this temporary calculation, allow wider range of stat - if we have
1297  * having that gives +5 and different object that gives -5 and stat
1298  * is maxed, we don't want different results based on order of
1299  * inventory.
1300  */
1301  check_stat_bounds(&(tmp->stats), -settings.max_stat, 2*settings.max_stat);
1302 
1303  /* these are the items that currently can change digestion, regeneration,
1304  * spell point recovery and mana point recovery. Seems sort of an arbitary
1305  * list, but other items store other info into stats array.
1306  */
1307  switch (tmp->type)
1308  {
1309  case WEAPON:
1310  case ARMOUR:
1311  case HELMET:
1312  case SHIELD:
1313  case RING:
1314  case BOOTS:
1315  case GLOVES:
1316  case AMULET:
1317  case GIRDLE:
1318  case BRACERS:
1319  case CLOAK:
1320  case DISEASE:
1321  case FORCE:
1322  case SKILL:
1323  op->contr->digestion += tmp->stats.food;
1324  op->contr->gen_hp += tmp->stats.hp;
1325  op->contr->gen_sp += tmp->stats.sp;
1326  op->contr->gen_grace += tmp->stats.grace;
1327  op->contr->gen_sp_armour += tmp->gen_sp_armour;
1328  /*FALLTHROUGH*/
1329 
1330  /* Bow and skill utils need to update item_power specifically.
1331  * This should fix bug #648
1332  * Neila Hawkins 2017-08-09
1333  */
1334  case BOW:
1335  case SKILL_TOOL:
1336  op->contr->item_power += tmp->item_power;
1337  break;
1338  }
1339  } /* if this is a player */
1340 
1341  /* Update slots used for items */
1342  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1343  for (i = 0; i < NUM_BODY_LOCATIONS; i++)
1344  op->body_used[i] += tmp->body_info[i];
1345  }
1346 
1347  if (tmp->type == SYMPTOM && tmp->last_sp) {
1348  /* Should take the worst disease of the bunch */
1349  if (((float)tmp->last_sp/100.0) < speed_reduce_from_disease)
1350  speed_reduce_from_disease = (float)tmp->last_sp/100.0;
1351  }
1352 
1353  /* Pos. and neg. protections are counted separate (-> pro/vuln).
1354  * (Negative protections are calculated extactly like positive.)
1355  * Resistance from potions are treated special as well. If there's
1356  * more than one potion-effect, the bigger prot.-value is taken.
1357  */
1358  if (tmp->type != POTION) {
1359  for (i = 0; i < NROFATTACKS; i++) {
1360  /* Potential for cursed potions, in which case we just can use
1361  * a straight MAX, as potion_resist is initialized to zero.
1362  */
1363  if (tmp->type == POTION_RESIST_EFFECT) {
1364  if (potion_resist[i])
1365  potion_resist[i] = MAX(potion_resist[i], tmp->resist[i]);
1366  else
1367  potion_resist[i] = tmp->resist[i];
1368  } else if (tmp->resist[i] > 0)
1369  prot[i] += ((100-prot[i])*tmp->resist[i])/100;
1370  else if (tmp->resist[i] < 0)
1371  vuln[i] += ((100-vuln[i])*(-tmp->resist[i]))/100;
1372  }
1373  }
1374 
1375  /* There may be other things that should not adjust the attacktype */
1376  if (tmp->type != BOW && tmp->type != SYMPTOM)
1377  op->attacktype |= tmp->attacktype;
1378 
1379  op->path_attuned |= tmp->path_attuned;
1380  op->path_repelled |= tmp->path_repelled;
1381  op->path_denied |= tmp->path_denied;
1382  op->stats.luck += tmp->stats.luck;
1383  op->move_type |= tmp->move_type;
1384 
1385  if (QUERY_FLAG(tmp, FLAG_LIFESAVE))
1386  SET_FLAG(op, FLAG_LIFESAVE);
1387  if (QUERY_FLAG(tmp, FLAG_REFL_SPELL))
1389  if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE))
1391  if (QUERY_FLAG(tmp, FLAG_STEALTH))
1392  SET_FLAG(op, FLAG_STEALTH);
1393  if (QUERY_FLAG(tmp, FLAG_XRAYS))
1394  SET_FLAG(op, FLAG_XRAYS);
1395  if (QUERY_FLAG(tmp, FLAG_BLIND))
1396  SET_FLAG(op, FLAG_BLIND);
1397  if (QUERY_FLAG(tmp, FLAG_SEE_IN_DARK))
1399  if (QUERY_FLAG(tmp, FLAG_PROBE))
1400  SET_FLAG(op, FLAG_PROBE);
1401 
1402  // Items can make the wielder confused.
1403  if (QUERY_FLAG(tmp, FLAG_CONFUSED))
1404  SET_FLAG(op, FLAG_CONFUSED);
1405 
1406  if (QUERY_FLAG(tmp, FLAG_UNDEAD) && !QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
1407  SET_FLAG(op, FLAG_UNDEAD);
1408 
1409  if (QUERY_FLAG(tmp, FLAG_MAKE_INVIS)) {
1411  op->invisible = 1;
1412  }
1413 
1414  if (tmp->stats.exp && tmp->type != SKILL) {
1415  added_speed += (float)tmp->stats.exp/3.0;
1416  }
1417 
1418  switch (tmp->type) {
1419  /* skills modifying the character -b.t. */
1420  /* for all skills and skill granting objects */
1421  case SKILL:
1422  if (!QUERY_FLAG(tmp, FLAG_APPLIED))
1423  break;
1424 
1425  if (IS_COMBAT_SKILL(tmp->subtype))
1426  wc_obj = tmp;
1427 
1428  if (op->chosen_skill) {
1429  LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name);
1430  }
1431  op->chosen_skill = tmp;
1432  if (tmp->stats.dam > 0) { /* skill is a 'weapon' */
1433  if (!QUERY_FLAG(op, FLAG_READY_WEAPON))
1434  weapon_speed = (int)WEAPON_SPEED(tmp);
1435  if (weapon_speed < 0)
1436  weapon_speed = 0;
1437  op->stats.dam += tmp->stats.dam*(1+(op->chosen_skill->level/9));
1438  op->stats.dam += tmp->magic;
1439  }
1440  if (tmp->stats.wc)
1441  wc -= (tmp->stats.wc+tmp->magic);
1442 
1443  if (tmp->slaying != NULL) {
1444  if (op->slaying != NULL)
1445  free_string(op->slaying);
1446  add_refcount(op->slaying = tmp->slaying);
1447  }
1448 
1449  if (tmp->stats.ac)
1450  ac -= (tmp->stats.ac+tmp->magic);
1451  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1452  op->contr->encumbrance += (int)3*tmp->weight/1000;
1453  if (op->type == PLAYER)
1454  op->contr->ranges[range_skill] = op;
1455  break;
1456 
1457  case SKILL_TOOL:
1458  if (op->chosen_skill) {
1459  LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name);
1460  }
1461  op->chosen_skill = tmp;
1462  if (op->type == PLAYER)
1463  op->contr->ranges[range_skill] = op;
1464  break;
1465 
1466  case SHIELD:
1467  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1468  op->contr->encumbrance += (int)tmp->weight/2000;
1469  /* fall through */
1470  case RING:
1471  case AMULET:
1472  case GIRDLE:
1473  case HELMET:
1474  case BOOTS:
1475  case GLOVES:
1476  case CLOAK:
1477  if (tmp->stats.wc)
1478  wc -= tmp->stats.wc;
1479  if (tmp->stats.dam)
1480  op->stats.dam += (tmp->stats.dam+tmp->magic);
1481  if (tmp->stats.ac)
1482  ac -= (tmp->stats.ac+tmp->magic);
1483  break;
1484 
1485  case WEAPON:
1486  wc -= tmp->stats.wc;
1487  if (tmp->stats.ac && tmp->stats.ac+tmp->magic > 0)
1488  ac -= tmp->stats.ac+tmp->magic;
1489  op->stats.dam += (tmp->stats.dam+tmp->magic);
1490  weapon_speed = ((int)WEAPON_SPEED(tmp)*2-tmp->magic)/2;
1491  if (weapon_speed < 0)
1492  weapon_speed = 0;
1493  if (tmp->slaying != NULL) {
1494  if (op->slaying != NULL)
1495  free_string(op->slaying);
1496  add_refcount(op->slaying = tmp->slaying);
1497  }
1498  /* If there is desire that two handed weapons should do
1499  * extra strength damage, this is where the code should
1500  * go.
1501  */
1502  op->current_weapon = tmp;
1503  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1504  op->contr->encumbrance += (int)3*tmp->weight/1000;
1505  break;
1506 
1507  case ARMOUR: /* Only the best of these three are used: */
1508  if (settings.spell_encumbrance == TRUE && op->type == PLAYER)
1509  op->contr->encumbrance += (int)tmp->weight/1000;
1510 
1511  /* ARMOUR falls through to here */
1512  /* fall through */
1513  case BRACERS:
1514  case FORCE:
1515  // Code simplification to reduce branching -- we don't need to sub/add ac and wc all the time.
1516  // Neila Hawkins 2018-05-28
1517  if (tmp->stats.wc) {
1518  // Since we are alreay here, make sure wc stacking also occurs for serpentman players.
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  wc -= tmp->stats.wc;
1525  }
1526  else if (best_wc < tmp->stats.wc) {
1527  wc += best_wc;
1528  best_wc = tmp->stats.wc;
1529  wc -= tmp->stats.wc;
1530  }
1531  }
1532  if (tmp->stats.ac) {
1533  /*
1534  * If we have a serpentman player, then we do bracers differently
1535  * to allow for both bracers they equip to apply to ac, instead of only the best.
1536  *
1537  * Neila Hawkins 2018-05-28
1538  */
1539  if (tmp->type == BRACERS && op->type == PLAYER && op->arch->name && strcmp(op->arch->name, "serpentman_player") == 0)
1540  {
1541  // Apply ac from bracers directly.
1542  // This also grants the side effect that armor and bracer ac are separate for serpentmen,
1543  // But that should be better than the extra bracers being mostly useless.
1544  ac -= tmp->stats.ac+tmp->magic;
1545  }
1546  else if (best_ac < tmp->stats.ac+tmp->magic) {
1547  ac += best_ac; /* Remove last bonus */
1548  best_ac = tmp->stats.ac+tmp->magic;
1549  ac -= (tmp->stats.ac+tmp->magic);
1550  }
1551  }
1552  if (tmp->stats.dam && tmp->type == BRACERS)
1553  op->stats.dam += (tmp->stats.dam+tmp->magic);
1554  if (ARMOUR_SPEED(tmp) && ARMOUR_SPEED(tmp)/10.0 < max)
1555  max = ARMOUR_SPEED(tmp)/10.0;
1556  break;
1557  } /* switch tmp->type */
1558  } /* item is equipped */
1559  } FOR_INV_FINISH(); /* for loop of items */
1560 
1561  /* We've gone through all the objects the player has equipped. For many things, we
1562  * have generated intermediate values which we now need to assign.
1563  */
1564 
1565  /* 'total resistance = total protections - total vulnerabilities'.
1566  * If there is an uncursed potion in effect, granting more protection
1567  * than that, we take: 'total resistance = resistance from potion'.
1568  * If there is a cursed (and no uncursed) potion in effect, we take
1569  * 'total resistance = vulnerability from cursed potion'.
1570  */
1571  for (i = 0; i < NROFATTACKS; i++) {
1572  op->resist[i] = prot[i]-vuln[i];
1573  if (potion_resist[i]
1574  && ((potion_resist[i] > op->resist[i]) || (potion_resist[i] < 0)))
1575  op->resist[i] = potion_resist[i];
1576  }
1577 
1578  fix_player(op, &ac, &wc, grace_obj, mana_obj, wc_obj, weapon_speed, added_speed);
1579 
1580  op->speed = op->speed*speed_reduce_from_disease;
1581 
1582  /* Max is determined by armour */
1583  if (op->speed > max)
1584  op->speed = max;
1585 
1586  op->speed += added_speed/10.0;
1587 
1588 
1589  /* Put a lower limit on speed. Note with this speed, you move once every
1590  * 20 ticks or so. This amounts to once every 3 seconds of realtime.
1591  */
1592  if (op->speed < 0.05 && op->type == PLAYER)
1593  op->speed = 0.05;
1594  /*
1595  * A lower limit should also apply to monsters. By ensuring speeds are
1596  * loaded in as positive, we can properly do this. Otherwise, slow effects
1597  * either push the monster's speed to be more negative (if we leave the input
1598  * speed as negative) or can reduce the speed by more than the monster's speed,
1599  * making it turn negative (if we load speeds as positive always).
1600  *
1601  * For whatever reason, op->type is 0 for some monsters, making FLAG_MONSTER
1602  * the useful information that indicates whether it is a monster or not.
1603  *
1604  * MIN_ACTIVE_SPEED is super low, so make our speed threshold be about .005 instead
1605  */
1606  if (op->speed < MIN_ACTIVE_SPEED*500 && QUERY_FLAG(op, FLAG_MONSTER)) {
1607  // If added_speed is less than zero, we're probably working with a slow effect.
1608  if (added_speed > 0)
1609  LOG(llevInfo, "fix_object: Monster %s has negative speed of %f.\n",
1610  op->name ? op->name : "(null)", op->speed);
1611  op->speed = MIN_ACTIVE_SPEED*500;
1612  }
1613 
1614  /* I want to limit the power of small monsters with big weapons: */
1615  if (op->type != PLAYER
1616  && op->stats.dam > op->arch->clone.stats.dam*3)
1617  op->stats.dam = op->arch->clone.stats.dam*3;
1618 
1619  /* Prevent overflows of wc - best you can get is ABS(120) - this
1620  * should be more than enough - remember, AC is also in 8 bits,
1621  * so its value is the same.
1622  */
1623  if (wc > 120)
1624  wc = 120;
1625  else if (wc < -120)
1626  wc = -120;
1627  op->stats.wc = wc;
1628 
1629  if (ac > 120)
1630  ac = 120;
1631  else if (ac < -120)
1632  ac = -120;
1633  op->stats.ac = ac;
1634 
1635  /* if for some reason the creature doesn't have any move type,
1636  * give them walking as a default.
1637  * The second case is a special case - to more closely mimic the
1638  * old behaviour - if your flying, your not walking - just
1639  * one or the other.
1640  */
1641  if (op->move_type == 0)
1642  op->move_type = MOVE_WALK;
1643  else if (op->move_type&(MOVE_FLY_LOW|MOVE_FLY_HIGH))
1644  op->move_type &= ~MOVE_WALK;
1645 
1646  object_update_speed(op);
1647 }
1648 
1661 int allowed_class(const object *op) {
1662  return op->stats.Dex > 0
1663  && op->stats.Str > 0
1664  && op->stats.Con > 0
1665  && op->stats.Int > 0
1666  && op->stats.Wis > 0
1667  && op->stats.Pow > 0
1668  && op->stats.Cha > 0;
1669 }
1670 
1688 void set_dragon_name(object *pl, const object *abil, const object *skin) {
1689  int atnr = -1; /* attacknumber of highest level */
1690  int level = 0; /* highest level */
1691  int i;
1692 
1693  /* Perhaps do something more clever? */
1694  if (!abil || !skin)
1695  return;
1696 
1697  /* first, look for the highest level */
1698  for (i = 0; i < NROFATTACKS; i++) {
1699  if (atnr_is_dragon_enabled(i)
1700  && (atnr == -1 || abil->resist[i] > abil->resist[atnr])) {
1701  level = abil->resist[i];
1702  atnr = i;
1703  }
1704  }
1705 
1706  /* now if there are equals at highest level, pick the one with focus,
1707  or else at random */
1708  if (atnr_is_dragon_enabled(abil->stats.exp)
1709  && abil->resist[abil->stats.exp] >= level)
1710  atnr = abil->stats.exp;
1711 
1712  level = (int)(level/5.);
1713 
1714  /* now set the new title */
1715  if (pl->contr != NULL) {
1716  player_set_dragon_title(pl->contr, level, attacks[atnr], skin->resist[atnr]);
1717  }
1718 }
1719 
1728 static void dragon_level_gain(object *who) {
1729  object *abil = NULL; /* pointer to dragon ability force*/
1730  object *skin = NULL; /* pointer to dragon skin force*/
1731 
1732  /* now grab the 'dragon_ability'-forces from the player's inventory */
1733  abil = object_find_by_type_and_arch_name(who, FORCE, "dragon_ability_force");
1734  skin = object_find_by_type_and_arch_name(who, FORCE, "dragon_skin_force");
1735  /* if the force is missing -> bail out */
1736  if (abil == NULL)
1737  return;
1738 
1739  /* The ability_force keeps track of maximum level ever achieved.
1740  * New abilties can only be gained by surpassing this max level
1741  */
1742  if (who->level > abil->level) {
1743  /* increase our focused ability */
1744  int lev = ++(abil->resist[abil->stats.exp]);
1745 
1746  if (lev > 0) {
1747  /* try to hand out a new ability-gift
1748  * if not the right level, this will handout nothing.
1749  */
1750  dragon_ability_gain(who, (int)abil->stats.exp, lev);
1751  }
1752 
1753  if (abil->last_eat > 0 && atnr_is_dragon_enabled(abil->last_eat)) {
1754  /* apply new ability focus */
1756  "Your metabolism now focuses on %s!",
1757  change_resist_msg[abil->last_eat]);
1758 
1759  abil->stats.exp = abil->last_eat;
1760  abil->last_eat = 0;
1761  }
1762 
1763  abil->level = who->level;
1764  }
1765 
1766  /* last but not least, set the new title for the dragon */
1767  set_dragon_name(who, abil, skin);
1768 }
1769 
1788 object *give_skill_by_name(object *op, const char *skill_name) {
1789  object *skill_obj;
1790  archetype *skill_arch;
1791 
1792  skill_arch = get_archetype_by_skill_name(skill_name, SKILL);
1793  if (!skill_arch) {
1794  LOG(llevError, "add_player_exp: couldn't find skill %s\n", skill_name);
1795  return NULL;
1796  }
1797  skill_obj = arch_to_object(skill_arch); /* never returns NULL. */
1798 
1799  /* clear the flag - exp goes into this bucket, but player
1800  * still doesn't know it.
1801  */
1802  CLEAR_FLAG(skill_obj, FLAG_CAN_USE_SKILL);
1803  skill_obj->stats.exp = 0;
1804  skill_obj->level = 1;
1805  // If we inexplicably have the skill (due to a skill merge bug, for example)
1806  // not reassigning skill_obj results in us pointing to a freed object.
1807  skill_obj = object_insert_in_ob(skill_obj, op);
1808  if (op->contr) {
1809  link_player_skills(op);
1810  }
1811  return skill_obj;
1812 }
1813 
1830 void player_lvl_adj(object *who, object *op) {
1831  char buf[MAX_BUF];
1832 
1833  assert(who);
1834 
1835  if (!op) /* when rolling stats */
1836  op = who;
1837 
1838  if (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1, who->expmul)) {
1839  do{
1840  op->level++;
1841 
1842  if (op == who && op->stats.exp > 1 && is_dragon_pl(who))
1843  dragon_level_gain(who);
1844 
1845  /* Only roll these if it is the player (who) that gained the level */
1846  if (op == who && (who->level < 11) && who->type == PLAYER) {
1847  who->contr->levhp[who->level] = die_roll(2, 4, who, PREFER_HIGH)+1;
1848  who->contr->levsp[who->level] = die_roll(2, 3, who, PREFER_HIGH);
1849  who->contr->levgrace[who->level] = die_roll(2, 2, who, PREFER_HIGH)-1;
1850  }
1851 
1852  fix_object(who); // TODO: Call this only once per function call?
1853  if (op->level > 1) {
1854  if (op->type != PLAYER)
1855  snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.", op->level, op->name);
1856  else
1857  snprintf(buf, sizeof(buf), "You are now level %d.", op->level);
1858 
1860  }
1861  } while (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1, who->expmul)); /* To increase more levels */
1862  } else if (op->level > 1 && op->stats.exp < level_exp(op->level, who->expmul)) {
1863  do{
1864  op->level--;
1865  fix_object(who); // TODO: Call this only once per function call?
1866  if (op->type != PLAYER)
1867  snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.", op->level, op->name);
1868  else
1869  snprintf(buf, sizeof(buf), "You are now level %d.", op->level);
1870 
1872  } while (op->level > 1 && op->stats.exp < level_exp(op->level, who->expmul)); /* To decrease more levels */
1873  }
1874 }
1875 
1885 int64_t level_exp(int level, double expmul) {
1886  if (level > settings.max_level)
1887  return expmul*levels[settings.max_level];
1888  return expmul*levels[level];
1889 }
1890 
1897 int exp_level(int64_t exp) {
1898  int level = 1;
1899  while ( level < settings.max_level && levels[level+1] <= exp )
1900  ++level;
1901  return level;
1902 }
1903 
1914 void calc_perm_exp(object *op) {
1915  if (op->total_exp < op->stats.exp)
1916  op->total_exp = op->stats.exp;
1917 
1918  /* Cap permanent experience. */
1919  if (op->total_exp < 0)
1920  op->total_exp = 0;
1921  else if (op->total_exp > MAX_EXPERIENCE * 100 / settings.permanent_exp_ratio)
1923 }
1924 
1932 object* find_applied_skill_by_name(const object* op, const char* name) {
1933  for (int i = 0; i < MAX_SKILLS; i++) {
1934  if (op->contr->last_skill_ob[i] != NULL) {
1935  // Skill objects can be removed without updating last_skill_ob. Clean
1936  // them up here if that's the case.
1937  if (QUERY_FLAG(op->contr->last_skill_ob[i], FLAG_REMOVED)) {
1938  LOG(llevDebug, "pruning removed object from last_skill_ob\n");
1939  op->contr->last_skill_ob[i] = NULL;
1940  continue;
1941  }
1942 
1943  if (op->contr->last_skill_ob[i]->skill != NULL) {
1944  if (!strcmp(op->contr->last_skill_ob[i]->skill, name)) {
1945  return op->contr->last_skill_ob[i];
1946  }
1947  } else {
1948  LOG(llevError,
1949  "%s's skill object %s does not have a skill name\n",
1950  op->name, op->contr->last_skill_ob[i]->name);
1951  }
1952  }
1953  }
1954  return NULL;
1955 }
1956 
1971 static void add_player_exp(object *op, int64_t exp, const char *skill_name, int flag) {
1972  object *skill_obj = NULL;
1973  int64_t limit, exp_to_add;
1974  int64_t added_skill_exp = 0, added_skill_total_exp = 0;
1975 
1976  /* prevents some forms of abuse. */
1977  if (op->contr->braced)
1978  exp = exp/5;
1979 
1980  /* Try to find the matching skill.
1981  * We do a shortcut/time saving mechanism first - see if it matches
1982  * chosen_skill. This means we don't need to search through
1983  * the players inventory.
1984  */
1985  if (skill_name) {
1986  if (op->chosen_skill
1987  && op->chosen_skill->type == SKILL
1988  && !strcmp(skill_name, op->chosen_skill->skill))
1989  skill_obj = op->chosen_skill;
1990  else {
1991  skill_obj = find_applied_skill_by_name(op, skill_name);
1992 
1993  /* Player doesn't have the skill. Check to see what to do, and give
1994  * it to the player if necessary
1995  */
1996  if (!skill_obj) {
1997  if (flag == SK_EXP_NONE)
1998  return;
1999  else if (flag == SK_EXP_ADD_SKILL)
2000  skill_obj = give_skill_by_name(op, skill_name);
2001  }
2002  }
2003  }
2004 
2005  /* Basically, you can never gain more experience in one shot
2006  * than half what you need to gain for next level.
2007  */
2008  exp_to_add = exp;
2009  /*
2010  * Make sure we aren't trying to go backwards when we hit maximum level,
2011  * but make sure we can still add to our permanent experience.
2012  *
2013  * -- Neila Hawkins 2015-05-24
2014  */
2015  if (op->level == settings.max_level)
2016  limit = levels[op->level] / 2;
2017  else
2018  limit = (levels[op->level+1]-levels[op->level])/2;
2019 
2020  if (exp_to_add > limit)
2021  exp_to_add = limit;
2022 
2023  if (skill_obj) {
2024  exp_to_add = exp;
2025  /*
2026  * Make sure we aren't trying to go backwards when we hit maximum level,
2027  * but make sure we can still add to our permanent experience.
2028  *
2029  * -- Neila Hawkins 2015-05-24
2030  */
2031  if (skill_obj->level == settings.max_level)
2032  limit = levels[skill_obj->level] / 2;
2033  else
2034  limit = (levels[skill_obj->level+1]-levels[skill_obj->level])/2;
2035 
2036  if (exp_to_add > limit)
2037  exp_to_add = limit;
2038  added_skill_exp = skill_obj->stats.exp;
2039  ADD_EXP(skill_obj->stats.exp, exp_to_add);
2040  added_skill_exp = skill_obj->stats.exp - added_skill_exp;
2042  added_skill_total_exp = skill_obj->total_exp;
2043  ADD_TOTALEXP(skill_obj->total_exp, exp_to_add);
2044  added_skill_total_exp = skill_obj->total_exp - added_skill_total_exp;
2045  calc_perm_exp(skill_obj);
2046  }
2047  player_lvl_adj(op, skill_obj);
2048  }
2049  else {
2050  added_skill_exp = added_skill_total_exp = exp_to_add;
2051  }
2052 
2053  ADD_EXP(op->stats.exp, (float)added_skill_exp*(skill_obj ? skill_obj->expmul : 1));
2055  ADD_TOTALEXP(op->total_exp, (float)added_skill_total_exp*(skill_obj ? skill_obj->expmul : 1));
2056  calc_perm_exp(op);
2057  }
2058 
2059  player_lvl_adj(op, NULL);
2060 }
2061 
2077 int64_t check_exp_loss(const object *op, int64_t exp) {
2078  int64_t del_exp;
2079 
2080  if (exp > op->stats.exp)
2081  exp = op->stats.exp;
2083  del_exp = (op->stats.exp-PERM_EXP(op->total_exp))*PERM_EXP_MAX_LOSS_RATIO;
2084  if (del_exp < 0)
2085  del_exp = 0;
2086  if (exp > del_exp)
2087  exp = del_exp;
2088  }
2089  return exp;
2090 }
2091 
2102 int64_t check_exp_adjust(const object *op, int64_t exp) {
2103  if (exp < 0)
2104  return check_exp_loss(op, exp);
2105  else
2106  return MIN(exp, MAX_EXPERIENCE-op->stats.exp);
2107 }
2108 
2131 static void subtract_player_exp(object *op, int64_t exp, const char *skill, int flag) {
2132  float fraction = (float)exp/(float)op->stats.exp;
2133  int64_t del_exp;
2134 
2135  FOR_INV_PREPARE(op, tmp)
2136  if (tmp->type == SKILL && tmp->stats.exp) {
2137  if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp(tmp->skill, skill)) {
2138  del_exp = check_exp_loss(tmp, exp);
2139  tmp->stats.exp -= del_exp;
2140  player_lvl_adj(op, tmp);
2141  } else if (flag != SK_SUBTRACT_SKILL_EXP) {
2142  /* only want to process other skills if we are not trying
2143  * to match a specific skill.
2144  */
2145  del_exp = check_exp_loss(tmp, tmp->stats.exp*fraction);
2146  tmp->stats.exp -= del_exp;
2147  player_lvl_adj(op, tmp);
2148  }
2149  }
2150  FOR_INV_FINISH();
2151  if (flag != SK_SUBTRACT_SKILL_EXP) {
2152  del_exp = check_exp_loss(op, exp);
2153  op->stats.exp -= del_exp;
2154  player_lvl_adj(op, NULL);
2155  }
2156 }
2157 
2179 void change_exp(object *op, int64_t exp, const char *skill_name, int flag) {
2180  /* safety */
2181  if (!op) {
2182  LOG(llevError, "change_exp() called for null object!\n");
2183  return;
2184  }
2185 
2186  /* if no change in exp, just return - most of the below code
2187  * won't do anything if the value is 0 anyways.
2188  */
2189  if (exp == 0)
2190  return;
2191 
2192  /* Monsters are easy - we just adjust their exp - we
2193  * don't adjust level, since in most cases it is unrelated to
2194  * the exp they have - the monsters exp represents what its
2195  * worth.
2196  */
2197  if (op->type != PLAYER && op->type != WEAPON) {
2198  /* Sanity check */
2199  if (!QUERY_FLAG(op, FLAG_ALIVE))
2200  return;
2201 
2202  /* reset exp to max allowed value. We subtract from
2203  * MAX_EXPERIENCE to prevent overflows. If the player somehow has
2204  * more than max exp, just return.
2205  */
2206  if (exp > 0 && (op->stats.exp > (MAX_EXPERIENCE-exp))) {
2207  exp = MAX_EXPERIENCE-op->stats.exp;
2208  if (exp < 0)
2209  return;
2210  }
2211 
2212  op->stats.exp += exp;
2213  } else if (op->type == WEAPON) { /* Weapons -- this allows us to make magic weapons that get stronger the more they are used. */
2214  // Handle adding exp -- Don't use level because other stuff already uses that.
2215  // The caller should determine what percentage of base exp this is.
2216  ADD_TOTALEXP(op->total_exp, exp);
2217  if (exp > 0) {
2218  // Check for a level-up
2219  while (level_exp(op->item_power+1, 1) < op->total_exp) {
2220  ++(op->item_power);
2221  }
2222  } else { /* Removing exp allows for the weapon's power to be reset if needed. */
2223  // Recalculate level
2224  while (level_exp(op->item_power, 1) > op->total_exp) {
2225  --(op->item_power);
2226  }
2227  }
2228  } else { /* Players only */
2229  if (exp > 0)
2230  add_player_exp(op, exp, skill_name, flag);
2231  else
2232  subtract_player_exp(op, FABS(exp), skill_name, flag);
2233  }
2234 }
2235 
2244 void apply_death_exp_penalty(object *op) {
2245  int64_t loss;
2246  int64_t percentage_loss; /* defined by the setting 'death_penalty_percent' */
2247  int64_t level_loss; /* defined by the setting 'death_penalty_levels */
2248 
2249  FOR_INV_PREPARE(op, tmp)
2250  if (tmp->type == SKILL && tmp->stats.exp) {
2251  percentage_loss = tmp->stats.exp*settings.death_penalty_ratio/100;
2252  level_loss = tmp->stats.exp-levels[MAX(0, tmp->level-settings.death_penalty_level)];
2253 
2254  /* With the revised exp system, you can get cases where
2255  * losing several levels would still require that you have more
2256  * exp than you currently have - this is true if the levels
2257  * tables is a lot harder.
2258  */
2259  if (level_loss < 0)
2260  level_loss = 0;
2261 
2262  loss = check_exp_loss(tmp, MIN(level_loss, percentage_loss));
2263 
2264  tmp->stats.exp -= loss;
2265  player_lvl_adj(op, tmp);
2266  }
2267  FOR_INV_FINISH();
2268 
2269  percentage_loss = op->stats.exp*settings.death_penalty_ratio/100;
2270  level_loss = op->stats.exp-levels[MAX(0, op->level-settings.death_penalty_level)];
2271  if (level_loss < 0)
2272  level_loss = 0;
2273  loss = check_exp_loss(op, MIN(level_loss, percentage_loss));
2274 
2275  op->stats.exp -= loss;
2276  player_lvl_adj(op, NULL);
2277 }
2278 
2293 int did_make_save(const object *op, int level, int bonus) {
2294  if (level > MAX_SAVE_LEVEL)
2296 
2297  if ((random_roll(1, 20, op, PREFER_HIGH)+bonus) < savethrow[level])
2298  return 0;
2299  return 1;
2300 }
2301 
2323 void share_exp(object *op, int64_t exp, const char *skill, int flag) {
2324  int shares = 0, count = 0;
2325  player *pl;
2326  partylist *party;
2327 
2328  if (op->type != PLAYER || op->contr->party == NULL) {
2329  change_exp(op, exp, skill, 0);
2330  return;
2331  }
2332 
2333  party = op->contr->party;
2334 
2335  for (pl = first_player; pl != NULL; pl = pl->next) {
2336  if (party && pl->ob->contr->party == party && on_same_map(pl->ob, op)) {
2337  count++;
2338  shares += (pl->ob->level+4);
2339  }
2340  }
2341 
2342  assert(shares > 0);
2343 
2344  if (count == 1 || shares > exp)
2345  change_exp(op, exp, skill, flag);
2346  else {
2347  int64_t share = exp/shares, given = 0, nexp;
2348  for (pl = first_player; pl != NULL; pl = pl->next) {
2349  if (party && pl->ob->contr->party == party && on_same_map(pl->ob, op)) {
2350  nexp = (pl->ob->level+4)*share;
2351  change_exp(pl->ob, nexp, skill, SK_EXP_TOTAL);
2352  given += nexp;
2353  }
2354  }
2355  exp -= given;
2356  /* give any remainder to the player */
2357  change_exp(op, exp, skill, flag);
2358  }
2359 }
2360 
2361 int get_cha_bonus(int stat) {
2363 }
2364 
2365 int get_dex_bonus(int stat) {
2367 }
2368 
2369 int get_thaco_bonus(int stat) {
2371 }
2372 
2373 uint32_t get_weight_limit(int stat) {
2375 }
2376 
2377 int get_learn_spell(int stat) {
2379 }
2380 
2381 int get_cleric_chance(int stat) {
2383 }
2384 
2385 int get_turn_bonus(int stat) {
2387 }
2388 
2389 int get_dam_bonus(int stat) {
2391 }
2392 
2393 float get_speed_bonus(int stat) {
2395 }
2396 
2397 int get_fear_bonus(int stat) {
2399 }
2400 
2401 static float get_con_bonus(int stat) {
2403 }
2404 
2405 static float get_sp_bonus(int stat) {
2407 }
2408 
2409 static float get_grace_bonus(int stat) {
2411 }
2412 
2421 static size_t get_index(int stat, size_t max_index) {
2422  size_t index;
2423 
2424  if (stat < 0) {
2425  return 0;
2426  }
2427 
2428  index = (size_t)stat;
2429  return MIN(index, max_index);
2430 }
2431 
2449 template <typename T>
2450 static int load_table(T **bonuses, FILE *fp, char *bonus_name)
2451 {
2452  char buf[MAX_BUF], *cp;
2453  int on_stat = 0;
2454  T tmp_bonus;
2455 
2456  *bonuses = static_cast<T *>(calloc(settings.max_stat+1, sizeof(T)));
2457 
2458  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2459  if (buf[0] == '#')
2460  continue;
2461 
2462  /* Skip over empty lines */
2463  if (buf[0] == '\n')
2464  continue;
2465 
2466  /* Do not care about opening brace */
2467  if (buf[0] == '{')
2468  continue;
2469 
2470  if (buf[0] == '}') {
2471  if ((on_stat-1) != settings.max_stat) {
2472  LOG(llevError,"Number of bonus does not match max stat (%d!=%d, bonus=%s)\n",
2473  on_stat, settings.max_stat, bonus_name);
2474  return 1;
2475  }
2476  else return 0;
2477  }
2478 
2479  /* If not any of the above values, must be the stat table,
2480  * or so we hope.
2481  */
2482  cp = buf;
2483  while (*cp != 0) {
2484  /* Skip over any non numbers */
2485  while (!isdigit(*cp) && *cp!='.' && *cp!='-' && *cp!='+' && *cp != 0)
2486  cp++;
2487 
2488  if (*cp == 0) break;
2489 
2490  if (std::is_same<T, float>::value)
2491  tmp_bonus = atof(cp);
2492  else
2493  tmp_bonus = atoi(cp);
2494 
2495  if (on_stat > settings.max_stat) {
2496  LOG(llevError,"Number of bonus entries exceed max stat (line=%s, bonus=%s)\n",
2497  buf, bonus_name);
2498  return 1;
2499  }
2500  (*bonuses)[on_stat] = tmp_bonus;
2501  on_stat++;
2502 
2503  /* Skip over any digits, as that is the number we just processed */
2504  while (isdigit(*cp) || *cp=='-' || *cp=='+' || (*cp == '.' && std::is_same<T, float>::value))
2505  cp++;
2506  }
2507  }
2508  /* This should never happen - we should always get the closing brace */
2509  LOG(llevError,"Reached end of file without getting close brace? bonus=%s\n", bonus_name);
2510  return 1;
2511 }
2512 
2520 void init_stats() {
2521  char buf[MAX_BUF], *cp;
2522  int error=0, i;
2523  FILE *fp;
2524  float *new_float_bonuses[NUM_FLOAT_BONUSES];
2525  int *new_int_bonuses[NUM_INT_BONUSES];
2526 
2527  snprintf(buf, sizeof(buf), "%s/stat_bonus", settings.confdir);
2528 
2529  memset(new_int_bonuses, 0, NUM_INT_BONUSES * sizeof(int));
2530  memset(new_float_bonuses, 0, NUM_FLOAT_BONUSES * sizeof(float));
2531 
2532  if ((fp = fopen(buf, "r")) == NULL) {
2533  LOG(llevError, "Fatal error: could not open experience table (%s)\n", buf);
2534  exit(1);
2535  }
2536  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
2537  if (buf[0] == '#')
2538  continue;
2539 
2540  /* eliminate newline */
2541  if ((cp = strrchr(buf, '\n')) != NULL)
2542  *cp = '\0';
2543 
2544  /* Skip over empty lines */
2545  if (buf[0] == 0)
2546  continue;
2547 
2548  /* Skip over leading spaces */
2549  cp = buf;
2550  while (isspace(*cp) && *cp != 0)
2551  cp++;
2552 
2553  if (!strncasecmp(cp, "max_stat", 8)) {
2554  int newmax = atoi(cp+8);
2555 
2556  /* newmax must be at least MIN_STAT and we do not currently
2557  * cupport decrease max stat on the fly - why this might be possible,
2558  * bounds checking for all objects would be needed, potentionally resetting
2559  * them.
2560  * If this is a reload, then on error, we just return without doing work.
2561  * If this is initial load, having an invalid stat range is an error, so
2562  * exit the program.
2563  */
2564  if (newmax < MIN_STAT || newmax < settings.max_stat) {
2565  LOG(llevError, "Got invalid max_stat (%d) from stat_bonus file\n", newmax);
2566  fclose(fp);
2567  exit(1);
2568  }
2569  settings.max_stat = newmax;
2570  continue;
2571  }
2572  /* max_stat needs to be set before any of the bonus values - we
2573  * need to know how large to make the array.
2574  */
2575  if (settings.max_stat == 0) {
2576  LOG(llevError, "Got bonus line or otherwise unknown value before max stat! (%s)\n",
2577  buf);
2578  fclose(fp);
2579  exit(1);
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<>(&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<>(&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  exit(1);
2633  } else {
2634  /* Everything check out - now copy the data into
2635  * the live arrays.
2636  */
2637  for (i=0; i<NUM_INT_BONUSES; i++) {
2638  free(int_bonuses[i]);
2639  int_bonuses[i] = new_int_bonuses[i];
2640  new_int_bonuses[i] = NULL;
2641  }
2642 
2643  for (i=0; i<NUM_FLOAT_BONUSES; i++) {
2644  free(float_bonuses[i]);
2645  float_bonuses[i] = new_float_bonuses[i];
2646  new_float_bonuses[i] = NULL;
2647  }
2648  }
2649 }
2650 
2652  fprintf(stderr, "Int stat bonuses:\n");
2653  for (int stat = 0; stat <= settings.max_stat+1; stat++) {
2654  fprintf(stderr, "%d", stat);
2655  for (int bonus = 0; bonus < NUM_INT_BONUSES; bonus++) {
2656  fprintf(stderr, " %d", int_bonuses[bonus][stat]);
2657  }
2658  fprintf(stderr, "\n");
2659  }
2660  fprintf(stderr, "Float stat bonuses:\n");
2661  for (int stat = 0; stat <= settings.max_stat+1; stat++) {
2662  fprintf(stderr, "%d", stat);
2663  for (int bonus = 0; bonus < NUM_FLOAT_BONUSES; bonus++) {
2664  fprintf(stderr, " %g", float_bonuses[bonus][stat]);
2665  }
2666  fprintf(stderr, "\n");
2667  }
2668 }
INT_THAC0_BONUS
#define INT_THAC0_BONUS
Definition: living.cpp:65
player::gen_sp
int16_t gen_sp
Bonuses to regeneration speed of sp.
Definition: player.h:127
ADD_TOTALEXP
#define ADD_TOTALEXP(exptotal, exp)
Definition: living.cpp:42
fix_player
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)
Complement to fix_object() for player.
Definition: living.cpp:897
player::do_los
uint32_t do_los
If true, need to call update_los() in draw(), and clear.
Definition: player.h:141
living::exp
int64_t exp
Experience.
Definition: living.h:47
get_grace_bonus
static float get_grace_bonus(int stat)
Definition: living.cpp:2409
PLAYER
@ PLAYER
Definition: object.h:112
player::next
player * next
Pointer to next player, NULL if this is last.
Definition: player.h:106
global.h
get_cha_bonus
int get_cha_bonus(int stat)
Definition: living.cpp:2361
first_player
player * first_player
First player.
Definition: init.cpp:106
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
living::maxhp
int16_t maxhp
Max hit points.
Definition: living.h:41
Settings::max_level
int16_t max_level
This is read out of exp_table.
Definition: global.h:303
DIFF_MSG
#define DIFF_MSG(flag, subtype1, subtype2, msg1, msg2)
Rather than having a whole bunch of if (flag) draw.
Definition: living.cpp:369
NUM_BODY_LOCATIONS
#define NUM_BODY_LOCATIONS
Number of body locations.
Definition: object.h:15
FLAG_CONFUSED
#define FLAG_CONFUSED
Will also be unable to cast spells.
Definition: define.h:311
BOW
@ BOW
Definition: object.h:123
BRACERS
@ BRACERS
Definition: object.h:222
levels
int64_t * levels
Number of levels for which we have experience.
Definition: exp.cpp:26
find_applied_skill_by_name
object * find_applied_skill_by_name(const object *op, const char *name)
Find a skill by name using the last_skill_ob list.
Definition: living.cpp:1932
CLOSE_CON
@ CLOSE_CON
Eneq((at)csd.uu.se): Id for close_container archetype.
Definition: object.h:234
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
FABS
#define FABS(x)
Decstations have trouble with fabs()...
Definition: define.h:22
SYMPTOM
@ SYMPTOM
Definition: object.h:250
object::path_attuned
uint32_t path_attuned
Paths the object is attuned to.
Definition: object.h:353
WAND
@ WAND
Definition: object.h:225
range_bow
@ range_bow
Bow.
Definition: player.h:31
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
FLAG_UNDEAD
#define FLAG_UNDEAD
Monster is undead.
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
drain_specific_stat
void drain_specific_stat(object *op, int deplete_stats)
Drain a specified stat from op.
Definition: living.cpp:728
player::digestion
int16_t digestion
Any bonuses/penalties to digestion.
Definition: player.h:125
player
One player.
Definition: player.h:105
GLOVES
@ GLOVES
Definition: object.h:218
GIRDLE
@ GIRDLE
Definition: object.h:228
player::gen_grace
int16_t gen_grace
Bonuses to regeneration speed of grace.
Definition: player.h:129
get_fear_bonus
int get_fear_bonus(int stat)
Definition: living.cpp:2397
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
FLAG_REFL_MISSILE
#define FLAG_REFL_MISSILE
Arrows will reflect from object.
Definition: define.h:273
ARCH_DEPLETION
#define ARCH_DEPLETION
Archetype for depletion.
Definition: object.h:590
MSG_TYPE_ATTRIBUTE_ATTACKTYPE_GAIN
#define MSG_TYPE_ATTRIBUTE_ATTACKTYPE_GAIN
Atacktypes here refer to.
Definition: newclient.h:553
MSG_TYPE_ATTRIBUTE_LEVEL_GAIN
#define MSG_TYPE_ATTRIBUTE_LEVEL_GAIN
Definition: newclient.h:574
get_cleric_chance
int get_cleric_chance(int stat)
Definition: living.cpp:2381
FLOAT_SP_BONUS
#define FLOAT_SP_BONUS
Definition: living.cpp:87
FLAG_SEE_IN_DARK
#define FLAG_SEE_IN_DARK
if set ob not effected by darkness
Definition: define.h:337
MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
End of a good effect.
Definition: newclient.h:578
Settings::permanent_exp_ratio
uint8_t permanent_exp_ratio
How much exp should be 'permenant' and unable to be lost.
Definition: global.h:259
object::item_power
int8_t item_power
Power rating of the object.
Definition: object.h:372
AT_PHYSICAL
#define AT_PHYSICAL
Definition: attack.h:76
object::arch
struct archetype * arch
Pointer to archetype.
Definition: object.h:424
object::speed
float speed
Frequency of object 'moves' relative to server tick rate.
Definition: object.h:337
if
if(!(yy_init))
Definition: loader.cpp:36435
object::invisible
int16_t invisible
How much longer the object will be invis.
Definition: object.h:370
NUM_FLOAT_BONUSES
#define NUM_FLOAT_BONUSES
Definition: living.cpp:89
float_bonuses
static float * float_bonuses[NUM_FLOAT_BONUSES]
Definition: living.cpp:90
living::Dex
int8_t Dex
Definition: living.h:36
player::ob
object * ob
The object representing the player.
Definition: player.h:177
ARMOUR
@ ARMOUR
Definition: object.h:125
int_bonus_names
static const char * int_bonus_names[NUM_INT_BONUSES]
Following array corresponds to the defines above, but are the text names as found in the file.
Definition: living.cpp:75
give_skill_by_name
object * give_skill_by_name(object *op, const char *skill_name)
Given the skill name skill_name, we find the skill archetype/object, set appropriate values,...
Definition: living.cpp:1788
WEAPON
@ WEAPON
Definition: object.h:124
arch_present_in_ob
object * arch_present_in_ob(const archetype *at, const object *op)
Searches for any objects with a matching archetype in the inventory of the given object.
Definition: object.cpp:3207
object::expmul
double expmul
needed experience = (calc_exp*expmul) - means some races/classes can need less/more exp to gain level...
Definition: object.h:407
SK_PRAYING
@ SK_PRAYING
Praying.
Definition: skills.h:49
player::levhp
int8_t levhp[11]
What hp bonus the player gained on that level.
Definition: player.h:186
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Changes to attributes (stats, resistances, etc)
Definition: newclient.h:409
MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS
#define MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS
context are pretty generic - things like reflection or lifesave are also under the protection categor...
Definition: newclient.h:560
check_exp_adjust
int64_t check_exp_adjust(const object *op, int64_t exp)
Returns the maximum experience the object can gain or lose.
Definition: living.cpp:2102
get_sp_bonus
static float get_sp_bonus(int stat)
Definition: living.cpp:2405
CHARISMA
@ CHARISMA
Definition: living.h:15
AMULET
@ AMULET
Definition: object.h:144
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
calc_perm_exp
void calc_perm_exp(object *op)
Ensure that the permanent experience requirements in an exp object are met.
Definition: living.cpp:1914
MIN
#define MIN(x, y)
Definition: compat.h:21
gain_msg
const char *const gain_msg[NUM_STATS]
Message when a player increases permanently a stat.
Definition: living.cpp:161
allowed_class
int allowed_class(const object *op)
Returns true if the given player is a legal class.
Definition: living.cpp:1661
SKILL
@ SKILL
Also see SKILL_TOOL (74) below.
Definition: object.h:148
INT_WEIGHT_LIMIT
#define INT_WEIGHT_LIMIT
Definition: living.cpp:66
esrv_update_spells
void esrv_update_spells(player *pl)
This looks for any spells the player may have that have changed their stats.
Definition: main.cpp:386
player::levsp
int8_t levsp[11]
What sp bonus the player gained on that level.
Definition: player.h:187
fix_object
void fix_object(object *op)
Updates all abilities given by applied objects in the inventory of the given object.
Definition: living.cpp:1132
NDI_RED
#define NDI_RED
Definition: newclient.h:249
FLOAT_CON_BONUS
#define FLOAT_CON_BONUS
This is basically same as above, but for bonuses in which we store the value as a float.
Definition: living.cpp:85
ATNR_PHYSICAL
#define ATNR_PHYSICAL
Definition: attack.h:49
POTION_RESIST_EFFECT
@ POTION_RESIST_EFFECT
A force, holding the effect of a resistance potion.
Definition: object.h:230
ARMOUR_SPEED
#define ARMOUR_SPEED(xyz)
Definition: define.h:460
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
FLAG_PROBE
#define FLAG_PROBE
Object displays HP information to player.
Definition: define.h:257
partylist
One party.
Definition: party.h:10
SK_EXP_NONE
#define SK_EXP_NONE
Player gets nothing.
Definition: skills.h:80
FLAG_APPLIED
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:235
NDI_BLUE
#define NDI_BLUE
Actually, it is Dodger Blue.
Definition: newclient.h:251
object::level
int16_t level
Level of creature or object.
Definition: object.h:361
FLAG_STEALTH
#define FLAG_STEALTH
Will wake monsters with less range.
Definition: define.h:312
player::applied_stats
living applied_stats
Stat changes due to gear or skills.
Definition: player.h:171
buf
StringBuffer * buf
Definition: readable.cpp:1565
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.cpp:2842
INT_TURN_BONUS
#define INT_TURN_BONUS
Definition: living.cpp:59
init_stats
void init_stats()
This loads statistic bonus/penalties from the stat_bonus file.
Definition: living.cpp:2520
MAX
#define MAX(x, y)
Definition: compat.h:24
WISDOM
@ WISDOM
Definition: living.h:14
MIN_PLAYER_SPEED
#define MIN_PLAYER_SPEED
Definition: config.h:96
object::resist
int16_t resist[NROFATTACKS]
Resistance adjustments for attacks.
Definition: object.h:351
lose_msg
const char *const lose_msg[NUM_STATS]
Message when a player decreases permanently a stat.
Definition: living.cpp:172
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
MIN_STAT
#define MIN_STAT
The minimum legal value of any stat.
Definition: define.h:33
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
dragon_ability_gain
void dragon_ability_gain(object *who, int atnr, int level)
When a dragon-player gains a new stage of evolution, he gets some treasure.
Definition: main.cpp:365
FLAG_ALIVE
#define FLAG_ALIVE
Object can fight (or be fought)
Definition: define.h:230
object::path_denied
uint32_t path_denied
Paths the object is denied access to.
Definition: object.h:355
add_statbonus
void add_statbonus(object *op)
Adds stat-bonuses given by the class which the player has chosen.
Definition: living.cpp:868
range_builder
@ range_builder
Map builder.
Definition: player.h:36
short_stat_name
const char *const short_stat_name[NUM_STATS]
Short name of stats.
Definition: living.cpp:194
object::carrying
int32_t carrying
How much weight this object contains.
Definition: object.h:377
Settings::spell_encumbrance
uint8_t spell_encumbrance
Encumbrance effects spells.
Definition: global.h:269
object::path_repelled
uint32_t path_repelled
Paths the object is repelled from.
Definition: object.h:354
CLOAK
@ CLOAK
Definition: object.h:209
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects,...
Definition: object.cpp:1545
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:557
HELMET
@ HELMET
Definition: object.h:141
object::chosen_skill
object * chosen_skill
The skill chosen to use.
Definition: object.h:396
dump_stat_bonuses
void dump_stat_bonuses()
Definition: living.cpp:2651
MSG_TYPE_ATTRIBUTE_STAT_LOSS
#define MSG_TYPE_ATTRIBUTE_STAT_LOSS
Definition: newclient.h:573
add_refcount
sstring add_refcount(sstring str)
This will increase the refcount of the string str.
Definition: shstr.cpp:210
MSG_TYPE_ATTRIBUTE_RACE
#define MSG_TYPE_ATTRIBUTE_RACE
Race-related changes.
Definition: newclient.h:568
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Start of a bad effect to the player.
Definition: newclient.h:569
Settings::death_penalty_ratio
uint8_t death_penalty_ratio
Hhow much exp should be lost at death.
Definition: global.h:260
NUM_INT_BONUSES
#define NUM_INT_BONUSES
Definition: living.cpp:67
MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Protections in this.
Definition: newclient.h:559
object::weapon_speed
float weapon_speed
The overall speed of this object.
Definition: object.h:339
POTION
@ POTION
Definition: object.h:116
subtract_player_exp
static void subtract_player_exp(object *op, int64_t exp, const char *skill, int flag)
Subtracts experience from player.
Definition: living.cpp:2131
MOVE_WALK
#define MOVE_WALK
Object walks.
Definition: define.h:392
BUILDER
@ BUILDER
Generic item builder, see subtypes below.
Definition: object.h:251
player::ranges
object * ranges[range_size]
Object for each range.
Definition: player.h:116
archetype::clone
object clone
An object from which to do object_copy()
Definition: object.h:487
add_string
sstring add_string(const char *str)
This will add 'str' to the hash table.
Definition: shstr.cpp:124
FLOAT_DEX_BONUS
#define FLOAT_DEX_BONUS
Definition: living.cpp:86
ROD
@ ROD
Definition: object.h:114
CONTAINER
@ CONTAINER
Definition: object.h:236
object::move_type
MoveType move_type
Type of movement this object uses.
Definition: object.h:436
set_attr_value
void set_attr_value(living *stats, int attr, int8_t value)
Sets Str/Dex/con/Wis/Cha/Int/Pow in stats to value, depending on what attr is (STR to POW).
Definition: living.cpp:218
player::encumbrance
int16_t encumbrance
How much our player is encumbered.
Definition: player.h:196
FLAG_BLIND
#define FLAG_BLIND
If set, object cannot see (visually)
Definition: define.h:336
MSG_TYPE_ATTRIBUTE_STAT_GAIN
#define MSG_TYPE_ATTRIBUTE_STAT_GAIN
Definition: newclient.h:572
exp_level
int exp_level(int64_t exp)
Returns the level for a given exp.
Definition: living.cpp:1897
FLAG_MAKE_INVIS
#define FLAG_MAKE_INVIS
(Item) gives invisibility when applied
Definition: define.h:328
object::last_eat
int32_t last_eat
How long since we last ate.
Definition: object.h:366
change_resist_msg
const char *const change_resist_msg[NROFATTACKS]
These are the descriptions of the resistances displayed when a player puts on/takes off an item.
Definition: init.cpp:70
MSG_TYPE_ATTRIBUTE_MOVE
#define MSG_TYPE_ATTRIBUTE_MOVE
A change in the movement type of the player.
Definition: newclient.h:566
POWER
@ POWER
Definition: living.h:17
object_update_speed
void object_update_speed(object *op)
Updates the speed of an object.
Definition: object.cpp:1334
player::levgrace
int8_t levgrace[11]
What grace bonus the player gained on that level.
Definition: player.h:188
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
living::dam
int16_t dam
How much damage this object does when hitting.
Definition: living.h:46
IS_GRACE_SKILL
#define IS_GRACE_SKILL(num)
Currently only one of these, but put the define here to make it easier to expand it in the future.
Definition: skills.h:120
player_lvl_adj
void player_lvl_adj(object *who, object *op)
For the new exp system.
Definition: living.cpp:1830
INT_CHA_BONUS
#define INT_CHA_BONUS
Definition: living.cpp:62
get_index
static size_t get_index(int stat, size_t max_index)
Limits a stat value to [0..max_index].
Definition: living.cpp:2421
FOR_INV_FINISH
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:671
player::item_power
int16_t item_power
Total item power of objects equipped.
Definition: player.h:130
living::food
int32_t food
How much food in stomach.
Definition: living.h:48
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Changes experience to a player/monster.
Definition: living.cpp:2179
int_bonuses
static int * int_bonuses[NUM_INT_BONUSES]
Definition: living.cpp:69
dragon_level_gain
static void dragon_level_gain(object *who)
This function is called when a dragon-player gains an overall level.
Definition: living.cpp:1728
level_exp
int64_t level_exp(int level, double expmul)
Returns how much experience is needed for a player to become the given level.
Definition: living.cpp:1885
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
Settings::confdir
const char * confdir
Configuration files.
Definition: global.h:248
sproto.h
drain_msg
const char *const drain_msg[NUM_STATS]
Message when a player is drained of a stat.
Definition: living.cpp:139
statname
const char *const statname[NUM_STATS]
Name of stats.
Definition: living.cpp:183
living::sp
int16_t sp
Spell points.
Definition: living.h:42
MAX_SKILLS
#define MAX_SKILLS
This is the maximum number of skills the game may handle.
Definition: skills.h:70
FLAG_CAN_USE_SKILL
#define FLAG_CAN_USE_SKILL
The monster can use skills.
Definition: define.h:321
IS_COMBAT_SKILL
#define IS_COMBAT_SKILL(num)
This macro is used in fix_object() to define if this is a sill that should be used to calculate wc's ...
Definition: skills.h:91
living::Int
int8_t Int
Definition: living.h:36
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.cpp:42
object::race
sstring race
Human, goblin, dragon, etc.
Definition: object.h:326
RING
@ RING
Definition: object.h:190
add_player_exp
static void add_player_exp(object *op, int64_t exp, const char *skill_name, int flag)
Add experience to a player - exp should only be positive.
Definition: living.cpp:1971
get_dam_bonus
int get_dam_bonus(int stat)
Definition: living.cpp:2389
DEXTERITY
@ DEXTERITY
Definition: living.h:12
Settings::death_penalty_level
uint8_t death_penalty_level
How many levels worth of exp may be lost on one death.
Definition: global.h:261
living
Various statistics of objects.
Definition: living.h:35
FLAG_MONSTER
#define FLAG_MONSTER
Will attack players.
Definition: define.h:245
player::last_skill_ob
object * last_skill_ob[MAX_SKILLS]
Exp objects sent to client.
Definition: player.h:153
apply_death_exp_penalty
void apply_death_exp_penalty(object *op)
Applies a death penalty experience, the size of this is defined by the settings death_penalty_percent...
Definition: living.cpp:2244
get_dex_bonus
int get_dex_bonus(int stat)
Definition: living.cpp:2365
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
MAX_EXPERIENCE
#define MAX_EXPERIENCE
Definition: living.cpp:99
player::orig_stats
living orig_stats
Permanent real stats of player.
Definition: player.h:167
object::current_weapon
object * current_weapon
Pointer to the weapon currently used.
Definition: object.h:380
get_weight_limit
uint32_t get_weight_limit(int stat)
Definition: living.cpp:2373
free_string
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.cpp:280
living::wc
int8_t wc
Weapon Class, lower WC increases probability of hitting.
Definition: living.h:37
RANDOM
#define RANDOM()
Definition: define.h:638
MOVE_FLY_LOW
#define MOVE_FLY_LOW
Low flying object.
Definition: define.h:393
player::gen_sp_armour
int16_t gen_sp_armour
Penalty to sp regen from armour.
Definition: player.h:128
living::maxgrace
int16_t maxgrace
Maximum grace.
Definition: living.h:45
player::search_str
char search_str[MAX_BUF]
Item we are looking for.
Definition: player.h:211
ADD_EXP
#define ADD_EXP(exptotal, exp)
Handy little macro that adds exp and keeps it within bounds.
Definition: living.cpp:41
change_abil
int change_abil(object *op, object *tmp)
Permanently alters an object's stats/flags based on another object.
Definition: living.cpp:394
MSG_TYPE_ATTRIBUTE_ATTACKTYPE_LOSS
#define MSG_TYPE_ATTRIBUTE_ATTACKTYPE_LOSS
the player gaining or losing these attacktypes not being a victim of an attacktype.
Definition: newclient.h:554
player::gen_hp
int16_t gen_hp
Bonuses to regeneration speed of hp.
Definition: player.h:126
living::Wis
int8_t Wis
Definition: living.h:36
range_misc
@ range_misc
Misc items.
Definition: player.h:33
FLAG_REMOVED
#define FLAG_REMOVED
Object is not in any map or invenory.
Definition: define.h:232
PERM_EXP
#define PERM_EXP(exptotal)
Convert saved total experience into permanent experience.
Definition: global.h:232
FLAG_REFL_SPELL
#define FLAG_REFL_SPELL
Spells (some) will reflect from object.
Definition: define.h:275
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
llevInfo
@ llevInfo
Information.
Definition: logger.h:12
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
object::slaying
sstring slaying
Which race to do double damage to.
Definition: object.h:327
object::glow_radius
int8_t glow_radius
indicates the glow radius of the object
Definition: object.h:374
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
get_thaco_bonus
int get_thaco_bonus(int stat)
Definition: living.cpp:2369
WEAPON_SPEED
#define WEAPON_SPEED(xyz)
Definition: define.h:462
is_dragon_pl
int is_dragon_pl(const object *op)
Checks if player is a dragon.
Definition: player.cpp:122
get_learn_spell
int get_learn_spell(int stat)
Definition: living.cpp:2377
FLAG_USE_ARMOUR
#define FLAG_USE_ARMOUR
(Monster) can wear armour/shield/helmet
Definition: define.h:295
living::maxsp
int16_t maxsp
Max spell points.
Definition: living.h:43
DISEASE
@ DISEASE
Definition: object.h:249
attacks
const char *const attacks[NROFATTACKS]
Attack type names.
Definition: living.cpp:129
change_attr_value
void change_attr_value(living *stats, int attr, int8_t value)
Like set_attr_value(), but instead the value (which can be negative) is added to the specified stat.
Definition: living.cpp:264
drain_stat
void drain_stat(object *op)
Drains a random stat from op.
Definition: living.cpp:716
living::Cha
int8_t Cha
Definition: living.h:36
given
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is given
Definition: stats.txt:127
STRENGTH
@ STRENGTH
Definition: living.h:11
object::skill
sstring skill
Name of the skill this object uses/grants.
Definition: object.h:329
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:266
get_turn_bonus
int get_turn_bonus(int stat)
Definition: living.cpp:2385
change_luck
void change_luck(object *op, int value)
Alter the object's luck.
Definition: living.cpp:796
player::braced
uint32_t braced
Will not move if braced, only attack.
Definition: player.h:139
get_speed_bonus
float get_speed_bonus(int stat)
Definition: living.cpp:2393
did_make_save
int did_make_save(const object *op, int level, int bonus)
This function takes an object (monster/player, op), and determines if it makes a basic save throw by ...
Definition: living.cpp:2293
Settings::max_stat
uint8_t max_stat
Maximum stat value - 255 should be sufficient.
Definition: global.h:325
SKILL_TOOL
@ SKILL_TOOL
Allows the use of a skill.
Definition: object.h:194
INT_DAM_BONUS
#define INT_DAM_BONUS
Definition: living.cpp:64
object_get_value
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4331
on_same_map
int on_same_map(const object *op1, const object *op2)
Checks whether 2 objects are on the same map or not.
Definition: map.cpp:2629
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
SK_SUBTRACT_SKILL_EXP
#define SK_SUBTRACT_SKILL_EXP
Used when removing exp.
Definition: skills.h:81
living::ac
int8_t ac
Armor Class, lower AC increases probability of not getting hit.
Definition: living.h:38
share_exp
void share_exp(object *op, int64_t exp, const char *skill, int flag)
Gives experience to a player/monster, sharing it with party if applicable.
Definition: living.cpp:2323
atnr_is_dragon_enabled
int atnr_is_dragon_enabled(int attacknr)
Determine if the attacktype represented by the specified attack-number is enabled for dragon players.
Definition: player.cpp:103
die_roll
int die_roll(int num, int size, const object *op, int goodbad)
Roll a number of dice (2d3, 4d6).
Definition: utils.cpp:122
object_find_by_type_and_arch_name
object * object_find_by_type_and_arch_name(const object *who, int type, const char *name)
Find object in inventory by type and archetype name.
Definition: object.cpp:4262
arch_to_object
object * arch_to_object(archetype *at)
Creates and returns a new object which is a copy of the given archetype.
Definition: arch.cpp:227
stats
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various stats
Definition: stats.txt:2
level
int level
Definition: readable.cpp:1563
float_bonus_names
static const char * float_bonus_names[NUM_FLOAT_BONUSES]
Definition: living.cpp:91
player::party
partylist * party
Party this player is part of.
Definition: player.h:203
INT_FEAR_BONUS
#define INT_FEAR_BONUS
The definitions below are indexes into the bonuses[] array.
Definition: living.cpp:58
restore_msg
const char *const restore_msg[NUM_STATS]
Message when a player has a stat restored.
Definition: living.cpp:150
MAX_SAVE_LEVEL
#define MAX_SAVE_LEVEL
Definition: living.cpp:103
PERM_EXP_MAX_LOSS_RATIO
#define PERM_EXP_MAX_LOSS_RATIO
Definition: config.h:290
prot
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is for a more detailed look at the skills doc file Skill remove use magic items phys no fire cold Fireborns are supposed to be fire spirits They re closely in tune with magic and are powerful and learn magic easily Being fire they are immune to fire and and vulnerable to cold They are vulnerable to ghosthit and drain because being mostly non anything which strikes directly at the spirit hits them harder race attacktype restrictions immunities prot vuln Quetzalcoatl physical no armour fire cold Quetzalcoatl s are now born knowing the spell of burning but because of their negative wisdom they have a very hard time learning new spells Their maximum natural wisdom is With the high intelligence they will typically have many spellpoints They can be very devastating at low level due to their low natural ac and can make mincemeat out of low level monsters at they really begin to have problems because they cannot use armour race attacktype restrictions immunities prot vuln Wraith phys none drain prot
Definition: stats.txt:185
MIN_ACTIVE_SPEED
#define MIN_ACTIVE_SPEED
Cut off point of when an object is put on the active list or not.
Definition: define.h:633
object::body_info
int8_t body_info[NUM_BODY_LOCATIONS]
Body info as loaded from the file.
Definition: object.h:382
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END
End of a bad effect.
Definition: newclient.h:571
check_exp_loss
int64_t check_exp_loss(const object *op, int64_t exp)
This function checks to make sure that object 'op' can lose 'exp' experience.
Definition: living.cpp:2077
player_set_dragon_title
void player_set_dragon_title(player *pl, int level, const char *attack, int skin_resist)
Updates the title of a dragon player to reflect the current level, attack type, and resistances.
Definition: player.cpp:202
skill
skill
Definition: arch-handbook.txt:585
MOVE_FLY_HIGH
#define MOVE_FLY_HIGH
High flying object.
Definition: define.h:394
player::character_load
float character_load
Value between 0 and 1 indicating how much the character is overloaded.
Definition: player.h:135
object_remove
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to.
Definition: object.cpp:1818
MSG_TYPE_ATTRIBUTE_LEVEL_LOSS
#define MSG_TYPE_ATTRIBUTE_LEVEL_LOSS
Definition: newclient.h:575
remove_depletion
int remove_depletion(object *op, int level)
Remove depletion from op, if present, and warn player of such restorations.
Definition: living.cpp:755
AT_CONFUSION
#define AT_CONFUSION
Definition: attack.h:81
SK_EXP_TOTAL
#define SK_EXP_TOTAL
Give player exp to total, no skill.
Definition: skills.h:79
remove_statbonus
void remove_statbonus(object *op)
Subtracts stat-bonuses given by the class which the player has chosen.
Definition: living.cpp:845
object_check_move_on
int object_check_move_on(object *op, object *originator)
Checks if any objects has a move_type that matches objects that effect this object on this space.
Definition: object.cpp:2997
FLOAT_GRACE_BONUS
#define FLOAT_GRACE_BONUS
Definition: living.cpp:88
INT_LEARN_SPELL
#define INT_LEARN_SPELL
Definition: living.cpp:61
vuln
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is for a more detailed look at the skills doc file Skill remove use magic items phys no fire vuln
Definition: stats.txt:163
archetype::name
sstring name
More definite name, like "generate_kobold".
Definition: object.h:484
SK_EXP_ADD_SKILL
#define SK_EXP_ADD_SKILL
Give the player the skill.
Definition: skills.h:78
FLAG_XRAYS
#define FLAG_XRAYS
X-ray vision.
Definition: define.h:300
object::body_used
int8_t body_used[NUM_BODY_LOCATIONS]
Calculated value based on items equipped.
Definition: object.h:383
living::grace
int16_t grace
Grace.
Definition: living.h:44
Settings::search_items
uint8_t search_items
Search_items command.
Definition: global.h:268
object::stats
living stats
Str, Con, Dex, etc.
Definition: object.h:378
MOVE_SWIM
#define MOVE_SWIM
Swimming object.
Definition: define.h:396
get_attr_value
int8_t get_attr_value(const living *stats, int attr)
Gets the value of a stat.
Definition: living.cpp:313
range_skill
@ range_skill
Use skill.
Definition: player.h:35
savethrow
static const int savethrow[MAX_SAVE_LEVEL+1]
Probability to avoid something.
Definition: living.cpp:113
MAX_PLAYER_SPEED
#define MAX_PLAYER_SPEED
Definition: config.h:97
bonus
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is for a more detailed look at the skills doc file Skill remove use magic items phys no fire cold Fireborns are supposed to be fire spirits They re closely in tune with magic and are powerful and learn magic easily Being fire they are immune to fire and and vulnerable to cold They are vulnerable to ghosthit and drain because being mostly non anything which strikes directly at the spirit hits them harder race attacktype restrictions immunities prot vuln Quetzalcoatl physical no armour fire cold Quetzalcoatl s are now born knowing the spell of burning but because of their negative wisdom bonus
Definition: stats.txt:176
INTELLIGENCE
@ INTELLIGENCE
Definition: living.h:16
BOOTS
@ BOOTS
Definition: object.h:217
object::total_exp
int64_t total_exp
All exp ever earned (used to calc perm_exp)
Definition: object.h:379
TRUE
#define TRUE
Definition: compat.h:11
link_player_skills
void link_player_skills(object *op)
This function goes through the player inventory and sets up the last_skills[] array in the player obj...
Definition: player.cpp:287
get_con_bonus
static float get_con_bonus(int stat)
Definition: living.cpp:2401
FLAG_READY_WEAPON
#define FLAG_READY_WEAPON
(Monster or Player) has a weapon readied
Definition: define.h:334
set_dragon_name
void set_dragon_name(object *pl, const object *abil, const object *skin)
Set the new dragon name after gaining levels or changing ability focus (later this can be extended to...
Definition: living.cpp:1688
living::Pow
int8_t Pow
Definition: living.h:36
SHIELD
@ SHIELD
Definition: object.h:140
object::attacktype
uint32_t attacktype
Bitmask of attacks this object does.
Definition: object.h:352
check_stat_bounds
void check_stat_bounds(living *stats, int8_t min_stat, int8_t max_stat)
Ensures that all stats (str/dex/con/wis/cha/int) are within the passed in range of min_stat and max_s...
Definition: living.cpp:354
living.h
NUM_STATS
@ NUM_STATS
Number of statistics.
Definition: living.h:18
INT_CLERIC_CHANCE
#define INT_CLERIC_CHANCE
Definition: living.cpp:60
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:664
living::hp
int16_t hp
Hit Points.
Definition: living.h:40
living::luck
int8_t luck
Affects thaco and ac from time to time.
Definition: living.h:39
FORCE
@ FORCE
Definition: object.h:229
BASE_WEAPON_SPEED
#define BASE_WEAPON_SPEED
Definition: config.h:108
INT_DEX_BONUS
#define INT_DEX_BONUS
Definition: living.cpp:63
MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START
Start of a good effect to the player.
Definition: newclient.h:576
CONSTITUTION
@ CONSTITUTION
Definition: living.h:13
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
FLAG_LIFESAVE
#define FLAG_LIFESAVE
Saves a players' life once, then destr.
Definition: define.h:305
IS_MANA_SKILL
#define IS_MANA_SKILL(num)
Like IS_COMBAT_SKILL above, but instead this is used to determine how many mana points the player has...
Definition: skills.h:106
living::Con
int8_t Con
Definition: living.h:36
load_table
static int load_table(T **bonuses, FILE *fp, char *bonus_name)
This loads up a stat table from the file - basically, it keeps processing until it gets the closing b...
Definition: living.cpp:2450
living::Str
int8_t Str
Definition: living.h:36
FREE_PLAYER_LOAD_PERCENT
#define FREE_PLAYER_LOAD_PERCENT
Definition: config.h:98
get_archetype_by_skill_name
archetype * get_archetype_by_skill_name(const char *skill, int type)
Retrieves an archetype by skill name and type.
Definition: arch.cpp:78