Crossfire Server, Trunk
attack.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
21 #include "global.h"
22 
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "living.h"
28 #include "material.h"
29 #include "skills.h"
30 #include "sounds.h"
31 #include "sproto.h"
32 
33 /*#define ATTACK_DEBUG*/
34 
35 static void slow_living(object *op, object *hitter, int dam);
36 static void deathstrike_living(object *op, object *hitter, int *dam);
37 static int adj_attackroll(object *hitter, object *target);
38 static int is_aimed_missile(object *op);
39 static int did_make_save_item(object *op, int type, object *originator);
40 static void poison_living(object *op, object *hitter, int dam);
41 
49 static void cancellation(object *op) {
50  if (op->invisible)
51  return;
52 
53  if (QUERY_FLAG(op, FLAG_ALIVE) || op->type == CONTAINER || op->type == THROWN_OBJ) {
54  /* Recur through the inventory */
59  } else if (FABS(op->magic) <= rndm(0, 5)) {
60  /* Nullify this object. This code could probably be more complete */
61  /* in what abilities it should cancel */
62  op->magic = 0;
67  if (op->env && op->env->type == PLAYER) {
69  }
70  }
71 }
72 
73 static void object_get_materialtype(object* op, materialtype_t** mt) {
74  if (op->materialname == NULL) {
75  for (*mt = materialt; *mt != NULL && (*mt)->next != NULL; *mt = (*mt)->next) {
76  if (op->material & (*mt)->material)
77  break;
78  }
79  } else
80  *mt = name_to_material(op->materialname);
81 }
82 
98 static int did_make_save_item(object *op, int type, object *originator) {
99  int i, roll, saves = 0, attacks = 0, number;
100  materialtype_t* mt;
102  if (mt == NULL)
103  return TRUE;
104  roll = rndm(1, 20);
105 
106  /* the attacktypes have no meaning for object saves
107  * If the type is only magic, don't adjust type - basically, if
108  * pure magic is hitting an object, it should save. However, if it
109  * is magic teamed with something else, then strip out the
110  * magic type, and instead let the fire, cold, or whatever component
111  * destroy the item. Otherwise, you get the case of poisoncloud
112  * destroying objects because it has magic attacktype.
113  */
114  if (type != AT_MAGIC)
118  AT_MAGIC);
119 
120  if (type == 0)
121  return TRUE;
122  if (roll == 20)
123  return TRUE;
124 
125  for (number = 0; number < NROFATTACKS; number++) {
126  i = 1<<number;
127  if (!(i&type))
128  continue;
129  attacks++;
130  if (op->resist[number] == 100)
131  saves++;
132  else if (roll >= mt->save[number]-op->magic-op->resist[number]/100)
133  saves++;
134  else if ((20-mt->save[number])/3 > originator->stats.dam)
135  saves++;
136  }
137 
138  if (saves == attacks || attacks == 0)
139  return TRUE;
140  if (saves == 0 || rndm(1, attacks) > saves)
141  return FALSE;
142  return TRUE;
143 }
144 
145 static void put_in_icecube(object* op, object* originator) {
146  archetype* at = find_archetype("icecube");
147  if (at == NULL)
148  return;
149  op = stop_item(op);
150  if (op == NULL)
151  return;
152 
153  // Put in existing icecube if one exists, otherwise make one
154  object* tmp = map_find_by_archetype(op->map, op->x, op->y, at);
155  if (tmp == NULL) {
156  tmp = arch_to_object(at);
157  /* This was in the old (pre new movement code) -
158  * icecubes have slow_move set to 1 - don't want
159  * that for ones we create.
160  */
161  tmp->move_slow_penalty = 0;
162  tmp->move_slow = 0;
163  object_insert_in_map_at(tmp, op->map, originator, 0, op->x, op->y);
164  }
165  if (!QUERY_FLAG(op, FLAG_REMOVED))
166  object_remove(op);
168 }
169 
182 void save_throw_object(object *op, uint32_t type, object *originator) {
183  int dam;
184 
185  if (!did_make_save_item(op, type, originator)) {
186  object *env = op->env;
187  int x = op->x, y = op->y;
188  mapstruct *m = op->map;
189  /* For use with burning off equipped items */
190  int weight = op->weight;
191 
192  op = stop_item(op);
193  if (op == NULL)
194  return;
195 
196  /*
197  * If this object is a transport and has players in it, make them disembark.
198  */
199  if (op->type == TRANSPORT && op->inv) {
200  if (op->map == NULL) {
201  LOG(llevError, "Transport %s not on a map but with an item %s in it?\n", op->name, op->inv->name);
202  } else {
203  char name[MAX_BUF];
204  query_name(op, name, sizeof(name));
206  if (inv->contr) {
208  "You are expelled from the %s during its destruction.",
209  name);
210  inv->contr->transport = NULL;
211  }
212  }
213  FOR_INV_FINISH();
214  }
215  }
216 
217 
218  /* Set off runes in the inventory of the object being destroyed. */
220  if (inv->type == RUNE)
221  spring_trap(inv, originator);
222  FOR_INV_FINISH();
223 
224  /* Hacked the following so that type LIGHTER will work.
225  * Also, objects which are potenital "lights" that are hit by
226  * flame/elect attacks will be set to glow. "lights" are any
227  * object with +/- glow_radius and an "other_arch" to change to.
228  * (and please note that we cant fail our save and reach this
229  * function if the object doesnt contain a material that can burn.
230  * So forget lighting magical swords on fire with this!) -b.t.
231  */
233  && op->other_arch
235  const char *arch = op->other_arch->name;
236  int no_pick = QUERY_FLAG(op, FLAG_NO_PICK);
237 
238  op = object_decrease_nrof(op, 1);
239  if (op)
240  fix_stopped_item(op, m, originator);
242  if (op != NULL) {
243  if (env) {
244  op->x = env->x,
245  op->y = env->y;
247  } else {
248  if (no_pick) {
250  }
251  object_insert_in_map_at(op, m, originator, 0, x, y);
252  }
253  }
254  return;
255  }
256  if (type&AT_CANCELLATION) { /* Cancellation. */
257  cancellation(op);
258  fix_stopped_item(op, m, originator);
259  return;
260  }
261  if (op->nrof > 1) {
262  op = object_decrease_nrof(op, rndm(0, op->nrof-1));
263  if (op)
264  fix_stopped_item(op, m, originator);
265  } else {
266  if (!QUERY_FLAG(op, FLAG_REMOVED))
267  object_remove(op);
269  }
270  if (type&(AT_FIRE|AT_ELECTRICITY)) {
271  if (env) {
272  /* Check to see if the item being burnt is being worn */
273  if (QUERY_FLAG(op, FLAG_APPLIED)) {
274  /* if the object is applied, it should be safe to assume env is a player or creature. */
275  if (env->resist[ATNR_FIRE] < 100)
276  /* Should the message type be something different? */
278  "OUCH! It burns!");
279  else
281  "Despite the flame, you feel nothing.");
282  /* burning off an item causes 1 point of fire damage for every kilogram of mass the item has */
283  dam = weight / 1000 * (100 - env->resist[ATNR_FIRE]) / 100;
284  /* Double the damage on cursed items */
285  if (QUERY_FLAG(op, FLAG_CURSED))
286  dam *= 2;
287  /* Triple the damage on damned items. A cursed and damned item would thus inflict 6x damage. */
288  if (QUERY_FLAG(op, FLAG_DAMNED))
289  dam *= 3;
290  env->stats.hp -= dam;
291  /* You die at -1, not 0 */
292  if (env->stats.hp < 0)
293  kill_player(env, NULL);
294  }
295  op = create_archetype("burnout");
296  op->x = env->x,
297  op->y = env->y;
299  } else {
300  object_replace_insert_in_map("burnout", originator);
301  }
302  }
303  return;
304  }
305  /* The value of 50 is arbitrary. */
306  if (type & AT_COLD && (op->resist[ATNR_COLD] < 50) &&
307  !QUERY_FLAG(op, FLAG_NO_PICK) && (RANDOM() & 2)) {
308  put_in_icecube(op, originator);
309  }
310 }
311 
327 int hit_map(object *op, int dir, uint32_t type, int full_hit) {
328  mapstruct *map;
329  int16_t x, y;
330  int retflag = 0; /* added this flag.. will return 1 if it hits a monster */
331  tag_t op_tag;
332 
333  if (QUERY_FLAG(op, FLAG_FREED)) {
334  LOG(llevError, "BUG: hit_map(): free object\n");
335  return 0;
336  }
337 
338  if (QUERY_FLAG(op, FLAG_REMOVED) || op->env != NULL) {
339  LOG(llevError, "BUG: hit_map(): hitter (arch %s, name %s) not on a map\n", op->arch->name, op->name);
340  return 0;
341  }
342 
343  if (!op->map) {
344  LOG(llevError, "BUG: hit_map(): %s has no map\n", op->name);
345  return 0;
346  }
347 
348  op = HEAD(op);
349  op_tag = op->count;
350 
351  map = op->map;
352  x = op->x+freearr_x[dir];
353  y = op->y+freearr_y[dir];
354  if (get_map_flags(map, &map, x, y, &x, &y)&P_OUT_OF_MAP)
355  return 0;
356 
357  /* peterm: a few special cases for special attacktypes --counterspell
358  * must be out here because it strikes things which are not alive
359  */
360 
361  if (type&AT_COUNTERSPELL) {
362  counterspell(op, dir); /* see spell_effect.c */
363 
364  /* If the only attacktype is counterspell or magic, don't need
365  * to do any further processing.
366  */
367  if (!(type&~(AT_COUNTERSPELL|AT_MAGIC))) {
368  return 0;
369  }
370  type &= ~AT_COUNTERSPELL;
371  }
372 
373  if (type&AT_CHAOS) {
376  type &= ~AT_CHAOS;
377  }
378 
379  FOR_MAP_PREPARE(map, x, y, tmp) {
380  if (QUERY_FLAG(tmp, FLAG_FREED)) {
381  LOG(llevError, "BUG: hit_map(): found freed object\n");
382  break;
383  }
384 
385  /* Something could have happened to 'tmp' while 'tmp->below' was processed.
386  * For example, 'tmp' was put in an icecube.
387  * This is one of the few cases where on_same_map should not be used.
388  */
389  if (tmp->map != map || tmp->x != x || tmp->y != y)
390  continue;
391 
392  tmp = HEAD(tmp);
393 
394  /* Need to hit everyone in the transport with this spell */
395  if (tmp->type == TRANSPORT) {
397  if (pl->type == PLAYER)
398  hit_player(pl, op->stats.dam, op, type, full_hit);
399  FOR_INV_FINISH();
400  }
401 
402  if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
403  hit_player(tmp, op->stats.dam, op, type, full_hit);
404  retflag |= 1;
405  if (object_was_destroyed(op, op_tag))
406  break;
407  }
408  /* Here we are potentially destroying an object. If the object has
409  * NO_PASS set, it is also immune - you can't destroy walls. Note
410  * that weak walls have is_alive set, which prevent objects from
411  * passing over/through them. We don't care what type of movement
412  * the wall blocks - if it blocks any type of movement, can't be
413  * destroyed right now.
414  */
415  else if ((tmp->material || tmp->materialname) && op->stats.dam > 0 && !tmp->move_block) {
417  if (object_was_destroyed(op, op_tag))
418  break;
419  }
420  } FOR_MAP_FINISH();
421  return 0;
422 }
423 
439 static void attack_message(int dam, int type, object *op, object *hitter) {
440  char buf[MAX_BUF], buf1[MAX_BUF], buf2[MAX_BUF];
441  int i, found = 0;
442  mapstruct *map;
443  object *owner;
444 
445  /* put in a few special messages for some of the common attacktypes
446  * a player might have. For example, fire, electric, cold, etc
447  * [garbled 20010919]
448  */
449 
450  if (dam == 9998 && op->type == DOOR) {
451  snprintf(buf1, sizeof(buf1), "unlock %s", op->name);
452  snprintf(buf2, sizeof(buf2), " unlocks");
453  found++;
454  }
455  if (dam < 0) {
456  snprintf(buf1, sizeof(buf1), "hit %s", op->name);
457  snprintf(buf2, sizeof(buf2), " hits");
458  found++;
459  } else if (dam == 0) {
460  snprintf(buf1, sizeof(buf1), "missed %s", op->name);
461  snprintf(buf2, sizeof(buf2), " misses");
462  found++;
463  } else if ((hitter->type == DISEASE
464  || hitter->type == SYMPTOM
465  || hitter->type == POISONING
466  || (type&AT_POISON && IS_LIVE(op))) && !found) {
467  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_SUFFER][i].level != -1; i++)
468  if (dam < attack_mess[ATM_SUFFER][i].level
469  || attack_mess[ATM_SUFFER][i+1].level == -1) {
470  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_SUFFER][i].buf1, op->name, attack_mess[ATM_SUFFER][i].buf2);
471  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_SUFFER][i].buf3);
472  found++;
473  break;
474  }
475  } else if (op->type == DOOR && !found) {
476  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_DOOR][i].level != -1; i++)
477  if (dam < attack_mess[ATM_DOOR][i].level
478  || attack_mess[ATM_DOOR][i+1].level == -1) {
479  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_DOOR][i].buf1, op->name, attack_mess[ATM_DOOR][i].buf2);
480  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_DOOR][i].buf3);
481  found++;
482  break;
483  }
484  } else if (hitter->type == PLAYER && IS_LIVE(op)) {
485  if (USING_SKILL(hitter, SK_KARATE)) {
486  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_KARATE][i].level != -1; i++)
487  if (dam < attack_mess[ATM_KARATE][i].level
488  || attack_mess[ATM_KARATE][i+1].level == -1) {
489  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_KARATE][i].buf1, op->name, attack_mess[ATM_KARATE][i].buf2);
490  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_KARATE][i].buf3);
491  found++;
492  break;
493  }
494  } else if (USING_SKILL(hitter, SK_CLAWING)) {
495  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_CLAW][i].level != -1; i++)
496  if (dam < attack_mess[ATM_CLAW][i].level
497  || attack_mess[ATM_CLAW][i+1].level == -1) {
498  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_CLAW][i].buf1, op->name, attack_mess[ATM_CLAW][i].buf2);
499  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_CLAW][i].buf3);
500  found++;
501  break;
502  }
503  } else if (USING_SKILL(hitter, SK_PUNCHING)) {
504  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_PUNCH][i].level != -1; i++)
505  if (dam < attack_mess[ATM_PUNCH][i].level
506  || attack_mess[ATM_PUNCH][i+1].level == -1) {
507  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_PUNCH][i].buf1, op->name, attack_mess[ATM_PUNCH][i].buf2);
508  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_PUNCH][i].buf3);
509  found++;
510  break;
511  }
512  } else if (USING_SKILL(hitter, SK_WRAITH_FEED)) {
513  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_WRAITH_FEED][i].level != -1; i++)
514  if (dam < attack_mess[ATM_WRAITH_FEED][i].level) {
515  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_WRAITH_FEED][i].buf1, op->name, attack_mess[ATM_WRAITH_FEED][i].buf2);
516  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_WRAITH_FEED][i].buf3);
517  found++;
518  break;
519  }
520  }
521  }
522  if (found) {
523  /* done */
524  } else if (IS_ARROW(hitter) && (type == AT_PHYSICAL || type == AT_MAGIC)) {
525  snprintf(buf1, sizeof(buf1), "hit"); /* just in case */
526  for (i = 0; i < MAXATTACKMESS; i++)
527  if (dam < attack_mess[ATM_ARROW][i].level
528  || attack_mess[ATM_ARROW][i+1].level == -1) {
529  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_ARROW][i].buf3);
530  found++;
531  break;
532  }
533  } else if (type&AT_DRAIN && IS_LIVE(op)) {
534  /* drain is first, because some items have multiple attypes */
535  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_DRAIN][i].level != -1; i++)
536  if (dam < attack_mess[ATM_DRAIN][i].level
537  || attack_mess[ATM_DRAIN][i+1].level == -1) {
538  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_DRAIN][i].buf1, op->name, attack_mess[ATM_DRAIN][i].buf2);
539  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_DRAIN][i].buf3);
540  found++;
541  break;
542  }
543  } else if (type&AT_ELECTRICITY && IS_LIVE(op)) {
544  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_ELEC][i].level != -1; i++)
545  if (dam < attack_mess[ATM_ELEC][i].level
546  || attack_mess[ATM_ELEC][i+1].level == -1) {
547  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_ELEC][i].buf1, op->name, attack_mess[ATM_ELEC][i].buf2);
548  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_ELEC][i].buf3);
549  found++;
550  break;
551  }
552  } else if (type&AT_COLD && IS_LIVE(op)) {
553  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_COLD][i].level != -1; i++)
554  if (dam < attack_mess[ATM_COLD][i].level
555  || attack_mess[ATM_COLD][i+1].level == -1) {
556  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_COLD][i].buf1, op->name, attack_mess[ATM_COLD][i].buf2);
557  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_COLD][i].buf3);
558  found++;
559  break;
560  }
561  } else if (type&AT_FIRE) {
562  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_FIRE][i].level != -1; i++)
563  if (dam < attack_mess[ATM_FIRE][i].level
564  || attack_mess[ATM_FIRE][i+1].level == -1) {
565  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_FIRE][i].buf1, op->name, attack_mess[ATM_FIRE][i].buf2);
566  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_FIRE][i].buf3);
567  found++;
568  break;
569  }
570  } else if (hitter->current_weapon != NULL) {
571  int mtype;
572 
573  switch (hitter->current_weapon->weapontype) {
574  case WEAP_HIT: mtype = ATM_BASIC; break;
575  case WEAP_SLASH: mtype = ATM_SLASH; break;
576  case WEAP_PIERCE: mtype = ATM_PIERCE; break;
577  case WEAP_CLEAVE: mtype = ATM_CLEAVE; break;
578  case WEAP_SLICE: mtype = ATM_SLICE; break;
579  case WEAP_STAB: mtype = ATM_STAB; break;
580  case WEAP_WHIP: mtype = ATM_WHIP; break;
581  case WEAP_CRUSH: mtype = ATM_CRUSH; break;
582  case WEAP_BLUD: mtype = ATM_BLUD; break;
583  default: mtype = ATM_BASIC; break;
584  }
585  for (i = 0; i < MAXATTACKMESS && attack_mess[mtype][i].level != -1; i++)
586  if (dam < attack_mess[mtype][i].level
587  || attack_mess[mtype][i+1].level == -1) {
588  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[mtype][i].buf1, op->name, attack_mess[mtype][i].buf2);
589  snprintf(buf2, sizeof(buf2), "%s", attack_mess[mtype][i].buf3);
590  found++;
591  break;
592  }
593  } else {
594  for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_BASIC][i].level != -1; i++)
595  if (dam < attack_mess[ATM_BASIC][i].level
596  || attack_mess[ATM_BASIC][i+1].level == -1) {
597  snprintf(buf1, sizeof(buf1), "%s %s%s", attack_mess[ATM_BASIC][i].buf1, op->name, attack_mess[ATM_BASIC][i].buf2);
598  snprintf(buf2, sizeof(buf2), "%s", attack_mess[ATM_BASIC][i].buf3);
599  found++;
600  break;
601  }
602  }
603 
604  if (!found) {
605  snprintf(buf1, sizeof(buf1), "hit");
606  snprintf(buf2, sizeof(buf2), "hits");
607  }
608 
609  if (dam > 0) {
610  if (hitter->chosen_skill)
611  play_sound_map(SOUND_TYPE_HIT, hitter, 0, hitter->chosen_skill->name);
612  }
613 
614  /* bail out if a monster is casting spells */
615  owner = object_get_owner(hitter);
616  if (hitter->type != PLAYER && (owner == NULL || owner->type != PLAYER))
617  return;
618 
619  /* scale down magic considerably. */
620  if (type&AT_MAGIC && rndm(0, 5))
621  return;
622 
623  /* Did a player hurt another player? Inform both! */
624  /* only show half the player->player combat messages */
625  if (op->type == PLAYER
626  && rndm(0, 1)
627  && ((owner != NULL ? owner : hitter)->type) == PLAYER) {
628  if (owner != NULL)
629  snprintf(buf, sizeof(buf), "%s's %s %s you.", owner->name, hitter->name, buf2);
630  else {
631  snprintf(buf, sizeof(buf), "%s%s you.", hitter->name, buf2);
632  }
634  } /* end of player hitting player */
635 
636  /* scale down these messages too */
637  /*if(hitter->type == PLAYER && rndm(0, 2) == 0) {*/
638  if (hitter->type == PLAYER) {
639  snprintf(buf, sizeof(buf), "You %s.", buf1);
641  buf);
642  } else if (owner != NULL && owner->type == PLAYER) {
643  /* look for stacked spells and start reducing the message chances */
644  if (hitter->type == SPELL_EFFECT
645  && (hitter->subtype == SP_EXPLOSION || hitter->subtype == SP_BULLET || hitter->subtype == SP_CONE)) {
646  i = 4;
647  map = hitter->map;
648  if (OUT_OF_REAL_MAP(map, hitter->x, hitter->y))
649  return;
651  if (next->type == SPELL_EFFECT
652  && (next->subtype == SP_EXPLOSION || next->subtype == SP_BULLET || next->subtype == SP_CONE)) {
653  i *= 3;
654  if (i > 10000)
655  /* no need to test more, and avoid overflows */
656  break;
657  }
658  FOR_MAP_FINISH();
659  if (i < 0)
660  return;
661  if (rndm(0, i) != 0)
662  return;
663  } else if (rndm(0, 5) != 0)
664  return;
665  play_sound_map(SOUND_TYPE_HIT, owner, 0, "hit");
667  "Your %s%s %s.",
668  hitter->name, buf2, op->name);
669  }
670 }
671 
683 static int get_attack_mode(object **target, object **hitter,
684  int *simple_attack) {
685  if (QUERY_FLAG(*target, FLAG_FREED) || QUERY_FLAG(*hitter, FLAG_FREED)) {
686  LOG(llevError, "BUG: get_attack_mode(): freed object\n");
687  return 1;
688  }
689  *target = HEAD(*target);
690  *hitter = HEAD(*hitter);
691  if ((*hitter)->env != NULL || (*target)->env != NULL) {
692  *simple_attack = 1;
693  return 0;
694  }
695  if (QUERY_FLAG(*target, FLAG_REMOVED)
697  || (*hitter)->map == NULL
698  || !on_same_map((*hitter), (*target))) {
699  LOG(llevError, "BUG: hitter (arch %s, name %s) with no relation to target\n", (*hitter)->arch->name, (*hitter)->name);
700  return 1;
701  }
702  *simple_attack = 0;
703  return 0;
704 }
705 
719 static int abort_attack(object *target, object *hitter, int simple_attack) {
720  int new_mode;
721 
722  if (hitter->env == target || target->env == hitter)
723  new_mode = 1;
724  else if (QUERY_FLAG(hitter, FLAG_REMOVED)
725  || QUERY_FLAG(target, FLAG_REMOVED)
726  || hitter->map == NULL || !on_same_map(hitter, target))
727  return 1;
728  else
729  new_mode = 0;
730  return new_mode != simple_attack;
731 }
732 
733 static void thrown_item_effect(object *, object *);
734 
750 static int attack_ob_simple(object *op, object *hitter, int base_dam, int base_wc) {
751  int simple_attack, roll, dam;
752  uint32_t type;
753  tag_t op_tag, hitter_tag;
754  const char *anim_suffix = NULL;
755 
756  if (get_attack_mode(&op, &hitter, &simple_attack))
757  return 1;
758 
759  /* This is used to handle script_weapons with weapons, only for players. */
760  if (hitter->type == PLAYER) {
761  if (hitter->current_weapon != NULL) {
762  if (events_execute_object_event(hitter->current_weapon, EVENT_ATTACKS,
763  hitter, op, NULL, SCRIPT_FIX_ALL) != 0)
764  return 0;
765  }
766  }
767 
768  if (hitter->current_weapon) {
769  anim_suffix = hitter->current_weapon->anim_suffix;
770  } else if (hitter->chosen_skill) {
771  anim_suffix = hitter->chosen_skill->anim_suffix;
772  }
773 
774  if (!anim_suffix) {
775  anim_suffix = "attack";
776  }
777  apply_anim_suffix(hitter, anim_suffix);
778 
779 
780  op_tag = op->count;
781  hitter_tag = hitter->count;
782  /*
783  * A little check to make it more difficult to dance forward and back
784  * to avoid ever being hit by monsters.
785  */
786  if (!simple_attack && QUERY_FLAG(op, FLAG_MONSTER)
787  && op->speed_left > -(FABS(op->speed))*0.3) {
788  /* Decrease speed BEFORE calling process_object. Otherwise, an
789  * infinite loop occurs, with process_object calling monster_move(),
790  * which then gets here again. By decreasing the speed before
791  * we call process_object, the 'if' statement above will fail.
792  */
793  op->speed_left--;
795  if (object_was_destroyed(op, op_tag)
796  || object_was_destroyed(hitter, hitter_tag)
797  || abort_attack(op, hitter, simple_attack))
798  return 1;
799  }
800 
801  roll = random_roll(1, 20, hitter, PREFER_HIGH);
802 
803  /* Adjust roll for various situations. */
804  if (!simple_attack)
805  roll += adj_attackroll(hitter, op);
806 
807  /* See if we hit the creature */
808  if (roll >= 20 || op->stats.ac >= base_wc-roll) {
809  if (settings.casting_time == TRUE) {
810  if (hitter->type == PLAYER && hitter->casting_time > -1) {
811  hitter->casting_time = -1;
813  "You attacked and lost your spell!");
814  }
815  if (op->casting_time > -1 && base_dam > 0) {
816  op->casting_time = -1;
817  if (op->type == PLAYER) {
819  "You were hit and lost your spell!");
822  "%s was hit by %s and lost a spell.",
823  op->name, hitter->name);
824  }
825  }
826  }
827  if (!simple_attack) {
828  /* If you hit something, the victim should *always *wake up.
829  * Before, invisible hitters could avoid doing this.
830  * -b.t. */
831  if (QUERY_FLAG(op, FLAG_SLEEP))
833 
834  /* If the victim can't see the attacker, it may alert others
835  * for help. */
836  if (op->type != PLAYER && !monster_can_see_enemy(op, hitter)
837  && !object_get_owner(op) && rndm(0, op->stats.Int))
839 
840  /* if you were hidden and hit by a creature, you are discovered*/
841  if (op->hide && QUERY_FLAG(hitter, FLAG_ALIVE)) {
842  make_visible(op);
843  if (op->type == PLAYER)
845  "You were hit by a wild attack. You are no longer hidden!");
846  }
847 
848  /* thrown items (hitter) will have various effects
849  * when they hit the victim. For things like thrown daggers,
850  * this sets 'hitter' to the actual dagger, and not the
851  * wrapper object.
852  */
854  if (object_was_destroyed(hitter, hitter_tag)
855  || object_was_destroyed(op, op_tag)
856  || abort_attack(op, hitter, simple_attack)) {
857  return 0;
858  }
859  }
860 
861  /* Need to do at least 1 damage, otherwise there is no point
862  * to go further and it will cause FPE's below.
863  */
864  if (base_dam <= 0)
865  base_dam = 1;
866 
867  type = hitter->attacktype;
868  if (!type)
869  type = AT_PHYSICAL;
870  /* Handle monsters that hit back */
871  if (!simple_attack && QUERY_FLAG(op, FLAG_HITBACK)
873  if (op->attacktype&AT_ACID && hitter->type == PLAYER)
875  "You are splashed by acid!\n");
876  hit_player(hitter, random_roll(0, (op->stats.dam), hitter, PREFER_LOW), op, op->attacktype, 1);
877  if (object_was_destroyed(op, op_tag)
878  || object_was_destroyed(hitter, hitter_tag)
879  || abort_attack(op, hitter, simple_attack)) {
880  return 0;
881  }
882  }
883 
884  /* In the new attack code, it should handle multiple attack
885  * types in its area, so remove it from here.
886  */
887  dam = hit_player(op, random_roll(1, base_dam, hitter, PREFER_HIGH), hitter, type, 1);
888  if (object_was_destroyed(op, op_tag)
889  || object_was_destroyed(hitter, hitter_tag)
890  || abort_attack(op, hitter, simple_attack)) {
891  return 0;
892  }
893  } else {/* end of if hitter hit op */
894  play_sound_map(SOUND_TYPE_HIT, hitter, 0, "miss");
895  dam = 0; /* if we missed, dam=0 */
896  }
897 
898  /*attack_message(dam, type, op, hitter);*/
899 
900  return dam;
901 }
902 
912 int attack_ob(object *op, object *hitter) {
913  hitter = HEAD(hitter);
914  return attack_ob_simple(op, hitter, hitter->stats.dam, hitter->stats.wc);
915 }
916 
927 static int stick_arrow(object *op, object *tmp) {
928  /* If the missile hit a player, we insert it in their inventory.
929  * However, if the missile is heavy, we don't do so (assume it falls
930  * to the ground after a hit). What a good value for this is up to
931  * debate - 5000 is 5 kg, so arrows, knives, and other light weapons
932  * stick around.
933  */
934  if (op->weight <= 5000 && tmp->stats.hp >= 0) {
935  object_remove(op);
937  return 1;
938  } else
939  return 0;
940 }
941 
954 object *hit_with_arrow(object *op, object *victim) {
955  object *container, *hitter;
956  int hit_something = 0;
957  tag_t victim_tag, hitter_tag, container_tag;
958  int16_t victim_x, victim_y;
959  mapstruct *victim_map;
960  const char *old_skill = NULL;
961 
962  /* Disassemble missile */
963  hitter = op->inv;
965  if (hitter->type != EVENT_CONNECTOR) {
966  break;
967  }
968  }
970  if (!hitter) {
971  container = NULL;
972  hitter = op;
973  if (free_no_drop(hitter))
974  return NULL;
975  } else {
976  container = op;
978  if (free_no_drop(hitter))
979  return NULL;
980 
981  object_insert_in_map_at(hitter, container->map, hitter, INS_NO_MERGE|INS_NO_WALK_ON, container->x, container->y);
982  /* Note that we now have an empty THROWN_OBJ on the map. Code that
983  * might be called until this THROWN_OBJ is either reassembled or
984  * removed at the end of this function must be able to deal with empty
985  * THROWN_OBJs. */
986  container_tag = container->count;
987  }
988 
989  /* Try to hit victim */
990  victim_x = victim->x;
991  victim_y = victim->y;
992  victim_map = victim->map;
993  victim_tag = victim->count;
994  hitter_tag = hitter->count;
995  /* FIXME provide also to script the skill? hitter is the throwed
996  items, but there is no information about the fact it was
997  thrown
998  */
1000  /*
1001  * temporary set the hitter's skill to the one associated with the
1002  * throw wrapper. This is needed to that thrower gets it's xp at the
1003  * correct level. This might proves an awfull hack :/ We should really
1004  * provide attack_ob_simple with the skill to use...
1005  */
1006  if (container != NULL) {
1007  old_skill = hitter->skill;
1008  hitter->skill = add_refcount(container->skill);
1009  }
1010  hit_something = attack_ob_simple(victim, hitter, op->stats.dam, op->stats.wc);
1011  }
1012  /* Arrow attacks door, rune of summoning is triggered, demon is put on
1013  * arrow, move_apply() calls this function, arrow sticks in demon,
1014  * attack_ob_simple() returns, and we've got an arrow that still exists
1015  * but is no longer on the map. Ugh. (Beware: Such things can happen at
1016  * other places as well!)
1017  */
1018  if (object_was_destroyed(hitter, hitter_tag) || hitter->env != NULL) {
1019  if (container) {
1020  object_remove(container);
1021  object_free_drop_inventory(container);
1022  }
1023  return NULL;
1024  }
1025  if (container != NULL) {
1026  free_string(hitter->skill);
1027  hitter->skill = old_skill;
1028  }
1029  /* Missile hit victim */
1030  /* if the speed is > 10, then this is a fast moving arrow, we go straight
1031  * through the target
1032  */
1033  if (hit_something && op->speed <= 10.0) {
1034  /* Stop arrow */
1035  if (container == NULL) {
1037  if (hitter == NULL)
1038  return NULL;
1039  } else {
1040  if(!object_was_destroyed(container, container_tag)) {
1041  object_remove(container);
1042  object_free_drop_inventory(container);
1043  }
1044  }
1045 
1046  /* Try to stick arrow into victim */
1047  if (!object_was_destroyed(victim, victim_tag)
1048  && stick_arrow(hitter, victim)) {
1049  object_set_owner(hitter, NULL);
1050  return NULL;
1051  }
1052 
1053  /* Else try to put arrow on victim's map square
1054  * remove check for P_WALL here. If the arrow got to this
1055  * space, that is good enough - with the new movement code,
1056  * there is now the potential for lots of spaces where something
1057  * can fly over but not otherwise move over. What is the correct
1058  * way to handle those otherwise?
1059  */
1060  if (victim_x != hitter->x || victim_y != hitter->y) {
1062  object_set_owner(hitter, NULL);
1063  object_insert_in_map_at(hitter, victim_map, hitter, 0, victim_x, victim_y);
1064  } else {
1065  /* Else leave arrow where it is */
1066  object_merge(hitter, NULL);
1067  object_set_owner(hitter, NULL);
1068  }
1069  return NULL;
1070  }
1071 
1072  if (hit_something && op->speed >= 10.0)
1073  op->speed -= 1.0;
1074 
1075  /* Missile missed victim - reassemble missile */
1076  if (container) {
1078  object_insert_in_ob(hitter, container);
1079  }
1080  return op;
1081 }
1089 static void tear_down_wall(object *op) {
1090  int perc = 0;
1091 
1092  if (!op->stats.maxhp) {
1093  LOG(llevError, "TEAR_DOWN wall %s had no maxhp.\n", op->name);
1094  perc = 1;
1095  } else if (!GET_ANIM_ID(op)) {
1096  /* Object has been called - no animations, so remove it */
1097  if (op->stats.hp < 0) {
1098  object_remove(op); /* Should update LOS */
1100  /* Don't know why this is here - object_remove() should do it for us */
1101  /*update_position(m, x, y);*/
1102  }
1103  return; /* no animations, so nothing more to do */
1104  }
1105  assert(op->stats.maxhp > 0);
1106  perc = NUM_ANIMATIONS(op)-((int)NUM_ANIMATIONS(op)*op->stats.hp)/op->stats.maxhp;
1107 
1108  if (perc >= (int)NUM_ANIMATIONS(op))
1109  perc = NUM_ANIMATIONS(op)-1;
1110  else if (perc < 1)
1111  perc = 1;
1112  SET_ANIMATION(op, perc);
1114  if (perc == NUM_ANIMATIONS(op)-1) { /* Reached the last animation */
1115  if (op->face == blank_face) {
1116  /* If the last face is blank, remove the ob */
1117  object_remove(op); /* Should update LOS */
1119 
1120  /* object_remove() should call update_position for us */
1121  /*update_position(m, x, y);*/
1122  } else { /* The last face was not blank, leave an image */
1124  update_all_los(op->map, op->x, op->y);
1125  op->move_block = 0;
1127  }
1128  }
1129 }
1130 
1138 static void scare_creature(object *target, object *hitter) {
1139  object *owner = object_get_owner(hitter);
1140 
1141  if (!owner)
1142  owner = hitter;
1143 
1144  SET_FLAG(target, FLAG_SCARED);
1145  if (!target->enemy)
1146  object_set_enemy(target, owner);
1147 }
1148 
1165 static int hit_with_one_attacktype(object *op, object *hitter, int dam, uint32_t attacknum) {
1166  int doesnt_slay = 1;
1167  char name_hitter[MAX_BUF], name_op[MAX_BUF];
1168 
1169  /* Catch anyone that may be trying to send us a bitmask instead of the number */
1170  if (attacknum >= NROFATTACKS) {
1171  LOG(llevError, "hit_with_one_attacktype: Invalid attacknumber passed: %u\n", attacknum);
1172  return 0;
1173  }
1174 
1175  if (dam < 0) {
1176  LOG(llevError, "hit_with_one_attacktype called with negative damage: %d\n", dam);
1177  return 0;
1178  }
1179 
1180  /* AT_INTERNAL is supposed to do exactly dam. Put a case here so
1181  * people can't mess with that or it otherwise get confused. */
1182  if (attacknum == ATNR_INTERNAL)
1183  return dam;
1184 
1185  if (hitter->slaying) {
1186  // Look for the race as one of many that can be listed.
1187  if ((op->race != NULL && strstr(op->race, hitter->slaying))
1188  /*
1189  * Since we can do multiple races, we should be able to avoid the arch check.
1190  * This gives a more flexible race system, so skeletal mages can be a type of skeleton and such
1191  || (op->arch && op->arch->name != NULL && strstr(op->arch->name, hitter->slaying))
1192  */) {
1193  doesnt_slay = 0;
1194  dam *= 3;
1195  }
1196  }
1197 
1198  /* Adjust the damage for resistance. Note that neg. values increase damage. */
1199  /*
1200  * Skip lifestealing here, because it undergoes a more specific resistance scaling
1201  * in its own section that involves the better of drain/life stealing resistance
1202  *
1203  * Daniel Hawkins 2018-05-31
1204  */
1205  if (attacknum != ATNR_LIFE_STEALING && op->resist[attacknum]) {
1206  /* basically: dam = dam*(100-op->resist[attacknum])/100;
1207  * in case 0>dam>1, we try to "simulate" a float value-effect */
1208  dam *= (100-op->resist[attacknum]);
1209  if (dam >= 100)
1210  dam /= 100;
1211  else
1212  dam = (dam > (random_roll(0, 99, op, PREFER_LOW))) ? 1 : 0;
1213  }
1214 
1215  /* Special hack. By default, if immune to something, you
1216  * shouldn't need to worry. However, acid is an exception, since
1217  * it can still damage your items. Only include attacktypes if
1218  * special processing is needed */
1219 
1220  if (op->resist[attacknum] >= 100
1221  && doesnt_slay
1222  && attacknum != ATNR_ACID)
1223  return 0;
1224 
1225  /* Keep this in order - makes things easier to find */
1226 
1227  switch (attacknum) {
1228  case ATNR_PHYSICAL:
1229  /* here also check for diseases */
1231  break;
1232 
1233  /* Don't need to do anything for:
1234  magic,
1235  fire,
1236  electricity,
1237  cold */
1238 
1239  case ATNR_CONFUSION:
1240  case ATNR_POISON:
1241  case ATNR_SLOW:
1242  case ATNR_PARALYZE:
1243  case ATNR_FEAR:
1244  case ATNR_CANCELLATION:
1245  case ATNR_DEPLETE:
1246  case ATNR_BLIND: {
1247  /* chance for inflicting a special attack depends on the
1248  * difference between attacker's and defender's level
1249  */
1250  int level_diff = MIN(110, MAX(0, op->level-hitter->level));
1251 
1252  /* First, only creatures/players with speed can be affected.
1253  * Second, just getting hit doesn't mean it always affects
1254  * you. Third, you still get a saving through against the
1255  * effect.
1256  */
1257  if (op->speed
1258  && (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER)
1259  && !(rndm(0, (attacknum == ATNR_SLOW ? 6 : 3)-1))
1260  && !did_make_save(op, level_diff, op->resist[attacknum]/10)) {
1261  /* Player has been hit by something */
1262  if (attacknum == ATNR_CONFUSION)
1263  confuse_living(op, hitter, dam);
1264  else if (attacknum == ATNR_POISON)
1265  poison_living(op, hitter, dam);
1266  else if (attacknum == ATNR_SLOW)
1267  slow_living(op, hitter, dam);
1268  else if (attacknum == ATNR_PARALYZE)
1269  paralyze_living(op, dam);
1270  else if (attacknum == ATNR_FEAR)
1272  else if (attacknum == ATNR_CANCELLATION)
1273  cancellation(op);
1274  else if (attacknum == ATNR_DEPLETE)
1275  drain_stat(op);
1276  else if (attacknum == ATNR_BLIND
1277  && !QUERY_FLAG(op, FLAG_UNDEAD)
1279  blind_living(op, hitter, dam);
1280  }
1281  dam = 0; /* These are all effects and don't do real damage */
1282  }
1283  break;
1284 
1285  case ATNR_ACID: {
1286  int flag = 0;
1287 
1288  /* Items only get corroded if you're not on a battleground and
1289  * if your acid resistance is below 50%. */
1290  if (!op_on_battleground(op, NULL, NULL, NULL)
1291  && (op->resist[ATNR_ACID] < 50)) {
1292  FOR_INV_PREPARE(op, tmp) {
1293  if (tmp->invisible)
1294  continue;
1295  if (!QUERY_FLAG(tmp, FLAG_APPLIED)
1296  || (tmp->resist[ATNR_ACID] >= 10))
1297  /* >= 10% acid res. on itmes will protect these */
1298  continue;
1299  if (!(tmp->material&M_IRON))
1300  continue;
1301  if (tmp->magic < -4) /* Let's stop at -5 */
1302  continue;
1303  if (tmp->type == RING
1304  /* removed boots and gloves from exclusion list in PR */
1305  || tmp->type == GIRDLE
1306  || tmp->type == AMULET
1307  || tmp->type == WAND
1308  || tmp->type == ROD)
1309  continue; /* To avoid some strange effects */
1310 
1311  /* High damage acid has better chance of corroding objects */
1312  if (rndm(0, dam+4) > random_roll(0, 39, op, PREFER_HIGH)+2*tmp->magic) {
1313  if (op->type == PLAYER) {
1314  /* Make this more visible */
1315  query_name(hitter, name_hitter, MAX_BUF);
1316  query_name(tmp, name_op, MAX_BUF);
1319  "The %s's acid corrodes your %s!",
1320  name_hitter, name_op);
1321  }
1322  flag = 1;
1323  tmp->magic--;
1324  if (op->type == PLAYER)
1326  }
1327  } FOR_INV_FINISH();
1328  if (flag)
1329  fix_object(op); /* Something was corroded */
1330  }
1331  }
1332  break;
1333 
1334  case ATNR_DRAIN: {
1335  /* rate is the proportion of exp drained. High rate means
1336  * not much is drained, low rate means a lot is drained.
1337  */
1338  int rate;
1339 
1340  if (op->resist[ATNR_DRAIN] >= 0)
1341  rate = 50+op->resist[ATNR_DRAIN]/2;
1342  else
1343  rate = 5000/(100-op->resist[ATNR_DRAIN]);
1344 
1345  /* full protection has no effect. Nothing else in this
1346  * function needs to get done, so just return. */
1347  if (!rate)
1348  return 0;
1349 
1350  if (op->stats.exp <= rate) {
1351  if (op->type == GOLEM)
1352  dam = 999; /* Its force is "sucked" away. 8) */
1353  else
1354  /* If we can't drain, lets try to do physical damage */
1356  } else {
1357  /* Randomly give the hitter some hp */
1358  if (hitter->stats.hp < hitter->stats.maxhp
1359  && (op->level > hitter->level)
1360  && random_roll(0, (op->level-hitter->level+2), hitter, PREFER_HIGH) > 3)
1361  hitter->stats.hp++;
1362 
1363  /* Can't do drains on battleground spaces.
1364  * Move the wiz check up here - before, the hitter wouldn't gain exp
1365  * exp, but the wiz would still lose exp! If drainee is a wiz,
1366  * nothing happens.
1367  * Try to credit the owner. We try to display player -> player drain
1368  * attacks, hence all the != PLAYER checks.
1369  */
1370  if (!op_on_battleground(hitter, NULL, NULL, NULL) && !QUERY_FLAG(op, FLAG_WAS_WIZ)) {
1371  object *owner = object_get_owner(hitter);
1372  int64_t orig_exp = op->stats.exp;
1373 
1374  change_exp(op, -op->stats.exp/rate, NULL, 0);
1375 
1376  if (owner && owner != hitter) {
1377  if (op->type != PLAYER || owner->type != PLAYER)
1378  change_exp(owner, MIN(op->stats.exp/(rate*2), orig_exp - op->stats.exp),
1379  hitter->chosen_skill ? hitter->chosen_skill->skill : NULL, SK_EXP_TOTAL);
1380  } else if (op->type != PLAYER || hitter->type != PLAYER) {
1381  change_exp(hitter, MIN(op->stats.exp/(rate*2), orig_exp - op->stats.exp),
1382  hitter->chosen_skill ? hitter->chosen_skill->skill : NULL, 0);
1383  }
1384  }
1385  dam = 1; /* Drain is an effect. Still return 1 - otherwise, if you have pure
1386  * drain attack, you won't know that you are actually sucking out EXP,
1387  * as the messages will say you missed
1388  */
1389  }
1390  }
1391  break;
1392 
1393  case ATNR_TURN_UNDEAD: {
1394  if (QUERY_FLAG(op, FLAG_UNDEAD)) {
1395  object *owner = object_get_owner(hitter) == NULL ? hitter : object_get_owner(hitter);
1396  const object *god = find_god(determine_god(owner));
1397  int div = 1;
1398 
1399  /* if undead are not an enemy of your god, you turn them
1400  * at half strength */
1401  if (!god
1402  || !god->slaying
1403  || strstr(god->slaying, undead_name) == NULL)
1404  div = 2;
1405 
1406  /* The previous code was highly suspect - resist turn undead/100 would
1407  * at best give a bonus of 1 - increase that to resist turn undead/20 -
1408  * this gives a bit higher bonus. Also the bonus was added to the wrong
1409  * side of the equation, actually making it easier to turn creatures
1410  * if they had that resistance.
1411  */
1412  if ((op->level*div + (op->resist[ATNR_TURN_UNDEAD] / 20)) < (get_turn_bonus(owner->stats.Wis)+owner->level))
1413  scare_creature(op, owner);
1414  } else
1415  dam = 0; /* don't damage non undead - should we damage undead? */
1416  }
1417  break;
1418 
1419  case ATNR_DEATH:
1420  deathstrike_living(op, hitter, &dam);
1421  break;
1422 
1423  case ATNR_CHAOS:
1424  query_name(op, name_op, MAX_BUF);
1425  query_name(hitter, name_hitter, MAX_BUF);
1426  LOG(llevError, "%s was hit by %s with non-specific chaos.\n", name_op, name_hitter);
1427  dam = 0;
1428  break;
1429 
1430  case ATNR_COUNTERSPELL:
1431  dam = 0;
1432  /* While counterspell is handled separately and filtered out when it
1433  * moves, players can still step on a square that has an active
1434  * counterspell. When this happens, do no damage because counterspell
1435  * has no effect on anything but spells. */
1436  break;
1437 
1438  case ATNR_HOLYWORD: {
1439  /* This has already been handled by hit_player,
1440  * no need to check twice -- DAMN */
1441 
1442  object *owner = object_get_owner(hitter) == NULL ? hitter : object_get_owner(hitter);
1443 
1444  /* As with turn undead above, give a bonus on the saving throw */
1445  if (op->level+(op->resist[ATNR_HOLYWORD]/100) < owner->level+get_turn_bonus(owner->stats.Wis))
1446  scare_creature(op, owner);
1447  }
1448  break;
1449 
1450  case ATNR_LIFE_STEALING: {
1451  int new_hp;
1452  /* this is replacement to drain for players, instead of taking
1453  * exp it takes hp. It is geared for players, probably not
1454  * much use giving it to monsters
1455  *
1456  * life stealing doesn't do a lot of damage, but it gives the
1457  * damage it does do to the player. Given that,
1458  * it only does 1/30'th normal damage (hence the divide by
1459  * 3000). Wraith get 1/2 of the damage, and therefore divide
1460  * by 200. This number may need tweaking for game balance.
1461  */
1462 
1463  int dam_modifier = is_wraith_pl(hitter) ? 200 : 3000;
1464 
1465  /* You can't steal life from something undead or not alive. */
1466  if (op->type == GOLEM
1467  || (QUERY_FLAG(op, FLAG_UNDEAD))
1468  || !(QUERY_FLAG(op, FLAG_ALIVE))
1469  || (op->type == DOOR))
1470  return 0;
1471  /* If drain protection is higher than life stealing, use that */
1472  if (op->resist[ATNR_DRAIN] >= op->resist[ATNR_LIFE_STEALING])
1473  dam = (dam*(100-op->resist[ATNR_DRAIN]))/dam_modifier;
1474  else
1475  dam = (dam*(100-op->resist[ATNR_LIFE_STEALING]))/dam_modifier;
1476  /* You die at -1 hp, not zero. */
1477  if (dam > op->stats.hp+1)
1478  dam = op->stats.hp+1;
1479  new_hp = hitter->stats.hp+dam;
1480  if (new_hp > hitter->stats.maxhp)
1481  new_hp = hitter->stats.maxhp;
1482  if (new_hp > hitter->stats.hp)
1483  hitter->stats.hp = new_hp;
1484 
1485  /* Wraith also get food through life stealing */
1486  if (is_wraith_pl(hitter)) {
1487  if (hitter->stats.food+dam >= MAX_FOOD)
1488  hitter->stats.food = MAX_FOOD;
1489  else
1490  hitter->stats.food += dam;
1491  fix_object(hitter);
1492  }
1493  }
1494  }
1495  return dam;
1496 }
1497 
1498 /*
1499  * This function is defined in party.c, but conditionally, something "make proto"
1500  * doesn't handle. So define it locally.
1501  */
1502 #ifdef PARTY_KILL_LOG
1503 void party_add_kill(partylist *party, const char *killer, const char *dead, long exp);
1504 #endif
1505 
1533 static int kill_object(object *op, int dam, object *hitter) {
1534  char kill_message[MAX_BUF];
1535  const char *skill;
1536  int maxdam = 0;
1537  int battleg = 0; /* true if op standing on battleground */
1538  int pk = 0; /* true if op and what controls hitter are both players*/
1539  object *owner = NULL;
1540  const object *skop = NULL;
1541 
1542  if (op->stats.hp >= 0)
1543  return -1;
1544 
1546  return 0;
1548 
1550 
1551  /* maxdam needs to be the amount of damage it took to kill
1552  * this creature. The function(s) that call us have already
1553  * adjusted the creatures HP total, so that is negative.
1554  */
1555  maxdam = dam+op->stats.hp+1;
1556 
1558  update_all_los(op->map, op->x, op->y); /* makes sure los will be recalculated */
1559 
1560  if (op->type == DOOR) {
1561  op->speed = 0.1;
1563  op->speed_left = -0.05;
1564  return maxdam;
1565  }
1566  if (QUERY_FLAG(op, FLAG_FRIENDLY) && op->type != PLAYER) {
1567  object *owner;
1568 
1570  owner = object_get_owner(op);
1571  if (owner != NULL
1572  && owner->type == PLAYER) {
1573  if (owner->contr->ranges[range_golem] == op) {
1574  owner->contr->ranges[range_golem] = NULL;
1575  owner->contr->golem_count = 0;
1576  }
1577 
1578  /*play_sound_player_only(owner1->contr, SOUND_PET_IS_KILLED, 0, 0);*/
1579  /* Maybe we should include the owner that killed this, maybe not */
1581  "Your pet, the %s, is killed by %s.",
1582  op->name, hitter->name);
1583  }
1584  /*
1585  * there can be items both friendly and without any owner, for instance
1586  * in various maps, so this isn't an error.
1587  else
1588  LOG(llevError, "BUG: hit_player(): Encountered golem without owner.\n");
1589  */
1590 
1591  object_remove(op);
1593  return maxdam;
1594  }
1595 
1596  /* Now lets start dealing with experience we get for killing something */
1597 
1598  owner = object_get_owner(hitter);
1599  if (owner == NULL)
1600  owner = hitter;
1601 
1602  /* is the victim (op) standing on battleground? */
1603  if (op_on_battleground(op, NULL, NULL, NULL))
1604  battleg = 1;
1605 
1606  /* is this player killing? -- Don't count it if you suicide, though. */
1607  if (op->type == PLAYER && owner->type == PLAYER && owner != op)
1608  pk = 1;
1609 
1610  /* Player killed something */
1611  if (owner->type == PLAYER) {
1612 
1613  /* Log players killing other players - makes it easier to detect
1614  * and filter out malicious player killers - that is why the
1615  * ip address is included.
1616  */
1617  if (op->type == PLAYER && !battleg) {
1618  time_t t = time(NULL);
1619  struct tm *tmv;
1620  char buf[256];
1621  char name[MAX_BUF];
1622 
1623  tmv = localtime(&t);
1624  strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", tmv);
1626 
1627  LOG(llevInfo, "%s PLAYER_KILL_PLAYER: %s (%s) killed %s\n", buf, owner->name, owner->contr->socket.host, name);
1628  }
1629 
1630  /* try to filter some things out - basically, if you are
1631  * killing a level 1 creature and your level 20, you
1632  * probably don't want to see that.
1633  */
1634  if (owner->level < op->level*2 || op->stats.exp > 1000) {
1635  if (owner != hitter) {
1636  char killed[MAX_BUF], with[MAX_BUF];
1637 
1638  query_name(op, killed, MAX_BUF);
1639  query_name(hitter, with, MAX_BUF);
1641  "You killed %s with %s.",
1642  killed, with);
1643  } else {
1644  char killed[MAX_BUF];
1645 
1646  query_name(op, killed, MAX_BUF);
1648  "You killed %s.",
1649  killed);
1650  }
1651  }
1652 
1653  /* If a player kills another player, not on
1654  * battleground, the "killer" looses 1 luck. Since this is
1655  * not reversible, it's actually quite a pain IMHO. -AV
1656  * Fix bug in that we were changing the luck of the hitter, not
1657  * player that the object belonged to - so if you killed another player
1658  * with spells, pets, whatever, there was no penalty.
1659  * Changed to make luck penalty configurable in settings.
1660  *
1661  * Simplified comparison since pk is no longer set to 1 if self-kill
1662  * -- SilverNexus 2017-06-17+
1663  */
1664  if (pk == 1 && !battleg)
1666 
1667  /* This code below deals with finding the appropriate skill
1668  * to credit exp to. This is a bit problematic - we should
1669  * probably never really have to look at current_weapon->skill
1670  */
1671  skill = NULL;
1672  if (hitter->skill && hitter->type != PLAYER)
1673  skill = hitter->skill;
1674  else if (owner->chosen_skill) {
1675  skill = owner->chosen_skill->skill;
1676  skop = owner->chosen_skill;
1677  } else if (QUERY_FLAG(owner, FLAG_READY_WEAPON))
1678  skill = owner->current_weapon->skill;
1679  else
1680  LOG(llevError, "kill_object - unable to find skill that killed monster\n");
1681 
1682  /* We have the skill we want to credit to - now find the object this goes
1683  * to. Make sure skop is an actual skill, and not a skill tool!
1684  */
1685  if ((!skop || skop->type != SKILL) && skill) {
1686  skop = find_applied_skill_by_name(owner, skill);
1687  }
1688  } /* Was it a player that hit somethign */
1689  else {
1690  skill = NULL;
1691  }
1692 
1693  /* Pet (or spell) killed something. */
1694  if (owner != hitter) {
1695  char name_op[MAX_BUF], name_hitter[MAX_BUF];
1696  const char *owner_prefix;
1697  const char *op_prefix;
1698 
1699  owner_prefix = !battleg && pk && owner->contr != NULL && !owner->contr->peaceful ? "hostile " : "";
1700  op_prefix = !battleg && pk && op->contr != NULL && !op->contr->peaceful ? "hostile " : "";
1701 
1702  query_name(op, name_op, MAX_BUF);
1703  query_name(hitter, name_hitter, MAX_BUF);
1704  snprintf(kill_message, sizeof(kill_message), "%s%s killed %s%s with %s%s.", owner_prefix, owner->name, op_prefix, name_op, name_hitter, battleg ? " (duel)" : (pk ? " (pk)" : ""));
1705  } else {
1706  const char *hitter_prefix;
1707  const char *op_prefix;
1708 
1709  hitter_prefix = !battleg && pk && hitter->contr != NULL && !hitter->contr->peaceful ? "hostile " : "";
1710  op_prefix = !battleg && pk && op->contr != NULL && !op->contr->peaceful ? "hostile " : "";
1711 
1712  snprintf(kill_message, sizeof(kill_message), "%s%s killed %s%s%s%s.", hitter_prefix, hitter->name, op_prefix, op->name,
1713  (QUERY_FLAG(hitter, FLAG_MONSTER)) || hitter->type == PLAYER ?
1714  " in hand to hand combat" : "", battleg ? " (duel)" : (pk ? " (pk)" : ""));
1715  }
1716  /* These may have been set in the player code section above */
1717  if (!skop)
1718  skop = hitter->chosen_skill;
1719  if (!skill && skop)
1720  skill = skop->skill;
1721 
1723  kill_message);
1724  play_sound_map(SOUND_TYPE_HIT, op, 0, "kill");
1725 
1726 
1727  /* If you didn't kill yourself, and your not the wizard */
1728  if (owner != op && !QUERY_FLAG(op, FLAG_WAS_WIZ)) {
1729  int64_t exp;
1730 
1731  exp = calc_skill_exp(owner, op, skop);
1732 
1733  /* Really don't give much experience for killing other players */
1734  if (op->type == PLAYER) {
1735  if (battleg) {
1737  "Your foe has fallen!\nVICTORY!!!");
1738  } else {
1739  exp = settings.pk_max_experience_percent*exp/100;
1740  if (settings.pk_max_experience >= 0)
1741  exp = MIN(settings.pk_max_experience, exp);
1742  /* Never exceed what victim can lose considering permanent exp. */
1743  exp = check_exp_loss(op, exp);
1744  }
1745  }
1746 
1747  /* Don't know why this is set this way - doesn't make
1748  * sense to just divide everything by two for no reason.
1749  */
1750 
1751  if (!settings.simple_exp)
1752  exp = exp/2;
1753 
1754  /* if op is standing on "battleground" (arena), no way to gain
1755  * exp by killing him
1756  */
1757  if (battleg)
1758  exp = 0;
1759 
1760 #ifdef PARTY_KILL_LOG
1761  if (owner->type == PLAYER && owner->contr->party != NULL) {
1762  char name[MAX_BUF];
1763  char op_name[MAX_BUF];
1764 
1765  query_name(owner, name, MAX_BUF);
1766  query_name(op, op_name, sizeof(op_name));
1767  party_add_kill(owner->contr->party, name, op_name, exp);
1768  }
1769 #endif
1770  share_exp(owner, exp, skill, SK_EXP_TOTAL);
1771  } /* end if person didn't kill himself */
1772 
1773  if (op->type != PLAYER) {
1774  object_remove(op);
1776  /* Player has been killed! */
1777  } else {
1778  if (owner->type == PLAYER) {
1779  snprintf(op->contr->killer, BIG_NAME, "%s the %s", owner->name, owner->contr->title);
1780  } else {
1781  strncpy(op->contr->killer, hitter->name, BIG_NAME);
1782  op->contr->killer[BIG_NAME-1] = '\0';
1783  }
1784  /* Need to run kill_player (just in case, make sure is not wiz) */
1785  if (!QUERY_FLAG(op, FLAG_WIZ))
1786  kill_player(op, owner->type == PLAYER ? owner : hitter);
1787  }
1788  /* This was return -1 - that doesn't seem correct - if we return -1, process
1789  * continues in the calling function.
1790  */
1791  return maxdam;
1792 }
1793 
1804 int friendly_fire(object *op, object *hitter) {
1805  object *owner;
1806  int friendlyfire;
1807 
1808  hitter = HEAD(hitter);
1809  friendlyfire = 0;
1810 
1811  if (op->type == PLAYER) {
1812  if (hitter->type == PLAYER && hitter->contr->peaceful == 1)
1813  return 1;
1814 
1815  owner = object_get_owner(hitter);
1816  if (owner != NULL) {
1817  if (owner->type == PLAYER && owner->contr->peaceful == 1)
1818  friendlyfire = 2;
1819  }
1820 
1821  if (hitter->type == SPELL
1822  || hitter->type == POISONING
1823  || hitter->type == DISEASE
1824  || hitter->type == RUNE)
1825  friendlyfire = 0;
1826  }
1827  return friendlyfire;
1828 }
1829 
1853 int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit) {
1854  int maxdam = 0, ndam = 0, magic = (type&AT_MAGIC);
1855  int maxattacktype, attacknum;
1856  int body_attack = op->head != NULL; /* Did we hit op's head? */
1857  int simple_attack;
1858  tag_t op_tag, hitter_tag;
1859  int rtn_kill = 0;
1860  int friendlyfire;
1861  object *owner;
1862 
1863  if (events_execute_object_event(op, EVENT_ATTACKED, hitter, hitter->current_weapon ? hitter->current_weapon : hitter, NULL, SCRIPT_FIX_ALL) != 0)
1864  return 0;
1866  if (events_execute_object_event(inv, EVENT_ATTACKED, hitter, hitter->current_weapon ? hitter->current_weapon : hitter, NULL, SCRIPT_FIX_ALL) != 0)
1867  return 0;
1868  FOR_INV_FINISH();
1869 
1870  if (get_attack_mode(&op, &hitter, &simple_attack))
1871  return 0;
1872 
1873  /* very simple: if our target has no_damage 1 set or is wiz, we can't hurt him */
1875  return 0;
1876 
1877  op_tag = op->count;
1878  hitter_tag = hitter->count;
1879 
1880  if (body_attack) {
1881  /* slow and paralyze must hit the head. But we don't want to just
1882  * return - we still need to process other attacks the spell still
1883  * might have. So just remove the paralyze and slow attacktypes,
1884  * and keep on processing if we have other attacktypes.
1885  * return if only magic or nothing is left - under normal code
1886  * we don't attack with pure magic if there is another attacktype.
1887  * Only do processing if the initial attacktype includes one of those
1888  * attack so we don't cancel out things like magic bullet.
1889  */
1890  if (type&(AT_PARALYZE|AT_SLOW)) {
1891  type &= ~(AT_PARALYZE|AT_SLOW);
1892  if (!type || type == AT_MAGIC)
1893  return 0;
1894  }
1895  }
1896 
1897  if (!simple_attack && op->type == DOOR) {
1898  object *tmp;
1899 
1901  if (tmp != NULL) {
1903  if (object_was_destroyed(hitter, hitter_tag)
1904  || object_was_destroyed(op, op_tag)
1905  || abort_attack(op, hitter, simple_attack))
1906  return 0;
1907  }
1908  }
1909 
1910  if (!QUERY_FLAG(op, FLAG_ALIVE) || op->stats.hp < 0) {
1911  /* FIXME: If a player is killed by a rune in a door, the
1912  * object_was_destroyed() check above doesn't return, and might get here.
1913  */
1914  LOG(llevDebug, "victim (arch %s, name %s) already dead in hit_player()\n", op->arch->name, op->name);
1915  return 0;
1916  }
1917 
1918 #ifdef ATTACK_DEBUG
1919  LOG(llevDebug, "hit player: attacktype %d, dam %d\n", type, dam);
1920 #endif
1921 
1922  if (magic) {
1923  /* basically: dam = dam*(100-op->resist[attacknum])/100;
1924  * in case 0>dam>1, we try to "simulate" a float value-effect */
1925  dam = dam*(100-op->resist[ATNR_MAGIC]);
1926  if (dam >= 100)
1927  dam /= 100;
1928  else
1929  dam = (dam > (rndm(0, 99))) ? 1 : 0;
1930  }
1931 
1932  /* AT_CHAOS here is a weapon or monster. Spells are handled by hit_map
1933  * We don't use shuffle_attack(), because that changes the it in the
1934  * creature structure, and thus is permanent until fix_object() is
1935  * called again. Chaos should change on each attack.
1936  */
1937  if (type&AT_CHAOS) {
1938  type = ATTACKS[RANDOM()%(sizeof(ATTACKS)/sizeof(*ATTACKS))].attacktype|AT_MAGIC;
1939  }
1940 
1941  /* Holyword is really an attacktype modifier (like magic is). If
1942  * holyword is part of an attacktype, then make sure the creature is
1943  * a proper match, otherwise no damage.
1944  */
1945  if (type&AT_HOLYWORD) {
1946  const object *god;
1947 
1948  if ((!hitter->slaying || (!(op->race && strstr(hitter->slaying, op->race))
1949  && !(op->name && strstr(hitter->slaying, op->name))))
1950  && (!QUERY_FLAG(op, FLAG_UNDEAD) || (hitter->title != NULL
1951  && (god = find_god(determine_god(hitter))) != NULL
1952  && god->race != NULL
1953  && strstr(god->race, undead_name) != NULL)))
1954  return 0;
1955  }
1956 
1957  maxattacktype = type; /* initialize this to something */
1958  for (attacknum = 0; attacknum < NROFATTACKS; attacknum++) {
1959  int attacktype;
1960 
1961  attacktype = 1<<attacknum;
1962 
1963  /* Magic isn't really a true attack type - it gets combined with other
1964  * attack types. As such, skip it over. However, if magic is
1965  * the only attacktype in the group, then still attack with it
1966  */
1967  if (attacktype == AT_MAGIC && (type&~AT_MAGIC))
1968  continue;
1969 
1970  /* Go through and hit the player with each attacktype, one by one.
1971  * hit_with_one_attacktype only figures out the damage, doesn't inflict
1972  * it. It will do the appropriate action for attacktypes with
1973  * effects (slow, paralization, etc.
1974  */
1975  if (type&attacktype) {
1976  ndam = hit_with_one_attacktype(op, hitter, dam, attacknum);
1977  /* the >= causes us to prefer messages from special attacks, if
1978  * the damage is equal.
1979  */
1980  if (ndam >= maxdam) {
1981  maxdam = ndam;
1982  maxattacktype = 1<<attacknum;
1983  }
1984  /* Special case: death attack always deals all damage, as it should kill the monster
1985  * right away. */
1986  if (attacktype == AT_DEATH && ndam > 0)
1987  full_hit = 1;
1988  }
1989  }
1990 
1991 
1992  /* if this is friendly fire then do a set % of damage only
1993  * Note - put a check in to make sure this attack is actually
1994  * doing damage - otherwise, the +1 in the coe below will make
1995  * an attack do damage before when it otherwise didn't
1996  * Only reduce damage if not on battlground - if in arena, do
1997  * full damage. Note that it is intentional that the check for
1998  * battleground is inside the friendlyfire if statement - op_on_battleground()
1999  * is a fairly costly function to call, and we don't want to call it for
2000  * every attack - by doing it only for friendlyfire, it shouldn't get called
2001  * that often
2002  */
2003  friendlyfire = friendly_fire(op, hitter);
2004  if (friendlyfire && maxdam) {
2005  if (!op_on_battleground(op, NULL, NULL, NULL)) {
2006  maxdam = ((dam*settings.set_friendly_fire)/100)+1;
2007 #ifdef ATTACK_DEBUG
2008  LOG(llevDebug, "Friendly fire (type:%d setting: %d%) did %d damage dropped to %d\n", friendlyfire, settings.set_friendly_fire, dam, maxdam);
2009 #endif
2010  }
2011  }
2012 
2013  if (!full_hit) {
2014  archetype *at;
2015  unsigned int area = 0;
2016  for (at = op->arch; at != NULL; at = at->more)
2017  area++;
2018 
2019  /* basically: maxdam /= area; we try to "simulate" a float
2020  value-effect */
2021  unsigned int remainder = 100*(maxdam%area)/area;
2022  maxdam /= area;
2023  if (RANDOM()%100 < remainder)
2024  maxdam++;
2025  }
2026 
2027 #ifdef ATTACK_DEBUG
2028  LOG(llevDebug, "Attacktype %d did %d damage\n", type, maxdam);
2029 #endif
2030 
2031  owner = object_get_owner(hitter);
2032  if (owner != NULL) {
2033  if (op->enemy != hitter)
2034  object_set_enemy(op, owner);
2035  } else if (QUERY_FLAG(hitter, FLAG_ALIVE))
2036  if (op->enemy == NULL || rndm(1, 20) == 0)
2038 
2039  if (QUERY_FLAG(op, FLAG_UNAGGRESSIVE) && op->type != PLAYER) {
2040  /* The unaggressives look after themselves 8) */
2043  }
2044 
2045  if (magic && did_make_save(op, op->level, 0))
2046  maxdam = maxdam/2;
2047 
2048  attack_message(maxdam, maxattacktype, op, hitter);
2049 
2050  op->stats.hp -= maxdam;
2051 
2052  /* Eneq(@csd.uu.se): Check to see if monster runs away. */
2053  if (op->stats.hp >= 0
2054  && (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER)
2055  && op->stats.hp < (int16_t)((int32_t)op->run_away * op->stats.maxhp / 100)) {
2056  if (QUERY_FLAG(op, FLAG_MONSTER))
2058  else
2060  }
2061 
2062  if (QUERY_FLAG(op, FLAG_TEAR_DOWN)) {
2063  if (maxdam)
2064  tear_down_wall(op);
2065  return maxdam; /* nothing more to do for wall */
2066  }
2067 
2068  /* See if the creature has been killed */
2069  rtn_kill = kill_object(op, maxdam, hitter);
2070  if (rtn_kill != -1)
2071  return rtn_kill;
2072 
2073  /* Used to be ghosthit removal - we now use the ONE_HIT flag. Note
2074  * that before if the player was immune to ghosthit, the monster
2075  * remained - that is no longer the case.
2076  */
2077  if (QUERY_FLAG(hitter, FLAG_ONE_HIT)) {
2082  /* Lets handle creatures that are splitting now */
2084  change_object(op);
2085  } else if (type&AT_DRAIN && hitter->type == GRIMREAPER && hitter->value++ > 10) {
2088  }
2089  return maxdam;
2090 }
2091 
2102 static void poison_living(object *op, object *hitter, int dam) {
2103  archetype *at = find_archetype("poisoning");
2104  if (at == NULL) {
2105  return;
2106  }
2107  object *tmp = arch_present_in_ob(at, op);
2108  const char *skill;
2109 
2110  if (tmp == NULL) {
2111  tmp = arch_to_object(at);
2112  if (tmp == NULL)
2113  LOG(llevError, "Failed to clone arch poisoning.\n");
2114  else {
2116  /* peterm: give poisoning some teeth. It should
2117  * be able to kill things better than it does:
2118  * damage should be dependent something--I choose to
2119  * do this: if it's a monster, the damage from the
2120  * poisoning goes as the level of the monster/2.
2121  * If anything else, goes as damage.
2122  */
2123 
2125  tmp->stats.dam += hitter->level/2;
2126  else
2127  tmp->stats.dam = dam;
2128 
2129  object_copy_owner(tmp, hitter); /* so we get credit for poisoning kills */
2130  skill = hitter->skill;
2131  if (!skill && hitter->chosen_skill)
2132  skill = hitter->chosen_skill->name;
2133 
2134  if (skill && skill != tmp->skill) {
2135  if (tmp->skill)
2136  free_string(tmp->skill);
2137  tmp->skill = add_refcount(skill);
2138  }
2139 
2140  tmp->stats.food += dam; /* more damage, longer poisoning */
2141 
2142  if (op->type == PLAYER) {
2143  /* player looses stats, maximum is -10 of each */
2144  tmp->stats.Con = MAX(-(dam/4+1), -10);
2145  tmp->stats.Str = MAX(-(dam/3+2), -10);
2146  tmp->stats.Dex = MAX(-(dam/6+1), -10);
2147  tmp->stats.Int = MAX(-dam/7, -10);
2149  fix_object(op);
2152  "You suddenly feel very ill.");
2153  }
2154  if (hitter->type == PLAYER)
2157  "You poison %s.",
2158  op->name);
2159  else {
2160  object *owner;
2161 
2162  owner = object_get_owner(hitter);
2163  if (owner != NULL && owner->type == PLAYER)
2164  draw_ext_info_format(NDI_UNIQUE, 0, owner,
2166  "Your %s poisons %s.",
2167  hitter->name, op->name);
2168  }
2169  tmp->speed_left = 0;
2170  }
2171  } else
2172  tmp->stats.food++;
2173 }
2174 
2175 int slow_living_by(object *op, const int speed_penalty) {
2176  archetype *at = find_archetype("slowness");
2177  if (at == NULL) {
2178  return 0;
2179  }
2180  object* tmp = arch_present_in_ob(at, op);
2181  int ret;
2182  if (tmp == NULL) {
2183  tmp = arch_to_object(at);
2184  tmp->stats.exp = -speed_penalty;
2186  ret = 1;
2187  }
2188  // If we are hitting for more intense slowness, override the old one.
2189  else if (tmp->stats.exp > -speed_penalty) {
2190  tmp->stats.exp = -speed_penalty;
2191  tmp->stats.food -= 3; // But also reduce the duration to compensate.
2192  ret = 2;
2193  } else {
2194  tmp->stats.food++;
2195  ret = 3;
2196  }
2198  tmp->speed_left = 0;
2199  fix_object(op);
2200  return ret;
2201 }
2202 
2214 static void slow_living(object *op, object *hitter, int dam) {
2215  // Used to calculate the speed penalty of the slow attempt
2216  int speed_penalty;
2217  (void)dam;
2218 
2224  // Higher level slow effects make you slower.
2225  speed_penalty = hitter->level - op->level + random_roll(1, 5, hitter, PREFER_LOW);
2226  // Resistance to slow will also affect how much you are slowed by.
2227  speed_penalty = speed_penalty * (100-op->resist[ATNR_SLOW]) / 100;
2228  // Make sure we actually have a penalty amount. We can assume that op is not immune in this method.
2229  if (speed_penalty < 1)
2230  speed_penalty = 1;
2231  else if (speed_penalty > 30) // Data barrier for stats.exp is 127, but that is huge slowness. Pick something less than that.
2232  speed_penalty = 30;
2233  switch (slow_living_by(op, speed_penalty)) {
2234  case 1:
2237  "The world suddenly moves very fast!");
2238  break;
2239  case 2:
2242  "The world moves even faster!");
2243  break;
2244 
2245  }
2246 }
2247 
2259 void confuse_living(object *op, object *hitter, int dam) {
2260  object *tmp;
2261  int maxduration;
2262  (void)hitter;
2263  (void)dam;
2264 
2265  tmp = object_present_in_ob_by_name(FORCE, "confusion", op);
2266  if (!tmp) {
2269  }
2272 
2273  /* Duration added per hit and max. duration of confusion both depend
2274  * on the player's resistance
2275  */
2276  tmp->speed = 0.05;
2277  tmp->subtype = FORCE_CONFUSION;
2278  tmp->duration = 8+MAX(1, 5*(100-op->resist[ATNR_CONFUSION])/100);
2279  if (tmp->name)
2280  free_string(tmp->name);
2281  tmp->name = add_string("confusion");
2282  maxduration = MAX(2, 30*(100-op->resist[ATNR_CONFUSION])/100);
2283  if (tmp->duration > maxduration)
2284  tmp->duration = maxduration;
2285 
2286  if (op->type == PLAYER && !QUERY_FLAG(op, FLAG_CONFUSED))
2288  "You suddenly feel very confused!");
2290 }
2291 
2302 void blind_living(object *op, object *hitter, int dam) {
2303  object *tmp, *owner;
2304  char victim[MAX_BUF];
2305 
2306  /* Save some work if we know it isn't going to affect the player */
2307  if (op->resist[ATNR_BLIND] == 100)
2308  return;
2309 
2311  if (!tmp) {
2312  tmp = create_archetype("blindness");
2315  /* use floats so we don't lose too much precision due to rounding errors.
2316  * speed is a float anyways.
2317  */
2318  tmp->speed = tmp->speed*(100.0-(float)op->resist[ATNR_BLIND])/100;
2319 
2321  change_abil(op, tmp); /* Mostly to display any messages */
2322  fix_object(op); /* This takes care of some other stuff */
2323 
2324  owner = object_get_owner(hitter);
2325  if (owner == NULL)
2326  owner = hitter;
2327 
2330  "Your attack blinds %s!",
2331  victim);
2332  }
2333  tmp->stats.food += dam;
2334  if (tmp->stats.food > 10)
2335  tmp->stats.food = 10;
2336 }
2337 
2346 void paralyze_living(object *op, int dam) {
2347  float effect, max;
2348 
2349  /* Do this as a float - otherwise, rounding might very well reduce this to 0 */
2350  effect = (float)dam*3.0*(100.0-op->resist[ATNR_PARALYZE])/100;
2351 
2352  if (effect == 0)
2353  return;
2354 
2355  op->speed_left -= FABS(op->speed)*effect;
2356 
2357  /* max number of ticks to be affected for. */
2358  max = (100-op->resist[ATNR_PARALYZE])/2;
2359  if (op->speed_left < -(FABS(op->speed)*max))
2360  op->speed_left = (float)-(FABS(op->speed)*max);
2361  // Set a paralyze flag and print a message to a player if the flag isn't set;
2362  // this tells the player that he/she has been hit by a paralysis attack.
2363  if (!QUERY_FLAG(op, FLAG_PARALYZED)) {
2365  if (op->type == PLAYER)
2367  "You limbs stop moving!");
2368  }
2369  /* If the flag is already set, then the paralysis is merely extended.
2370  * At this point, we do nothing.
2371  * It may be worthwhile to give players another message on paralysis extensions.
2372  *
2373  * Daniel Hawkins 2017-08-22
2374  */
2375 }
2376 
2397 static void deathstrike_living(object *op, object *hitter, int *dam) {
2398  int atk_lev, def_lev, kill_lev, roll;
2399 
2400  if (hitter->slaying) {
2401  if (!((QUERY_FLAG(op, FLAG_UNDEAD) && strstr(hitter->slaying, undead_name))
2402  || (op->race && strstr(op->race, hitter->slaying))))
2403  {
2404  *dam = 0; // Don't do damage if we aren't deathstriking them.
2405  return;
2406  }
2407  } else
2408  if (QUERY_FLAG(op, FLAG_UNDEAD))
2409  {
2410  *dam = 0; // Don't do damage if we aren't deathstriking them.
2411  return;
2412  }
2413 
2414  def_lev = op->level;
2415  if (def_lev < 1) {
2416  LOG(llevError, "deathstrike_living: arch %s (%s in %s at %d, %d) has level < 1\n", op->arch->name, op->name, op->map->name, op->x, op->y);
2417  def_lev = 1;
2418  }
2419  /*
2420  * Redo this calculation -- you could essentially only kill creatures less than half your level,
2421  * making death extremely weak at high levels.
2422  * Refactoring to use a d20 roll with a fairly high DC (still dependent on level)
2423  * Also, toss in a resistance-based hit modifier.
2424  * Higher resistance requires higher levels in order to kill with a death attack.
2425  *
2426  * Daniel Hawkins 2018-05-21
2427  */
2428  atk_lev = (hitter->chosen_skill ? hitter->chosen_skill->level : hitter->level);
2429  /* LOG(llevDebug, "Deathstrike - attack level %d, defender level %d\n", atk_lev, def_lev); */
2430 
2431  roll = random_roll(1, 20, hitter, PREFER_HIGH);
2432  kill_lev = roll - 18 + atk_lev; // Use 19+ as a kill for same level and no resistance;
2433  kill_lev = kill_lev * (100 - op->resist[ATNR_DEATH]) / 100; // Do not compress to *= for roundoff reasons.
2434 
2435  // If we hit, then kill them. Otherwise, damage is 0.
2436  if (kill_lev > def_lev) {
2437  *dam = op->stats.hp+10; /* take all hp. they can still save for 1/2 */
2438  /* I think this doesn't really do much. Because of
2439  * integer rounding, this only makes any difference if the
2440  * attack level is double the defender level.
2441  */
2442  *dam *= kill_lev/def_lev;
2443  }
2444  else {
2445  *dam = 0; /* no harm done */
2446  }
2447 }
2448 
2461 static void thrown_item_effect(object *hitter, object *victim) {
2462  if (!QUERY_FLAG(hitter, FLAG_ALIVE)) {
2463  /* May not need a switch for just 2 types, but this makes it
2464  * easier for expansion.
2465  */
2466  switch (hitter->type) {
2467  case POTION:
2468  /* should player get a save throw instead of checking magic protection? */
2471  && (victim->resist[ATNR_MAGIC] < 60))
2472  (void)ob_apply(hitter, victim, 0);
2473  break;
2474 
2475  case POISON: /* poison drinks */
2476  /* As with potions, should monster get a save? */
2479  && (victim->resist[ATNR_POISON] < 60))
2480  (void)ob_apply(victim, hitter, 0);
2481  break;
2482 
2483  /* Removed case statements that did nothing.
2484  * food may be poisonous, but monster must be willing to eat it,
2485  * so we don't handle it here.
2486  * Containers should perhaps break open, but that code was disabled.
2487  */
2488  }
2489  }
2490 }
2491 
2501 static int adj_attackroll(object *hitter, object *target) {
2502  object *attacker = hitter;
2503  int adjust = 0;
2504 
2505  /* safety */
2506  if (!target || !hitter || !hitter->map || !target->map || !on_same_map(hitter, target)) {
2507  LOG(llevError, "BUG: adj_attackroll(): hitter and target not on same map\n");
2508  return 0;
2509  }
2510 
2511  /* aimed missiles use the owning object's sight */
2512  if (is_aimed_missile(hitter)) {
2513  attacker = object_get_owner(hitter);
2514  if (attacker == NULL)
2515  attacker = hitter;
2516  /* A player who saves but hasn't quit still could have objects
2517  * owned by him - need to handle that case to avoid crashes.
2518  */
2519  if (QUERY_FLAG(attacker, FLAG_REMOVED))
2520  attacker = hitter;
2521  } else if (!QUERY_FLAG(hitter, FLAG_ALIVE))
2522  return 0;
2523 
2524  /* determine the condtions under which we make an attack.
2525  * Add more cases, as the need occurs. */
2526 
2527  if (!monster_can_see_enemy(attacker, target)) {
2528  /* target is unseen */
2529  if (target->invisible || QUERY_FLAG(attacker, FLAG_BLIND))
2530  adjust -= 10;
2531  /* dark map penalty for the hitter, though xray can help for a player */
2532  else if (target->map && target->map->darkness > 0 && !monster_stand_in_light(target) && (hitter->type != PLAYER || !player_can_view(hitter, target)))
2533  adjust -= target->map->darkness;
2534  }
2535 
2536  if (QUERY_FLAG(attacker, FLAG_SCARED))
2537  adjust -= 3;
2538 
2539  if (QUERY_FLAG(target, FLAG_UNAGGRESSIVE))
2540  adjust += 1;
2541 
2542  if (QUERY_FLAG(target, FLAG_SCARED))
2543  adjust += 1;
2544 
2545  if (QUERY_FLAG(attacker, FLAG_CONFUSED))
2546  adjust -= 3;
2547 
2548  /* if we attack at a different 'altitude' its harder
2549  * Note - only make this adjustment if the target actually
2550  * has a move type. Doors don't (they don't move), and
2551  * this would evaluate as true. If anything, something without
2552  * a move type should be easier to hit.
2553  */
2554  if (target->move_type && (attacker->move_type&target->move_type) == 0)
2555  adjust -= 2;
2556 
2557  return adjust;
2558 }
2559 
2568 static int is_aimed_missile(object *op) {
2569  /* I broke what used to be one big if into a few nested
2570  * ones so that figuring out the logic is at least possible.
2571  */
2572  if (op && (op->move_type&MOVE_FLYING)) {
2573  if (op->type == ARROW || op->type == THROWN_OBJ)
2574  return 1;
2575  else if (op->type == SPELL_EFFECT
2576  && (op->subtype == SP_BULLET || op->subtype == SP_EXPLOSION))
2577  return 1;
2578  }
2579  return 0;
2580 }
ATM_BLUD
#define ATM_BLUD
Definition: attack.h:39
Settings::casting_time
uint8_t casting_time
Definition: global.h:266
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
give.next
def next
Definition: give.py:44
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:520
PLAYER
@ PLAYER
Definition: object.h:107
confuse_living
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.c:2259
ATNR_PARALYZE
#define ATNR_PARALYZE
Definition: attack.h:61
global.h
add_refcount
sstring add_refcount(sstring str)
Definition: shstr.c:210
ATNR_CANCELLATION
#define ATNR_CANCELLATION
Definition: attack.h:64
Settings::simple_exp
uint8_t simple_exp
Definition: global.h:259
liv::dam
int16_t dam
Definition: living.h:46
INS_NO_WALK_ON
#define INS_NO_WALK_ON
Definition: object.h:569
add_string
sstring add_string(const char *str)
Definition: shstr.c:124
object_remove
void object_remove(object *op)
Definition: object.c:1806
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:728
pl::golem_count
uint32_t golem_count
Definition: player.h:106
object_set_enemy
void object_set_enemy(object *op, object *enemy)
Definition: object.c:908
AT_POISON
#define AT_POISON
Definition: attack.h:86
ATNR_INTERNAL
#define ATNR_INTERNAL
Definition: attack.h:72
attackmess::level
int level
Definition: attack.h:119
AT_MAGIC
#define AT_MAGIC
Definition: attack.h:77
object_find_by_type2
object * object_find_by_type2(const object *who, int type1, int type2)
Definition: object.c:4045
FLAG_CONFUSED
#define FLAG_CONFUSED
Definition: define.h:311
FORCE_CONFUSION
#define FORCE_CONFUSION
Definition: spells.h:144
llevError
@ llevError
Definition: logger.h:11
FABS
#define FABS(x)
Definition: define.h:22
EVENT_CONNECTOR
@ EVENT_CONNECTOR
Definition: object.h:227
ATM_KARATE
#define ATM_KARATE
Definition: attack.h:29
SYMPTOM
@ SYMPTOM
Definition: object.h:245
WAND
@ WAND
Definition: object.h:220
MSG_TYPE_ADMIN_PLAYER
#define MSG_TYPE_ADMIN_PLAYER
Definition: newclient.h:496
object_copy_owner
void object_copy_owner(object *op, object *clone)
Definition: object.c:886
FLAG_UNDEAD
#define FLAG_UNDEAD
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
FLAG_GENERATOR
#define FLAG_GENERATOR
Definition: define.h:248
monster_stand_in_light
int monster_stand_in_light(object *op)
Definition: monster.c:2662
GIRDLE
@ GIRDLE
Definition: object.h:223
ATNR_ACID
#define ATNR_ACID
Definition: attack.h:55
object_update
void object_update(object *op, int action)
Definition: object.c:1407
diamondslots.x
x
Definition: diamondslots.py:15
obj::count
tag_t count
Definition: object.h:300
obj::map
struct mapdef * map
Definition: object.h:298
AT_ELECTRICITY
#define AT_ELECTRICITY
Definition: attack.h:79
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
archininventory.arch
arch
DIALOGCHECK MINARGS 1 MAXARGS 1
Definition: archininventory.py:16
pl::peaceful
uint32_t peaceful
Definition: player.h:133
SK_CLAWING
@ SK_CLAWING
Definition: skills.h:50
attack_message
static void attack_message(int dam, int type, object *op, object *hitter)
Definition: attack.c:439
make_visible
void make_visible(object *op)
Definition: player.c:3909
op_on_battleground
int op_on_battleground(object *op, int *x, int *y, archetype **trophy)
Definition: player.c:4202
FALSE
#define FALSE
Definition: compat.h:14
AT_PHYSICAL
#define AT_PHYSICAL
Definition: attack.h:76
MSG_TYPE_ATTACK_DID_HIT
#define MSG_TYPE_ATTACK_DID_HIT
Definition: newclient.h:612
pl::socket
socket_struct socket
Definition: player.h:94
get_turn_bonus
int get_turn_bonus(int stat)
Definition: living.c:2374
pl
Definition: player.h:92
adj_attackroll
static int adj_attackroll(object *hitter, object *target)
Definition: attack.c:2501
share_exp
void share_exp(object *op, int64_t exp, const char *skill, int flag)
Definition: living.c:2312
TRAP
@ TRAP
Definition: object.h:241
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.c:205
M_IRON
#define M_IRON
Definition: material.h:15
ATM_PIERCE
#define ATM_PIERCE
Definition: attack.h:33
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
obj::invisible
int16_t invisible
Definition: object.h:363
arch_present_in_ob
object * arch_present_in_ob(const archetype *at, const object *op)
Definition: object.c:3172
cancellation
static void cancellation(object *op)
Definition: attack.c:49
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:405
SP_CONE
#define SP_CONE
Definition: spells.h:81
commongive.inv
inv
Definition: commongive.py:28
ATM_FIRE
#define ATM_FIRE
Definition: attack.h:27
blank_face
const Face * blank_face
Definition: image.c:35
ATNR_SLOW
#define ATNR_SLOW
Definition: attack.h:60
SET_ANIMATION
#define SET_ANIMATION(ob, newanim)
Definition: global.h:160
AMULET
@ AMULET
Definition: object.h:139
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Definition: object.h:143
play_sound_map
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.c:108
RUNE
@ RUNE
Definition: object.h:240
ATM_BASIC
#define ATM_BASIC
Definition: attack.h:28
FLAG_SCARED
#define FLAG_SCARED
Definition: define.h:271
Settings::pk_luck_penalty
int16_t pk_luck_penalty
Definition: global.h:253
SK_KARATE
@ SK_KARATE
Definition: skills.h:38
Ice.tmp
int tmp
Definition: Ice.py:207
FLAG_ONE_HIT
#define FLAG_ONE_HIT
Definition: define.h:343
NDI_RED
#define NDI_RED
Definition: newclient.h:245
remove_friendly_object
void remove_friendly_object(object *op)
Definition: friend.c:56
shuffle_attack
void shuffle_attack(object *op)
Definition: spell_util.c:1032
TRANSPORT
@ TRANSPORT
Definition: object.h:108
ATNR_PHYSICAL
#define ATNR_PHYSICAL
Definition: attack.h:49
materialt
EXTERN materialtype_t * materialt
Definition: material.h:42
get_attack_mode
static int get_attack_mode(object **target, object **hitter, int *simple_attack)
Definition: attack.c:683
WEAP_WHIP
#define WEAP_WHIP
Definition: define.h:83
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
ATM_ARROW
#define ATM_ARROW
Definition: attack.h:23
FLAG_BLOCKSVIEW
#define FLAG_BLOCKSVIEW
Definition: define.h:269
skills.h
ATNR_TURN_UNDEAD
#define ATNR_TURN_UNDEAD
Definition: attack.h:62
range_golem
@ range_golem
Definition: player.h:21
MAXATTACKMESS
#define MAXATTACKMESS
Definition: attack.h:19
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
events_execute_object_event
int events_execute_object_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: events.cpp:259
FLAG_BLIND
#define FLAG_BLIND
Definition: define.h:336
did_make_save_item
static int did_make_save_item(object *op, int type, object *originator)
Definition: attack.c:98
AT_LIFE_STEALING
#define AT_LIFE_STEALING
Definition: attack.h:100
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:415
POISONING
@ POISONING
Definition: object.h:218
MAX
#define MAX(x, y)
Definition: compat.h:24
kill_object
static int kill_object(object *op, int dam, object *hitter)
Definition: attack.c:1533
SOUND_TYPE_HIT
#define SOUND_TYPE_HIT
Definition: newclient.h:337
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.c:288
AT_DEATH
#define AT_DEATH
Definition: attack.h:93
ATM_SLICE
#define ATM_SLICE
Definition: attack.h:35
FLAG_NO_PICK
#define FLAG_NO_PICK
Definition: define.h:239
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.c:294
find_applied_skill_by_name
object * find_applied_skill_by_name(const object *op, const char *name)
Definition: living.c:1921
ATNR_CONFUSION
#define ATNR_CONFUSION
Definition: attack.h:54
ob_apply
method_ret ob_apply(object *op, object *applier, int aflags)
Definition: ob_methods.c:44
deathstrike_living
static void deathstrike_living(object *op, object *hitter, int *dam)
Definition: attack.c:2397
ATNR_HOLYWORD
#define ATNR_HOLYWORD
Definition: attack.h:70
archt
Definition: object.h:468
settings
struct Settings settings
Definition: init.c:39
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
Settings::pk_max_experience
int64_t pk_max_experience
Definition: global.h:309
MSG_TYPE_ATTACK_DID_KILL
#define MSG_TYPE_ATTACK_DID_KILL
Definition: newclient.h:615
attack_ob
int attack_ob(object *op, object *hitter)
Definition: attack.c:912
object_merge
object * object_merge(object *op, object *top)
Definition: object.c:2018
undead_name
const EXTERN char * undead_name
Definition: global.h:155
obj::chosen_skill
struct obj * chosen_skill
Definition: object.h:390
obj::slaying
sstring slaying
Definition: object.h:320
ATM_STAB
#define ATM_STAB
Definition: attack.h:36
free_string
void free_string(sstring str)
Definition: shstr.c:280
ATNR_BLIND
#define ATNR_BLIND
Definition: attack.h:71
m
static event_registration m
Definition: citylife.cpp:425
AT_CHAOS
#define AT_CHAOS
Definition: attack.h:94
name_to_material
materialtype_t * name_to_material(const char *name)
Definition: utils.c:248
hit_with_arrow
object * hit_with_arrow(object *op, object *victim)
Definition: attack.c:954
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
FLAG_TEAR_DOWN
#define FLAG_TEAR_DOWN
Definition: define.h:279
obj::current_weapon
struct obj * current_weapon
Definition: object.h:374
SP_BULLET
#define SP_BULLET
Definition: spells.h:79
disinfect.map
map
Definition: disinfect.py:4
POISON
@ POISON
Definition: object.h:113
USING_SKILL
#define USING_SKILL(op, skill)
Definition: skills.h:85
FLAG_WAS_WIZ
#define FLAG_WAS_WIZ
Definition: define.h:234
WEAP_CLEAVE
#define WEAP_CLEAVE
Definition: define.h:80
Settings::pk_max_experience_percent
int pk_max_experience_percent
Definition: global.h:310
obj::name
sstring name
Definition: object.h:312
free_no_drop
int free_no_drop(object *op)
Definition: time.c:557
determine_god
const char * determine_god(object *op)
Definition: gods.c:106
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Definition: newclient.h:565
ATM_SUFFER
#define ATM_SUFFER
Definition: attack.h:41
MSG_TYPE_APPLY_SUCCESS
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:603
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:584
AT_COLD
#define AT_COLD
Definition: attack.h:80
POTION
@ POTION
Definition: object.h:111
FLAG_RUN_AWAY
#define FLAG_RUN_AWAY
Definition: define.h:280
Settings::set_friendly_fire
uint16_t set_friendly_fire
Definition: global.h:271
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
check_physically_infect
void check_physically_infect(object *victim, object *hitter)
Definition: disease.c:663
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:658
hit_map
int hit_map(object *op, int dir, uint32_t type, int full_hit)
Definition: attack.c:327
_materialtype::next
struct _materialtype * next
Definition: material.h:38
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:752
HEAD
#define HEAD(op)
Definition: object.h:594
ATTACKS
EXTERN Chaos_Attacks ATTACKS[22]
Definition: attack.h:134
MSG_TYPE_ATTACK_PET_DIED
#define MSG_TYPE_ATTACK_PET_DIED
Definition: newclient.h:616
ROD
@ ROD
Definition: object.h:109
CONTAINER
@ CONTAINER
Definition: object.h:231
_materialtype::save
int8_t save[NROFATTACKS]
Definition: material.h:36
WEAP_HIT
#define WEAP_HIT
Definition: define.h:77
FLAG_FREED
#define FLAG_FREED
Definition: define.h:233
socket_struct::host
char * host
Definition: newserver.h:100
attack_mess
EXTERN attackmess_t attack_mess[NROFATTACKMESS][MAXATTACKMESS]
Definition: attack.h:131
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:367
MSG_TYPE_ATTACK
#define MSG_TYPE_ATTACK
Definition: newclient.h:409
material.h
object_present_in_ob
object * object_present_in_ob(uint8_t type, const object *op)
Definition: object.c:3118
MOVE_FLYING
#define MOVE_FLYING
Definition: define.h:395
FLAG_NO_DAMAGE
#define FLAG_NO_DAMAGE
Definition: define.h:356
obj::x
int16_t x
Definition: object.h:328
scare_creature
static void scare_creature(object *target, object *hitter)
Definition: attack.c:1138
INS_NO_MERGE
#define INS_NO_MERGE
Definition: object.h:567
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
fix_object
void fix_object(object *op)
Definition: living.c:1126
ATNR_DRAIN
#define ATNR_DRAIN
Definition: attack.h:56
FLAG_PARALYZED
#define FLAG_PARALYZED
Definition: define.h:371
counterspell
void counterspell(object *op, int dir)
Definition: spell_effect.c:2922
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:315
_materialtype
Definition: material.h:32
mapdef::darkness
uint8_t darkness
Definition: map.h:345
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:675
archt::more
struct archt * more
Definition: object.h:471
fix_stopped_item
void fix_stopped_item(object *op, mapstruct *map, object *originator)
Definition: time.c:487
GOLEM
@ GOLEM
Definition: object.h:145
fix_stopped_arrow
object * fix_stopped_arrow(object *op)
Definition: time.c:504
rndm
int rndm(int min, int max)
Definition: utils.c:162
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
ATNR_COUNTERSPELL
#define ATNR_COUNTERSPELL
Definition: attack.h:68
say.max
dictionary max
Definition: say.py:148
ATM_PUNCH
#define ATM_PUNCH
Definition: attack.h:31
tag_t
uint32_t tag_t
Definition: object.h:12
hit_player
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.c:1853
obj::env
struct obj * env
Definition: object.h:294
ATNR_POISON
#define ATNR_POISON
Definition: attack.h:59
sproto.h
ARROW
@ ARROW
Definition: object.h:117
slow_living_by
int slow_living_by(object *op, const int speed_penalty)
Definition: attack.c:2175
ATNR_DEATH
#define ATNR_DEATH
Definition: attack.h:66
MSG_TYPE_VICTIM_WAS_HIT
#define MSG_TYPE_VICTIM_WAS_HIT
Definition: newclient.h:651
obj::enemy
struct obj * enemy
Definition: object.h:385
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:748
mapdef
Definition: map.h:324
GET_ANIM_ID
#define GET_ANIM_ID(ob)
Definition: global.h:163
RING
@ RING
Definition: object.h:185
BLINDNESS
@ BLINDNESS
Definition: object.h:147
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:242
FLAG_SPLITTING
#define FLAG_SPLITTING
Definition: define.h:266
party_struct
Definition: party.h:10
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.c:833
FLAG_HITBACK
#define FLAG_HITBACK
Definition: define.h:267
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:250
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2215
MAX_BUF
#define MAX_BUF
Definition: define.h:35
object_get_materialtype
static void object_get_materialtype(object *op, materialtype_t **mt)
Definition: attack.c:73
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:281
RANDOM
#define RANDOM()
Definition: define.h:642
ATNR_FIRE
#define ATNR_FIRE
Definition: attack.h:51
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:721
obj::y
int16_t y
Definition: object.h:328
FLAG_KNOWN_MAGICAL
#define FLAG_KNOWN_MAGICAL
Definition: define.h:319
attacks
const char *const attacks[NROFATTACKS]
Definition: living.c:130
is_wraith_pl
int is_wraith_pl(object *op)
Definition: player.c:176
OUT_OF_REAL_MAP
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:218
poison_living
static void poison_living(object *op, object *hitter, int dam)
Definition: attack.c:2102
ATM_WRAITH_FEED
#define ATM_WRAITH_FEED
Definition: attack.h:42
slow_living
static void slow_living(object *op, object *hitter, int dam)
Definition: attack.c:2214
sounds.h
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
monster_can_see_enemy
int monster_can_see_enemy(object *op, object *enemy)
Definition: monster.c:2705
tear_down_wall
static void tear_down_wall(object *op)
Definition: attack.c:1089
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
llevInfo
@ llevInfo
Definition: logger.h:12
obj::type
uint8_t type
Definition: object.h:341
Floor.t
t
Definition: Floor.py:62
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
MSG_TYPE_ATTACK_PET_HIT
#define MSG_TYPE_ATTACK_PET_HIT
Definition: newclient.h:613
AT_BLIND
#define AT_BLIND
Definition: attack.h:98
WEAP_BLUD
#define WEAP_BLUD
Definition: define.h:85
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
ATNR_CHAOS
#define ATNR_CHAOS
Definition: attack.h:67
EVENT_DEATH
#define EVENT_DEATH
Definition: events.h:24
obj::stats
living stats
Definition: object.h:371
paralyze_living
void paralyze_living(object *op, int dam)
Definition: attack.c:2346
WEAP_STAB
#define WEAP_STAB
Definition: define.h:82
obj::contr
struct pl * contr
Definition: object.h:277
ATM_CLEAVE
#define ATM_CLEAVE
Definition: attack.h:34
AT_SLOW
#define AT_SLOW
Definition: attack.h:87
SK_PUNCHING
@ SK_PUNCHING
Definition: skills.h:36
WEAP_SLASH
#define WEAP_SLASH
Definition: define.h:78
AT_TURN_UNDEAD
#define AT_TURN_UNDEAD
Definition: attack.h:89
WEAP_PIERCE
#define WEAP_PIERCE
Definition: define.h:79
map_find_by_archetype
object * map_find_by_archetype(mapstruct *m, int x, int y, const archetype *at)
Definition: object.c:3068
SK_WRAITH_FEED
@ SK_WRAITH_FEED
Definition: skills.h:57
GRIMREAPER
@ GRIMREAPER
Definition: object.h:130
ATNR_DEPLETE
#define ATNR_DEPLETE
Definition: attack.h:65
apply_anim_suffix
void apply_anim_suffix(object *who, const char *suffix)
Definition: anim.c:149
friendly_fire
int friendly_fire(object *op, object *hitter)
Definition: attack.c:1804
reputation.victim
victim
Definition: reputation.py:14
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
DISEASE
@ DISEASE
Definition: object.h:244
did_make_save
int did_make_save(const object *op, int level, int bonus)
Definition: living.c:2282
liv::Wis
int8_t Wis
Definition: living.h:36
give.op
op
Definition: give.py:33
NDI_ALL
#define NDI_ALL
Definition: newclient.h:263
ATM_WHIP
#define ATM_WHIP
Definition: attack.h:37
monster_npc_call_help
void monster_npc_call_help(object *op)
Definition: monster.c:1972
WEAP_CRUSH
#define WEAP_CRUSH
Definition: define.h:84
check_exp_loss
int64_t check_exp_loss(const object *op, int64_t exp)
Definition: living.c:2066
AT_DEPLETE
#define AT_DEPLETE
Definition: attack.h:92
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:278
ATM_ELEC
#define ATM_ELEC
Definition: attack.h:25
ATM_CRUSH
#define ATM_CRUSH
Definition: attack.h:38
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.c:360
SPELL_EFFECT
@ SPELL_EFFECT
Definition: object.h:215
blind_living
void blind_living(object *op, object *hitter, int dam)
Definition: attack.c:2302
ATNR_MAGIC
#define ATNR_MAGIC
Definition: attack.h:50
MSG_TYPE_ATTACK_FUMBLE
#define MSG_TYPE_ATTACK_FUMBLE
Definition: newclient.h:614
diamondslots.y
y
Definition: diamondslots.py:16
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
buf
StringBuffer * buf
Definition: readable.c:1606
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:2168
NUM_ANIMATIONS
#define NUM_ANIMATIONS(ob)
Definition: global.h:169
put_in_icecube
static void put_in_icecube(object *op, object *originator)
Definition: attack.c:145
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2820
update_all_los
void update_all_los(const mapstruct *map, int x, int y)
Definition: los.c:536
object_update_speed
void object_update_speed(object *op)
Definition: object.c:1317
object_handle_death_animation
void object_handle_death_animation(object *op)
Definition: object.c:5371
obj::move_type
MoveType move_type
Definition: object.h:428
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:232
pl::ranges
object * ranges[range_size]
Definition: player.h:103
change_object
void change_object(object *op)
Definition: time.c:584
ATM_SLASH
#define ATM_SLASH
Definition: attack.h:32
make_face_from_files.int
int
Definition: make_face_from_files.py:26
IS_LIVE
#define IS_LIVE(op)
Definition: define.h:173
WEAP_SLICE
#define WEAP_SLICE
Definition: define.h:81
object_present_in_ob_by_name
object * object_present_in_ob_by_name(int type, const char *str, const object *op)
Definition: object.c:3153
save_throw_object
void save_throw_object(object *op, uint32_t type, object *originator)
Definition: attack.c:182
UPD_NAME
#define UPD_NAME
Definition: newclient.h:318
AT_COUNTERSPELL
#define AT_COUNTERSPELL
Definition: attack.h:95
obj::skill
sstring skill
Definition: object.h:322
AT_ACID
#define AT_ACID
Definition: attack.h:82
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:2067
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:309
stop_item
object * stop_item(object *op)
Definition: time.c:447
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.c:1533
AT_FEAR
#define AT_FEAR
Definition: attack.h:90
is_aimed_missile
static int is_aimed_missile(object *op)
Definition: attack.c:2568
abort_attack
static int abort_attack(object *target, object *hitter, int simple_attack)
Definition: attack.c:719
EVENT_ATTACKED
#define EVENT_ATTACKED
Definition: events.h:20
object_replace_insert_in_map
void object_replace_insert_in_map(const char *arch_string, object *op)
Definition: object.c:2560
hit_with_one_attacktype
static int hit_with_one_attacktype(object *op, object *hitter, int dam, uint32_t attacknum)
Definition: attack.c:1165
DOOR
@ DOOR
Definition: object.h:126
player_can_view
int player_can_view(object *pl, object *op)
Definition: player.c:4103
AT_CONFUSION
#define AT_CONFUSION
Definition: attack.h:81
SK_EXP_TOTAL
#define SK_EXP_TOTAL
Definition: skills.h:79
object_decrease_nrof
object * object_decrease_nrof(object *op, uint32_t i)
Definition: object.c:2639
attack_ob_simple
static int attack_ob_simple(object *op, object *hitter, int base_dam, int base_wc)
Definition: attack.c:750
ATNR_COLD
#define ATNR_COLD
Definition: attack.h:53
AT_CANCELLATION
#define AT_CANCELLATION
Definition: attack.h:91
death_message.hitter
hitter
Definition: death_message.py:33
drain_stat
void drain_stat(object *op)
Definition: living.c:717
BIG_NAME
#define BIG_NAME
Definition: define.h:42
EVENT_ATTACKS
#define EVENT_ATTACKS
Definition: events.h:21
reputation.killer
def killer
Definition: reputation.py:13
FLAG_IS_LIGHTABLE
#define FLAG_IS_LIGHTABLE
Definition: define.h:278
AT_PARALYZE
#define AT_PARALYZE
Definition: attack.h:88
pl::party
partylist * party
Definition: player.h:188
TRUE
#define TRUE
Definition: compat.h:11
ATM_COLD
#define ATM_COLD
Definition: attack.h:26
AT_HOLYWORD
#define AT_HOLYWORD
Definition: attack.h:97
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:311
change_abil
int change_abil(object *op, object *tmp)
Definition: living.c:395
SPELL
@ SPELL
Definition: object.h:214
AT_GHOSTHIT
#define AT_GHOSTHIT
Definition: attack.h:85
find_god
const object * find_god(const char *name)
Definition: gods.c:80
FLAG_READY_WEAPON
#define FLAG_READY_WEAPON
Definition: define.h:334
EVENT_GKILL
#define EVENT_GKILL
Definition: events.h:41
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:408
ATM_CLAW
#define ATM_CLAW
Definition: attack.h:30
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
on_same_map
int on_same_map(const object *op1, const object *op2)
Definition: map.c:2647
ATNR_LIFE_STEALING
#define ATNR_LIFE_STEALING
Definition: attack.h:73
living.h
SP_EXPLOSION
#define SP_EXPLOSION
Definition: spells.h:80
kill_player
void kill_player(object *op, const object *killer)
Definition: player.c:3452
if
if(!(yy_init))
Definition: loader.c:2589
THROWN_OBJ
@ THROWN_OBJ
Definition: object.h:146
FLAG_SLEEP
#define FLAG_SLEEP
Definition: define.h:307
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:402
IS_ARROW
#define IS_ARROW(op)
Definition: define.h:178
ATM_DOOR
#define ATM_DOOR
Definition: attack.h:40
thrown_item_effect
static void thrown_item_effect(object *, object *)
Definition: attack.c:2461
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:668
MAX_FOOD
static const int32_t MAX_FOOD
Definition: define.h:461
FORCE
@ FORCE
Definition: object.h:224
process_object
int process_object(object *op)
Definition: time.c:791
AT_DRAIN
#define AT_DRAIN
Definition: attack.h:83
pl::title
char title[BIG_NAME]
Definition: player.h:169
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,...)
Definition: main.c:319
obj::level
int16_t level
Definition: object.h:354
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Definition: events.cpp:27
llevDebug
@ llevDebug
Definition: logger.h:13
change_luck
void change_luck(object *op, int value)
Definition: living.c:797
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
attackmess::buf2
char * buf2
Definition: attack.h:121
FORCE_NAME
#define FORCE_NAME
Definition: spells.h:169
ATM_DRAIN
#define ATM_DRAIN
Definition: attack.h:24
give.name
name
Definition: give.py:27
stick_arrow
static int stick_arrow(object *op, object *tmp)
Definition: attack.c:927
ATNR_FEAR
#define ATNR_FEAR
Definition: attack.h:63
object_get_owner
object * object_get_owner(object *op)
Definition: object.c:797
AT_FIRE
#define AT_FIRE
Definition: attack.h:78
level
Definition: level.py:1
MSG_TYPE_APPLY_UNAPPLY
#define MSG_TYPE_APPLY_UNAPPLY
Definition: newclient.h:602