Crossfire Server, Trunk
attack.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
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 static int hit_with_one_attacktype(
42  object *op, object *hitter, int dam, uint32_t attacknum);
43 
51 static void cancellation(object *op) {
52  if (op->invisible)
53  return;
54 
55  if (QUERY_FLAG(op, FLAG_ALIVE) || op->type == CONTAINER || op->type == THROWN_OBJ) {
56  /* Recur through the inventory */
61  } else if (FABS(op->magic) <= rndm(0, 5)) {
62  /* Nullify this object. This code could probably be more complete */
63  /* in what abilities it should cancel */
64  op->magic = 0;
69  if (op->env && op->env->type == PLAYER) {
71  }
72  }
73 }
74 
75 static void object_get_materialtype(object* op, materialtype_t** mt) {
77  if (op->materialname == NULL) {
78  for (auto material : materials) {
79  *mt = material;
80  if (op->material & (*mt)->material) {
81  return;
82  }
83  }
84  *mt = nullptr;
85  } else
86  *mt = name_to_material(op->materialname);
87 }
88 
104 static int did_make_save_item(object *op, int type, object *originator) {
105  int i, roll, saves = 0, attacks = 0, number;
106  materialtype_t* mt;
108  if (mt == NULL)
109  return TRUE;
110  roll = rndm(1, 20);
111 
112  /* the attacktypes have no meaning for object saves
113  * If the type is only magic, don't adjust type - basically, if
114  * pure magic is hitting an object, it should save. However, if it
115  * is magic teamed with something else, then strip out the
116  * magic type, and instead let the fire, cold, or whatever component
117  * destroy the item. Otherwise, you get the case of poisoncloud
118  * destroying objects because it has magic attacktype.
119  */
120  if (type != AT_MAGIC)
124  AT_MAGIC);
125 
126  if (type == 0)
127  return TRUE;
128  if (roll == 20)
129  return TRUE;
130 
131  for (number = 0; number < NROFATTACKS; number++) {
132  i = 1<<number;
133  if (!(i&type))
134  continue;
135  attacks++;
136  if (op->resist[number] == 100)
137  saves++;
138  else if (roll >= mt->save[number]-op->magic-op->resist[number]/100)
139  saves++;
140  else if ((20-mt->save[number])/3 > originator->stats.dam)
141  saves++;
142  }
143 
144  if (saves == attacks || attacks == 0)
145  return TRUE;
146  if (saves == 0 || rndm(1, attacks) > saves)
147  return FALSE;
148  return TRUE;
149 }
150 
151 static void put_in_icecube(object* op, object* originator) {
152  archetype* at = find_archetype("icecube");
153  if (at == NULL)
154  return;
155  op = stop_item(op);
156  if (op == NULL)
157  return;
158 
159  // Put in existing icecube if one exists, otherwise make one
160  object* tmp = map_find_by_archetype(op->map, op->x, op->y, at);
161  if (tmp == NULL) {
162  tmp = arch_to_object(at);
163  /* This was in the old (pre new movement code) -
164  * icecubes have slow_move set to 1 - don't want
165  * that for ones we create.
166  */
167  tmp->move_slow_penalty = 0;
168  tmp->move_slow = 0;
169  object_insert_in_map_at(tmp, op->map, originator, 0, op->x, op->y);
170  }
171  if (!QUERY_FLAG(op, FLAG_REMOVED))
172  object_remove(op);
174 }
175 
176 object *blame(object *op) {
177  if (op == NULL) {
178  return NULL;
179  }
180  if (op->type == PLAYER) {
181  return op;
182  }
183  object *owner = object_get_owner(op);
184  if (owner != NULL && owner->type == PLAYER) {
185  return owner;
186  }
187  return NULL;
188 }
189 
202 void save_throw_object(object *op, uint32_t type, object *originator) {
203  int dam;
204 
205  if (!did_make_save_item(op, type, originator)) {
206  object *env = op->env;
207  int x = op->x, y = op->y;
208  mapstruct *m = op->map;
209  /* For use with burning off equipped items */
210  int weight = op->weight;
211 
212  op = stop_item(op);
213  if (op == NULL)
214  return;
215 
216  /*
217  * If this object is a transport and has players in it, make them disembark.
218  */
219  if (op->type == TRANSPORT && op->inv) {
220  if (op->map == NULL) {
221  LOG(llevError, "Transport %s not on a map but with an item %s in it?\n", op->name, op->inv->name);
222  } else {
223  char name[MAX_BUF];
224  query_name(op, name, sizeof(name));
226  if (inv->contr) {
228  "You are expelled from the %s during its destruction.",
229  name);
230  inv->contr->transport = NULL;
231  }
232  }
233  FOR_INV_FINISH();
234  }
235  }
236 
237 
238  /* Set off runes in the inventory of the object being destroyed. */
240  if (inv->type == RUNE)
241  spring_trap(inv, originator);
242  FOR_INV_FINISH();
243 
244  if (QUERY_FLAG(op, FLAG_UNPAID)) {
245  object *badguy = blame(originator);
246  if (badguy != NULL) {
247  // Punish player for destroying unpaid item
248  commit_crime(badguy, "Destruction of unpaid property");
249  }
250  }
251 
252  /* Hacked the following so that type LIGHTER will work.
253  * Also, objects which are potenital "lights" that are hit by
254  * flame/elect attacks will be set to glow. "lights" are any
255  * object with +/- glow_radius and an "other_arch" to change to.
256  * (and please note that we cant fail our save and reach this
257  * function if the object doesnt contain a material that can burn.
258  * So forget lighting magical swords on fire with this!) -b.t.
259  */
261  && op->other_arch
263  const char *arch = op->other_arch->name;
264  int no_pick = QUERY_FLAG(op, FLAG_NO_PICK);
265 
266  op = object_decrease_nrof(op, 1);
267  if (op)
268  fix_stopped_item(op, m, originator);
270  if (op != NULL) {
271  if (env) {
272  op->x = env->x,
273  op->y = env->y;
275  } else {
276  if (no_pick) {
278  }
279  object_insert_in_map_at(op, m, originator, 0, x, y);
280  }
281  }
282  return;
283  }
284  if (type&AT_CANCELLATION) { /* Cancellation. */
285  cancellation(op);
286  fix_stopped_item(op, m, originator);
287  return;
288  }
289  if (op->nrof > 1) {
290  op = object_decrease_nrof(op, rndm(0, op->nrof-1));
291  if (op)
292  fix_stopped_item(op, m, originator);
293  } else {
294  if (!QUERY_FLAG(op, FLAG_REMOVED))
295  object_remove(op);
297  }
298  if (type&(AT_FIRE|AT_ELECTRICITY)) {
299  if (env) {
300  /* Check to see if the item being burnt is being worn */
301  if (QUERY_FLAG(op, FLAG_APPLIED)) {
302  /* if the object is applied, it should be safe to assume env is a player or creature. */
303  if (env->resist[ATNR_FIRE] < 100)
304  /* Should the message type be something different? */
306  "OUCH! It burns!");
307  else
309  "Despite the flame, you feel nothing.");
310  /* burning off an item causes 1 point of fire damage for every kilogram of mass the item has */
311  dam = weight / 1000 * (100 - env->resist[ATNR_FIRE]) / 100;
312  /* Double the damage on cursed items */
313  if (QUERY_FLAG(op, FLAG_CURSED))
314  dam *= 2;
315  /* Triple the damage on damned items. A cursed and damned item would thus inflict 6x damage. */
316  if (QUERY_FLAG(op, FLAG_DAMNED))
317  dam *= 3;
318  env->stats.hp -= dam;
319  /* You die at -1, not 0 */
320  if (env->stats.hp < 0)
321  kill_player(env, NULL);
322  }
323  op = create_archetype("burnout");
324  op->x = env->x,
325  op->y = env->y;
327  } else {
328  object_replace_insert_in_map("burnout", originator);
329  }
330  }
331  return;
332  }
333  /* The value of 50 is arbitrary. */
334  if (type & AT_COLD && (op->resist[ATNR_COLD] < 50) &&
335  !QUERY_FLAG(op, FLAG_NO_PICK) && (RANDOM() & 2)) {
336  put_in_icecube(op, originator);
337  }
338 }
339 
355 int hit_map(object *op, int dir, uint32_t type, int full_hit) {
356  mapstruct *map;
357  int16_t x, y;
358  int retflag = 0; /* added this flag.. will return 1 if it hits a monster */
359  tag_t op_tag;
360 
361  if (QUERY_FLAG(op, FLAG_FREED)) {
362  LOG(llevError, "BUG: hit_map(): free object\n");
363  return 0;
364  }
365 
366  if (QUERY_FLAG(op, FLAG_REMOVED) || op->env != NULL) {
367  LOG(llevError, "BUG: hit_map(): hitter (arch %s, name %s) not on a map\n", op->arch->name, op->name);
368  return 0;
369  }
370 
371  if (!op->map) {
372  LOG(llevError, "BUG: hit_map(): %s has no map\n", op->name);
373  return 0;
374  }
375 
376  op = HEAD(op);
377  op_tag = op->count;
378 
379  map = op->map;
380  x = op->x+freearr_x[dir];
381  y = op->y+freearr_y[dir];
382  if (get_map_flags(map, &map, x, y, &x, &y)&P_OUT_OF_MAP)
383  return 0;
384 
385  /* peterm: a few special cases for special attacktypes --counterspell
386  * must be out here because it strikes things which are not alive
387  */
388 
389  if (type&AT_COUNTERSPELL) {
390  counterspell(op, dir); /* see spell_effect.c */
391 
392  /* If the only attacktype is counterspell or magic, don't need
393  * to do any further processing.
394  */
395  if (!(type&~(AT_COUNTERSPELL|AT_MAGIC))) {
396  return 0;
397  }
398  type &= ~AT_COUNTERSPELL;
399  }
400 
401  if (type&AT_CHAOS) {
404  type &= ~AT_CHAOS;
405  }
406 
407  FOR_MAP_PREPARE(map, x, y, tmp) {
408  if (QUERY_FLAG(tmp, FLAG_FREED)) {
409  LOG(llevError, "BUG: hit_map(): found freed object\n");
410  break;
411  }
412 
413  /* Something could have happened to 'tmp' while 'tmp->below' was processed.
414  * For example, 'tmp' was put in an icecube.
415  * This is one of the few cases where on_same_map should not be used.
416  */
417  if (tmp->map != map || tmp->x != x || tmp->y != y)
418  continue;
419 
420  tmp = HEAD(tmp);
421 
422  /* Need to hit everyone in the transport with this spell */
423  if (tmp->type == TRANSPORT) {
425  if (pl->type == PLAYER)
426  hit_player(pl, op->stats.dam, op, type, full_hit);
427  FOR_INV_FINISH();
428  }
429 
430  if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
431  hit_player(tmp, op->stats.dam, op, type, full_hit);
432  retflag |= 1;
433  if (object_was_destroyed(op, op_tag))
434  break;
435  }
436  /* Here we are potentially destroying an object. If the object has
437  * NO_PASS set, it is also immune - you can't destroy walls. Note
438  * that weak walls have is_alive set, which prevent objects from
439  * passing over/through them. We don't care what type of movement
440  * the wall blocks - if it blocks any type of movement, can't be
441  * destroyed right now.
442  */
443  else if ((tmp->material || tmp->materialname) && op->stats.dam > 0 && !tmp->move_block) {
445  if (object_was_destroyed(op, op_tag))
446  break;
447  }
448  } FOR_MAP_FINISH();
449  return 0;
450 }
451 
463 static uint8_t get_attack_message_type(int type, const object *op, const object *hitter) {
464  if ((hitter->type == DISEASE
465  || hitter->type == SYMPTOM
466  || hitter->type == POISONING
467  || (type&AT_POISON && IS_LIVE(op)))) {
468  return ATM_SUFFER;
469  }
470 
471  if (op->type == DOOR) {
472  return ATM_DOOR;
473  }
474 
475  if (hitter->type == PLAYER && IS_LIVE(op)) {
476  if (USING_SKILL(hitter, SK_KARATE)) {
477  return ATM_KARATE;
478  }
479  if (USING_SKILL(hitter, SK_CLAWING)) {
480  return ATM_CLAW;
481  }
483  return ATM_PUNCH;
484  }
486  return ATM_WRAITH_FEED;
487  }
488  }
489 
490  if (IS_ARROW(hitter) && (type == AT_PHYSICAL || type == AT_MAGIC)) {
491  return ATM_ARROW;
492  }
493 
494  if (type&AT_DRAIN && IS_LIVE(op)) {
495  return ATM_DRAIN;
496  }
497  if (type&AT_ELECTRICITY && IS_LIVE(op)) {
498  return ATM_ELEC;
499  }
500  if (type&AT_COLD && IS_LIVE(op)) {
501  return ATM_COLD;
502  }
503  if (type&AT_FIRE) {
504  return ATM_FIRE;
505  }
506 
507  if (hitter->current_weapon != NULL) {
508  switch (hitter->current_weapon->weapontype) {
509  case WEAP_HIT: return ATM_BASIC;
510  case WEAP_SLASH: return ATM_SLASH;
511  case WEAP_PIERCE: return ATM_PIERCE;
512  case WEAP_CLEAVE: return ATM_CLEAVE;
513  case WEAP_SLICE: return ATM_SLICE;
514  case WEAP_STAB: return ATM_STAB;
515  case WEAP_WHIP: return ATM_WHIP;
516  case WEAP_CRUSH: return ATM_CRUSH;
517  case WEAP_BLUD: return ATM_BLUD;
518  default: return ATM_BASIC;
519  }
520  }
521 
522  return ATM_BASIC;
523 }
524 
538 void get_attack_message_for_attack_type(int dam, uint8_t atm_type, const char *victim_name, char *buf1, char *buf2) {
539  snprintf(buf1, MAX_BUF, "hit");
540  snprintf(buf2, MAX_BUF, "hits");
541 
542  for (int i = 0; i < MAXATTACKMESS && attack_mess[atm_type][i].level != -1; i++) {
543  if (dam < attack_mess[atm_type][i].level
544  || attack_mess[atm_type][i+1].level == -1) {
545  snprintf(buf1, MAX_BUF, "%s %s%s", attack_mess[atm_type][i].buf1, victim_name, attack_mess[atm_type][i].buf2);
546  snprintf(buf2, MAX_BUF, "%s", attack_mess[atm_type][i].buf3);
547  return;
548  }
549  }
550 }
551 
569 static void get_attack_message(int dam, int type, const object *op, const object *hitter, char *buf1, char *buf2) {
570  if (dam == 9998 && op->type == DOOR) {
571  snprintf(buf1, MAX_BUF, "unlock %s", op->name);
572  snprintf(buf2, MAX_BUF, " unlocks");
573  return;
574  }
575  if (dam < 0) {
576  snprintf(buf1, MAX_BUF, "hit %s", op->name);
577  snprintf(buf2, MAX_BUF, " hits");
578  return;
579  }
580  if (dam == 0) {
581  snprintf(buf1, MAX_BUF, "missed %s", op->name);
582  snprintf(buf2, MAX_BUF, " misses");
583  return;
584  }
585 
586  snprintf(buf1, MAX_BUF, "hit");
587  snprintf(buf2, MAX_BUF, "hits");
588 
589  uint8_t atm_type = get_attack_message_type(type, op, hitter);
590  get_attack_message_for_attack_type(dam, atm_type, op->name, buf1, buf2);
591 }
592 
608 static void attack_message(int dam, int type, object *op, object *hitter) {
609  char buf[MAX_BUF], buf1[MAX_BUF], buf2[MAX_BUF];
610  object *owner;
611 
612  if (dam > 0) {
613  if (hitter->chosen_skill)
614  play_sound_map(SOUND_TYPE_HIT, hitter, 0, hitter->chosen_skill->name);
615  }
616 
617  /* bail out if a monster is casting spells */
618  owner = object_get_owner(hitter);
619  if (hitter->type != PLAYER && (owner == NULL || owner->type != PLAYER))
620  return;
621 
622  /* scale down magic considerably. */
623  if (type&AT_MAGIC && rndm(0, 5))
624  return;
625 
626  get_attack_message(dam, type, op, hitter, buf1, buf2);
627 
628  /* Did a player hurt another player? Inform both! */
629  /* only show half the player->player combat messages */
630  if (op->type == PLAYER
631  && rndm(0, 1)
632  && ((owner != NULL ? owner : hitter)->type) == PLAYER) {
633  if (owner != NULL)
634  snprintf(buf, sizeof(buf), "%s's %s%s you.", owner->name, hitter->name, buf2);
635  else {
636  snprintf(buf, sizeof(buf), "%s%s you.", hitter->name, buf2);
637  }
639  } /* end of player hitting player */
640 
641  /* scale down these messages too */
642  /*if(hitter->type == PLAYER && rndm(0, 2) == 0) {*/
643  if (hitter->type == PLAYER) {
644  snprintf(buf, sizeof(buf), "You %s.", buf1);
646  buf);
647  } else if (owner != NULL && owner->type == PLAYER) {
648  /* look for stacked spells and start reducing the message chances */
649  if (hitter->type == SPELL_EFFECT
650  && (hitter->subtype == SP_EXPLOSION || hitter->subtype == SP_BULLET || hitter->subtype == SP_CONE)) {
651  int i = 4;
652  mapstruct *map = hitter->map;
653  if (OUT_OF_REAL_MAP(map, hitter->x, hitter->y))
654  return;
656  if (next->type == SPELL_EFFECT
657  && (next->subtype == SP_EXPLOSION || next->subtype == SP_BULLET || next->subtype == SP_CONE)) {
658  i *= 3;
659  if (i > 10000)
660  /* no need to test more, and avoid overflows */
661  break;
662  }
663  FOR_MAP_FINISH();
664  if (i < 0)
665  return;
666  if (rndm(0, i) != 0)
667  return;
668  } else if (rndm(0, 5) != 0)
669  return;
670  play_sound_map(SOUND_TYPE_HIT, owner, 0, "hit");
672  "Your %s%s %s.",
673  hitter->name, buf2, op->name);
674  }
675 }
676 
688 static int get_attack_mode(object **target, object **hitter,
689  int *simple_attack) {
690  if (QUERY_FLAG(*target, FLAG_FREED) || QUERY_FLAG(*hitter, FLAG_FREED)) {
691  LOG(llevError, "BUG: get_attack_mode(): freed object\n");
692  return 1;
693  }
694  *target = HEAD(*target);
695  *hitter = HEAD(*hitter);
696  if ((*hitter)->env != NULL || (*target)->env != NULL) {
697  *simple_attack = 1;
698  return 0;
699  }
700  if (QUERY_FLAG(*target, FLAG_REMOVED)
702  || (*hitter)->map == NULL
703  || !on_same_map((*hitter), (*target))) {
704  LOG(llevError, "BUG: hitter (arch %s, name %s) with no relation to target\n", (*hitter)->arch->name, (*hitter)->name);
705  return 1;
706  }
707  *simple_attack = 0;
708  return 0;
709 }
710 
724 static int abort_attack(object *target, object *hitter, int simple_attack) {
725  int new_mode;
726 
727  if (hitter->env == target || target->env == hitter)
728  new_mode = 1;
729  else if (QUERY_FLAG(hitter, FLAG_REMOVED)
730  || QUERY_FLAG(target, FLAG_REMOVED)
731  || hitter->map == NULL || !on_same_map(hitter, target))
732  return 1;
733  else
734  new_mode = 0;
735  return new_mode != simple_attack;
736 }
737 
738 static void thrown_item_effect(object *, object *);
739 
767 static int attack_ob_simple(object *op, object *hitter, int base_dam, int wc) {
768  int simple_attack, roll, dam;
769  uint32_t type;
770  tag_t op_tag, hitter_tag;
771  const char *anim_suffix = NULL;
772 
773  if (get_attack_mode(&op, &hitter, &simple_attack))
774  return 1;
775 
776  /* This is used to handle script_weapons with weapons, only for players. */
777  if (hitter->type == PLAYER) {
778  if (hitter->current_weapon != NULL) {
779  if (events_execute_object_event(hitter->current_weapon, EVENT_ATTACKS,
780  hitter, op, NULL, SCRIPT_FIX_ALL) != 0)
781  return 0;
782  }
783  }
784 
785  if (hitter->current_weapon) {
786  anim_suffix = hitter->current_weapon->anim_suffix;
787  } else if (hitter->chosen_skill) {
788  anim_suffix = hitter->chosen_skill->anim_suffix;
789  }
790 
791  if (!anim_suffix) {
792  anim_suffix = "attack";
793  }
794  apply_anim_suffix(hitter, anim_suffix);
795 
796 
797  op_tag = op->count;
798  hitter_tag = hitter->count;
799  /*
800  * A little check to make it more difficult to dance forward and back
801  * to avoid ever being hit by monsters.
802  */
803  if (!simple_attack && QUERY_FLAG(op, FLAG_MONSTER)
804  && op->speed_left > -(FABS(op->speed))*0.3) {
805  /* Decrease speed BEFORE calling process_object. Otherwise, an
806  * infinite loop occurs, with process_object calling monster_move(),
807  * which then gets here again. By decreasing the speed before
808  * we call process_object, the 'if' statement above will fail.
809  */
810  op->speed_left--;
812  if (object_was_destroyed(op, op_tag)
813  || object_was_destroyed(hitter, hitter_tag)
814  || abort_attack(op, hitter, simple_attack))
815  return 1;
816  }
817 
818  roll = random_roll(1, 20, hitter, PREFER_HIGH);
819 
820  /* Adjust roll for various situations. */
821  if (!simple_attack)
822  roll += adj_attackroll(hitter, op);
823 
824  /* See if we hit the creature */
825  if (roll >= 20 || op->stats.ac >= wc-roll) {
826  if (settings.casting_time == TRUE) {
827  if (hitter->type == PLAYER && hitter->casting_time > -1) {
828  hitter->casting_time = -1;
830  "You attacked and lost your spell!");
831  }
832  if (op->casting_time > -1 && base_dam > 0) {
833  op->casting_time = -1;
834  if (op->type == PLAYER) {
836  "You were hit and lost your spell!");
839  "%s was hit by %s and lost a spell.",
840  op->name, hitter->name);
841  }
842  }
843  }
844  if (!simple_attack) {
845  /* If you hit something, the victim should *always *wake up.
846  * Before, invisible hitters could avoid doing this.
847  * -b.t. */
848  if (QUERY_FLAG(op, FLAG_SLEEP))
850 
851  /* If the victim can't see the attacker, it may alert others
852  * for help. */
853  if (op->type != PLAYER && !monster_can_see_enemy(op, hitter)
854  && !object_get_owner(op) && rndm(0, op->stats.Int))
856 
857  /* if you were hidden and hit by a creature, you are discovered*/
858  if (op->hide && QUERY_FLAG(hitter, FLAG_ALIVE)) {
859  make_visible(op);
860  if (op->type == PLAYER)
862  "You were hit by a wild attack. You are no longer hidden!");
863  }
864 
865  /* thrown items (hitter) will have various effects
866  * when they hit the victim. For things like thrown daggers,
867  * this sets 'hitter' to the actual dagger, and not the
868  * wrapper object.
869  */
871  if (object_was_destroyed(hitter, hitter_tag)
872  || object_was_destroyed(op, op_tag)
873  || abort_attack(op, hitter, simple_attack)) {
874  return 0;
875  }
876  }
877 
878  /* Need to do at least 1 damage, otherwise there is no point
879  * to go further and it will cause FPE's below.
880  */
881  if (base_dam <= 0)
882  base_dam = 1;
883 
884  type = hitter->attacktype;
885  if (!type)
886  type = AT_PHYSICAL;
887  /* Handle monsters that hit back */
888  if (!simple_attack && QUERY_FLAG(op, FLAG_HITBACK)
890  if (op->attacktype&AT_ACID && hitter->type == PLAYER)
892  "You are splashed by acid!\n");
893  hit_player(hitter, random_roll(0, (op->stats.dam), hitter, PREFER_LOW), op, op->attacktype, 1);
894  if (object_was_destroyed(op, op_tag)
895  || object_was_destroyed(hitter, hitter_tag)
896  || abort_attack(op, hitter, simple_attack)) {
897  return 0;
898  }
899  }
900 
901  /* In the new attack code, it should handle multiple attack
902  * types in its area, so remove it from here.
903  */
904  dam = hit_player(op, random_roll(1, base_dam, hitter, PREFER_HIGH), hitter, type, 1);
905  if (object_was_destroyed(op, op_tag)
906  || object_was_destroyed(hitter, hitter_tag)
907  || abort_attack(op, hitter, simple_attack)) {
908  return 0;
909  }
910 
911  if (object_value_set(hitter, "is_guard") && is_criminal(op)) {
912  // Guards may attack non-criminals (e.g. if they are a pet or get
913  // hit), but don't arrest unless target is actually criminal.
915  "The %s arrests you!", hitter->name);
916  player_arrest(op);
917  }
918  } else {/* end of if hitter hit op */
919  play_sound_map(SOUND_TYPE_HIT, hitter, 0, "miss");
920  dam = 0; /* if we missed, dam=0 */
921  }
922 
923  /*attack_message(dam, type, op, hitter);*/
924 
925  return dam;
926 }
927 
937 int attack_ob(object *op, object *hitter) {
938  hitter = HEAD(hitter);
939  return attack_ob_simple(op, hitter, hitter->stats.dam, hitter->stats.wc);
940 }
941 
952 static int stick_arrow(object *op, object *tmp) {
953  /* If the missile hit a player, we insert it in their inventory.
954  * However, if the missile is heavy, we don't do so (assume it falls
955  * to the ground after a hit). What a good value for this is up to
956  * debate - 5000 is 5 kg, so arrows, knives, and other light weapons
957  * stick around.
958  */
959  if (op->weight <= 5000 && tmp->stats.hp >= 0) {
960  object_remove(op);
962  return 1;
963  } else
964  return 0;
965 }
966 
979 object *hit_with_arrow(object *op, object *victim) {
980  object *container, *hitter;
981  int hit_something = 0;
982  tag_t victim_tag, hitter_tag, container_tag;
983  int16_t victim_x, victim_y;
984  mapstruct *victim_map;
985  const char *old_skill = NULL;
986 
987  /* Disassemble missile */
988  hitter = op->inv;
990  if (hitter->type != EVENT_CONNECTOR) {
991  break;
992  }
993  }
995  if (!hitter) {
996  container = NULL;
997  hitter = op;
998  if (free_no_drop(hitter))
999  return NULL;
1000  } else {
1001  container = op;
1003  if (free_no_drop(hitter))
1004  return NULL;
1005 
1006  object_insert_in_map_at(hitter, container->map, hitter, INS_NO_MERGE|INS_NO_WALK_ON, container->x, container->y);
1007  /* Note that we now have an empty THROWN_OBJ on the map. Code that
1008  * might be called until this THROWN_OBJ is either reassembled or
1009  * removed at the end of this function must be able to deal with empty
1010  * THROWN_OBJs. */
1011  container_tag = container->count;
1012  }
1013 
1014  /* Try to hit victim */
1015  victim_x = victim->x;
1016  victim_y = victim->y;
1017  victim_map = victim->map;
1018  victim_tag = victim->count;
1019  hitter_tag = hitter->count;
1020  /* FIXME provide also to script the skill? hitter is the throwed
1021  items, but there is no information about the fact it was
1022  thrown
1023  */
1025  /*
1026  * temporary set the hitter's skill to the one associated with the
1027  * throw wrapper. This is needed to that thrower gets it's xp at the
1028  * correct level. This might proves an awfull hack :/ We should really
1029  * provide attack_ob_simple with the skill to use...
1030  */
1031  if (container != NULL) {
1032  old_skill = hitter->skill;
1033  hitter->skill = add_refcount(container->skill);
1034  }
1035  hit_something = attack_ob_simple(victim, hitter, op->stats.dam, op->stats.wc);
1036  }
1037  /* Arrow attacks door, rune of summoning is triggered, demon is put on
1038  * arrow, move_apply() calls this function, arrow sticks in demon,
1039  * attack_ob_simple() returns, and we've got an arrow that still exists
1040  * but is no longer on the map. Ugh. (Beware: Such things can happen at
1041  * other places as well!)
1042  */
1043  if (object_was_destroyed(hitter, hitter_tag) || hitter->env != NULL) {
1044  if (container) {
1045  object_remove(container);
1046  object_free_drop_inventory(container);
1047  }
1048  return NULL;
1049  }
1050  if (container != NULL) {
1051  free_string(hitter->skill);
1052  hitter->skill = old_skill;
1053  }
1054  /* Missile hit victim */
1055  /* if the speed is > 10, then this is a fast moving arrow, we go straight
1056  * through the target
1057  */
1058  if (hit_something && op->speed <= 10.0) {
1059  /* Stop arrow */
1060  if (container == NULL) {
1062  if (hitter == NULL)
1063  return NULL;
1064  } else {
1065  if(!object_was_destroyed(container, container_tag)) {
1066  object_remove(container);
1067  object_free_drop_inventory(container);
1068  }
1069  }
1070 
1071  /* Try to stick arrow into victim */
1072  if (!object_was_destroyed(victim, victim_tag)
1073  && stick_arrow(hitter, victim)) {
1074  object_set_owner(hitter, NULL);
1075  return NULL;
1076  }
1077 
1078  /* Else try to put arrow on victim's map square
1079  * remove check for P_WALL here. If the arrow got to this
1080  * space, that is good enough - with the new movement code,
1081  * there is now the potential for lots of spaces where something
1082  * can fly over but not otherwise move over. What is the correct
1083  * way to handle those otherwise?
1084  */
1085  if (victim_x != hitter->x || victim_y != hitter->y) {
1087  object_set_owner(hitter, NULL);
1088  object_insert_in_map_at(hitter, victim_map, hitter, 0, victim_x, victim_y);
1089  } else {
1090  /* Else leave arrow where it is */
1091  object_merge(hitter, NULL);
1092  object_set_owner(hitter, NULL);
1093  }
1094  return NULL;
1095  }
1096 
1097  if (hit_something && op->speed >= 10.0)
1098  op->speed -= 1.0;
1099 
1100  /* Missile missed victim - reassemble missile */
1101  if (container) {
1103  object_insert_in_ob(hitter, container);
1104  }
1105  return op;
1106 }
1114 static void tear_down_wall(object *op) {
1115  int perc = 0;
1116 
1117  if (!op->stats.maxhp) {
1118  LOG(llevError, "TEAR_DOWN wall %s had no maxhp.\n", op->name);
1119  perc = 1;
1120  } else if (!GET_ANIM_ID(op)) {
1121  /* Object has been called - no animations, so remove it */
1122  if (op->stats.hp < 0) {
1123  object_remove(op); /* Should update LOS */
1125  /* Don't know why this is here - object_remove() should do it for us */
1126  /*update_position(m, x, y);*/
1127  }
1128  return; /* no animations, so nothing more to do */
1129  }
1130  assert(op->stats.maxhp > 0);
1131  perc = NUM_ANIMATIONS(op)-((int)NUM_ANIMATIONS(op)*op->stats.hp)/op->stats.maxhp;
1132 
1133  if (perc >= (int)NUM_ANIMATIONS(op))
1134  perc = NUM_ANIMATIONS(op)-1;
1135  else if (perc < 1)
1136  perc = 1;
1137  SET_ANIMATION(op, perc);
1139  if (perc == NUM_ANIMATIONS(op)-1) { /* Reached the last animation */
1140  if (op->face == blank_face) {
1141  /* If the last face is blank, remove the ob */
1142  object_remove(op); /* Should update LOS */
1144 
1145  /* object_remove() should call update_position for us */
1146  /*update_position(m, x, y);*/
1147  } else { /* The last face was not blank, leave an image */
1149  update_all_los(op->map, op->x, op->y);
1150  op->move_block = 0;
1152  }
1153  }
1154 }
1155 
1163 static void scare_creature(object *target, object *hitter) {
1164  object *owner = object_get_owner(hitter);
1165 
1166  if (!owner)
1167  owner = hitter;
1168 
1169  SET_FLAG(target, FLAG_SCARED);
1170  if (!target->enemy)
1171  object_set_enemy(target, owner);
1172 }
1173 
1174 static int hit_with_drain(object *op, object *hitter, int dam) {
1175  /* rate is the proportion of exp drained. High rate means
1176  * not much is drained, low rate means a lot is drained.
1177  */
1178  int rate;
1179 
1180  // Victim loses 1/rate of their current XP, so this scales from 4%
1181  // of their XP at -100 resist drain, to 2% at no resistance, to 1%
1182  // at 100% resist drain -- so even 100% resistance doesn't entirely
1183  // protect you!
1184  if (op->resist[ATNR_DRAIN] >= 0)
1185  rate = 50+op->resist[ATNR_DRAIN]/2;
1186  else
1187  rate = 5000/(100-op->resist[ATNR_DRAIN]);
1188 
1189  // This should be impossible, but just in case...
1190  if (!rate)
1191  return 0;
1192 
1193  int exp_to_drain = op->stats.exp/rate;
1194  if (hitter->type != PLAYER) {
1195  // Monsters stop draining once they have gained 5x their original exp
1196  // value. In practice this means they can drain up to 10x their original
1197  // exp from the player, so (e.g.) a grimreaper (exp 800) can drain up to
1198  // 8000 exp from the player and keep 4000 of it.
1199  exp_to_drain = MIN(exp_to_drain,
1200  hitter->arch->clone.stats.exp*6 - hitter->stats.exp);
1201  }
1202 
1203  if (exp_to_drain <= 1) {
1204  // Target is too protected, too low on exp, or attacker is too full.
1205  if (op->type == GOLEM)
1206  return 999; // The force animating it is drained away.
1207  else
1208  // Treat it like a physical attack instead.
1210  }
1211 
1212  // If we get this far we are definitely actually draining some exp from the
1213  // victim.
1214 
1215  // Randomly give the hitter some hp.
1216  if (hitter->stats.hp < hitter->stats.maxhp
1217  && (op->level > hitter->level)
1218  && random_roll(0, (op->level-hitter->level+2), hitter, PREFER_HIGH) > 3)
1219  hitter->stats.hp++;
1220 
1221  // Don't drain exp on battleground spaces or drain the wiz. Still return the
1222  // full damage amount though. Note that checking this as late as we do means
1223  // the life-restore side effect still works, we just don't transfer any exp.
1224  if (op_on_battleground(hitter, NULL, NULL, NULL)
1225  || QUERY_FLAG(op, FLAG_WAS_WIZ))
1226  return dam;
1227 
1228  int64_t orig_exp = op->stats.exp;
1229  change_exp(op, -exp_to_drain, NULL, 0);
1230  exp_to_drain = orig_exp - op->stats.exp; // Now the actual amount drained.
1231 
1232  object *owner = object_get_owner(hitter);
1233  if (owner && owner != hitter) {
1234  // If the attack was made by a pet, credit the pet's owner. If they
1235  // don't know the skill the pet attacked with, just give them generic
1236  // exp.
1237  change_exp(owner, exp_to_drain/2,
1238  hitter->chosen_skill ? hitter->chosen_skill->skill : NULL,
1239  SK_EXP_TOTAL);
1240  } else {
1241  // Otherwise just credit the attacker directly, and give them the skill
1242  // if they don't already have it.
1243  change_exp(hitter, exp_to_drain/2,
1244  hitter->chosen_skill ? hitter->chosen_skill->skill : NULL,
1246  }
1247 
1248  // Nominal 1 point of damage, so that if you have a pure drain attack it is
1249  // still considered successful and you get the messages for a drain attack
1250  // rather than for a miss.
1251  return 1;
1252 }
1253 
1270 static int hit_with_one_attacktype(object *op, object *hitter, int dam, uint32_t attacknum) {
1271  int doesnt_slay = 1;
1272  char name_hitter[MAX_BUF], name_op[MAX_BUF];
1273 
1274  /* Catch anyone that may be trying to send us a bitmask instead of the number */
1275  if (attacknum >= NROFATTACKS) {
1276  LOG(llevError, "hit_with_one_attacktype: Invalid attacknumber passed: %u\n", attacknum);
1277  return 0;
1278  }
1279 
1280  if (dam < 0) {
1281  LOG(llevError, "hit_with_one_attacktype called with negative damage: %d\n", dam);
1282  return 0;
1283  }
1284 
1285  /* AT_INTERNAL is supposed to do exactly dam. Put a case here so
1286  * people can't mess with that or it otherwise get confused. */
1287  if (attacknum == ATNR_INTERNAL)
1288  return dam;
1289 
1290  if (hitter->slaying) {
1291  // Look for the race as one of many that can be listed.
1292  if ((op->race != NULL && strstr(op->race, hitter->slaying))
1293  /*
1294  * Since we can do multiple races, we should be able to avoid the arch check.
1295  * This gives a more flexible race system, so skeletal mages can be a type of skeleton and such
1296  || (op->arch && op->arch->name != NULL && strstr(op->arch->name, hitter->slaying))
1297  */) {
1298  doesnt_slay = 0;
1299  dam *= 3;
1300  }
1301  }
1302 
1303  /* Adjust the damage for resistance. Note that neg. values increase damage. */
1304  /*
1305  * Skip lifestealing here, because it undergoes a more specific resistance scaling
1306  * in its own section that involves the better of drain/life stealing resistance
1307  *
1308  * Daniel Hawkins 2018-05-31
1309  */
1310  if (attacknum != ATNR_LIFE_STEALING && op->resist[attacknum]) {
1311  /* basically: dam = dam*(100-op->resist[attacknum])/100;
1312  * in case 0>dam>1, we try to "simulate" a float value-effect */
1313  dam *= (100-op->resist[attacknum]);
1314  if (dam >= 100)
1315  dam /= 100;
1316  else
1317  dam = (dam > (random_roll(0, 99, op, PREFER_LOW))) ? 1 : 0;
1318  }
1319 
1320  /* Special hack. By default, if immune to something, you
1321  * shouldn't need to worry. However, acid is an exception, since
1322  * it can still damage your items. Only include attacktypes if
1323  * special processing is needed */
1324 
1325  if (op->resist[attacknum] >= 100
1326  && doesnt_slay
1327  && attacknum != ATNR_ACID)
1328  return 0;
1329 
1330  /* Keep this in order - makes things easier to find */
1331 
1332  switch (attacknum) {
1333  case ATNR_PHYSICAL:
1334  /* here also check for diseases */
1336  break;
1337 
1338  /* Don't need to do anything for:
1339  magic,
1340  fire,
1341  electricity,
1342  cold */
1343 
1344  case ATNR_CONFUSION:
1345  case ATNR_POISON:
1346  case ATNR_SLOW:
1347  case ATNR_PARALYZE:
1348  case ATNR_FEAR:
1349  case ATNR_CANCELLATION:
1350  case ATNR_DEPLETE:
1351  case ATNR_BLIND: {
1352  /* chance for inflicting a special attack depends on the
1353  * difference between attacker's and defender's level
1354  */
1355  int level_diff = MIN(110, MAX(0, op->level-hitter->level));
1356 
1357  /* First, only creatures/players with speed can be affected.
1358  * Second, just getting hit doesn't mean it always affects
1359  * you. Third, you still get a saving through against the
1360  * effect.
1361  */
1362  if (op->speed
1363  && (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER)
1364  && !(rndm(0, (attacknum == ATNR_SLOW ? 6 : 3)-1))
1365  && !did_make_save(op, level_diff, op->resist[attacknum]/10)) {
1366  /* Player has been hit by something */
1367  if (attacknum == ATNR_CONFUSION)
1368  confuse_living(op, hitter, dam);
1369  else if (attacknum == ATNR_POISON)
1370  poison_living(op, hitter, dam);
1371  else if (attacknum == ATNR_SLOW)
1372  slow_living(op, hitter, dam);
1373  else if (attacknum == ATNR_PARALYZE)
1374  paralyze_living(op, dam);
1375  else if (attacknum == ATNR_FEAR)
1377  else if (attacknum == ATNR_CANCELLATION)
1378  cancellation(op);
1379  else if (attacknum == ATNR_DEPLETE)
1380  drain_stat(op);
1381  else if (attacknum == ATNR_BLIND
1382  && !QUERY_FLAG(op, FLAG_UNDEAD)
1384  blind_living(op, hitter, dam);
1385  }
1386  dam = 0; /* These are all effects and don't do real damage */
1387  }
1388  break;
1389 
1390  case ATNR_ACID: {
1391  int flag = 0;
1392 
1393  /* Items only get corroded if you're not on a battleground and
1394  * if your acid resistance is below 50%. */
1395  if (!op_on_battleground(op, NULL, NULL, NULL)
1396  && (op->resist[ATNR_ACID] < 50)) {
1397  FOR_INV_PREPARE(op, tmp) {
1398  if (tmp->invisible)
1399  continue;
1400  if (!QUERY_FLAG(tmp, FLAG_APPLIED)
1401  || (tmp->resist[ATNR_ACID] >= 10))
1402  /* >= 10% acid res. on itmes will protect these */
1403  continue;
1404  if (!(tmp->material&M_IRON))
1405  continue;
1406  if (tmp->magic < -4) /* Let's stop at -5 */
1407  continue;
1408  if (tmp->type == RING
1409  /* removed boots and gloves from exclusion list in PR */
1410  || tmp->type == GIRDLE
1411  || tmp->type == AMULET
1412  || tmp->type == WAND
1413  || tmp->type == ROD)
1414  continue; /* To avoid some strange effects */
1415 
1416  /* High damage acid has better chance of corroding objects */
1417  if (rndm(0, dam+4) > random_roll(0, 39, op, PREFER_HIGH)+2*tmp->magic) {
1418  if (op->type == PLAYER) {
1419  /* Make this more visible */
1420  query_name(hitter, name_hitter, MAX_BUF);
1421  query_name(tmp, name_op, MAX_BUF);
1424  "The %s's acid corrodes your %s!",
1425  name_hitter, name_op);
1426  }
1427  flag = 1;
1428  tmp->magic--;
1429  if (op->type == PLAYER)
1431  }
1432  } FOR_INV_FINISH();
1433  if (flag)
1434  fix_object(op); /* Something was corroded */
1435  }
1436  }
1437  break;
1438 
1439  case ATNR_DRAIN:
1440  return hit_with_drain(op, hitter, dam);
1441 
1442  case ATNR_TURN_UNDEAD: {
1443  if (QUERY_FLAG(op, FLAG_UNDEAD)) {
1444  object *owner = object_get_owner(hitter) == NULL ? hitter : object_get_owner(hitter);
1445  const object *god = find_god(determine_god(owner));
1446  int div = 1;
1447 
1448  /* if undead are not an enemy of your god, you turn them
1449  * at half strength */
1450  if (!god
1451  || !god->slaying
1452  || strstr(god->slaying, undead_name) == NULL)
1453  div = 2;
1454 
1455  /* The previous code was highly suspect - resist turn undead/100 would
1456  * at best give a bonus of 1 - increase that to resist turn undead/20 -
1457  * this gives a bit higher bonus. Also the bonus was added to the wrong
1458  * side of the equation, actually making it easier to turn creatures
1459  * if they had that resistance.
1460  */
1461  if ((op->level*div + (op->resist[ATNR_TURN_UNDEAD] / 20)) < (get_turn_bonus(owner->stats.Wis)+owner->level))
1462  scare_creature(op, owner);
1463  } else
1464  dam = 0; /* don't damage non undead - should we damage undead? */
1465  }
1466  break;
1467 
1468  case ATNR_DEATH:
1469  deathstrike_living(op, hitter, &dam);
1470  break;
1471 
1472  case ATNR_CHAOS:
1473  query_name(op, name_op, MAX_BUF);
1474  query_name(hitter, name_hitter, MAX_BUF);
1475  LOG(llevError, "%s was hit by %s with non-specific chaos.\n", name_op, name_hitter);
1476  dam = 0;
1477  break;
1478 
1479  case ATNR_COUNTERSPELL:
1480  dam = 0;
1481  /* While counterspell is handled separately and filtered out when it
1482  * moves, players can still step on a square that has an active
1483  * counterspell. When this happens, do no damage because counterspell
1484  * has no effect on anything but spells. */
1485  break;
1486 
1487  case ATNR_HOLYWORD: {
1488  /* This has already been handled by hit_player,
1489  * no need to check twice -- DAMN */
1490 
1491  object *owner = object_get_owner(hitter) == NULL ? hitter : object_get_owner(hitter);
1492 
1493  /* As with turn undead above, give a bonus on the saving throw */
1494  if (op->level+(op->resist[ATNR_HOLYWORD]/100) < owner->level+get_turn_bonus(owner->stats.Wis))
1495  scare_creature(op, owner);
1496  }
1497  break;
1498 
1499  case ATNR_LIFE_STEALING: {
1500  int new_hp;
1501  /* this is replacement to drain for players, instead of taking
1502  * exp it takes hp. It is geared for players, probably not
1503  * much use giving it to monsters
1504  *
1505  * life stealing doesn't do a lot of damage, but it gives the
1506  * damage it does do to the player. Given that,
1507  * it only does 1/30'th normal damage (hence the divide by
1508  * 3000). Wraith get 1/2 of the damage, and therefore divide
1509  * by 200. This number may need tweaking for game balance.
1510  */
1511 
1512  int dam_modifier = is_wraith_pl(hitter) ? 200 : 3000;
1513 
1514  /* You can't steal life from something undead or not alive. */
1515  if (op->type == GOLEM
1516  || (QUERY_FLAG(op, FLAG_UNDEAD))
1517  || !(QUERY_FLAG(op, FLAG_ALIVE))
1518  || (op->type == DOOR))
1519  return 0;
1520  /* If drain protection is higher than life stealing, use that */
1521  if (op->resist[ATNR_DRAIN] >= op->resist[ATNR_LIFE_STEALING])
1522  dam = (dam*(100-op->resist[ATNR_DRAIN]))/dam_modifier;
1523  else
1524  dam = (dam*(100-op->resist[ATNR_LIFE_STEALING]))/dam_modifier;
1525  /* You die at -1 hp, not zero. */
1526  if (dam > op->stats.hp+1)
1527  dam = op->stats.hp+1;
1528  new_hp = hitter->stats.hp+dam;
1529  if (new_hp > hitter->stats.maxhp)
1530  new_hp = hitter->stats.maxhp;
1531  if (new_hp > hitter->stats.hp)
1532  hitter->stats.hp = new_hp;
1533 
1534  /* Wraith also get food through life stealing */
1535  if (is_wraith_pl(hitter)) {
1536  if (hitter->stats.food+dam >= MAX_FOOD)
1537  hitter->stats.food = MAX_FOOD;
1538  else
1539  hitter->stats.food += dam;
1540  fix_object(hitter);
1541  }
1542  }
1543  }
1544  return dam;
1545 }
1546 
1547 /*
1548  * This function is defined in party.c, but conditionally, something "make proto"
1549  * doesn't handle. So define it locally.
1550  */
1551 #ifdef PARTY_KILL_LOG
1552 void party_add_kill(partylist *party, const char *killer, const char *dead, long exp);
1553 #endif
1554 
1582 static int kill_object(object *op, int dam, object *hitter) {
1583  char kill_message[MAX_BUF];
1584  const char *skill;
1585  int maxdam = 0;
1586  int battleg = 0; /* true if op standing on battleground */
1587  int pk = 0; /* true if op and what controls hitter are both players*/
1588  object *owner = NULL;
1589  const object *skop = NULL;
1590 
1591  if (op->stats.hp >= 0)
1592  return -1;
1593 
1595  return 0;
1597 
1599 
1600  /* maxdam needs to be the amount of damage it took to kill
1601  * this creature. The function(s) that call us have already
1602  * adjusted the creatures HP total, so that is negative.
1603  */
1604  maxdam = dam+op->stats.hp+1;
1605 
1607  update_all_los(op->map, op->x, op->y); /* makes sure los will be recalculated */
1608 
1609  if (op->type == DOOR) {
1610  op->speed = 0.1;
1612  op->speed_left = -0.05;
1613  return maxdam;
1614  }
1615  if (QUERY_FLAG(op, FLAG_FRIENDLY) && op->type != PLAYER) {
1616  object *owner;
1617 
1619  owner = object_get_owner(op);
1620  if (owner != NULL
1621  && owner->type == PLAYER) {
1622  if (owner->contr->ranges[range_golem] == op) {
1623  owner->contr->ranges[range_golem] = NULL;
1624  owner->contr->golem_count = 0;
1625  }
1626 
1627  /*play_sound_player_only(owner1->contr, SOUND_PET_IS_KILLED, 0, 0);*/
1628  /* Maybe we should include the owner that killed this, maybe not */
1630  "Your pet, the %s, is killed by %s.",
1631  op->name, hitter->name);
1632  }
1633  /*
1634  * there can be items both friendly and without any owner, for instance
1635  * in various maps, so this isn't an error.
1636  else
1637  LOG(llevError, "BUG: hit_player(): Encountered golem without owner.\n");
1638  */
1639 
1640  object_remove(op);
1642  return maxdam;
1643  }
1644 
1645  /* Now lets start dealing with experience we get for killing something */
1646 
1647  owner = object_get_owner(hitter);
1648  if (owner == NULL)
1649  owner = hitter;
1650 
1651  /* is the victim (op) standing on battleground? */
1652  if (op_on_battleground(op, NULL, NULL, NULL))
1653  battleg = 1;
1654 
1655  /* is this player killing? -- Don't count it if you suicide, though. */
1656  if (op->type == PLAYER && owner->type == PLAYER && owner != op)
1657  pk = 1;
1658 
1659  /* Player killed something */
1660  if (owner->type == PLAYER) {
1661 
1662  /* Log players killing other players - makes it easier to detect
1663  * and filter out malicious player killers - that is why the
1664  * ip address is included.
1665  */
1666  if (op->type == PLAYER && !battleg) {
1667  time_t t = time(NULL);
1668  struct tm *tmv;
1669  char buf[256];
1670  char name[MAX_BUF];
1671 
1672  tmv = localtime(&t);
1673  strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", tmv);
1675 
1676  LOG(llevInfo, "%s PLAYER_KILL_PLAYER: %s (%s) killed %s\n", buf, owner->name, owner->contr->socket->host, name);
1677  }
1678 
1679  /* try to filter some things out - basically, if you are
1680  * killing a level 1 creature and your level 20, you
1681  * probably don't want to see that.
1682  */
1683  if (owner->level < op->level*2 || op->stats.exp > 1000) {
1684  if (owner != hitter) {
1685  char killed[MAX_BUF], with[MAX_BUF];
1686 
1687  query_name(op, killed, MAX_BUF);
1688  query_name(hitter, with, MAX_BUF);
1690  "You killed %s with %s.",
1691  killed, with);
1692  } else {
1693  char killed[MAX_BUF];
1694 
1695  query_name(op, killed, MAX_BUF);
1697  "You killed %s.",
1698  killed);
1699  }
1700  }
1701 
1702  /* If a player kills another player, not on
1703  * battleground, the "killer" looses 1 luck. Since this is
1704  * not reversible, it's actually quite a pain IMHO. -AV
1705  * Fix bug in that we were changing the luck of the hitter, not
1706  * player that the object belonged to - so if you killed another player
1707  * with spells, pets, whatever, there was no penalty.
1708  * Changed to make luck penalty configurable in settings.
1709  *
1710  * Simplified comparison since pk is no longer set to 1 if self-kill
1711  * -- SilverNexus 2017-06-17+
1712  */
1713  if (pk == 1 && !battleg)
1715 
1716  /* This code below deals with finding the appropriate skill
1717  * to credit exp to. This is a bit problematic - we should
1718  * probably never really have to look at current_weapon->skill
1719  */
1720  skill = NULL;
1721  if (hitter->skill && hitter->type != PLAYER)
1722  skill = hitter->skill;
1723  else if (owner->chosen_skill) {
1724  skill = owner->chosen_skill->skill;
1725  skop = owner->chosen_skill;
1726  } else if (QUERY_FLAG(owner, FLAG_READY_WEAPON))
1727  skill = owner->current_weapon->skill;
1728  else
1729  LOG(llevError, "kill_object - unable to find skill that killed monster\n");
1730 
1731  /* We have the skill we want to credit to - now find the object this goes
1732  * to. Make sure skop is an actual skill, and not a skill tool!
1733  */
1734  if ((!skop || skop->type != SKILL) && skill) {
1735  skop = find_applied_skill_by_name(owner, skill);
1736  }
1737  } /* Was it a player that hit somethign */
1738  else {
1739  skill = NULL;
1740  }
1741 
1742  /* Pet (or spell) killed something. */
1743  if (owner != hitter) {
1744  char name_op[MAX_BUF], name_hitter[MAX_BUF];
1745  const char *owner_prefix;
1746  const char *op_prefix;
1747 
1748  owner_prefix = !battleg && pk && owner->contr != NULL && !owner->contr->peaceful ? "hostile " : "";
1749  op_prefix = !battleg && pk && op->contr != NULL && !op->contr->peaceful ? "hostile " : "";
1750 
1751  query_name(op, name_op, MAX_BUF);
1752  query_name(hitter, name_hitter, MAX_BUF);
1753  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)" : ""));
1754  } else {
1755  const char *hitter_prefix;
1756  const char *op_prefix;
1757 
1758  hitter_prefix = !battleg && pk && hitter->contr != NULL && !hitter->contr->peaceful ? "hostile " : "";
1759  op_prefix = !battleg && pk && op->contr != NULL && !op->contr->peaceful ? "hostile " : "";
1760 
1761  snprintf(kill_message, sizeof(kill_message), "%s%s killed %s%s%s%s.", hitter_prefix, hitter->name, op_prefix, op->name,
1762  (QUERY_FLAG(hitter, FLAG_MONSTER)) || hitter->type == PLAYER ?
1763  " in hand to hand combat" : "", battleg ? " (duel)" : (pk ? " (pk)" : ""));
1764  }
1765  /* These may have been set in the player code section above */
1766  if (!skop)
1767  skop = hitter->chosen_skill;
1768  if (!skill && skop)
1769  skill = skop->skill;
1770 
1772  kill_message);
1773  play_sound_map(SOUND_TYPE_HIT, op, 0, "kill");
1774 
1775 
1776  /* If you didn't kill yourself, and your not the wizard */
1777  if (owner != op && !QUERY_FLAG(op, FLAG_WAS_WIZ)) {
1778  int64_t exp;
1779 
1780  exp = calc_skill_exp(owner, op, skop);
1781 
1782  /* Really don't give much experience for killing other players */
1783  if (op->type == PLAYER) {
1784  if (battleg) {
1786  "Your foe has fallen!\nVICTORY!!!");
1787  } else {
1788  exp = settings.pk_max_experience_percent*exp/100;
1789  if (settings.pk_max_experience >= 0)
1790  exp = MIN(settings.pk_max_experience, exp);
1791  /* Never exceed what victim can lose considering permanent exp. */
1792  exp = check_exp_loss(op, exp);
1793  }
1794  }
1795 
1796  /* Don't know why this is set this way - doesn't make
1797  * sense to just divide everything by two for no reason.
1798  */
1799 
1800  if (!settings.simple_exp)
1801  exp = exp/2;
1802 
1803  /* if op is standing on "battleground" (arena), no way to gain
1804  * exp by killing him
1805  */
1806  if (battleg)
1807  exp = 0;
1808 
1809 #ifdef PARTY_KILL_LOG
1810  if (owner->type == PLAYER && owner->contr->party != NULL) {
1811  char name[MAX_BUF];
1812  char op_name[MAX_BUF];
1813 
1814  query_name(owner, name, MAX_BUF);
1815  query_name(op, op_name, sizeof(op_name));
1816  party_add_kill(owner->contr->party, name, op_name, exp);
1817  }
1818 #endif
1819  share_exp(owner, exp, skill, SK_EXP_TOTAL);
1820  } /* end if person didn't kill himself */
1821 
1822  if (op->type != PLAYER) {
1823  object_remove(op);
1825  /* Player has been killed! */
1826  } else {
1827  if (owner->type == PLAYER) {
1828  snprintf(op->contr->killer, BIG_NAME, "%s the %s", owner->name, owner->contr->title);
1829  } else {
1830  strncpy(op->contr->killer, hitter->name, BIG_NAME);
1831  op->contr->killer[BIG_NAME-1] = '\0';
1832  }
1833  /* Need to run kill_player (just in case, make sure is not wiz) */
1834  if (!QUERY_FLAG(op, FLAG_WIZ))
1835  kill_player(op, owner->type == PLAYER ? owner : hitter);
1836  }
1837  /* This was return -1 - that doesn't seem correct - if we return -1, process
1838  * continues in the calling function.
1839  */
1840  return maxdam;
1841 }
1842 
1853 int friendly_fire(object *op, object *hitter) {
1854  object *owner;
1855  int friendlyfire;
1856 
1857  hitter = HEAD(hitter);
1858  friendlyfire = 0;
1859 
1860  if (op->type == PLAYER) {
1861  if (hitter->type == PLAYER && hitter->contr->peaceful == 1)
1862  return 1;
1863 
1864  owner = object_get_owner(hitter);
1865  if (owner != NULL) {
1866  if (owner->type == PLAYER && owner->contr->peaceful == 1)
1867  friendlyfire = 2;
1868  }
1869 
1870  if (hitter->type == SPELL
1871  || hitter->type == POISONING
1872  || hitter->type == DISEASE
1873  || hitter->type == RUNE)
1874  friendlyfire = 0;
1875  }
1876  return friendlyfire;
1877 }
1878 
1902 int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit) {
1903  int maxdam = 0, ndam = 0, magic = (type&AT_MAGIC);
1904  int maxattacktype, attacknum;
1905  int body_attack = op->head != NULL; /* Did we hit op's head? */
1906  int simple_attack;
1907  tag_t op_tag, hitter_tag;
1908  int rtn_kill = 0;
1909  int friendlyfire;
1910  object *owner;
1911 
1912  if (events_execute_object_event(op, EVENT_ATTACKED, hitter, hitter->current_weapon ? hitter->current_weapon : hitter, NULL, SCRIPT_FIX_ALL) != 0)
1913  return 0;
1915  if (events_execute_object_event(inv, EVENT_ATTACKED, hitter, hitter->current_weapon ? hitter->current_weapon : hitter, NULL, SCRIPT_FIX_ALL) != 0)
1916  return 0;
1917  FOR_INV_FINISH();
1918 
1919  if (get_attack_mode(&op, &hitter, &simple_attack))
1920  return 0;
1921 
1922  /* very simple: if our target has no_damage 1 set or is wiz, we can't hurt him */
1924  return 0;
1925 
1926  op_tag = op->count;
1927  hitter_tag = hitter->count;
1928 
1929  if (body_attack) {
1930  /* slow and paralyze must hit the head. But we don't want to just
1931  * return - we still need to process other attacks the spell still
1932  * might have. So just remove the paralyze and slow attacktypes,
1933  * and keep on processing if we have other attacktypes.
1934  * return if only magic or nothing is left - under normal code
1935  * we don't attack with pure magic if there is another attacktype.
1936  * Only do processing if the initial attacktype includes one of those
1937  * attack so we don't cancel out things like magic bullet.
1938  */
1939  if (type&(AT_PARALYZE|AT_SLOW)) {
1940  type &= ~(AT_PARALYZE|AT_SLOW);
1941  if (!type || type == AT_MAGIC)
1942  return 0;
1943  }
1944  }
1945 
1946  if (!simple_attack && op->type == DOOR) {
1947  object *tmp;
1948 
1950  if (tmp != NULL) {
1952  if (object_was_destroyed(hitter, hitter_tag)
1953  || object_was_destroyed(op, op_tag)
1954  || abort_attack(op, hitter, simple_attack))
1955  return 0;
1956  }
1957  }
1958 
1959  if (!QUERY_FLAG(op, FLAG_ALIVE) || op->stats.hp < 0) {
1960  /* FIXME: If a player is killed by a rune in a door, the
1961  * object_was_destroyed() check above doesn't return, and might get here.
1962  */
1963  LOG(llevDebug, "victim (arch %s, name %s) already dead in hit_player()\n", op->arch->name, op->name);
1964  return 0;
1965  }
1966 
1967 #ifdef ATTACK_DEBUG
1968  LOG(llevDebug, "hit player: attacktype %d, dam %d\n", type, dam);
1969 #endif
1970 
1971  if (magic) {
1972  /* basically: dam = dam*(100-op->resist[attacknum])/100;
1973  * in case 0>dam>1, we try to "simulate" a float value-effect */
1974  dam = dam*(100-op->resist[ATNR_MAGIC]);
1975  if (dam >= 100)
1976  dam /= 100;
1977  else
1978  dam = (dam > (rndm(0, 99))) ? 1 : 0;
1979  }
1980 
1981  /* AT_CHAOS here is a weapon or monster. Spells are handled by hit_map
1982  * We don't use shuffle_attack(), because that changes the it in the
1983  * creature structure, and thus is permanent until fix_object() is
1984  * called again. Chaos should change on each attack.
1985  */
1986  if (type&AT_CHAOS) {
1987  type = ATTACKS[RANDOM()%(sizeof(ATTACKS)/sizeof(*ATTACKS))].attacktype|AT_MAGIC;
1988  }
1989 
1990  /* Holyword is really an attacktype modifier (like magic is). If
1991  * holyword is part of an attacktype, then make sure the creature is
1992  * a proper match, otherwise no damage.
1993  */
1994  if (type&AT_HOLYWORD) {
1995  const object *god;
1996 
1997  if ((!hitter->slaying || (!(op->race && strstr(hitter->slaying, op->race))
1998  && !(op->name && strstr(hitter->slaying, op->name))))
1999  && (!QUERY_FLAG(op, FLAG_UNDEAD) || (hitter->title != NULL
2000  && (god = find_god(determine_god(hitter))) != NULL
2001  && god->race != NULL
2002  && strstr(god->race, undead_name) != NULL)))
2003  return 0;
2004  }
2005 
2006  maxattacktype = type; /* initialize this to something */
2007  for (attacknum = 0; attacknum < NROFATTACKS; attacknum++) {
2008  int attacktype;
2009 
2010  attacktype = 1<<attacknum;
2011 
2012  /* Magic isn't really a true attack type - it gets combined with other
2013  * attack types. As such, skip it over. However, if magic is
2014  * the only attacktype in the group, then still attack with it
2015  */
2016  if (attacktype == AT_MAGIC && (type&~AT_MAGIC))
2017  continue;
2018 
2019  /* Go through and hit the player with each attacktype, one by one.
2020  * hit_with_one_attacktype only figures out the damage, doesn't inflict
2021  * it. It will do the appropriate action for attacktypes with
2022  * effects (slow, paralization, etc.
2023  */
2024  if (type&attacktype) {
2025  ndam = hit_with_one_attacktype(op, hitter, dam, attacknum);
2026  /* the >= causes us to prefer messages from special attacks, if
2027  * the damage is equal.
2028  */
2029  if (ndam >= maxdam) {
2030  maxdam = ndam;
2031  maxattacktype = 1<<attacknum;
2032  }
2033  /* Special case: death attack always deals all damage, as it should kill the monster
2034  * right away. */
2035  if (attacktype == AT_DEATH && ndam > 0)
2036  full_hit = 1;
2037  }
2038  }
2039 
2040 
2041  /* if this is friendly fire then do a set % of damage only
2042  * Note - put a check in to make sure this attack is actually
2043  * doing damage - otherwise, the +1 in the coe below will make
2044  * an attack do damage before when it otherwise didn't
2045  * Only reduce damage if not on battlground - if in arena, do
2046  * full damage. Note that it is intentional that the check for
2047  * battleground is inside the friendlyfire if statement - op_on_battleground()
2048  * is a fairly costly function to call, and we don't want to call it for
2049  * every attack - by doing it only for friendlyfire, it shouldn't get called
2050  * that often
2051  */
2052  friendlyfire = friendly_fire(op, hitter);
2053  if (friendlyfire && maxdam) {
2054  if (!op_on_battleground(op, NULL, NULL, NULL)) {
2055  maxdam = ((dam*settings.set_friendly_fire)/100)+1;
2056 #ifdef ATTACK_DEBUG
2057  LOG(llevDebug, "Friendly fire (type:%d setting: %d%) did %d damage dropped to %d\n", friendlyfire, settings.set_friendly_fire, dam, maxdam);
2058 #endif
2059  }
2060  }
2061 
2062  if (!full_hit) {
2063  archetype *at;
2064  unsigned int area = 0;
2065  for (at = op->arch; at != NULL; at = at->more)
2066  area++;
2067 
2068  /* basically: maxdam /= area; we try to "simulate" a float
2069  value-effect */
2070  unsigned int remainder = 100*(maxdam%area)/area;
2071  maxdam /= area;
2072  if (RANDOM()%100 < remainder)
2073  maxdam++;
2074  }
2075 
2076 #ifdef ATTACK_DEBUG
2077  LOG(llevDebug, "Attacktype %d did %d damage\n", type, maxdam);
2078 #endif
2079 
2080  owner = object_get_owner(hitter);
2081  if (owner != NULL) {
2082  if (op->enemy != hitter)
2083  object_set_enemy(op, owner);
2084  } else if (QUERY_FLAG(hitter, FLAG_ALIVE))
2085  if (op->enemy == NULL || rndm(1, 20) == 0)
2087 
2088  if (QUERY_FLAG(op, FLAG_UNAGGRESSIVE) && op->type != PLAYER) {
2089  /* The unaggressives look after themselves 8) */
2092  }
2093 
2094  if (magic && did_make_save(op, op->level, 0))
2095  maxdam = maxdam/2;
2096 
2097  attack_message(maxdam, maxattacktype, op, hitter);
2098 
2099  op->stats.hp -= maxdam;
2100 
2101  /* Eneq(@csd.uu.se): Check to see if monster runs away. */
2102  if (op->stats.hp >= 0
2103  && (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER)
2104  && op->stats.hp < (int16_t)((int32_t)op->run_away * op->stats.maxhp / 100)) {
2105  if (QUERY_FLAG(op, FLAG_MONSTER))
2107  else
2109  }
2110 
2111  if (QUERY_FLAG(op, FLAG_TEAR_DOWN)) {
2112  if (maxdam)
2113  tear_down_wall(op);
2114  return maxdam; /* nothing more to do for wall */
2115  }
2116 
2117  /* See if the creature has been killed */
2118  rtn_kill = kill_object(op, maxdam, hitter);
2119 
2120  // If the attacker has one_hit, it dies after attacking.
2121  // This used to be part of the ghosthit attacktype, but is now a separate
2122  // flag. Note that the attacker is removed whether or not it killed (or
2123  // even damaged) the target.
2124  if (QUERY_FLAG(hitter, FLAG_ONE_HIT)) {
2129  }
2130 
2131  // Target is dead? Nothing else to do.
2132  if (rtn_kill != -1)
2133  return rtn_kill;
2134 
2135  // If it survived, handle splitting monsters here.
2137  change_object(op);
2138  }
2139 
2140  return maxdam;
2141 }
2142 
2153 static void poison_living(object *op, object *hitter, int dam) {
2154  archetype *at = find_archetype("poisoning");
2155  if (at == NULL) {
2156  return;
2157  }
2158  object *tmp = arch_present_in_ob(at, op);
2159  const char *skill;
2160 
2161  if (tmp == NULL) {
2162  tmp = arch_to_object(at);
2163  if (tmp == NULL)
2164  LOG(llevError, "Failed to clone arch poisoning.\n");
2165  else {
2167  /* peterm: give poisoning some teeth. It should
2168  * be able to kill things better than it does:
2169  * damage should be dependent something--I choose to
2170  * do this: if it's a monster, the damage from the
2171  * poisoning goes as the level of the monster/2.
2172  * If anything else, goes as damage.
2173  */
2174 
2176  tmp->stats.dam += hitter->level/2;
2177  else
2178  tmp->stats.dam = dam;
2179 
2180  object_copy_owner(tmp, hitter); /* so we get credit for poisoning kills */
2181  skill = hitter->skill;
2182  if (!skill && hitter->chosen_skill)
2183  skill = hitter->chosen_skill->name;
2184 
2185  if (skill && skill != tmp->skill) {
2186  if (tmp->skill)
2187  free_string(tmp->skill);
2188  tmp->skill = add_refcount(skill);
2189  }
2190 
2191  tmp->stats.food += dam; /* more damage, longer poisoning */
2192 
2193  if (op->type == PLAYER) {
2194  /* player looses stats, maximum is -10 of each */
2195  tmp->stats.Con = MAX(-(dam/4+1), -10);
2196  tmp->stats.Str = MAX(-(dam/3+2), -10);
2197  tmp->stats.Dex = MAX(-(dam/6+1), -10);
2198  tmp->stats.Int = MAX(-dam/7, -10);
2200  fix_object(op);
2203  "You suddenly feel very ill.");
2204  }
2205  if (hitter->type == PLAYER)
2208  "You poison %s.",
2209  op->name);
2210  else {
2211  object *owner;
2212 
2213  owner = object_get_owner(hitter);
2214  if (owner != NULL && owner->type == PLAYER)
2215  draw_ext_info_format(NDI_UNIQUE, 0, owner,
2217  "Your %s poisons %s.",
2218  hitter->name, op->name);
2219  }
2220  tmp->speed_left = 0;
2221  }
2222  } else
2223  tmp->stats.food++;
2224 }
2225 
2226 int slow_living_by(object *op, const int speed_penalty) {
2227  archetype *at = find_archetype("slowness");
2228  if (at == NULL) {
2229  return 0;
2230  }
2231  object* tmp = arch_present_in_ob(at, op);
2232  int ret;
2233  if (tmp == NULL) {
2234  tmp = arch_to_object(at);
2235  tmp->stats.exp = -speed_penalty;
2237  ret = 1;
2238  }
2239  // If we are hitting for more intense slowness, override the old one.
2240  else if (tmp->stats.exp > -speed_penalty) {
2241  tmp->stats.exp = -speed_penalty;
2242  tmp->stats.food -= 3; // But also reduce the duration to compensate.
2243  ret = 2;
2244  } else {
2245  tmp->stats.food++;
2246  ret = 3;
2247  }
2249  tmp->speed_left = 0;
2250  fix_object(op);
2251  return ret;
2252 }
2253 
2265 static void slow_living(object *op, object *hitter, int dam) {
2266  // Used to calculate the speed penalty of the slow attempt
2267  int speed_penalty;
2268  (void)dam;
2269 
2275  // Higher level slow effects make you slower.
2276  speed_penalty = hitter->level - op->level + random_roll(1, 5, hitter, PREFER_LOW);
2277  // Resistance to slow will also affect how much you are slowed by.
2278  speed_penalty = speed_penalty * (100-op->resist[ATNR_SLOW]) / 100;
2279  // Make sure we actually have a penalty amount. We can assume that op is not immune in this method.
2280  if (speed_penalty < 1)
2281  speed_penalty = 1;
2282  else if (speed_penalty > 30) // Data barrier for stats.exp is 127, but that is huge slowness. Pick something less than that.
2283  speed_penalty = 30;
2284  switch (slow_living_by(op, speed_penalty)) {
2285  case 1:
2288  "The world suddenly moves very fast!");
2289  break;
2290  case 2:
2293  "The world moves even faster!");
2294  break;
2295 
2296  }
2297 }
2298 
2310 void confuse_living(object *op, object *hitter, int dam) {
2311  object *tmp;
2312  int maxduration;
2313  (void)hitter;
2314  (void)dam;
2315 
2316  tmp = object_present_in_ob_by_name(FORCE, "confusion", op);
2317  if (!tmp) {
2320  }
2323 
2324  /* Duration added per hit and max. duration of confusion both depend
2325  * on the player's resistance
2326  */
2327  tmp->speed = 0.05;
2328  tmp->subtype = FORCE_CONFUSION;
2329  tmp->duration = 8+MAX(1, 5*(100-op->resist[ATNR_CONFUSION])/100);
2330  if (tmp->name)
2331  free_string(tmp->name);
2332  tmp->name = add_string("confusion");
2333  maxduration = MAX(2, 30*(100-op->resist[ATNR_CONFUSION])/100);
2334  if (tmp->duration > maxduration)
2335  tmp->duration = maxduration;
2336 
2337  if (op->type == PLAYER && !QUERY_FLAG(op, FLAG_CONFUSED))
2339  "You suddenly feel very confused!");
2341 }
2342 
2353 void blind_living(object *op, object *hitter, int dam) {
2354  object *tmp, *owner;
2355  char victim[MAX_BUF];
2356 
2357  /* Save some work if we know it isn't going to affect the player */
2358  if (op->resist[ATNR_BLIND] == 100)
2359  return;
2360 
2362  if (!tmp) {
2363  tmp = create_archetype("blindness");
2366  /* use floats so we don't lose too much precision due to rounding errors.
2367  * speed is a float anyways.
2368  */
2369  tmp->speed = tmp->speed*(100.0-(float)op->resist[ATNR_BLIND])/100;
2370 
2372  change_abil(op, tmp); /* Mostly to display any messages */
2373  fix_object(op); /* This takes care of some other stuff */
2374 
2375  owner = object_get_owner(hitter);
2376  if (owner == NULL)
2377  owner = hitter;
2378 
2381  "Your attack blinds %s!",
2382  victim);
2383  }
2384  tmp->stats.food += dam;
2385  if (tmp->stats.food > 10)
2386  tmp->stats.food = 10;
2387 }
2388 
2397 void paralyze_living(object *op, int dam) {
2398  float effect, max;
2399 
2400  /* Do this as a float - otherwise, rounding might very well reduce this to 0 */
2401  effect = (float)dam*3.0*(100.0-op->resist[ATNR_PARALYZE])/100;
2402 
2403  if (effect == 0)
2404  return;
2405 
2406  op->speed_left -= FABS(op->speed)*effect;
2407 
2408  /* max number of ticks to be affected for. */
2409  max = (100-op->resist[ATNR_PARALYZE])/2;
2410  if (op->speed_left < -(FABS(op->speed)*max))
2411  op->speed_left = (float)-(FABS(op->speed)*max);
2412  // Set a paralyze flag and print a message to a player if the flag isn't set;
2413  // this tells the player that he/she has been hit by a paralysis attack.
2414  if (!QUERY_FLAG(op, FLAG_PARALYZED)) {
2416  if (op->type == PLAYER)
2418  "You limbs stop moving!");
2419  }
2420  /* If the flag is already set, then the paralysis is merely extended.
2421  * At this point, we do nothing.
2422  * It may be worthwhile to give players another message on paralysis extensions.
2423  *
2424  * Daniel Hawkins 2017-08-22
2425  */
2426 }
2427 
2448 static void deathstrike_living(object *op, object *hitter, int *dam) {
2449  int atk_lev, def_lev, kill_lev, roll;
2450 
2451  if (hitter->slaying) {
2452  if (!((QUERY_FLAG(op, FLAG_UNDEAD) && strstr(hitter->slaying, undead_name))
2453  || (op->race && strstr(op->race, hitter->slaying))))
2454  {
2455  *dam = 0; // Don't do damage if we aren't deathstriking them.
2456  return;
2457  }
2458  } else
2459  if (QUERY_FLAG(op, FLAG_UNDEAD))
2460  {
2461  *dam = 0; // Don't do damage if we aren't deathstriking them.
2462  return;
2463  }
2464 
2465  def_lev = op->level;
2466  if (def_lev < 1) {
2467  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);
2468  def_lev = 1;
2469  }
2470  /*
2471  * Redo this calculation -- you could essentially only kill creatures less than half your level,
2472  * making death extremely weak at high levels.
2473  * Refactoring to use a d50 roll with a fairly high DC (still dependent on level)
2474  * Also, toss in a resistance-based hit modifier.
2475  * Higher resistance requires higher levels in order to kill with a death attack.
2476  *
2477  * Daniel Hawkins 2018-05-21
2478  */
2479  atk_lev = (hitter->chosen_skill ? hitter->chosen_skill->level : hitter->level);
2480  /* LOG(llevDebug, "Deathstrike - attack level %d, defender level %d\n", atk_lev, def_lev); */
2481 
2482  roll = random_roll(1, 50, hitter, PREFER_HIGH);
2483  kill_lev = roll - 48 + atk_lev; // Use 49+ as a kill for same level and no resistance; that's 4% + 2%/level
2484  kill_lev = kill_lev * (100 - op->resist[ATNR_DEATH]) / 100; // Do not compress to *= for roundoff reasons.
2485 
2486  // If we hit, then kill them. Otherwise, damage is 0.
2487  if (kill_lev > def_lev) {
2488  *dam = op->stats.hp+10; /* take all hp. they can still save for 1/2 */
2489  /* I think this doesn't really do much. Because of
2490  * integer rounding, this only makes any difference if the
2491  * attack level is double the defender level.
2492  */
2493  *dam *= kill_lev/def_lev;
2494  }
2495  else {
2496  *dam = 0; /* no harm done */
2497  }
2498 }
2499 
2512 static void thrown_item_effect(object *hitter, object *victim) {
2513  if (!QUERY_FLAG(hitter, FLAG_ALIVE)) {
2514  /* May not need a switch for just 2 types, but this makes it
2515  * easier for expansion.
2516  */
2517  switch (hitter->type) {
2518  case POTION:
2519  /* should player get a save throw instead of checking magic protection? */
2522  && (victim->resist[ATNR_MAGIC] < 60))
2523  (void)ob_apply(hitter, victim, 0);
2524  break;
2525 
2526  case POISON: /* poison drinks */
2527  /* As with potions, should monster get a save? */
2530  && (victim->resist[ATNR_POISON] < 60))
2531  (void)ob_apply(victim, hitter, 0);
2532  break;
2533 
2534  /* Removed case statements that did nothing.
2535  * food may be poisonous, but monster must be willing to eat it,
2536  * so we don't handle it here.
2537  * Containers should perhaps break open, but that code was disabled.
2538  */
2539  }
2540  }
2541 }
2542 
2552 static int adj_attackroll(object *hitter, object *target) {
2553  object *attacker = hitter;
2554  int adjust = 0;
2555 
2556  /* safety */
2557  if (!target || !hitter || !hitter->map || !target->map || !on_same_map(hitter, target)) {
2558  LOG(llevError, "BUG: adj_attackroll(): hitter and target not on same map\n");
2559  return 0;
2560  }
2561 
2562  /* aimed missiles use the owning object's sight */
2563  if (is_aimed_missile(hitter)) {
2564  attacker = object_get_owner(hitter);
2565  if (attacker == NULL)
2566  attacker = hitter;
2567  /* A player who saves but hasn't quit still could have objects
2568  * owned by him - need to handle that case to avoid crashes.
2569  */
2570  if (QUERY_FLAG(attacker, FLAG_REMOVED))
2571  attacker = hitter;
2572  } else if (!QUERY_FLAG(hitter, FLAG_ALIVE))
2573  return 0;
2574 
2575  /* determine the condtions under which we make an attack.
2576  * Add more cases, as the need occurs. */
2577 
2578  if (!monster_can_see_enemy(attacker, target)) {
2579  /* target is unseen */
2580  if (target->invisible || QUERY_FLAG(attacker, FLAG_BLIND))
2581  adjust -= 10;
2582  /* dark map penalty for the hitter, though xray can help for a player */
2583  else if (target->map && target->map->darkness > 0 && !monster_stand_in_light(target) && (hitter->type != PLAYER || !player_can_view(hitter, target)))
2584  adjust -= target->map->darkness;
2585  }
2586 
2587  if (QUERY_FLAG(attacker, FLAG_SCARED))
2588  adjust -= 3;
2589 
2590  if (QUERY_FLAG(target, FLAG_UNAGGRESSIVE))
2591  adjust += 1;
2592 
2593  if (QUERY_FLAG(target, FLAG_SCARED))
2594  adjust += 1;
2595 
2596  if (QUERY_FLAG(attacker, FLAG_CONFUSED))
2597  adjust -= 3;
2598 
2599  /* if we attack at a different 'altitude' its harder
2600  * Note - only make this adjustment if the target actually
2601  * has a move type. Doors don't (they don't move), and
2602  * this would evaluate as true. If anything, something without
2603  * a move type should be easier to hit.
2604  */
2605  if (target->move_type && (attacker->move_type&target->move_type) == 0)
2606  adjust -= 2;
2607 
2608  return adjust;
2609 }
2610 
2619 static int is_aimed_missile(object *op) {
2620  /* I broke what used to be one big if into a few nested
2621  * ones so that figuring out the logic is at least possible.
2622  */
2623  if (op && (op->move_type&MOVE_FLYING)) {
2624  if (op->type == ARROW || op->type == THROWN_OBJ)
2625  return 1;
2626  else if (op->type == SPELL_EFFECT
2627  && (op->subtype == SP_BULLET || op->subtype == SP_EXPLOSION))
2628  return 1;
2629  }
2630  return 0;
2631 }
ATM_BLUD
#define ATM_BLUD
Definition: attack.h:39
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:70
object_value_set
bool object_value_set(const object *op, const char *const key)
Definition: object.cpp:4367
give.next
def next
Definition: give.py:44
did_make_save_item
static int did_make_save_item(object *op, int type, object *originator)
Definition: attack.cpp:104
get_attack_message_type
static uint8_t get_attack_message_type(int type, const object *op, const object *hitter)
Definition: attack.cpp:463
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:524
PLAYER
@ PLAYER
Definition: object.h:112
object_get_owner
object * object_get_owner(object *op)
Definition: object.cpp:804
ATNR_PARALYZE
#define ATNR_PARALYZE
Definition: attack.h:61
area
Python Guilds Quick outline Add a guild(mapmakers) this is still a problem *after dropping the token to gain access to the stove area
Definition: README.txt:5
global.h
deathstrike_living
static void deathstrike_living(object *op, object *hitter, int *dam)
Definition: attack.cpp:2448
cancellation
static void cancellation(object *op)
Definition: attack.cpp:51
ATNR_CANCELLATION
#define ATNR_CANCELLATION
Definition: attack.h:64
settings
struct Settings settings
Definition: init.cpp:139
INS_NO_WALK_ON
#define INS_NO_WALK_ON
Definition: object.h:573
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
remove_friendly_object
void remove_friendly_object(object *op)
Definition: friend.cpp:52
stick_arrow
static int stick_arrow(object *op, object *tmp)
Definition: attack.cpp:952
AT_POISON
#define AT_POISON
Definition: attack.h:86
ATNR_INTERNAL
#define ATNR_INTERNAL
Definition: attack.h:72
AT_MAGIC
#define AT_MAGIC
Definition: attack.h:77
FLAG_CONFUSED
#define FLAG_CONFUSED
Definition: define.h:311
find_applied_skill_by_name
object * find_applied_skill_by_name(const object *op, const char *name)
Definition: living.cpp:1921
abort_attack
static int abort_attack(object *target, object *hitter, int simple_attack)
Definition: attack.cpp:724
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:232
ATM_KARATE
#define ATM_KARATE
Definition: attack.h:29
SYMPTOM
@ SYMPTOM
Definition: object.h:250
WAND
@ WAND
Definition: object.h:225
MSG_TYPE_ADMIN_PLAYER
#define MSG_TYPE_ADMIN_PLAYER
Definition: newclient.h:485
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
FLAG_UNDEAD
#define FLAG_UNDEAD
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
archetype::more
archetype * more
Definition: object.h:477
FLAG_GENERATOR
#define FLAG_GENERATOR
Definition: define.h:248
monster_stand_in_light
int monster_stand_in_light(object *op)
Definition: monster.cpp:2723
GIRDLE
@ GIRDLE
Definition: object.h:228
ATNR_ACID
#define ATNR_ACID
Definition: attack.h:55
player::golem_count
uint32_t golem_count
Definition: player.h:119
diamondslots.x
x
Definition: diamondslots.py:15
hit_player
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.cpp:1902
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
SK_CLAWING
@ SK_CLAWING
Definition: skills.h:50
map_find_by_archetype
object * map_find_by_archetype(mapstruct *m, int x, int y, const archetype *at)
Definition: object.cpp:3109
name_to_material
materialtype_t * name_to_material(const char *name)
Definition: utils.cpp:248
object_merge
object * object_merge(object *op, object *top)
Definition: object.cpp:2046
make_visible
void make_visible(object *op)
Definition: player.cpp:3956
op_on_battleground
int op_on_battleground(object *op, int *x, int *y, archetype **trophy)
Definition: player.cpp:4249
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:601
object_set_enemy
void object_set_enemy(object *op, object *enemy)
Definition: object.cpp:915
object::invisible
int16_t invisible
Definition: object.h:370
TRAP
@ TRAP
Definition: object.h:246
object::x
int16_t x
Definition: object.h:335
player::peaceful
uint32_t peaceful
Definition: player.h:146
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.cpp:205
get_attack_message
static void get_attack_message(int dam, int type, const object *op, const object *hitter, char *buf1, char *buf2)
Definition: attack.cpp:569
M_IRON
#define M_IRON
Definition: material.h:15
Settings::pk_luck_penalty
int16_t pk_luck_penalty
Definition: global.h:257
ATM_PIERCE
#define ATM_PIERCE
Definition: attack.h:33
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
object::map
struct mapstruct * map
Definition: object.h:305
arch_present_in_ob
object * arch_present_in_ob(const archetype *at, const object *op)
Definition: object.cpp:3213
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.cpp:840
object_handle_death_animation
void object_handle_death_animation(object *op)
Definition: object.cpp:5394
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:394
SP_CONE
#define SP_CONE
Definition: spells.h:81
commongive.inv
inv
Definition: commongive.py:29
ATM_FIRE
#define ATM_FIRE
Definition: attack.h:27
ATNR_SLOW
#define ATNR_SLOW
Definition: attack.h:60
SET_ANIMATION
#define SET_ANIMATION(ob, newanim)
Definition: global.h:162
time
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
Definition: arch-handbook.txt:206
AMULET
@ AMULET
Definition: object.h:144
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Definition: object.h:148
play_sound_map
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.cpp:113
object::count
tag_t count
Definition: object.h:307
Ice.tmp
int tmp
Definition: Ice.py:207
RUNE
@ RUNE
Definition: object.h:245
ATM_BASIC
#define ATM_BASIC
Definition: attack.h:28
FLAG_SCARED
#define FLAG_SCARED
Definition: define.h:271
fix_object
void fix_object(object *op)
Definition: living.cpp:1125
SK_KARATE
@ SK_KARATE
Definition: skills.h:38
FLAG_ONE_HIT
#define FLAG_ONE_HIT
Definition: define.h:343
NDI_RED
#define NDI_RED
Definition: newclient.h:234
blank_face
const Face * blank_face
Definition: image.cpp:36
shuffle_attack
void shuffle_attack(object *op)
Definition: spell_util.cpp:1033
confuse_living
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.cpp:2310
TRANSPORT
@ TRANSPORT
Definition: object.h:113
ATNR_PHYSICAL
#define ATNR_PHYSICAL
Definition: attack.h:49
object::enemy
object * enemy
Definition: object.h:391
WEAP_WHIP
#define WEAP_WHIP
Definition: define.h:83
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
hit_map
int hit_map(object *op, int dir, uint32_t type, int full_hit)
Definition: attack.cpp:355
friendly_fire
int friendly_fire(object *op, object *hitter)
Definition: attack.cpp:1853
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:34
partylist
Definition: party.h:10
materials
std::vector< materialtype_t * > materials
Definition: init.cpp:128
MAXATTACKMESS
#define MAXATTACKMESS
Definition: attack.h:19
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
object::level
int16_t level
Definition: object.h:361
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:292
FLAG_BLIND
#define FLAG_BLIND
Definition: define.h:336
AT_LIFE_STEALING
#define AT_LIFE_STEALING
Definition: attack.h:100
buf
StringBuffer * buf
Definition: readable.cpp:1552
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:404
POISONING
@ POISONING
Definition: object.h:223
MAX
#define MAX(x, y)
Definition: compat.h:24
Settings::simple_exp
uint8_t simple_exp
Definition: global.h:263
SOUND_TYPE_HIT
#define SOUND_TYPE_HIT
Definition: newclient.h:326
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
ATNR_CONFUSION
#define ATNR_CONFUSION
Definition: attack.h:54
find_god
const object * find_god(const char *name)
Definition: holy.cpp:317
ATNR_HOLYWORD
#define ATNR_HOLYWORD
Definition: attack.h:70
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
attack_ob
int attack_ob(object *op, object *hitter)
Definition: attack.cpp:937
hit_with_one_attacktype
static int hit_with_one_attacktype(object *op, object *hitter, int dam, uint32_t attacknum)
Definition: attack.cpp:1270
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
blind_living
void blind_living(object *op, object *hitter, int dam)
Definition: attack.cpp:2353
MSG_TYPE_ATTACK_DID_KILL
#define MSG_TYPE_ATTACK_DID_KILL
Definition: newclient.h:604
ATM_STAB
#define ATM_STAB
Definition: attack.h:36
ATNR_BLIND
#define ATNR_BLIND
Definition: attack.h:71
object::y
int16_t y
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:425
AT_CHAOS
#define AT_CHAOS
Definition: attack.h:94
Settings::set_friendly_fire
uint16_t set_friendly_fire
Definition: global.h:275
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
save_throw_object
void save_throw_object(object *op, uint32_t type, object *originator)
Definition: attack.cpp:202
object_update
void object_update(object *op, int action)
Definition: object.cpp:1429
object::contr
struct player * contr
Definition: object.h:284
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
FLAG_TEAR_DOWN
#define FLAG_TEAR_DOWN
Definition: define.h:279
object::chosen_skill
object * chosen_skill
Definition: object.h:396
SP_BULLET
#define SP_BULLET
Definition: spells.h:79
disinfect.map
map
Definition: disinfect.py:4
POISON
@ POISON
Definition: object.h:118
USING_SKILL
#define USING_SKILL(op, skill)
Definition: skills.h:85
object_find_by_type2
object * object_find_by_type2(const object *who, int type1, int type2)
Definition: object.cpp:4028
FLAG_WAS_WIZ
#define FLAG_WAS_WIZ
Definition: define.h:234
WEAP_CLEAVE
#define WEAP_CLEAVE
Definition: define.h:80
is_criminal
bool is_criminal(object *op)
Definition: player.cpp:312
add_refcount
sstring add_refcount(sstring str)
Definition: shstr.cpp:210
free_no_drop
int free_no_drop(object *op)
Definition: time.cpp:565
adj_attackroll
static int adj_attackroll(object *hitter, object *target)
Definition: attack.cpp:2552
determine_god
const char * determine_god(object *op)
Definition: gods.cpp:55
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.cpp:305
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Definition: newclient.h:554
ATM_SUFFER
#define ATM_SUFFER
Definition: attack.h:41
object_present_in_ob_by_name
object * object_present_in_ob_by_name(int type, const char *str, const object *op)
Definition: object.cpp:3194
MSG_TYPE_APPLY_SUCCESS
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:592
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:592
AT_COLD
#define AT_COLD
Definition: attack.h:80
POTION
@ POTION
Definition: object.h:116
FLAG_RUN_AWAY
#define FLAG_RUN_AWAY
Definition: define.h:280
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
player::ranges
object * ranges[range_size]
Definition: player.h:116
check_physically_infect
void check_physically_infect(object *victim, object *hitter)
Definition: disease.cpp:663
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.cpp:667
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
apply_anim_suffix
void apply_anim_suffix(object *who, const char *suffix)
Definition: anim.cpp:150
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
HEAD
#define HEAD(op)
Definition: object.h:598
commit_crime
void commit_crime(object *op, const char *description)
Definition: player.cpp:307
MSG_TYPE_ATTACK_PET_DIED
#define MSG_TYPE_ATTACK_PET_DIED
Definition: newclient.h:605
ROD
@ ROD
Definition: object.h:114
CONTAINER
@ CONTAINER
Definition: object.h:236
object::move_type
MoveType move_type
Definition: object.h:434
WEAP_HIT
#define WEAP_HIT
Definition: define.h:77
FLAG_FREED
#define FLAG_FREED
Definition: define.h:233
hit_with_arrow
object * hit_with_arrow(object *op, object *victim)
Definition: attack.cpp:979
socket_struct::host
char * host
Definition: newserver.h:100
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:388
MSG_TYPE_ATTACK
#define MSG_TYPE_ATTACK
Definition: newclient.h:398
material.h
MOVE_FLYING
#define MOVE_FLYING
Definition: define.h:395
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1344
FLAG_NO_DAMAGE
#define FLAG_NO_DAMAGE
Definition: define.h:356
slow_living_by
int slow_living_by(object *op, const int speed_penalty)
Definition: attack.cpp:2226
object::type
uint8_t type
Definition: object.h:348
INS_NO_MERGE
#define INS_NO_MERGE
Definition: object.h:571
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
living::dam
int16_t dam
Definition: living.h:46
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.cpp:2909
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:304
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
fix_stopped_item
void fix_stopped_item(object *op, mapstruct *map, object *originator)
Definition: time.cpp:495
GOLEM
@ GOLEM
Definition: object.h:150
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.cpp:2168
fix_stopped_arrow
object * fix_stopped_arrow(object *op)
Definition: time.cpp:512
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:14
archetype
Definition: object.h:474
ATNR_POISON
#define ATNR_POISON
Definition: attack.h:59
sproto.h
ARROW
@ ARROW
Definition: object.h:122
ATNR_DEATH
#define ATNR_DEATH
Definition: attack.h:66
MSG_TYPE_VICTIM_WAS_HIT
#define MSG_TYPE_VICTIM_WAS_HIT
Definition: newclient.h:640
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:750
GET_ANIM_ID
#define GET_ANIM_ID(ob)
Definition: global.h:165
Settings::casting_time
uint8_t casting_time
Definition: global.h:270
RING
@ RING
Definition: object.h:190
materialtype_t::save
int8_t save[NROFATTACKS]
Definition: material.h:36
BLINDNESS
@ BLINDNESS
Definition: object.h:152
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:231
weight
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your and press< Return > You can also use say if you feel like typing a little extra Other NPCs may not speak to but display intelligence with their movement Some monsters can be and may attack the nearest of your enemies Others can be in that they follow you around and help you in your quest to kill enemies and find treasure SPECIAL ITEMS There are many special items which can be found in of these the most important may be the signs all a player must do is apply the handle In the case of the player must move items over the button to hold it down Some of the larger buttons may need very large items to be moved onto before they can be activated Gates and locked but be for you could fall down into a pit full of ghosts or dragons and not be able to get back out Break away sometimes it may be worth a player s time to test the walls of a map for secret doors Fire such as missile weapons and spells you will notice them going up in smoke ! So be careful not to destroy valuable items Spellbooks sometimes a player can learn the other times they cannot There are many different types of books and scrolls out there Improve item have lower weight
Definition: survival-guide.txt:100
FLAG_SPLITTING
#define FLAG_SPLITTING
Definition: define.h:266
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.cpp:2095
ATTACKS
Chaos_Attacks ATTACKS[22]
Definition: init.cpp:81
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
undead_name
const char * undead_name
Definition: init.cpp:125
FLAG_HITBACK
#define FLAG_HITBACK
Definition: define.h:267
get_attack_mode
static int get_attack_mode(object **target, object **hitter, int *simple_attack)
Definition: attack.cpp:688
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:248
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2170
MAX_BUF
#define MAX_BUF
Definition: define.h:35
blame
object * blame(object *op)
Definition: attack.cpp:176
is_wraith_pl
int is_wraith_pl(object *op)
Definition: player.cpp:173
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
object_present_in_ob
object * object_present_in_ob(uint8_t type, const object *op)
Definition: object.cpp:3159
object::current_weapon
object * current_weapon
Definition: object.h:380
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
RANDOM
#define RANDOM()
Definition: define.h:644
ATNR_FIRE
#define ATNR_FIRE
Definition: attack.h:51
thrown_item_effect
static void thrown_item_effect(object *, object *)
Definition: attack.cpp:2512
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
FLAG_KNOWN_MAGICAL
#define FLAG_KNOWN_MAGICAL
Definition: define.h:319
change_abil
int change_abil(object *op, object *tmp)
Definition: living.cpp:394
OUT_OF_REAL_MAP
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:216
attack_mess
attackmess_t attack_mess[NROFATTACKMESS][MAXATTACKMESS]
Definition: init.cpp:78
living::Wis
int8_t Wis
Definition: living.h:36
ATM_WRAITH_FEED
#define ATM_WRAITH_FEED
Definition: attack.h:42
attack_message
static void attack_message(int dam, int type, object *op, object *hitter)
Definition: attack.cpp:608
sounds.h
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
object_decrease_nrof
object * object_decrease_nrof(object *op, uint32_t i)
Definition: object.cpp:2667
monster_can_see_enemy
int monster_can_see_enemy(object *op, object *enemy)
Definition: monster.cpp:2766
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
llevInfo
@ llevInfo
Definition: logger.h:12
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
object::slaying
sstring slaying
Definition: object.h:327
MSG_TYPE_ATTACK_PET_HIT
#define MSG_TYPE_ATTACK_PET_HIT
Definition: newclient.h:602
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:25
object::name
sstring name
Definition: object.h:319
WEAP_STAB
#define WEAP_STAB
Definition: define.h:82
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
tear_down_wall
static void tear_down_wall(object *op)
Definition: attack.cpp:1114
WEAP_PIERCE
#define WEAP_PIERCE
Definition: define.h:79
SK_WRAITH_FEED
@ SK_WRAITH_FEED
Definition: skills.h:57
MSG_TYPE_VICTIM_WAS_PUSHED
#define MSG_TYPE_VICTIM_WAS_PUSHED
Definition: newclient.h:646
ATNR_DEPLETE
#define ATNR_DEPLETE
Definition: attack.h:65
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.cpp:299
reputation.victim
victim
Definition: reputation.py:14
DISEASE
@ DISEASE
Definition: object.h:249
attacks
const char *const attacks[NROFATTACKS]
Definition: living.cpp:129
mapstruct
Definition: map.h:314
object::env
object * env
Definition: object.h:301
drain_stat
void drain_stat(object *op)
Definition: living.cpp:716
give.op
op
Definition: give.py:33
NDI_ALL
#define NDI_ALL
Definition: newclient.h:252
ATM_WHIP
#define ATM_WHIP
Definition: attack.h:37
monster_npc_call_help
void monster_npc_call_help(object *op)
Definition: monster.cpp:2014
WEAP_CRUSH
#define WEAP_CRUSH
Definition: define.h:84
AT_DEPLETE
#define AT_DEPLETE
Definition: attack.h:92
Floor.t
t
Definition: Floor.py:62
object::skill
sstring skill
Definition: object.h:329
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:266
ATM_ELEC
#define ATM_ELEC
Definition: attack.h:25
ATM_CRUSH
#define ATM_CRUSH
Definition: attack.h:38
object_get_materialtype
static void object_get_materialtype(object *op, materialtype_t **mt)
Definition: attack.cpp:75
get_turn_bonus
int get_turn_bonus(int stat)
Definition: living.cpp:2374
change_luck
void change_luck(object *op, int value)
Definition: living.cpp:796
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.cpp:359
did_make_save
int did_make_save(const object *op, int level, int bonus)
Definition: living.cpp:2282
set_materialname
void set_materialname(object *op)
Definition: utils.cpp:297
SPELL_EFFECT
@ SPELL_EFFECT
Definition: object.h:220
attack_ob_simple
static int attack_ob_simple(object *op, object *hitter, int base_dam, int wc)
Definition: attack.cpp:767
ATNR_MAGIC
#define ATNR_MAGIC
Definition: attack.h:50
MSG_TYPE_ATTACK_FUMBLE
#define MSG_TYPE_ATTACK_FUMBLE
Definition: newclient.h:603
diamondslots.y
y
Definition: diamondslots.py:16
on_same_map
int on_same_map(const object *op1, const object *op2)
Definition: map.cpp:2625
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
ob_apply
method_ret ob_apply(object *op, object *applier, int aflags)
Definition: ob_methods.cpp:44
hit_with_drain
static int hit_with_drain(object *op, object *hitter, int dam)
Definition: attack.cpp:1174
paralyze_living
void paralyze_living(object *op, int dam)
Definition: attack.cpp:2397
NUM_ANIMATIONS
#define NUM_ANIMATIONS(ob)
Definition: global.h:171
player_arrest
int player_arrest(object *who)
Definition: c_wiz.cpp:789
share_exp
void share_exp(object *op, int64_t exp, const char *skill, int flag)
Definition: living.cpp:2312
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
player::party
partylist * party
Definition: player.h:203
change_object
void change_object(object *op)
Definition: time.cpp:592
ATM_SLASH
#define ATM_SLASH
Definition: attack.h:32
make_face_from_files.int
int
Definition: make_face_from_files.py:32
IS_LIVE
#define IS_LIVE(op)
Definition: define.h:173
WEAP_SLICE
#define WEAP_SLICE
Definition: define.h:81
attackmess_t::level
int level
Definition: attack.h:119
UPD_NAME
#define UPD_NAME
Definition: newclient.h:307
AT_COUNTERSPELL
#define AT_COUNTERSPELL
Definition: attack.h:95
check_exp_loss
int64_t check_exp_loss(const object *op, int64_t exp)
Definition: living.cpp:2066
AT_ACID
#define AT_ACID
Definition: attack.h:82
Settings::pk_max_experience_percent
int pk_max_experience_percent
Definition: global.h:314
stop_item
object * stop_item(object *op)
Definition: time.cpp:455
AT_FEAR
#define AT_FEAR
Definition: attack.h:90
scare_creature
static void scare_creature(object *target, object *hitter)
Definition: attack.cpp:1163
skill
skill
Definition: arch-handbook.txt:585
process_object
void process_object(object *op)
Definition: time.cpp:796
object_remove
void object_remove(object *op)
Definition: object.cpp:1828
EVENT_ATTACKED
#define EVENT_ATTACKED
Definition: events.h:21
DOOR
@ DOOR
Definition: object.h:131
player_can_view
int player_can_view(object *pl, object *op)
Definition: player.cpp:4150
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
AT_CONFUSION
#define AT_CONFUSION
Definition: attack.h:81
SK_EXP_TOTAL
#define SK_EXP_TOTAL
Definition: skills.h:79
update_all_los
void update_all_los(const mapstruct *map, int x, int y)
Definition: los.cpp:595
object_copy_owner
void object_copy_owner(object *op, object *clone)
Definition: object.cpp:893
ATNR_COLD
#define ATNR_COLD
Definition: attack.h:53
AT_CANCELLATION
#define AT_CANCELLATION
Definition: attack.h:91
SK_EXP_ADD_SKILL
#define SK_EXP_ADD_SKILL
Definition: skills.h:78
player::socket
socket_struct * socket
Definition: player.h:107
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
materialtype_t
Definition: material.h:32
death_message.hitter
hitter
Definition: death_message.py:33
object::stats
living stats
Definition: object.h:378
Settings::pk_max_experience
int64_t pk_max_experience
Definition: global.h:313
player::title
char title[BIG_NAME]
Definition: player.h:184
put_in_icecube
static void put_in_icecube(object *op, object *originator)
Definition: attack.cpp:151
BIG_NAME
#define BIG_NAME
Definition: define.h:42
get_attack_message_for_attack_type
void get_attack_message_for_attack_type(int dam, uint8_t atm_type, const char *victim_name, char *buf1, char *buf2)
Definition: attack.cpp:538
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.cpp:299
EVENT_ATTACKS
#define EVENT_ATTACKS
Definition: events.h:22
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
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
SPELL
@ SPELL
Definition: object.h:219
AT_GHOSTHIT
#define AT_GHOSTHIT
Definition: attack.h:85
mapstruct::darkness
uint8_t darkness
Definition: map.h:336
FLAG_READY_WEAPON
#define FLAG_READY_WEAPON
Definition: define.h:334
EVENT_GKILL
#define EVENT_GKILL
Definition: events.h:42
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:397
ATM_CLAW
#define ATM_CLAW
Definition: attack.h:30
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
kill_object
static int kill_object(object *op, int dam, object *hitter)
Definition: attack.cpp:1582
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
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.cpp:3488
if
if(!(yy_init))
Definition: loader.c:2626
THROWN_OBJ
@ THROWN_OBJ
Definition: object.h:151
FLAG_SLEEP
#define FLAG_SLEEP
Definition: define.h:307
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:391
IS_ARROW
#define IS_ARROW(op)
Definition: define.h:178
ATM_DOOR
#define ATM_DOOR
Definition: attack.h:40
is_aimed_missile
static int is_aimed_missile(object *op)
Definition: attack.cpp:2619
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
MAX_FOOD
static const int32_t MAX_FOOD
Definition: define.h:461
FORCE
@ FORCE
Definition: object.h:229
AT_DRAIN
#define AT_DRAIN
Definition: attack.h:83
poison_living
static void poison_living(object *op, object *hitter, int dam)
Definition: attack.cpp:2153
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Definition: events.cpp:27
object_replace_insert_in_map
void object_replace_insert_in_map(const char *arch_string, object *op)
Definition: object.cpp:2588
llevDebug
@ llevDebug
Definition: logger.h:13
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
FORCE_NAME
#define FORCE_NAME
Definition: spells.h:169
ATM_DRAIN
#define ATM_DRAIN
Definition: attack.h:24
ATNR_FEAR
#define ATNR_FEAR
Definition: attack.h:63
slow_living
static void slow_living(object *op, object *hitter, int dam)
Definition: attack.cpp:2265
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:591