Atrinik Server  4.0
living.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
31 #include <global.h>
32 #include <monster_data.h>
33 #include <arch.h>
34 #include <plugin.h>
35 #include <player.h>
36 #include <object.h>
37 
39 #define ENCUMBRANCE_LIMIT 65.0
40 
44 double dam_bonus[MAX_STAT + 1] = {
45  -2.5, // 0
46  -2.0, -2.0, -1.5, -1.5, -1.5, // 1-5
47  -1.0, -1.0, -1.0, -0.5, -0.5, // 6-10
48  0.0, 0.0, 0.0, 0.0, 0.0, // 11-15
49  0.5, 0.5, 1.0, 1.0, 1.5, // 16-20
50  1.5, 1.5, 2.0, 2.0, 2.5, // 21-25
51  3.0, 3.5, 4.0, 4.5, 5.0, // 26-30
52 };
53 
57 int wc_bonus[MAX_STAT + 1] = {
58  -5, // 0
59  -4, -4, -3, -3, -3, // 1-5
60  -2, -2, -2, -1, -1, // 6-10
61  0, 0, 0, 0, 0, // 11-15
62  1, 1, 2, 2, 3, // 16-20
63  3, 3, 4, 4, 5, // 21-25
64  5, 5, 6, 7, 8, // 26-30
65 };
66 
70 static double hp_bonus[MAX_STAT + 1] = {
71  -0.8, // 0
72  -0.6, -0.5, -0.4, -0.35, -0.3, // 1-5
73  -0.25, -0.2, -0.15, -0.11, -0.07, // 6-10
74  0.0, 0.0, 0.0, 0.0, 0.0, // 11-15
75  0.1, 0.15, 0.2, 0.25, 0.3, // 16-20
76  0.35, 0.4, 0.45, 0.5, 0.55, // 21-25
77  0.6, 0.7, 0.8, 0.9, 1.0 // 26-30
78 };
79 
84 static double sp_bonus[MAX_STAT + 1] = {
85  -0.8, // 0
86  -0.6, -0.5, -0.4, -0.35, -0.3, // 1-5
87  -0.25, -0.2, -0.15, -0.11, -0.07, // 6-10
88  0.0, 0.0, 0.0, 0.0, 0.0, // 11-15
89  0.1, 0.2, 0.3, 0.4, 0.5, // 16-20
90  0.6, 0.7, 0.8, 0.9, 1.0, // 21-25
91  1.1, 1.4, 1.6, 1.8, 2.0, // 26-30
92 };
93 
97 float speed_bonus[MAX_STAT + 1] = {
98  -0.4, // 0
99  -0.4, -0.3, -0.3, -0.2, // 1-5
100  -0.2, -0.2, -0.1, -0.1, -0.1, // 6-10
101  -0.05, 0.0, 0.0, 0.0, 0.025, 0.05, // 11-15
102  0.075, 0.1, 0.125, 0.15, 0.175, 0.2, // 16-20
103  0.225, 0.25, 0.275, 0.3, // 21-25
104  0.325, 0.35, 0.4, 0.45, 0.5, // 26-30
105 };
106 
111  2.0, // 0
112  1.9, 1.8, 1.7, 1.6, 1.5, // 1-5
113  1.4, 1.3, 1.2, 1.1, 1.0, // 6-10
114  1.05, 1.0, 1.0, 1.0, 1.0, // 11-15
115  0.98, 0.96, 0.94, 0.92, 0.9, // 16-20
116  0.88, 0.84, 0.80, 0.77, 0.73, // 21-25
117  0.7, 0.65, 0.6, 0.55, 0.5, // 26-30
118 };
119 
129 uint32_t weight_limit[MAX_STAT + 1] = {
130  20000,
131  25000, 30000, 35000, 40000, 50000,
132  60000, 70000, 80000, 90000, 100000,
133  110000, 120000, 130000, 140000, 150000,
134  165000, 180000, 195000, 210000, 225000,
135  240000, 255000, 270000, 285000, 300000,
136  325000, 350000, 375000, 400000, 450000
137 };
138 
143 int learn_spell[MAX_STAT + 1] = {
144  0, 0, 0, 1, 2, 4, 8, 12, 16, 25, 36, 45, 55, 65, 70, 75, 80, 85, 90, 95, 100, 100, 100, 100, 100,
145  100, 100, 100, 100, 100, 100
146 };
147 
153  0, // 0
154  0, 0, 0, 0, 0, // 1-5
155  20, 18, 16, 14, 13, // 6-10
156  12, 11, 10, 9, 8, // 11-15
157  7, 6, 5, 4, 3, // 16-20
158  2, 1, 1, 1, 1, // 21-25
159  1, 1, 1, 1, 1, // 26-30
160 };
161 
165 int savethrow[MAXLEVEL + 1] = {
166  18,
167  18, 17, 16, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9,
168  9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
169  6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4,
170  4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,
171  2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
172  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
173 };
174 
176 static const char *const drain_msg[NUM_STATS] = {
177  "Oh no! You are weakened!",
178  "You're feeling clumsy!",
179  "You feel less healthy",
180  "Watch out, your mind is going!",
181  "Your spirit feels drained!"
182 };
183 
185 const char *const restore_msg[NUM_STATS] = {
186  "You feel your strength return.",
187  "You feel your agility return.",
188  "You feel your health return.",
189  "You feel your memory return.",
190  "You feel your spirits return."
191 };
192 
194 static const char *const gain_msg[NUM_STATS] = {
195  "You feel stronger.",
196  "You feel more agile.",
197  "You feel healthy.",
198  "You feel smarter.",
199  "You feel more potent."
200 };
201 
203 const char *const lose_msg[NUM_STATS] = {
204  "You feel weaker!",
205  "You feel clumsy!",
206  "You feel less healthy!",
207  "You feel stupid!",
208  "You feel less potent!"
209 };
210 
212 const char *const statname[NUM_STATS] = {
213  "strength",
214  "dexterity",
215  "constitution",
216  "intelligence",
217  "power"
218 };
219 
221 const char *const short_stat_name[NUM_STATS] = {
222  "Str",
223  "Dex",
224  "Con",
225  "Int",
226  "Pow"
227 };
228 
239 void set_attr_value(living *stats, int attr, int8_t value)
240 {
241  switch (attr) {
242  case STR:
243  stats->Str = value;
244  break;
245 
246  case DEX:
247  stats->Dex = value;
248  break;
249 
250  case CON:
251  stats->Con = value;
252  break;
253 
254  case POW:
255  stats->Pow = value;
256  break;
257 
258  case INT:
259  stats->Int = value;
260  break;
261  }
262 }
263 
277 void change_attr_value(living *stats, int attr, int8_t value)
278 {
279  int16_t result;
280 
281  if (value == 0) {
282  return;
283  }
284 
285  result = get_attr_value(stats, attr) + value;
286 
287  /* Prevent possible overflow of the stat. */
288  if (result > INT8_MAX || result < INT8_MIN) {
289  return;
290  }
291 
292  set_attr_value(stats, attr, result);
293 }
294 
305 int8_t get_attr_value(const living *stats, int attr)
306 {
307  switch (attr) {
308  case STR:
309  return stats->Str;
310 
311  case DEX:
312  return stats->Dex;
313 
314  case CON:
315  return stats->Con;
316 
317  case INT:
318  return stats->Int;
319 
320  case POW:
321  return stats->Pow;
322  }
323 
324  return 0;
325 }
326 
334 {
335  int i, v;
336 
337  for (i = 0; i < NUM_STATS; i++) {
338  v = get_attr_value(stats, i);
339 
340  if (v > MAX_STAT) {
341  set_attr_value(stats, i, MAX_STAT);
342  } else if (v < MIN_STAT) {
343  set_attr_value(stats, i, MIN_STAT);
344  }
345  }
346 }
347 
353 void drain_stat(object *op)
354 {
355  drain_specific_stat(op, rndm(1, NUM_STATS) - 1);
356 }
357 
365 void drain_specific_stat(object *op, int deplete_stats)
366 {
367  object *tmp;
368  archetype_t *at = arch_find("depletion");
369 
370  if (!at) {
371  LOG(BUG, "Couldn't find archetype depletion.");
372  return;
373  } else {
374  tmp = object_find_arch(op, at);
375 
376  if (!tmp) {
377  tmp = arch_to_object(at);
378  tmp = object_insert_into(tmp, op, 0);
379  SET_FLAG(tmp, FLAG_APPLIED);
380  }
381  }
382 
383  draw_info(COLOR_GRAY, op, drain_msg[deplete_stats]);
384  change_attr_value(&tmp->stats, deplete_stats, -1);
386 }
387 
395 static void
396 living_apply_flags (object *op,
397  const object *item)
398 {
399  HARD_ASSERT(op != NULL);
400  HARD_ASSERT(item != NULL);
401 
402  op->path_attuned |= item->path_attuned;
403  op->path_repelled |= item->path_repelled;
404  op->path_denied |= item->path_denied;
405 
406  op->terrain_flag |= item->terrain_type;
407 
408  if (QUERY_FLAG(item, FLAG_LIFESAVE)) {
409  SET_FLAG(op, FLAG_LIFESAVE);
410  }
411 
412  if (QUERY_FLAG(item, FLAG_REFL_SPELL)) {
414  }
415 
416  if (QUERY_FLAG(item, FLAG_REFL_MISSILE)) {
418  }
419 
420  if (QUERY_FLAG(item, FLAG_STEALTH)) {
421  SET_FLAG(op, FLAG_STEALTH);
422  }
423 
424  if (QUERY_FLAG(item, FLAG_XRAYS)) {
425  SET_FLAG(op, FLAG_XRAYS);
426 
427  if (op->type == PLAYER) {
428  CONTR(op)->update_los = 1;
429  }
430  }
431 
432  if (QUERY_FLAG(item, FLAG_BLIND)) {
433  SET_FLAG(op, FLAG_BLIND);
434 
435  if (op->type == PLAYER) {
436  CONTR(op)->update_los = 1;
437  }
438  }
439 
440  if (QUERY_FLAG(item, FLAG_SEE_IN_DARK)) {
442  }
443 
444  if (QUERY_FLAG(item, FLAG_CAN_PASS_THRU)) {
446  }
447 
448  if (QUERY_FLAG(item, FLAG_MAKE_ETHEREAL)) {
451  }
452 
453  if (QUERY_FLAG(item, FLAG_MAKE_INVISIBLE)) {
455 
456  if (op->type == PLAYER) {
457  CONTR(op)->cs->ext_title_flag = 1;
458  }
459  }
460 
461  if (QUERY_FLAG(item, FLAG_SEE_INVISIBLE)) {
463  }
464 
465  if (QUERY_FLAG(item, FLAG_FLYING)) {
466  SET_FLAG(op, FLAG_FLYING);
467  }
468 
469  if (QUERY_FLAG(item, FLAG_UNDEAD)) {
470  SET_FLAG(op, FLAG_UNDEAD);
471  }
472 
473  if (QUERY_FLAG(item, FLAG_CONFUSED)) {
474  SET_FLAG(op, FLAG_CONFUSED);
475  }
476 }
477 
496 static void
498  object *op,
499  const object *item,
500  double *attacks,
501  double *protect_bonus,
502  double *protect_malus,
503  int *max_bonus_hp,
504  int *max_bonus_sp,
505  double *max_speed,
506  double *added_speed,
507  double *bonus_speed)
508 {
509  HARD_ASSERT(op != NULL);
510  HARD_ASSERT(item != NULL);
511  HARD_ASSERT(protect_bonus != NULL);
512  HARD_ASSERT(protect_malus != NULL);
513  HARD_ASSERT(max_bonus_hp != NULL);
514  HARD_ASSERT(max_bonus_sp != NULL);
515  HARD_ASSERT(added_speed != NULL);
516  HARD_ASSERT(bonus_speed != NULL);
517 
518  /* If this is an armour piece with a speed cap, update player's max speed
519  * if it's higher than the cap. */
520  if (IS_ARMOR(item) && ARMOUR_SPEED(item) != 0 &&
521  *max_speed > ARMOUR_SPEED(item) / 10.0) {
522  *max_speed = ARMOUR_SPEED(item) / 10.0;
523  }
524 
525  /* Add the armour class, factoring in the item's magic. */
526  if (item->stats.ac != 0) {
527  op->stats.ac += item->stats.ac + item->magic;
528  }
529 
530  if (item->block != 0) {
531  op->block += item->block + item->magic;
532  }
533 
534  if (item->absorb != 0) {
535  op->absorb += item->absorb + item->magic;
536  }
537 
538  /* Add stats for non-ranged items. */
539  if (!OBJECT_IS_RANGED(item)) {
540  /* Add the weapon class, factoring in the item's magic. */
541  if (item->stats.wc != 0) {
542  op->stats.wc += item->stats.wc + item->magic;
543  }
544 
545  /* Add the damage, factoring in the item's magic. This affects melee
546  * weapons damage. */
547  if (item->stats.dam) {
548  int16_t dam = item->stats.dam + item->magic;
549  op->stats.dam += dam;
550 
551  if (!IS_WEAPON(item)) {
552  pl->dam_bonus += dam;
553  }
554  }
555 
556  for (int i = 0; i < NROFATTACKS; i++) {
557  double mod = item->item_condition / 100.0;
558 
559  if (item->protection[i] > 0) {
560  double bonus = 100.0 - protect_bonus[i];
561  bonus *= item->protection[i] * mod;
562  bonus /= 100.0;
563  protect_bonus[i] += bonus;
564  } else if (item->protection[i] < 0) {
565  double malus = 100.0 - protect_malus[i];
566  malus *= -item->protection[i];
567  malus /= 100.0;
568  protect_malus[i] += malus;
569  }
570 
571  if (item->attack[i] > 0) {
572  attacks[i] += item->attack[i] * mod;
573  }
574  }
575 
576  /* Add the speed modifier. */
577  if (item->stats.exp != 0 && item->type != SKILL) {
578  if (item->stats.exp > 0) {
579  *added_speed += item->stats.exp / 3.0;
580  *bonus_speed += 1.0 + item->stats.exp / 3.0;
581  } else {
582  *added_speed += item->stats.exp;
583  }
584  }
585  }
586 
587  /* If it's not any sort of weapon, add some extra stats such as max HP/SP,
588  * digestion, etc. */
589  if (!IS_WEAPON(item) && !OBJECT_IS_RANGED(item)) {
590  *max_bonus_hp += item->stats.maxhp;
591  *max_bonus_sp += item->stats.maxsp;
592 
593  pl->digestion += item->stats.food;
594  pl->gen_sp += item->stats.sp;
595  pl->gen_hp += item->stats.hp;
596  pl->gen_sp_armour += item->last_heal;
597  }
598 
599  pl->item_power += item->item_power;
600 
601  for (int i = 0; i < NUM_STATS; i++) {
602  int8_t value = get_attr_value(&item->stats, i);
603  change_attr_value(&op->stats, i, value);
604  }
605 
606  living_apply_flags(op, item);
607 }
608 
614 void living_update_player(object *op)
615 {
616  HARD_ASSERT(op != NULL);
617 
618  op = HEAD(op);
619 
620  SOFT_ASSERT(op->type == PLAYER, "Called with non-player: %s",
621  object_get_str(op));
622 
623  if (QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) {
624  return;
625  }
626 
627  player *pl = CONTR(op);
628 
629  pl->digestion = 3;
630  pl->gen_hp = 1;
631  pl->gen_sp = 1;
632  pl->gen_sp_armour = 0;
633  pl->item_power = 0;
634  pl->quest_container = NULL;
635  pl->dam_bonus = 0;
636 
637  for (int i = 0; i < NUM_STATS; i++) {
638  int8_t value = get_attr_value(&op->arch->clone.stats, i);
639  set_attr_value(&op->stats, i, value);
640  }
641 
642  op->stats.wc = op->arch->clone.stats.wc;
643  op->stats.ac = op->arch->clone.stats.ac;
644  op->stats.dam = op->arch->clone.stats.dam;
645 
646  op->block = op->arch->clone.block;
647  op->absorb = op->arch->clone.absorb;
648 
649  op->stats.maxhp = op->arch->clone.stats.maxhp;
650  op->stats.maxsp = op->arch->clone.stats.maxsp;
651 
652  op->stats.wc_range = op->arch->clone.stats.wc_range;
653 
654  op->speed = op->arch->clone.speed;
655  op->weapon_speed = 0;
656  op->path_attuned = op->arch->clone.path_attuned;
658  op->path_denied = op->arch->clone.path_denied;
659  /* Reset terrain moving abilities */
660  op->terrain_flag = op->arch->clone.terrain_flag;
661 
663 
664  if (!QUERY_FLAG(&op->arch->clone, FLAG_XRAYS)) {
665  CLEAR_FLAG(op, FLAG_XRAYS);
666  }
667 
668  if (!QUERY_FLAG(&op->arch->clone, FLAG_CAN_PASS_THRU)) {
669  CLEAR_MULTI_FLAG(op, FLAG_CAN_PASS_THRU);
670  }
671 
672  if (!QUERY_FLAG(&op->arch->clone, FLAG_IS_ETHEREAL)) {
673  CLEAR_MULTI_FLAG(op, FLAG_IS_ETHEREAL);
674  }
675 
676  if (!QUERY_FLAG(&op->arch->clone, FLAG_LIFESAVE)) {
678  }
679 
680  if (!QUERY_FLAG(&op->arch->clone, FLAG_STEALTH)) {
682  }
683 
684  if (!QUERY_FLAG(&op->arch->clone, FLAG_BLIND)) {
685  CLEAR_FLAG(op, FLAG_BLIND);
686  }
687 
688  if (!QUERY_FLAG(&op->arch->clone, FLAG_FLYING)) {
689  CLEAR_MULTI_FLAG(op, FLAG_FLYING);
690  }
691 
692  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_SPELL)) {
694  }
695 
696  if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_MISSILE)) {
698  }
699 
700  if (!QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD)) {
701  CLEAR_FLAG(op, FLAG_UNDEAD);
702  }
703 
704  if (!QUERY_FLAG(&op->arch->clone, FLAG_SEE_IN_DARK)) {
706  }
707 
708  if (!QUERY_FLAG(&op->arch->clone, FLAG_IS_INVISIBLE)) {
710  }
711 
712  if (!QUERY_FLAG(&op->arch->clone, FLAG_SEE_INVISIBLE)) {
714  }
715 
716  if (!QUERY_FLAG(&op->arch->clone, FLAG_CONFUSED)) {
718  }
719 
720  if (!QUERY_FLAG(&op->arch->clone, FLAG_BLIND)) {
721  CLEAR_FLAG(op, FLAG_BLIND);
722  }
723 
726  }
727 
728  /* Initializing player arrays from the values in player archetype clone */
729  memset(&pl->equipment, 0, sizeof(pl->equipment));
730  memset(&pl->skill_ptr, 0, sizeof(pl->skill_ptr));
731  memcpy(&op->protection, &op->arch->clone.protection,
732  sizeof(op->protection));
733  memcpy(&op->attack, &op->arch->clone.attack, sizeof(op->attack));
734 
735  int8_t old_glow = op->glow_radius;
736  int8_t light = op->arch->clone.glow_radius;
737 
738  double attacks[NROFATTACKS] = {0};
739  double protect_bonus[NROFATTACKS] = {0};
740  double protect_malus[NROFATTACKS] = {0};
741 
742  int protect_exact_bonus[NROFATTACKS] = {0};
743  int protect_exact_malus[NROFATTACKS] = {0};
744  int8_t potion_protection_bonus[NROFATTACKS] = {0};
745  int8_t potion_protection_malus[NROFATTACKS] = {0};
746  uint8_t potion_attack[NROFATTACKS] = {0};
747 
748  double max_speed = 9.0;
749  double added_speed = 0.0;
750  double bonus_speed = 0.0;
751  double speed_reduce_from_disease = 1.0;
752 
753  bool ring_left = false;
754  bool ring_right = false;
755 
756  int max_bonus_hp = 0;
757  int max_bonus_sp = 0;
758 
759  FOR_INV_PREPARE(op, tmp) {
760  if (tmp->type == QUEST_CONTAINER) {
761  if (likely(pl->quest_container == NULL)) {
762  pl->quest_container = tmp;
763  } else {
764  LOG(ERROR, "Found duplicate quest container object %s",
765  object_get_str(tmp));
766  object_remove(tmp, 0);
767  object_destroy(tmp);
768  }
769 
770  continue;
771  }
772 
773  if (tmp->type == SCROLL || tmp->type == POTION ||
774  (tmp->type == CONTAINER && !OBJECT_IS_AMMO(tmp)) ||
775  tmp->type == LIGHT_REFILL) {
776  /* Skip objects that do not give any stats, and require no
777  * further handling. */
778  continue;
779  }
780 
781  if (tmp->glow_radius > light) {
782  if (tmp->type != LIGHT_APPLY || QUERY_FLAG(tmp, FLAG_APPLIED)) {
783  light = tmp->glow_radius;
784  }
785  }
786 
787  if (tmp->type == SKILL) {
788  if (likely(pl->skill_ptr[tmp->stats.sp] == NULL)) {
789  pl->skill_ptr[tmp->stats.sp] = tmp;
790  } else {
791  LOG(ERROR, "Found duplicate skill object %s",
792  object_get_str(tmp));
793  object_remove(tmp, 0);
794  object_destroy(tmp);
795  continue;
796  }
797  }
798 
799  /* Rest of the code only deals with applied objects, so skip those
800  * that are not. */
801  if (!QUERY_FLAG(tmp, FLAG_APPLIED)) {
802  continue;
803  }
804 
805  if (tmp->type == POTION_EFFECT) {
806  for (int i = 0; i < NUM_STATS; i++) {
807  int8_t value = get_attr_value(&tmp->stats, i);
808  change_attr_value(&op->stats, i, value);
809  }
810 
811  for (int i = 0; i < NROFATTACKS; i++) {
812  if (tmp->protection[i] > potion_protection_bonus[i]) {
813  potion_protection_bonus[i] = tmp->protection[i];
814  } else if (tmp->protection[i] <
815  potion_protection_malus[i]) {
816  potion_protection_malus[i] = tmp->protection[i];
817  }
818 
819  if (tmp->attack[i] > potion_attack[i]) {
820  potion_attack[i] = tmp->attack[i];
821  }
822  }
823 
824  living_apply_flags(op, tmp);
825  } else if (tmp->type == FORCE ||
826  tmp->type == POISONING ||
827  tmp->type == DISEASE ||
828  tmp->type == SYMPTOM) {
829  if (ARMOUR_SPEED(tmp) != 0 &&
830  max_speed > ARMOUR_SPEED(tmp) / 10.0) {
831  max_speed = ARMOUR_SPEED(tmp) / 10.0;
832  }
833 
834  for (int i = 0; i < NUM_STATS; i++) {
835  int8_t value = get_attr_value(&tmp->stats, i);
836  change_attr_value(&op->stats, i, value);
837  }
838 
839  if (tmp->type != DISEASE && tmp->type != SYMPTOM &&
840  tmp->type != POISONING) {
841  if (tmp->stats.wc != 0) {
842  op->stats.wc += tmp->stats.wc + tmp->magic;
843  }
844 
845  if (tmp->stats.dam != 0) {
846  int16_t dam = tmp->stats.dam + tmp->magic;
847  op->stats.dam += dam;
848  pl->dam_bonus += dam;
849  }
850 
851  if (tmp->stats.ac != 0) {
852  op->stats.ac += tmp->stats.ac + tmp->magic;
853  }
854 
855  op->stats.maxhp += tmp->stats.maxhp;
856  op->stats.maxsp += tmp->stats.maxsp;
857  }
858 
859  if (tmp->type == DISEASE || tmp->type == SYMPTOM) {
860  speed_reduce_from_disease = tmp->last_sp / 100.0;
861  if (DBL_EQUAL(speed_reduce_from_disease, 0.0)) {
862  speed_reduce_from_disease = 1.0;
863  }
864  } else if (tmp->stats.exp != 0) {
865  if (tmp->stats.exp > 0) {
866  added_speed += tmp->stats.exp / 3.0;
867  bonus_speed += 1.0 + tmp->stats.exp / 3.0;
868  } else {
869  added_speed += tmp->stats.exp;
870  }
871  }
872 
873  for (int i = 0; i < NROFATTACKS; i++) {
874  if (tmp->protection[i] > protect_exact_bonus[i]) {
875  protect_exact_bonus[i] = tmp->protection[i];
876  } else if (tmp->protection[i] < 0) {
877  protect_exact_malus[i] += -tmp->protection[i];
878  }
879 
880  if (tmp->attack[i] > 0 && tmp->type != DISEASE &&
881  tmp->type != SYMPTOM && tmp->type != POISONING) {
882  unsigned int attack = op->attack[i] + tmp->attack[i];
883  op->attack[i] = MIN(UINT8_MAX, attack);
884  }
885  }
886 
887  living_apply_flags(op, tmp);
888  } else if (OBJECT_IS_AMMO(tmp)) {
889  pl->equipment[PLAYER_EQUIP_AMMO] = tmp;
890  } else if (tmp->type == AMULET) {
891  pl->equipment[PLAYER_EQUIP_AMULET] = tmp;
892  } else if (tmp->type == WEAPON) {
893  pl->equipment[PLAYER_EQUIP_WEAPON] = tmp;
894  } else if (OBJECT_IS_RANGED(tmp)) {
895  pl->equipment[PLAYER_EQUIP_WEAPON_RANGED] = tmp;
896  } else if (tmp->type == GLOVES) {
897  pl->equipment[PLAYER_EQUIP_GAUNTLETS] = tmp;
898  } else if (tmp->type == RING) {
899  if (!ring_left) {
900  pl->equipment[PLAYER_EQUIP_RING_LEFT] = tmp;
901  ring_left = true;
902  } else if (!ring_right) {
903  pl->equipment[PLAYER_EQUIP_RING_RIGHT] = tmp;
904  ring_right = true;
905  } else {
906  LOG(ERROR, "Unexpected applied ring: %s",
907  object_get_str(tmp));
908  CLEAR_FLAG(tmp, FLAG_APPLIED);
909  }
910  } else if (tmp->type == HELMET) {
911  pl->equipment[PLAYER_EQUIP_HELM] = tmp;
912  } else if (tmp->type == ARMOUR) {
913  pl->equipment[PLAYER_EQUIP_ARMOUR] = tmp;
914  } else if (tmp->type == GIRDLE) {
915  pl->equipment[PLAYER_EQUIP_BELT] = tmp;
916  } else if (tmp->type == PANTS) {
917  pl->equipment[PLAYER_EQUIP_PANTS] = tmp;
918  } else if (tmp->type == BOOTS) {
919  pl->equipment[PLAYER_EQUIP_BOOTS] = tmp;
920  } else if (tmp->type == CLOAK) {
921  pl->equipment[PLAYER_EQUIP_CLOAK] = tmp;
922  } else if (tmp->type == BRACERS) {
923  pl->equipment[PLAYER_EQUIP_BRACERS] = tmp;
924  } else if (tmp->type == SHIELD) {
925  pl->equipment[PLAYER_EQUIP_SHIELD] = tmp;
926  } else if (tmp->type == LIGHT_APPLY) {
927  pl->equipment[PLAYER_EQUIP_LIGHT] = tmp;
928  } else if (tmp->type == RING && ring_left) {
929  pl->equipment[PLAYER_EQUIP_RING_RIGHT] = tmp;
930  } else if (tmp->type == SKILL_ITEM) {
931  pl->equipment[PLAYER_EQUIP_SKILL_ITEM] = tmp;
932  } else if (tmp->type == TRINKET) {
934  op,
935  tmp,
936  attacks,
937  protect_bonus,
938  protect_malus,
939  &max_bonus_hp,
940  &max_bonus_sp,
941  &max_speed,
942  &added_speed,
943  &bonus_speed);
944  } else {
945  LOG(BUG, "Unexpected applied object: %s",
946  object_get_str(tmp));
947  CLEAR_FLAG(tmp, FLAG_APPLIED);
948  }
949  } FOR_INV_FINISH();
950 
951  for (int i = 0; i < PLAYER_EQUIP_MAX; i++) {
952  if (pl->equipment[i] == NULL) {
953  continue;
954  }
955 
956  /* No bonuses from the shield, if a two-handed weapon or ranged weapon
957  * is equipped. */
958  if (i == PLAYER_EQUIP_SHIELD &&
959  ((pl->equipment[PLAYER_EQUIP_WEAPON] != NULL &&
960  QUERY_FLAG(pl->equipment[PLAYER_EQUIP_WEAPON],
961  FLAG_TWO_HANDED)) ||
962  (pl->equipment[PLAYER_EQUIP_WEAPON_RANGED] != NULL &&
963  QUERY_FLAG(pl->equipment[PLAYER_EQUIP_WEAPON_RANGED],
964  FLAG_TWO_HANDED)))) {
965  continue;
966  }
967 
968  if (i == PLAYER_EQUIP_AMMO || i == PLAYER_EQUIP_LIGHT ||
969  i == PLAYER_EQUIP_SKILL_ITEM) {
970  continue;
971  }
972 
974  op,
975  pl->equipment[i],
976  attacks,
977  protect_bonus,
978  protect_malus,
979  &max_bonus_hp,
980  &max_bonus_sp,
981  &max_speed,
982  &added_speed,
983  &bonus_speed);
984  }
985 
986  for (int i = 0; i < NROFATTACKS; i++) {
987  unsigned int attack = op->attack[i] + attacks[i] + potion_attack[i];
988  op->attack[i] = MIN(UINT8_MAX, attack);
989 
990  /* Add in the potion protections. */
991  if (potion_protection_bonus[i] > 0) {
992  double bonus = 100.0 - protect_bonus[i];
993  bonus *= potion_protection_bonus[i];
994  bonus /= 100.0;
995  protect_bonus[i] += bonus;
996  }
997 
998  if (potion_protection_malus[i] < 0) {
999  double malus = 100.0 - protect_malus[i];
1000  malus *= -potion_protection_malus[i];
1001  malus /= 100.0;
1002  protect_malus[i] += malus;
1003  }
1004 
1005  int protection = protect_bonus[i] + protect_exact_bonus[i] -
1006  protect_malus[i] - protect_exact_malus[i];
1007  op->protection[i] = MIN(100, MAX(-100, protection));
1008  }
1009 
1010  check_stat_bounds(&op->stats);
1011 
1012  /* Now the speed thing... */
1013  op->speed += speed_bonus[op->stats.Dex];
1014 
1015  if (added_speed >= 0.0) {
1016  op->speed += added_speed / 10.0;
1017  } else {
1018  op->speed /= 1.0 - added_speed;
1019  }
1020 
1021  if (op->speed > max_speed) {
1022  op->speed = max_speed;
1023  }
1024 
1025  /* Calculate real speed */
1026  op->speed += bonus_speed / 10.0f;
1027 
1028  /* Put a lower limit on speed. Note with this speed, you move once every
1029  * 100 ticks or so. This amounts to once every 12 seconds of realtime. */
1030  op->speed = op->speed * speed_reduce_from_disease;
1031 
1032  /* Don't reduce under this value */
1033  if (op->speed < 0.01f) {
1034  op->speed = 0.01f;
1035  } else if (!pl->tgm) {
1036  /* Max kg we can carry */
1037  double f = (weight_limit[op->stats.Str] / 100.0f) * ENCUMBRANCE_LIMIT;
1038 
1039  if (op->carrying > f) {
1040  if (op->carrying >= weight_limit[op->stats.Str]) {
1041  op->speed = 0.01f;
1042  } else {
1043  /* Total encumbrance weight part */
1044  f = (weight_limit[op->stats.Str] - f);
1045  /* Value from 0.0 to 1.0 encumbrance */
1046  f = (weight_limit[op->stats.Str] - op->carrying) / f;
1047 
1048  if (f < 0.0f) {
1049  f = 0.0f;
1050  } else if (f > 1.0f) {
1051  f = 1.0f;
1052  }
1053 
1054  op->speed *= f;
1055 
1056  if (op->speed < 0.01f) {
1057  op->speed = 0.01f;
1058  }
1059  }
1060  }
1061  }
1062 
1063  object_update_speed(op);
1064 
1065  /* The player is invisible; shouldn't emit any light. */
1066  if (QUERY_FLAG(op, FLAG_IS_INVISIBLE)) {
1067  light = 0;
1068  }
1069 
1070  op->glow_radius = light;
1071 
1072  if (op->map != NULL && old_glow != light) {
1073  adjust_light_source(op->map, op->x, op->y, light - old_glow);
1074  }
1075 
1076  op->stats.ac += op->level;
1077 
1078  op->stats.maxhp *= op->level + 3;
1079  op->stats.maxsp *= (pl->skill_ptr[SK_WIZARDRY_SPELLS] != NULL ?
1080  pl->skill_ptr[SK_WIZARDRY_SPELLS]->level : 1) + 3;
1081 
1082  /* Now adjust with the % of the stats malus/bonus. */
1083  op->stats.maxhp += op->stats.maxhp * hp_bonus[op->stats.Con];
1084  op->stats.maxhp += max_bonus_hp;
1085 
1086  double maxsp = op->stats.maxsp;
1087  op->stats.maxsp += maxsp * sp_bonus[op->stats.Int];
1088  op->stats.maxsp += maxsp * (sp_bonus[op->stats.Pow] / 2.5);
1089  op->stats.maxsp += max_bonus_sp;
1090 
1091  if (op->stats.maxhp < 1) {
1092  op->stats.maxhp = 1;
1093  }
1094 
1095  if (op->stats.maxsp < 1) {
1096  op->stats.maxsp = 1;
1097  }
1098 
1099  if (op->stats.hp == -1) {
1100  op->stats.hp = op->stats.maxhp;
1101  }
1102 
1103  if (op->stats.sp == -1) {
1104  op->stats.sp = op->stats.maxsp;
1105  }
1106 
1107  /* Cap the pools to <= max */
1108  if (op->stats.hp > op->stats.maxhp) {
1109  op->stats.hp = op->stats.maxhp;
1110  }
1111 
1112  if (op->stats.sp > op->stats.maxsp) {
1113  op->stats.sp = op->stats.maxsp;
1114  }
1115 
1116  object *weapon = pl->equipment[PLAYER_EQUIP_WEAPON];
1117  if (weapon != NULL && weapon->type == WEAPON && weapon->item_skill != 0) {
1118  op->weapon_speed = weapon->last_grace;
1119  op->stats.wc += SKILL_LEVEL(pl, weapon->item_skill - 1);
1120  double dam = op->stats.dam;
1121  dam *= LEVEL_DAMAGE(SKILL_LEVEL(pl, weapon->item_skill - 1));
1122  dam *= weapon->item_condition / 100.0;
1123  op->stats.dam = dam;
1124 
1125  if (weapon->slaying != NULL) {
1126  FREE_AND_ADD_REF_HASH(op->slaying, weapon->slaying);
1127  }
1128 
1129  if (QUERY_FLAG(weapon, FLAG_IS_ASSASSINATION)) {
1131  }
1132  } else {
1133  if (pl->skill_ptr[SK_UNARMED] != NULL) {
1135 
1136  for (int i = 0; i < NROFATTACKS; i++) {
1137  if (pl->skill_ptr[SK_UNARMED]->attack[i] == 0) {
1138  continue;
1139  }
1140 
1141  unsigned int attack = op->attack[i] +
1142  pl->skill_ptr[SK_UNARMED]->attack[i];
1143  op->attack[i] = MIN(UINT8_MAX, attack);
1144  }
1145  }
1146 
1147  op->stats.wc += SKILL_LEVEL(pl, SK_UNARMED);
1148  op->stats.dam *= LEVEL_DAMAGE(SKILL_LEVEL(pl, SK_UNARMED)) / 2.0;
1149  }
1150 
1151  /* Add damage bonus based on strength. */
1152  op->stats.dam += op->stats.dam * dam_bonus[op->stats.Str] / 10.0;
1153  if (op->stats.dam < 1) {
1154  op->stats.dam = 1;
1155  }
1156 
1157  /* Add WC bonus based on dexterity. */
1158  op->stats.wc += wc_bonus[op->stats.Dex];
1159 
1160  if (pl->quest_container == NULL) {
1161  LOG(ERROR, "Player %s has no quest container, fixing.",
1162  object_get_str(op));
1163  object *quest_container = arch_get(QUEST_CONTAINER_ARCHETYPE);
1164  object_insert_into(quest_container, op, 0);
1165  pl->quest_container = quest_container;
1166  }
1167 }
1168 
1174 void living_update_monster(object *op)
1175 {
1176  object *base;
1177 
1178  HARD_ASSERT(op != NULL);
1179 
1180  op = HEAD(op);
1181 
1182  SOFT_ASSERT(op->type == MONSTER, "Called with non-monster: %s",
1183  object_get_str(op));
1184 
1185  if (QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) {
1186  return;
1187  }
1188 
1189  if (op->custom_attrset == NULL) {
1190  monster_data_init(op);
1191  }
1192 
1193  /* Will insert or/and return base info */
1194  base = living_get_base_info(op);
1195 
1197  op->absorb = 0;
1198  op->block = 0;
1199  memcpy(&op->protection, &op->arch->clone.protection,
1200  sizeof(op->protection));
1201 
1202  op->stats.maxhp = (base->stats.maxhp * (op->level + 3) + (op->level / 2) *
1203  base->stats.maxhp) / 10;
1204  op->stats.maxsp = base->stats.maxsp * (op->level + 1);
1205 
1206  if (op->stats.hp == -1) {
1207  op->stats.hp = op->stats.maxhp;
1208  }
1209 
1210  if (op->stats.sp == -1) {
1211  op->stats.sp = op->stats.maxsp;
1212  }
1213 
1214  /* Cap the pools to <= max */
1215  if (op->stats.hp > op->stats.maxhp) {
1216  op->stats.hp = op->stats.maxhp;
1217  }
1218 
1219  if (op->stats.sp > op->stats.maxsp) {
1220  op->stats.sp = op->stats.maxsp;
1221  }
1222 
1223  op->stats.ac = base->stats.ac + op->level;
1224  /* + level / 3 to catch up the equipment improvements of
1225  * the players in armour items. */
1226  op->stats.wc = base->stats.wc + op->level + (op->level / 3);
1227  op->stats.dam = base->stats.dam;
1228 
1229  if (base->stats.wc_range) {
1230  op->stats.wc_range = base->stats.wc_range;
1231  } else {
1232  /* Default value if not set in arch */
1233  op->stats.wc_range = 20;
1234  }
1235 
1236  for (object *tmp = op->inv; tmp != NULL; tmp = tmp->below) {
1237  CLEAR_FLAG(tmp, FLAG_APPLIED);
1238 
1239  /* Check for bow and use it! */
1240  if (tmp->type == BOW && QUERY_FLAG(op, FLAG_USE_BOW) &&
1241  check_good_weapon(op, tmp)) {
1242  SET_FLAG(tmp, FLAG_APPLIED);
1243  SET_FLAG(op, FLAG_READY_BOW);
1244  } else if (QUERY_FLAG(op, FLAG_USE_ARMOUR) && IS_ARMOR(tmp) &&
1245  check_good_armour(op, tmp)) {
1246  SET_FLAG(tmp, FLAG_APPLIED);
1247  } else if (QUERY_FLAG(op, FLAG_USE_WEAPON) && tmp->type == WEAPON &&
1248  check_good_weapon(op, tmp)) {
1249  SET_FLAG(tmp, FLAG_APPLIED);
1250  } else if (tmp->type == EVENT_OBJECT && tmp->sub_type == EVENT_AI &&
1251  tmp->path_attuned & EVENT_FLAG(EVENT_AI_GUARD_STOP)) {
1252  op->behavior |= BEHAVIOR_GUARD;
1253  }
1254 
1255  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1256  if (tmp->type == WEAPON) {
1257  if (tmp->stats.dam != 0) {
1258  op->stats.dam += tmp->stats.dam + tmp->magic;
1259  }
1260 
1261  if (tmp->stats.wc != 0) {
1262  op->stats.wc += tmp->stats.wc + tmp->magic;
1263  }
1264  } else if (IS_ARMOR(tmp)) {
1265  for (int i = 0; i < NROFATTACKS; i++) {
1266  int protect = op->protection[i] + tmp->protection[i];
1267  protect = MIN(100, protect);
1268  op->protection[i] = protect;
1269  }
1270 
1271  if (tmp->stats.ac != 0) {
1272  op->stats.ac += tmp->stats.ac + tmp->magic;
1273  }
1274  }
1275 
1276  if (tmp->block != 0) {
1277  op->block += tmp->block + tmp->magic;
1278  }
1279 
1280  if (tmp->absorb != 0) {
1281  op->absorb += tmp->absorb + tmp->magic;
1282  }
1283  }
1284  }
1285 
1286  if (op->more && QUERY_FLAG(op, FLAG_FRIENDLY)) {
1287  SET_MULTI_FLAG(op, FLAG_FRIENDLY);
1288  }
1289 
1290  op->stats.dam = (int16_t) (((double) op->stats.dam *
1291  ((LEVEL_DAMAGE(op->level) + MAX(LEVEL_DAMAGE(op->level / 3.0) -
1292  0.75f, 0.0)) * (0.925f + 0.05 * (op->level / 10.0)))) / 10.0f);
1293 
1294  /* Add a special decrease of power for monsters level 1-5 */
1295  if (op->level <= 5) {
1296  double d = 1.0f - ((0.35f / 5.0f) * (double) (6 - op->level));
1297 
1298  op->stats.dam = (int16_t) ((double) op->stats.dam * d);
1299 
1300  if (op->stats.dam < 1) {
1301  op->stats.dam = 1;
1302  }
1303 
1304  op->stats.maxhp = (int32_t) ((double) op->stats.maxhp * d);
1305 
1306  if (op->stats.maxhp < 1) {
1307  op->stats.maxhp = 1;
1308  }
1309 
1310  if (op->stats.hp > op->stats.maxhp) {
1311  op->stats.hp = op->stats.maxhp;
1312  }
1313  }
1314 
1315  set_mobile_speed(op, 0);
1316 }
1317 
1329 static int living_update_display(object *op, object *refop, player *refpl)
1330 {
1331  int ret, i, stat_new, stat_old;
1332  player *pl;
1333 
1334  HARD_ASSERT(op != NULL);
1335  HARD_ASSERT(refop != NULL);
1336  HARD_ASSERT(refpl != NULL);
1337 
1338  /* Only players for now. */
1339  HARD_ASSERT(op->type == PLAYER);
1340 
1341  pl = CONTR(op);
1342 
1343  ret = 0;
1344 
1345  if (MIN(op->attack[ATNR_CONFUSION], 1) !=
1346  MIN(refop->attack[ATNR_CONFUSION], 1)) {
1347  ret++;
1348 
1349  if (op->attack[ATNR_CONFUSION] != 0) {
1350  draw_info(COLOR_WHITE, op, "Your hands begin to glow red.");
1351  } else {
1352  draw_info(COLOR_GRAY, op, "Your hands stop glowing red.");
1353  }
1354  }
1355 
1356  if (QUERY_FLAG(op, FLAG_LIFESAVE) != QUERY_FLAG(refop, FLAG_LIFESAVE)) {
1357  ret++;
1358 
1359  if (QUERY_FLAG(op, FLAG_LIFESAVE)) {
1360  draw_info(COLOR_WHITE, op, "You feel very protected.");
1361  } else {
1362  draw_info(COLOR_GRAY, op, "You don't feel protected anymore.");
1363  }
1364  }
1365 
1366  if (QUERY_FLAG(op, FLAG_REFL_MISSILE) !=
1367  QUERY_FLAG(refop, FLAG_REFL_MISSILE)) {
1368  ret++;
1369 
1370  if (QUERY_FLAG(op, FLAG_REFL_MISSILE)) {
1371  draw_info(COLOR_WHITE, op, "You feel more safe now, somehow.");
1372  } else {
1373  draw_info(COLOR_GRAY, op, "Suddenly you feel less safe, somehow.");
1374  }
1375  }
1376 
1377  if (QUERY_FLAG(op, FLAG_REFL_SPELL) !=
1378  QUERY_FLAG(refop, FLAG_REFL_SPELL)) {
1379  ret++;
1380 
1381  if (QUERY_FLAG(op, FLAG_REFL_SPELL)) {
1382  draw_info(COLOR_WHITE, op, "A magic force shimmers around you.");
1383  } else {
1384  draw_info(COLOR_GRAY, op, "The magic force fades away.");
1385  }
1386  }
1387 
1388  if (QUERY_FLAG(op, FLAG_FLYING) != QUERY_FLAG(refop, FLAG_FLYING)) {
1389  ret++;
1390 
1391  if (QUERY_FLAG(op, FLAG_FLYING)) {
1392  draw_info(COLOR_WHITE, op, "You start to float in the air!");
1393  } else {
1394  draw_info(COLOR_GRAY, op, "You float down to the ground.");
1395  }
1396  }
1397 
1398  /* Becoming UNDEAD... a special treatment for this flag. Only those not
1399  * originally undead may change their status */
1400  if (QUERY_FLAG(op, FLAG_UNDEAD) != QUERY_FLAG(refop, FLAG_UNDEAD) &&
1401  !QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD)) {
1402  ret++;
1403 
1404  if (QUERY_FLAG(op, FLAG_UNDEAD)) {
1405  FREE_AND_COPY_HASH(op->race, "undead");
1406  draw_info(COLOR_GRAY, op, "Your lifeforce drains away!");
1407  } else {
1409 
1410  if (op->arch->clone.race) {
1411  FREE_AND_COPY_HASH(op->race, op->arch->clone.race);
1412  }
1413 
1414  draw_info(COLOR_WHITE, op, "Your lifeforce returns!");
1415  }
1416  }
1417 
1418  if (QUERY_FLAG(op, FLAG_STEALTH) != QUERY_FLAG(refop, FLAG_STEALTH)) {
1419  ret++;
1420 
1421  if (QUERY_FLAG(op, FLAG_STEALTH)) {
1422  draw_info(COLOR_WHITE, op, "You walk quieter.");
1423  } else {
1424  draw_info(COLOR_GRAY, op, "You walk noisier.");
1425  }
1426  }
1427 
1428  if (QUERY_FLAG(op, FLAG_SEE_INVISIBLE) !=
1429  QUERY_FLAG(refop, FLAG_SEE_INVISIBLE)) {
1430  ret++;
1431 
1432  if (QUERY_FLAG(op, FLAG_SEE_INVISIBLE)) {
1433  draw_info(COLOR_WHITE, op, "You see invisible things.");
1434  } else {
1435  draw_info(COLOR_GRAY, op, "Your vision becomes less clear.");
1436  }
1437  }
1438 
1439  if (QUERY_FLAG(op, FLAG_IS_INVISIBLE) !=
1440  QUERY_FLAG(refop, FLAG_IS_INVISIBLE)) {
1441  ret++;
1442 
1443  if (QUERY_FLAG(op, FLAG_IS_INVISIBLE)) {
1444  draw_info(COLOR_WHITE, op, "You become transparent.");
1445  } else {
1446  draw_info(COLOR_GRAY, op, "You can see yourself.");
1447  }
1448  }
1449 
1450  if (QUERY_FLAG(op, FLAG_BLIND) !=
1451  QUERY_FLAG(refop, FLAG_BLIND)) {
1452  ret++;
1453 
1454  if (QUERY_FLAG(op, FLAG_BLIND)) {
1455  draw_info(COLOR_GRAY, op, "You are blinded.");
1456  } else {
1457  draw_info(COLOR_WHITE, op, "Your vision returns.");
1458  }
1459 
1460  pl->update_los = 1;
1461  }
1462 
1463  if (QUERY_FLAG(op, FLAG_CONFUSED) !=
1464  QUERY_FLAG(refop, FLAG_CONFUSED)) {
1465  ret++;
1466 
1467  if (QUERY_FLAG(op, FLAG_CONFUSED)) {
1468  draw_info(COLOR_GRAY, op, "You suddenly feel very confused!");
1469  } else {
1470  draw_info(COLOR_WHITE, op, "You regain your senses.");
1471  }
1472  }
1473 
1474  if (QUERY_FLAG(op, FLAG_SEE_IN_DARK) !=
1475  QUERY_FLAG(refop, FLAG_SEE_IN_DARK)) {
1476  ret++;
1477 
1478  if (QUERY_FLAG(op, FLAG_SEE_IN_DARK)) {
1479  draw_info(COLOR_WHITE, op, "Your vision is better in the dark.");
1480  } else {
1481  draw_info(COLOR_GRAY, op, "You see less well in the dark.");
1482  }
1483  }
1484 
1485  if (QUERY_FLAG(op, FLAG_XRAYS) != QUERY_FLAG(refop, FLAG_XRAYS)) {
1486  ret++;
1487 
1488  if (QUERY_FLAG(op, FLAG_XRAYS)) {
1489  draw_info(COLOR_GRAY, op, "Everything becomes transparent.");
1490  } else {
1491  draw_info(COLOR_GRAY, op, "Everything suddenly looks very solid.");
1492  }
1493  }
1494 
1495  if (op->stats.maxhp != refop->stats.maxhp) {
1496  ret++;
1497 
1498  if (op->stats.maxhp > refop->stats.maxhp) {
1499  draw_info(COLOR_WHITE, op, "You feel more healthy!");
1500  } else {
1501  draw_info(COLOR_GRAY, op, "You feel much less healthy!");
1502  }
1503  }
1504 
1505  if (op->stats.maxsp != refop->stats.maxsp) {
1506  ret++;
1507 
1508  if (op->stats.maxsp > refop->stats.maxsp) {
1509  draw_info(COLOR_WHITE, op, "You feel one with the powers of "
1510  "magic!");
1511  } else {
1512  draw_info(COLOR_GRAY, op, "You suddenly feel more mundane.");
1513  }
1514  }
1515 
1516  if (pl->gen_hp != refpl->gen_hp) {
1517  ret++;
1518 
1519  if (pl->gen_hp > refpl->gen_hp) {
1520  draw_info(COLOR_WHITE, op, "You feel your health regeneration "
1521  "speeding up.");
1522  } else {
1523  draw_info(COLOR_GRAY, op, "You feel your health regeneration "
1524  "slowing down.");
1525  }
1526  }
1527 
1528  if (pl->gen_sp != refpl->gen_sp) {
1529  ret++;
1530 
1531  if (pl->gen_sp > refpl->gen_sp) {
1532  draw_info(COLOR_WHITE, op, "You feel your mana regeneration "
1533  "speeding up.");
1534  } else {
1535  draw_info(COLOR_GRAY, op, "You feel your mana regeneration "
1536  "slowing down.");
1537  }
1538  }
1539 
1540  if (pl->gen_sp_armour != refpl->gen_sp_armour) {
1541  ret++;
1542 
1543  if (pl->gen_sp_armour > refpl->gen_sp_armour) {
1544  draw_info(COLOR_GRAY, op, "You feel the weight of your armour "
1545  "impairing your ability to concentrate on magic.");
1546  } else {
1547  draw_info(COLOR_WHITE, op, "You feel able to concentrate clearer.");
1548  }
1549  }
1550 
1551  if (pl->digestion != refpl->digestion) {
1552  ret++;
1553 
1554  if (pl->digestion > refpl->digestion) {
1555  draw_info(COLOR_WHITE, op, "You feel your digestion slowing down.");
1556  } else {
1557  draw_info(COLOR_GRAY, op, "You feel your digestion speeding up.");
1558  }
1559  }
1560 
1561  /* Messages for changed protections */
1562  for (i = 0; i < NROFATTACKS; i++) {
1563  if (op->protection[i] != refop->protection[i]) {
1564  ret++;
1565 
1566  if (op->protection[i] > refop->protection[i]) {
1567  draw_info_format(COLOR_GREEN, op, "Your protection to %s rises "
1568  "to %d%%.", attack_name[i], op->protection[i]);
1569  } else {
1570  draw_info_format(COLOR_BLUE, op, "Your protection to %s drops "
1571  "to %d%%.", attack_name[i], op->protection[i]);
1572  }
1573  }
1574  }
1575 
1576  /* Messages for changed attuned/repelled/denied paths. */
1577  if (op->path_attuned != refop->path_attuned ||
1578  op->path_repelled != refop->path_repelled ||
1579  op->path_denied != refop->path_denied) {
1580  uint32_t path;
1581 
1582  for (i = 0; i < PATH_NUM; i++) {
1583  path = 1U << i;
1584 
1585  if ((op->path_attuned & path) && !(refop->path_attuned & path)) {
1586  draw_info_format(COLOR_WHITE, op, "You feel your magical "
1587  "powers attuned towards the path of %s.",
1588  spellpathnames[i]);
1589  } else if ((refop->path_attuned & path) &&
1590  !(op->path_attuned & path)) {
1591  draw_info_format(COLOR_GRAY, op, "You no longer feel your "
1592  "magical powers attuned towards the path of %s.",
1593  spellpathnames[i]);
1594  }
1595 
1596  if ((op->path_repelled & path) && !(refop->path_repelled & path)) {
1597  draw_info_format(COLOR_WHITE, op, "You feel your magical "
1598  "powers repelled from the path of %s.",
1599  spellpathnames[i]);
1600  } else if ((refop->path_repelled & path) &&
1601  !(op->path_repelled & path)) {
1602  draw_info_format(COLOR_GRAY, op, "You no longer feel your "
1603  "magical powers repelled from the path of %s.",
1604  spellpathnames[i]);
1605  }
1606 
1607  if ((op->path_denied & path) && !(refop->path_denied & path)) {
1608  draw_info_format(COLOR_WHITE, op, "You feel your magical "
1609  "powers shift, and become unable to cast spells from "
1610  "the path of %s.", spellpathnames[i]);
1611  } else if ((refop->path_denied & path) &&
1612  !(op->path_denied & path)) {
1613  draw_info_format(COLOR_GRAY, op, "You feel your magical powers "
1614  "shift, and become able to cast spells from the path "
1615  "of %s.", spellpathnames[i]);
1616  }
1617  }
1618  }
1619 
1620  for (i = 0; i < NUM_STATS; i++) {
1621  stat_new = get_attr_value(&op->stats, i);
1622  stat_old = get_attr_value(&refop->stats, i);
1623 
1624  if (stat_new != stat_old) {
1625  ret++;
1626 
1627  if (stat_new > stat_old) {
1628  draw_info(COLOR_WHITE, op, gain_msg[i]);
1629  } else {
1630  draw_info(COLOR_GRAY, op, lose_msg[i]);
1631  }
1632  }
1633  }
1634 
1635  if (pl->item_power != refpl->item_power &&
1636  (pl->item_power > settings.item_power_factor * op->level ||
1637  refpl->item_power > settings.item_power_factor * op->level)) {
1638  if (pl->item_power > refpl->item_power) {
1639  draw_info(COLOR_GRAY, op, "You feel the combined power of your "
1640  "equipped items begin gnawing on your "
1641  "soul!");
1642  } else if (pl->item_power > settings.item_power_factor * op->level) {
1643  draw_info(COLOR_GRAY, op, "You feel your soul return closer to "
1644  "normal.");
1645  } else {
1646  draw_info(COLOR_WHITE, op, "You feel your soul return to normal.");
1647  }
1648  }
1649 
1650  return ret;
1651 }
1652 
1661 int living_update(object *op)
1662 {
1663  HARD_ASSERT(op != NULL);
1664 
1665  op = HEAD(op);
1666 
1667  if (QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) {
1668  return 0;
1669  }
1670 
1671  if (op->type == PLAYER) {
1672  object refop;
1673  player refpl, *pl;
1674 
1675  pl = CONTR(op);
1676 
1677  /* Remember both the old state of the object and the player. */
1678  memcpy(&refop, op, sizeof(refop));
1679  memcpy(&refpl, pl, sizeof(refpl));
1680 
1681  /* Update player. */
1683 
1684  return living_update_display(op, &refop, &refpl);
1685  }
1686 
1687  if (QUERY_FLAG(op, FLAG_MONSTER)) {
1688  /* Update monster. */
1690  return 0;
1691  }
1692 
1693  LOG(DEVEL, "Updating unhandled object: %s", object_get_str(op));
1694  return 0;
1695 }
1696 
1705 object *living_find_base_info(object *op)
1706 {
1707  HARD_ASSERT(op != NULL);
1708 
1709  SOFT_ASSERT_RC(op->head == NULL, NULL, "Called on non-head part: %s",
1710  object_get_str(op));
1711  SOFT_ASSERT_RC(op->type == MONSTER, NULL, "Object is not a monster: %s",
1712  object_get_str(op));
1713 
1714  for (object *tmp = op->inv; tmp != NULL; tmp = tmp->below) {
1715  if (tmp->type == BASE_INFO) {
1716  return tmp;
1717  }
1718  }
1719 
1720  return NULL;
1721 }
1722 
1732 object *living_get_base_info(object *op)
1733 {
1734  HARD_ASSERT(op != NULL);
1735 
1736  SOFT_ASSERT_RC(op->head == NULL, NULL, "Called on non-head part: %s",
1737  object_get_str(op));
1738  SOFT_ASSERT_RC(op->type == MONSTER, NULL, "Object is not a monster: %s",
1739  object_get_str(op));
1740 
1741  object *tmp = living_find_base_info(op);
1742  if (tmp != NULL) {
1743  return tmp;
1744  }
1745 
1746  tmp = object_get();
1747  tmp->arch = op->arch;
1748  /* Copy without putting it on active list */
1749  object_copy(tmp, op, true);
1750  tmp->type = BASE_INFO;
1751  tmp->speed_left = tmp->speed;
1752  /* Ensure this object will not be active in any way */
1753  tmp->speed = 0.0f;
1754  tmp->face = arches[ARCH_BASE_INFO]->clone.face;
1755  SET_FLAG(tmp, FLAG_NO_DROP);
1756  CLEAR_FLAG(tmp, FLAG_ANIMATE);
1757  CLEAR_FLAG(tmp, FLAG_FRIENDLY);
1758  CLEAR_FLAG(tmp, FLAG_MONSTER);
1759  /* And put it in the monster. */
1760  tmp = object_insert_into(tmp, op, 0);
1761 
1762  /* Store position (for returning home after aggro is lost). */
1763  object *env = object_get_env(op);
1764  if (env->map != NULL) {
1765  tmp->x = env->x;
1766  tmp->y = env->y;
1767  FREE_AND_ADD_REF_HASH(tmp->slaying, env->map->path);
1768  }
1769 
1770  return tmp;
1771 }
1772 
1785 void set_mobile_speed(object *op, int idx)
1786 {
1787  object *base;
1788  double speed, tmp;
1789 
1790  base = living_get_base_info(op);
1791 
1792  speed = base->speed_left;
1793  tmp = op->speed;
1794 
1795  if (idx) {
1796  op->speed = speed * idx;
1797  } else {
1798  /* We will generate the speed by setting of the monster */
1799 
1800  /* If not slowed... */
1801  if (!QUERY_FLAG(op, FLAG_SLOW_MOVE)) {
1802  speed += base->speed_left;
1803  }
1804 
1805  /* Valid enemy - monster is fighting! */
1806  if (OBJECT_VALID(op->enemy, op->enemy_count)) {
1807  speed += base->speed_left * 2;
1808  }
1809 
1810  op->speed = speed;
1811  }
1812 
1813  /* Update speed if needed */
1814  if (DBL_EQUAL(tmp, 0.0) != DBL_EQUAL(op->speed, 0.0)) {
1815  object_update_speed(op);
1816  }
1817 }
#define SCROLL
Definition: define.h:453
#define FREE_AND_ADD_REF_HASH(_sv_, _nv_)
Definition: global.h:116
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:239
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
#define MONSTER
Definition: define.h:353
void object_destroy(object *ob)
Definition: object.c:1441
const char *const attack_name[NROFATTACKS]
Definition: attack.c:57
int16_t ac
Definition: living.h:93
#define EVENT_AI_GUARD_STOP
Definition: plugin.h:111
void drain_stat(object *op)
Definition: living.c:353
uint8_t tgm
Definition: player.h:528
object * object_get_env(object *op)
Definition: object.c:631
const char * race
Definition: object.h:174
const char *const restore_msg[NUM_STATS]
Definition: living.c:185
double weapon_speed
Definition: object.h:475
#define DISEASE
Definition: define.h:538
tag_t enemy_count
Definition: object.h:216
#define QUEST_CONTAINER
Definition: define.h:489
void * custom_attrset
Definition: object.h:160
static double hp_bonus[MAX_STAT+1]
Definition: living.c:70
int16_t dam_bonus
Damage bonuses from equipment (not melee weapon).
Definition: player.h:507
static const char *const stats[]
Definition: stats.c:38
object * object_get(void)
Definition: object.c:993
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:1691
#define FOR_INV_FINISH()
Definition: define.h:1698
#define FLAG_ANIMATE
Definition: define.h:912
uint16_t terrain_type
Definition: object.h:298
shstr * path
Definition: map.h:568
double speed_left
Definition: object.h:472
#define FLAG_FRIENDLY
Definition: define.h:926
#define GIRDLE
Definition: define.h:461
int16_t last_heal
Definition: object.h:310
#define BOW
Definition: define.h:174
uint8_t item_skill
Definition: object.h:378
#define SHIELD
Definition: define.h:214
void object_copy(object *op, const object *src, bool no_speed)
Definition: object.c:886
const char * slaying
Definition: object.h:180
#define FLAG_SEE_INVISIBLE
Definition: define.h:946
The 'base_info' archetype.
Definition: arch.h:57
#define FLAG_BLIND
Definition: define.h:884
object * arch_get(const char *name)
Definition: arch.c:430
int64_t exp
Definition: living.h:69
#define BRACERS
Definition: define.h:433
#define FLAG_MAKE_INVISIBLE
Definition: define.h:1259
Definition: living.h:67
#define PLAYER
Definition: define.h:122
#define FLAG_NO_DROP
Definition: define.h:1070
int8_t get_attr_value(const living *stats, int attr)
Definition: living.c:305
uint32_t path_attuned
Definition: object.h:255
int16_t sp
Definition: living.h:78
object * skill_ptr[NROFSKILLS]
Definition: player.h:194
object * object_find_arch(object *op, archetype_t *at)
Definition: object.c:2258
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
#define EVENT_OBJECT
Definition: define.h:481
uint32_t path_repelled
Definition: object.h:258
uint8_t absorb
Definition: object.h:421
const char *const spellpathnames[NRSPELLPATHS]
Definition: spellist.h:349
struct archetype * arch
Definition: object.h:225
struct obj * enemy
Definition: object.h:196
#define EVENT_FLAG(x)
Definition: plugin.h:170
int8_t Int
Definition: living.h:112
static const char *const drain_msg[NUM_STATS]
Definition: living.c:176
#define POISONING
Definition: define.h:437
void object_remove(object *op, int flags)
Definition: object.c:1623
#define FLAG_SLOW_MOVE
Definition: define.h:916
uint8_t attack[NROFATTACKS]
Definition: object.h:436
#define QUEST_CONTAINER_ARCHETYPE
Definition: define.h:1503
int16_t maxsp
Definition: living.h:81
int8_t Con
Definition: living.h:109
int32_t hp
Definition: living.h:72
#define CON
Definition: living.h:43
#define FLAG_SEE_IN_DARK
Definition: define.h:1198
#define FLAG_IS_ASSASSINATION
Definition: define.h:1335
static int living_update_display(object *op, object *refop, player *refpl)
Definition: living.c:1329
#define ARMOUR
Definition: define.h:182
#define MIN_STAT
Definition: define.h:49
#define FLAG_NO_FIX_PLAYER
Definition: define.h:1036
#define EVENT_AI
Definition: plugin.h:101
int16_t y
Definition: object.h:276
int32_t maxhp
Definition: living.h:75
uint32_t path_denied
Definition: object.h:261
#define FLAG_LIFESAVE
Definition: define.h:1125
archetype_t * arches[ARCH_MAX]
Definition: arch.c:45
#define BEHAVIOR_GUARD
Definition: object.h:620
static const char *const gain_msg[NUM_STATS]
Definition: living.c:194
const char * object_get_str(const object *op)
Definition: object.c:3151
Definition: arch.h:40
#define FLAG_STEALTH
Definition: define.h:1146
#define FLAG_IS_ETHEREAL
Definition: define.h:892
#define OBJECT_IS_RANGED(_ob)
Definition: object.h:828
uint8_t item_condition
Definition: object.h:369
#define FLAG_USE_WEAPON
Definition: define.h:1094
void monster_data_init(object *op)
Definition: monster_data.c:48
const char *const statname[NUM_STATS]
Definition: living.c:212
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
struct mapdef * map
Definition: object.h:139
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.c:365
#define FLAG_IS_INVISIBLE
Definition: define.h:888
int16_t dam
Definition: living.h:87
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:277
uint32_t carrying
Definition: object.h:157
#define SET_FLAG(xyz, p)
Definition: define.h:741
uint32_t weight_limit[MAX_STAT+1]
Definition: living.c:129
object * arch_to_object(archetype_t *at)
Definition: arch.c:446
void set_mobile_speed(object *op, int idx)
Definition: living.c:1785
struct obj * below
Definition: object.h:114
#define CONTAINER
Definition: define.h:493
#define POW
Definition: living.h:47
int16_t last_grace
Definition: object.h:316
signed char gen_sp
Definition: player.h:286
#define FLAG_CAN_PASS_THRU
Definition: define.h:1058
int learn_spell[MAX_STAT+1]
Definition: living.c:143
#define DEX
Definition: living.h:41
uint8_t wc_range
Definition: living.h:100
int8_t item_power
Definition: object.h:442
#define FLAG_FLYING
Definition: define.h:918
#define FLAG_MAKE_ETHEREAL
Definition: define.h:1263
void living_update_monster(object *op)
Definition: living.c:1174
signed char digestion
Definition: player.h:277
double speed
Definition: object.h:469
#define ENCUMBRANCE_LIMIT
Definition: living.c:39
#define HEAD(op)
Definition: object.h:657
#define POTION
Definition: define.h:138
uint32_t update_los
Definition: player.h:440
uint8_t block
Definition: object.h:416
#define FLAG_READY_BOW
Definition: define.h:1106
object * living_get_base_info(object *op)
Definition: living.c:1732
int16_t x
Definition: object.h:273
#define POTION_EFFECT
Definition: define.h:469
void adjust_light_source(mapstruct *map, int x, int y, int light)
Definition: light.c:257
int16_t wc
Definition: living.h:90
#define FLAG_TWO_HANDED
Definition: define.h:1082
#define LIGHT_APPLY
Definition: define.h:336
struct settings_struct settings
Definition: init.c:55
int check_good_weapon(object *who, object *item)
Definition: monster.c:1764
#define MAXLEVEL
Definition: global.h:221
const char *const lose_msg[NUM_STATS]
Definition: living.c:203
#define LEVEL_DAMAGE(level)
Definition: living.h:61
#define FLAG_APPLIED
Definition: define.h:1182
int wc_bonus[MAX_STAT+1]
Definition: living.c:57
int8_t protection[NROFATTACKS]
Definition: object.h:439
double falling_mitigation[MAX_STAT+1]
Definition: living.c:110
#define NUM_STATS
Definition: living.h:49
#define RING
Definition: define.h:320
#define FLAG_XRAYS
Definition: define.h:1110
#define FLAG_REFL_MISSILE
Definition: define.h:1024
static double sp_bonus[MAX_STAT+1]
Definition: living.c:84
living stats
Definition: object.h:481
int8_t Dex
Definition: living.h:106
signed char gen_sp_armour
Definition: player.h:280
float speed_bonus[MAX_STAT+1]
Definition: living.c:97
#define FLAG_REFL_SPELL
Definition: define.h:1028
object * equipment[PLAYER_EQUIP_MAX]
Definition: player.h:191
static void living_apply_flags(object *op, const object *item)
Definition: living.c:396
uint8_t behavior
Definition: object.h:399
#define SKILL
Definition: define.h:246
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
#define WEAPON
Definition: define.h:178
uint16_t terrain_flag
Definition: object.h:301
void check_stat_bounds(living *stats)
Definition: living.c:333
int living_update(object *op)
Definition: living.c:1661
#define FREE_AND_CLEAR_HASH(_nv_)
Definition: global.h:130
int monster_signal_chance[MAX_STAT+1]
Definition: living.c:152
#define FLAG_MONSTER
Definition: define.h:922
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
#define FLAG_USE_ARMOUR
Definition: define.h:1090
#define HELMET
Definition: define.h:218
int8_t Str
Definition: living.h:103
int8_t glow_radius
Definition: object.h:338
#define INT
Definition: living.h:45
int8_t Pow
Definition: living.h:115
struct obj * inv
Definition: object.h:123
#define BOOTS
Definition: define.h:413
struct obj * head
Definition: object.h:136
#define SYMPTOM
Definition: define.h:542
double item_power_factor
Definition: global.h:413
#define FLAG_USE_BOW
Definition: define.h:1086
#define FLAG_UNDEAD
Definition: define.h:1012
#define GLOVES
Definition: define.h:417
#define FLAG_CONFUSED
Definition: define.h:872
#define IS_WEAPON(op)
Definition: define.h:837
signed char gen_hp
Definition: player.h:283
double dam_bonus[MAX_STAT+1]
Definition: living.c:44
#define PANTS
Definition: define.h:222
object * living_find_base_info(object *op)
Definition: living.c:1705
void object_update_speed(object *op)
Definition: object.c:1043
#define AMULET
Definition: define.h:234
#define CLOAK
Definition: define.h:381
New_Face * face
Definition: object.h:234
object * quest_container
Definition: player.h:215
const char *const short_stat_name[NUM_STATS]
Definition: living.c:221
int8_t level
Definition: object.h:347
#define FORCE
Definition: define.h:465
#define OBJECT_IS_AMMO(_ob)
Definition: object.h:833
struct obj * more
Definition: object.h:133
#define LIGHT_REFILL
Definition: define.h:361
#define STR
Definition: living.h:39
int8_t magic
Definition: object.h:341
int16_t item_power
Definition: player.h:499
object clone
An object from which to do object_copy().
Definition: arch.h:47
int savethrow[MAXLEVEL+1]
Definition: living.c:165
#define BASE_INFO
Definition: define.h:421
#define PATH_NUM
Definition: spells.h:79
archetype_t * arch_find(const char *name)
Definition: arch.c:407
#define SKILL_ITEM
Definition: define.h:271
#define MAX_STAT
Definition: define.h:47
int check_good_armour(object *who, object *item)
Definition: monster.c:1811
int16_t food
Definition: living.h:84
#define IS_ARMOR(op)
Definition: define.h:839
static void living_update_player_item(player *pl, object *op, const object *item, double *attacks, double *protect_bonus, double *protect_malus, int *max_bonus_hp, int *max_bonus_sp, double *max_speed, double *added_speed, double *bonus_speed)
Definition: living.c:497
void living_update_player(object *op)
Definition: living.c:614