Atrinik Server  4.0
attack.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <monster_data.h>
32 #include <faction.h>
33 #include <plugin.h>
34 #include <arch.h>
35 #include <attack.h>
36 #include <player.h>
37 #include <object.h>
38 #include <exp.h>
39 #include <disease.h>
40 
46 const char *const attack_save[NROFATTACKS] = {
47  "impact", "slash", "cleave", "pierce", "weaponmagic",
48  "fire", "cold", "electricity", "poison", "acid",
49  "magic", "lifesteal", "blind", "paralyze", "force",
50  "godpower", "chaos", "drain", "slow", "confusion",
51  "internal"
52 };
53 
57 const char *const attack_name[NROFATTACKS] = {
58  "impact", "slash", "cleave", "pierce", "weapon magic",
59  "fire", "cold", "electricity", "poison", "acid",
60  "magic", "lifesteal", "blind", "paralyze", "force",
61  "godpower", "chaos", "drain", "slow", "confusion",
62  "internal"
63 };
64 
79 static int
80 attack_check_sanity (object *op, object *hitter, bool *attack_map)
81 {
82  HARD_ASSERT(op != NULL);
83  HARD_ASSERT(hitter != NULL);
84 
85  if (OBJECT_FREE(op) || OBJECT_FREE(hitter)) {
86  return false;
87  }
88 
89  if (hitter->env != NULL || op->env != NULL ||
90  hitter->map == NULL || op->map == NULL) {
91  if (attack_map != NULL) {
92  *attack_map = false;
93  }
94 
95  return true;
96  }
97 
98  if (QUERY_FLAG(op, FLAG_REMOVED) || QUERY_FLAG(hitter, FLAG_REMOVED)) {
99  return false;
100  }
101 
102  if (attack_map != NULL) {
103  *attack_map = true;
104  }
105 
106  return true;
107 }
108 
122 static bool
123 attack_check_abort (object *op, object *hitter, bool attack_map)
124 {
125  bool new_attack_map;
126 
127  if (hitter->env == op || op->env == hitter) {
128  new_attack_map = false;
129  } else if (QUERY_FLAG(op, FLAG_REMOVED) ||
130  QUERY_FLAG(hitter, FLAG_REMOVED) ||
131  hitter->map == NULL ||
132  op->map == NULL) {
133  return true;
134  } else {
135  new_attack_map = true;
136  }
137 
138  return new_attack_map != attack_map;
139 }
140 
153 static int
154 attack_roll_adjust (object *op, object *hitter)
155 {
156  HARD_ASSERT(op != NULL);
157  HARD_ASSERT(hitter != NULL);
158  SOFT_ASSERT_RC(op->map != NULL, 0, "Object without map: %s",
159  object_get_str(op));
160  SOFT_ASSERT_RC(hitter->map != NULL, 0, "Object without map: %s",
161  object_get_str(hitter));
162 
163  if (!on_same_map(op, hitter)) {
164  return 0;
165  }
166 
167  if (OBJECT_IS_PROJECTILE(hitter)) {
168  object *owner = object_owner(hitter);
169  if (owner != NULL) {
170  hitter = owner;
171  }
172  }
173 
174  if (!IS_LIVE(hitter)) {
175  return 0;
176  }
177 
178  int adjust = 0;
179 
180  /* Invisible means, we can't see it - same for blind. */
181  if (IS_INVISIBLE(op, hitter) || QUERY_FLAG(hitter, FLAG_BLIND)) {
182  adjust -= 12;
183  }
184 
185  if (QUERY_FLAG(hitter, FLAG_SCARED)) {
186  adjust -= 3;
187  }
188 
189  if (QUERY_FLAG(op, FLAG_SCARED)) {
190  adjust += 1;
191  }
192 
193  if (QUERY_FLAG(op, FLAG_UNAGGRESSIVE)) {
194  adjust += 1;
195  }
196 
197  if (QUERY_FLAG(op, FLAG_CONFUSED)) {
198  adjust += 1;
199  }
200 
201  if (QUERY_FLAG(hitter, FLAG_CONFUSED)) {
202  adjust -= 3;
203  }
204 
205  /* If we attack at a different 'altitude' it's harder */
206  if (QUERY_FLAG(hitter, FLAG_FLYING) != QUERY_FLAG(op, FLAG_FLYING)) {
207  adjust -= 2;
208  }
209 
210  if (hitter->direction == op->direction) {
211  /* Backstab */
212  adjust += 5;
213  } else if (hitter->direction == absdir(op->direction - 1) ||
214  hitter->direction == absdir(op->direction + 1)) {
215  /* Sidestab */
216  adjust += 2;
217  }
218 
219  /* If the monster had to turn to attack since it last saw its enemy, it's
220  * a fairly large disadvantage. */
221  mapstruct *enemy_map;
222  uint16_t enemy_x, enemy_y;
223  if (hitter->type == MONSTER &&
225  &enemy_map,
226  &enemy_x,
227  &enemy_y)) {
228  rv_vector rv;
229  if (!get_rangevector_from_mapcoords(hitter->map,
230  hitter->x,
231  hitter->y,
232  enemy_map,
233  enemy_x,
234  enemy_y,
235  &rv,
236  0) ||
237  rv.direction != hitter->direction) {
238  adjust -= 6;
239  }
240  }
241 
242  return adjust;
243 }
244 
259 int
260 attack_object (object *op, object *hitter)
261 {
262  HARD_ASSERT(op != NULL);
263  HARD_ASSERT(hitter != NULL);
264 
265  op = HEAD(op);
266  hitter = HEAD(hitter);
267 
268  bool attack_map;
269  if (!attack_check_sanity(op, hitter, &attack_map)) {
270  return 0;
271  }
272 
273  /* Trigger the ATTACK event */
274  int ret = trigger_event(EVENT_ATTACK,
275  hitter,
276  hitter,
277  op,
278  NULL,
279  0,
280  hitter->stats.dam,
281  hitter->stats.wc,
282  0);
283  if (ret != 0) {
284  return MAX(0, ret);
285  }
286 
287  /* Face the victim. */
288  rv_vector dir;
289  if (get_rangevector(hitter, op, &dir, 0)) {
290  hitter->direction = dir.direction;
291  }
292 
293  /* Update the animation flags to use the attacking animation. */
294  hitter->anim_flags |= ANIM_FLAG_ATTACKING;
295  hitter->anim_flags &= ~ANIM_FLAG_STOP_ATTACKING;
296 
297  if (op->type == PLAYER) {
298  CONTR(op)->last_combat = pticks;
299  }
300 
301  if (hitter->type == PLAYER) {
302  CONTR(hitter)->last_combat = pticks;
303  }
304 
305  if (unlikely(hitter->stats.dam == 0)) {
306  return 0;
307  }
308 
309  int roll_adjust = 0;
310  if (attack_map) {
311  roll_adjust += attack_roll_adjust(op, hitter);
312  }
313 
314  if (hitter->stats.wc_range == 0) {
315  log_error("Hitter with no wc_range, fixing: %s",
316  object_get_str(hitter));
317  hitter->stats.wc_range = 20;
318  }
319 
320  /* Roll to try to hit the creature. */
321  int roll = rndm(1, hitter->stats.wc_range);
322  if (roll != hitter->stats.wc_range &&
323  op->stats.ac > hitter->stats.wc + roll + roll_adjust) {
324  /* We missed. */
325  if (hitter->type == ARROW) {
326  return 0;
327  }
328 
329  if (hitter->type == PLAYER) {
330  play_sound_map(hitter->map,
331  CMD_SOUND_EFFECT,
332  "miss_player1.ogg",
333  hitter->x,
334  hitter->y,
335  0,
336  0);
337  draw_info_format(COLOR_ORANGE, hitter, "You miss %s!", op->name);
338  draw_info_format(COLOR_PURPLE, op, "%s misses you!", hitter->name);
339  } else {
340  play_sound_map(hitter->map,
341  CMD_SOUND_EFFECT,
342  "miss_mob1.ogg",
343  hitter->x,
344  hitter->y,
345  0,
346  0);
347  draw_info_format(COLOR_PURPLE, op, "%s misses you!", hitter->name);
348  }
349 
350  return 0;
351  }
352 
353  /* At this point, the victim will never sleep any longer. */
354  CLEAR_FLAG(op, FLAG_SLEEP);
355 
356  const char *sound;
357  if (hitter->type == ARROW) {
358  sound = "arrow_hit.ogg";
359  } else if (hitter->attack[ATNR_SLASH] != 0) {
360  sound = "hit_slash.ogg";
361  } else if (hitter->attack[ATNR_CLEAVE] != 0) {
362  sound = "hit_cleave.ogg";
363  } else if (hitter->attack[ATNR_IMPACT] != 0) {
364  sound = "hit_impact.ogg";
365  } else {
366  sound = "hit_pierce.ogg";
367  }
368 
369  /* Play a hit sound. */
370  play_sound_map(hitter->map,
371  CMD_SOUND_EFFECT,
372  sound,
373  hitter->x,
374  hitter->y,
375  0,
376  0);
377 
378  int dam;
379  OBJECTS_DESTROYED_BEGIN(op, hitter) {
380  if (attack_map && QUERY_FLAG(op, FLAG_HITBACK) && IS_LIVE(hitter)) {
381  dam = attack_hit(hitter, op, rndm(0, op->stats.dam));
382  if (OBJECTS_DESTROYED_ANY(op, hitter) ||
383  attack_check_abort(op, hitter, attack_map)) {
384  return dam;
385  }
386  }
387 
388  int real_dam = rndm(hitter->stats.dam * 0.8 + 1.0, hitter->stats.dam);
389  dam = attack_hit(op, hitter, real_dam);
390 
391  /* Remove the if directives if you add ANY code dealing with op/hitter
392  * after this point. */
393 #if 0
394  if (OBJECTS_DESTROYED_ANY(op, hitter) ||
395  attack_check_abort(op, hitter, attack_map)) {
396  return dam;
397  }
398 #endif
400 
401  return dam;
402 }
403 
416 static bool
417 attack_block_hit (object *op, object *hitter, double *damage)
418 {
419  HARD_ASSERT(op != NULL);
420  HARD_ASSERT(hitter != NULL);
421  HARD_ASSERT(damage != NULL);
422 
423  /* Only melee hits from living attackers and projectiles can be blocked. */
424  if (!IS_LIVE(hitter) && !OBJECT_IS_PROJECTILE(hitter)) {
425  return false;
426  }
427 
428  /* Players must have a melee weapon skill readied. */
429  if (op->type == PLAYER && op->chosen_skill != NULL &&
431  return false;
432  }
433 
434  if (op->block != 0) {
435  object *hit_obj = OWNER(hitter);
436 
437  int chance = 20 + hit_obj->level - op->level;
438  chance -= rndm(op->block / 2.0 + 0.5, op->block);
439  chance = MAX(3, chance);
440 
441  if (rndm_chance(chance)) {
442  return true;
443  }
444  }
445 
446  if (op->absorb == 0) {
447  return false;
448  }
449 
450  /* Absorb some of the damage. */
451  double absorb = (100.0 - MIN(90, op->absorb)) / 100.0;
452  (*damage) *= absorb;
453  return false;
454 }
455 
470 static void
471 send_attack_msg (object *op,
472  object *hitter,
473  atnr_t atnr,
474  double dam_done,
475  double dam_orig)
476 {
477  HARD_ASSERT(op != NULL);
478  HARD_ASSERT(hitter != NULL);
479 
480  if (op->type == PLAYER) {
481  draw_info_format(COLOR_PURPLE, op,
482  "%s hit you for %d (%d) damage.",
483  hitter->name,
484  (int) (dam_done + 0.5),
485  (int) (dam_done - dam_orig + 0.5));
486  }
487 
488  const char *hitter_name =
489  atnr == ATNR_INTERNAL ? hitter->name : attack_name[atnr];
490  if (hitter->type == PLAYER || ((hitter = object_owner(hitter)) != NULL &&
491  hitter->type == PLAYER)) {
492  draw_info_format(COLOR_ORANGE, hitter,
493  "You hit %s for %d (%d) with %s.",
494  op->name,
495  (int) (dam_done + 0.5),
496  (int) (dam_done - dam_orig + 0.5),
497  hitter_name);
498  }
499 }
500 
521 static double
523  object *hitter,
524  double dam,
525  double dam_orig,
526  atnr_t atnr)
527 {
528  HARD_ASSERT(op != NULL);
529  HARD_ASSERT(hitter != NULL);
530  HARD_ASSERT(dam >= 0.0);
531 
532  /* Adjust the damage based on the attacker's attack type value. */
533  double modifier = hitter->attack[atnr] / 100.0;
534  dam *= modifier;
535  if (dam_orig > 0 && dam < 1.0) {
536  dam = 1.0;
537  }
538 
539  dam_orig *= modifier;
540 
541 #define ATTACK_PROTECT_DAMAGE() \
542  do { \
543  dam *= (100.0 - op->protection[atnr]) / 100.0; \
544  } while (0)
545 
546  /* AT_INTERNAL is supposed to do exactly 'dam' amount of damage. */
547  if (atnr == ATNR_INTERNAL) {
548  /* Handle the poisoning object type. */
549  if (hitter->type == POISONING) {
550  /* Map to poison... */
551  atnr = ATNR_POISON;
552 
553  if (op->protection[atnr] == 100) {
554  send_attack_msg(op, hitter, atnr, 0.0, dam_orig);
555  return 0.0;
556  }
557 
558  ATTACK_PROTECT_DAMAGE();
559  }
560 
561  send_attack_msg(op, hitter, atnr, dam, dam_orig);
562  return dam;
563  }
564 
565  /* Check for complete immunity. */
566  if (op->protection[atnr] == 100) {
567  send_attack_msg(op, hitter, atnr, 0, dam_orig);
568  return 0.0;
569  }
570 
571  switch (atnr) {
572  case ATNR_IMPACT:
573  case ATNR_SLASH:
574  case ATNR_CLEAVE:
575  case ATNR_PIERCE:
576  disease_physically_infect(op, hitter);
577  ATTACK_PROTECT_DAMAGE();
578  send_attack_msg(op, hitter, atnr, dam, dam_orig);
579  break;
580 
581  case ATNR_POISON:
582  ATTACK_PROTECT_DAMAGE();
583  send_attack_msg(op, hitter, atnr, dam, dam_orig);
584 
585  if (dam > 0.0 && IS_LIVE(op)) {
586  attack_perform_poison(op, hitter, dam);
587  }
588 
589  break;
590 
591  case ATNR_CONFUSION:
592  case ATNR_SLOW:
593  case ATNR_PARALYZE:
594  case ATNR_BLIND: {
595  int ldiff = MIN(MAXLEVEL, MAX(0, op->level - hitter->level));
596 
597  if (!DBL_EQUAL(op->speed, 0.0) && IS_LIVE(op) &&
598  rndm_chance(atnr == ATNR_SLOW ? 6 : 3) &&
599  ((rndm(1, 20) + op->protection[atnr] / 10) < savethrow[ldiff])) {
600  if (atnr == ATNR_CONFUSION) {
601  draw_info_format(COLOR_ORANGE, hitter,
602  "You confuse %s!", op->name);
603  draw_info_format(COLOR_PURPLE, op,
604  "%s confused you!", hitter->name);
606  } else if (atnr == ATNR_SLOW) {
607  draw_info_format(COLOR_ORANGE, hitter,
608  "You slow %s!", op->name);
609  draw_info_format(COLOR_PURPLE, op,
610  "%s slowed you!", hitter->name);
612  } else if (atnr == ATNR_PARALYZE) {
613  draw_info_format(COLOR_ORANGE, hitter,
614  "You paralyze %s!", op->name);
615  draw_info_format(COLOR_PURPLE, op,
616  "%s paralyzed you!", hitter->name);
617  attack_peform_paralyze(op, dam);
618  } else if (atnr == ATNR_BLIND && !QUERY_FLAG(op, FLAG_UNDEAD)) {
619  draw_info_format(COLOR_ORANGE, hitter,
620  "You blind %s!", op->name);
621  draw_info_format(COLOR_PURPLE, op,
622  "%s blinded you!", hitter->name);
623  attack_perform_blind(op, hitter, dam);
624  }
625  }
626 
627  dam = 0.0;
628  break;
629  }
630 
631  case ATNR_LIFESTEAL: {
632  ATTACK_PROTECT_DAMAGE();
633  send_attack_msg(op, hitter, atnr, dam, dam_orig);
634 
635  object *owner = OWNER(hitter);
636  owner->stats.hp += dam;
637  if (owner->stats.hp > owner->stats.maxhp) {
638  owner->stats.hp = owner->stats.maxhp;
639  }
640 
641  break;
642  }
643 
644  default:
645  ATTACK_PROTECT_DAMAGE();
646  send_attack_msg(op, hitter, atnr, dam, dam_orig);
647  break;
648  }
649 
650  return dam;
651 
652 #undef ATTACK_PROTECT_DAMAGE
653 }
654 
668 int
669 attack_hit (object *op, object *hitter, int dam)
670 {
671  HARD_ASSERT(op != NULL);
672  HARD_ASSERT(hitter != NULL);
673 
674  op = HEAD(op);
675  hitter = HEAD(hitter);
676 
677  /* Cannot hit players in god-mode. */
678  if (op->type == PLAYER && CONTR(op)->tgm) {
679  return 0;
680  }
681 
682  /* Objects with the invulnerable flag cannot be damaged. */
683  if (QUERY_FLAG(op, FLAG_INVULNERABLE)) {
684  return 0;
685  }
686 
687  /* The object doesn't have any HP left. */
688  if (unlikely(op->stats.hp < 0)) {
689  return 0;
690  }
691 
692  object *hitter_owner = object_owner(hitter);
693 
694  /* Sanity check: If the hitter has ownercount (so it had an owner)
695  * but the owner itself is no longer valid, we won't do any damage,
696  * otherwise player could fire an arrow, logout, and the arrow itself
697  * would cause damage to anything it hits, even friendly creatures. */
698  if (unlikely(hitter->ownercount != 0 && hitter_owner == NULL)) {
699  return 0;
700  }
701 
702  /* Check if the object to hit has any HP left */
703  if (op->stats.hp < 0) {
704  return 0;
705  }
706 
707  if (hitter_owner == NULL) {
708  hitter_owner = hitter;
709  }
710 
711  /* Do not let friendly objects attack each other. */
712  if (is_friend_of(hitter_owner, op)) {
713  return 0;
714  }
715 
716  /* Check for PVP areas. */
717  if (op->type == PLAYER && hitter_owner->type == PLAYER &&
718  !pvp_area(op, hitter_owner)) {
719  return 0;
720  }
721 
722  if (!attack_check_sanity(op, hitter, NULL)) {
723  return 0;
724  }
725 
726  double damage = dam;
727  if (hitter->slaying != NULL && hitter->slaying == op->race) {
728  if (QUERY_FLAG(hitter, FLAG_IS_ASSASSINATION)) {
729  damage *= 2.25;
730  } else {
731  damage *= 1.75;
732  }
733  }
734 
735  if (hitter_owner->type == MONSTER && hitter_owner->level > op->level) {
736  double modifier = hitter_owner->level - op->level;
737  modifier /= MIN(20, op->level);
738  damage += dam / 2.0 * modifier;
739  }
740 
741  double dam_orig = damage;
742  double maxdam = 0.0;
743 
744  /* Try to block the attack. */
745  if (attack_block_hit(op, hitter, &damage)) {
746  draw_info_format(COLOR_PURPLE, hitter, "%s blocked your attack!",
747  op->name);
748  draw_info_format(COLOR_ORANGE, op, "You block %s!",
749  hitter->name);
750  } else if (damage > 0.0) {
751  /* Go through and hit the player with each attacktype, one by one.
752  * hit_player_attacktype only figures out the damage, doesn't inflict
753  * it. It will do the appropriate action for attacktypes with
754  * effects (slow, paralization, etc). */
755  for (atnr_t atnr = 0; atnr < NROFATTACKS; atnr++) {
756  if (hitter->attack[atnr] != 0) {
757  maxdam += attack_hit_attacktype(op,
758  hitter,
759  damage,
760  dam_orig,
761  atnr);
762  }
763  }
764  }
765 
766  /* If one gets attacked, the attacker will become the enemy */
767  if (!OBJECT_VALID(op->enemy, op->enemy_count) &&
768  !IS_INVISIBLE(hitter_owner, op) &&
770  set_npc_enemy(op, hitter_owner, NULL);
771  }
772 
773  /* Reset the cache so that we send damage numbers to clients. */
774  if (op->damage_round_tag != global_round_tag) {
775  op->last_damage = 0;
776  op->damage_round_tag = global_round_tag;
777  }
778 
779  if (hitter_owner->type == PLAYER) {
780  CONTR(hitter_owner)->last_combat = pticks;
781  CONTR(hitter_owner)->stat_damage_dealt += maxdam;
782  }
783 
784  if (op->type == PLAYER) {
785  CONTR(op)->last_combat = pticks;
786  CONTR(op)->stat_damage_taken += maxdam;
787  }
788 
789  op->last_damage += maxdam;
790 
791  /* For the purposes of statistics and damage visible on-screen, we want to
792  * show the full damage. However, to the function's callers, we only want
793  * to return the total damage dealt to the object, capping it at the
794  * object's hp. */
795  if (maxdam > op->stats.hp) {
796  maxdam = op->stats.hp;
797  }
798 
799  /* Damage the target got */
800  op->stats.hp -= maxdam;
801 
802  /* Check to see if monster runs away. */
803  if (op->stats.hp >= 0 && QUERY_FLAG(op, FLAG_MONSTER) &&
804  op->stats.hp < op->run_away / 100.0 * op->stats.maxhp) {
805  SET_FLAG(op, FLAG_RUN_AWAY);
806  }
807 
808  /* Reached 0 or less HP, kill the object. */
809  if (op->stats.hp <= 0) {
810  attack_kill(op, hitter);
811  }
812 
813  return maxdam;
814 }
815 
829 void
830 attack_hit_map (object *op, int dir, bool multi_reduce)
831 {
832  HARD_ASSERT(op != NULL);
833 
834  if (OBJECT_FREE(op)) {
835  return;
836  }
837 
838  op = HEAD(op);
839 
840  if (op->map == NULL || op->stats.dam == 0) {
841  return;
842  }
843 
844  int x = op->x + freearr_x[dir];
845  int y = op->y + freearr_y[dir];
846  mapstruct *m = get_map_from_coord(op->map, &x, &y);
847  if (m == NULL) {
848  return;
849  }
850 
851  object *owner = OWNER(op);
852  owner = HEAD(owner);
853 
854  object *tmp;
855  FOR_MAP_LAYER_BEGIN(m, x, y, LAYER_LIVING, -1, tmp) {
856  tmp = HEAD(tmp);
857 
858  /* Cones with race set can only damage members of that race. */
859  if (op->type == CONE && op->race != NULL && tmp->race != op->race) {
860  continue;
861  }
862 
863  /* Check friendship status. */
864  if (is_friend_of(owner, tmp)) {
865  continue;
866  }
867 
868  int dam = op->stats.dam;
869  if (multi_reduce && tmp->quick_pos != 0) {
870  dam /= (tmp->quick_pos >> 4) + 1;
871  }
872 
873  attack_hit(tmp, op, dam);
875 }
876 
887 static inline void
888 share_kill_exp_one (object *op, int64_t exp_gain, object *skill)
889 {
890  HARD_ASSERT(op != NULL);
891  HARD_ASSERT(skill != NULL);
892 
893  if (exp_gain != 0) {
894  add_exp(op, exp_gain, skill->stats.sp, 0);
895  } else {
896  draw_info(COLOR_WHITE, op,
897  "Your enemy wasn't worth any experience to you.");
898  }
899 }
900 
913 static void
914 share_kill_exp (object *op, int64_t exp_gain, object *skill)
915 {
916  HARD_ASSERT(op != NULL);
917  HARD_ASSERT(skill != NULL);
918 
919  int shares = 0, count = 0;
920 
921  /* No party, no sharing. */
922  if (CONTR(op)->party == NULL) {
923  share_kill_exp_one(op, exp_gain, skill);
924  return;
925  }
926 
927  party_struct *party = CONTR(op)->party;
928  for (objectlink *ol = party->members; ol != NULL; ol = ol->next) {
929  if (!on_same_map(ol->objlink.ob, op)) {
930  continue;
931  }
932 
933  object *player_skill =
934  CONTR(ol->objlink.ob)->skill_ptr[skill->stats.sp];
935  if (player_skill == NULL) {
936  continue;
937  }
938 
939  count++;
940  shares += player_skill->level + 4;
941  }
942 
943  if (count <= 1 || shares > exp_gain) {
944  share_kill_exp_one(op, exp_gain, skill);
945  } else {
946  int64_t share = exp_gain / shares;
947  int64_t given = 0;
948  for (objectlink *ol = party->members; ol != NULL; ol = ol->next) {
949  if (ol->objlink.ob == op || !on_same_map(ol->objlink.ob, op)) {
950  continue;
951  }
952 
953  object *player_skill =
954  CONTR(ol->objlink.ob)->skill_ptr[skill->stats.sp];
955  if (player_skill == NULL) {
956  continue;
957  }
958 
959  given += add_exp(ol->objlink.ob,
960  (player_skill->level + 4) * share,
961  skill->stats.sp,
962  0);
963  }
964 
965  exp_gain -= given;
966  share_kill_exp_one(op, exp_gain, skill);
967  }
968 }
969 
980 bool
981 attack_kill (object *op, object *hitter)
982 {
983  HARD_ASSERT(op != NULL);
984  HARD_ASSERT(hitter != NULL);
985 
986  /* Players in god-mode cannot die. */
987  if (op->type == PLAYER && CONTR(op)->tgm) {
988  return false;
989  }
990 
991  /* Trigger the DEATH event. */
992  if (trigger_event(EVENT_DEATH, hitter, op, NULL, NULL, 0, 0, 0, 0) != 0) {
993  return false;
994  }
995 
996  /* Only when some damage is stored, and we're on a map. */
997  if (op->damage_round_tag == global_round_tag && op->map != NULL) {
998  SET_MAP_DAMAGE(op->map, op->x, op->y, op->sub_layer, op->last_damage);
999  SET_MAP_RTAG(op->map, op->x, op->y, op->sub_layer, global_round_tag);
1000  }
1001 
1002  if (op->map != NULL) {
1003  play_sound_map(op->map,
1004  CMD_SOUND_EFFECT,
1005  "kill.ogg",
1006  op->x,
1007  op->y,
1008  0,
1009  0);
1010  }
1011 
1012  /* Figure out who to credit for the kill. */
1013  object *owner = OWNER(hitter);
1014 
1015  /* Player killed something. */
1016  if (owner->type == PLAYER) {
1017  char *name = object_get_name_s(op, owner);
1018  if (owner != hitter) {
1019  char *hitter_name = object_get_name_s(hitter, owner);
1020  draw_info_format(COLOR_WHITE, owner, "You killed %s with %s.", name,
1021  hitter_name);
1022  efree(hitter_name);
1023  } else {
1024  draw_info_format(COLOR_WHITE, owner, "You killed %s.", name);
1025  }
1026  efree(name);
1027 
1028  if (op->type == MONSTER) {
1029  CONTR(owner)->stat_kills_mob++;
1030  statistic_update("kills", owner, 1, op->name);
1031 
1032  if (object_get_value(op, "was_provoked") == NULL) {
1033  shstr *faction_name = object_get_value(op, "faction");
1034  if (faction_name != NULL) {
1035  faction_t faction = faction_find(faction_name);
1036  if (faction != NULL) {
1037  faction_update_kill(faction, CONTR(owner));
1038  } else {
1039  LOG(ERROR, "Invalid faction: %s for %s", faction_name,
1040  object_get_str(op));
1041  }
1042  }
1043  }
1044  } else if (op->type == PLAYER) {
1045  CONTR(owner)->stat_kills_pvp++;
1046  }
1047  }
1048 
1049  bool is_pvp = pvp_area(NULL, op);
1050 
1051  /* Killed a player in PvP area. */
1052  if (is_pvp && op->type == PLAYER && owner->type == PLAYER) {
1053  draw_info(COLOR_WHITE, owner, "Your foe has fallen!\nVICTORY!!!");
1054  }
1055 
1056  int64_t exp_gain = 0;
1057 
1058  /* Killed a monster and it wasn't in PvP area, so give exp. */
1059  if (!is_pvp && owner->type == PLAYER && op->type != PLAYER) {
1060  /* Figure out the skill that should gain experience. If the hitter
1061  * has chosen_skill set, we will use that, otherwise try to use
1062  * owner's chosen_skill. */
1063  object *skill = hitter->chosen_skill;
1064  if (skill == NULL) {
1065  skill = owner->chosen_skill;
1066  }
1067 
1068  if (skill != NULL) {
1069  /* Calculate how much experience to gain. */
1070  exp_gain = calc_skill_exp(owner, op, skill->level);
1071  /* Give the experience, sharing it with party members if
1072  * applicable. */
1073  share_kill_exp(owner, exp_gain, skill);
1074  }
1075  }
1076 
1077  /* Player has been killed. */
1078  if (op->type == PLAYER) {
1079  /* Tell everyone that this player has died. */
1080  char *name = object_get_name_s(op, NULL);
1081  char *hitter_name = object_get_name_s(hitter, NULL);
1082  char *owner_name = object_get_name_s(owner, NULL);
1083 
1084  if (owner != hitter) {
1085  draw_info_format(COLOR_WHITE, NULL, "%s killed %s with %s%s.",
1086  owner_name,
1087  name,
1088  hitter_name,
1089  is_pvp ? " (duel)" : "");
1090  } else {
1091  draw_info_format(COLOR_WHITE, NULL, "%s killed %s%s.",
1092  hitter_name,
1093  name,
1094  is_pvp ? " (duel)" : "");
1095  }
1096 
1097  StringBuffer *sb = stringbuffer_new();
1098 
1099  /* Update player's killer. */
1100  if (owner->type == PLAYER) {
1101  stringbuffer_append_printf(sb,
1102  "%s the %s",
1103  owner_name,
1104  owner->race);
1105  } else {
1106  stringbuffer_append_printf(sb,
1107  "%s",
1108  owner_name);
1109  }
1110 
1111  char *cp = stringbuffer_finish(sb);
1112  player_set_killer(CONTR(op), cp);
1113  efree(cp);
1114 
1115  efree(name);
1116  efree(hitter_name);
1117  efree(owner_name);
1118 
1119  /* And actually kill the player. */
1120  kill_player(op);
1121  } else {
1122  /* Monster or something else has been killed, so remove it from the
1123  * active list. */
1124  op->speed = 0.0;
1125  object_update_speed(op);
1126 
1127  /*
1128  * Rules:
1129  * 1. Monster will drop corpse for his target, not the killer (unless
1130  * killer == target).
1131  * 2. NPC kill hit will overwrite player target on drop.
1132  * 3. Kill hit will count if target was an NPC.
1133  */
1134  if (owner->type != PLAYER ||
1135  op->enemy == NULL ||
1136  op->enemy->type != PLAYER) {
1137  op->enemy = owner;
1138  op->enemy_count = owner->count;
1139  }
1140 
1141  /* Monster killed another monster. */
1142  if (owner->type == MONSTER) {
1143  /* No loot */
1145  /* Force an empty corpse though. */
1147  } else if (exp_gain == 0) {
1148  /* No exp, no loot and no corpse. */
1150  }
1151 
1152  object_destruct(op);
1153  }
1154 
1155  return true;
1156 }
1157 
1168 void
1169 attack_perform_poison (object *op, object *hitter, double dam)
1170 {
1171  HARD_ASSERT(op != NULL);
1172  HARD_ASSERT(hitter != NULL);
1173 
1174  /* We only poison living things. */
1175  if (!IS_LIVE(op)) {
1176  return;
1177  }
1178 
1179  /* Make sure poisoning doesn't stack more poisoning by itself. */
1180  if (hitter->type == POISONING) {
1181  return;
1182  }
1183 
1184  /* Save some work if we know it isn't going to affect the player */
1185  if (op->protection[ATNR_POISON] == 100) {
1186  return;
1187  }
1188 
1189  static archetype_t *at = NULL;
1190  if (at == NULL) {
1191  at = arch_find("poisoning");
1192  SOFT_ASSERT(at != NULL, "Could not find poisoning archetype");
1193  }
1194 
1195  int dam2 = rndm(dam / 2.0 + 1.0, dam);
1196  if (dam2 > op->stats.maxhp / 3) {
1197  dam2 = op->stats.maxhp / 3;
1198  } else if (dam2 < 1) {
1199  dam2 = 1;
1200  }
1201 
1202  object *tmp = object_find_arch(op, at);
1203  if (tmp == NULL) {
1204  tmp = arch_to_object(at);
1205  tmp->level = hitter->level;
1206  tmp->stats.dam = dam2;
1207 
1208  /* So we get credit for poisoning kills */
1209  if (IS_LIVE(hitter)) {
1210  object_owner_set(tmp, hitter);
1211  }
1212 
1213  SET_FLAG(tmp, FLAG_APPLIED);
1214  tmp = object_insert_into(tmp, op, 0);
1215  SOFT_ASSERT(tmp != NULL, "Failed to insert poisoning into %s",
1216  object_get_str(op));
1217 
1218  if (op->type == PLAYER) {
1219  char *name = object_get_name_s(hitter, op);
1220  draw_info_format(COLOR_WHITE, op, "%s has poisoned you!", name);
1221  efree(name);
1222  } else {
1223  if (hitter->type == PLAYER) {
1224  char *name = object_get_name_s(op, hitter);
1225  draw_info_format(COLOR_WHITE, hitter, "You poisoned %s!", name);
1226  efree(name);
1227  } else if (object_owner(hitter) != NULL &&
1228  hitter->owner->type == PLAYER) {
1229  char *name = object_get_name_s(op, hitter->owner);
1230  char *hitter_name = object_get_name_s(hitter, hitter->owner);
1231  draw_info_format(COLOR_WHITE, hitter->owner, "%s poisoned %s!",
1232  hitter_name, name);
1233  efree(name);
1234  efree(hitter_name);
1235  }
1236  }
1237 
1238  tmp->speed_left = 0;
1239  } else {
1240  tmp->stats.food++;
1241  esrv_update_item(UPD_EXTRA, tmp);
1242 
1243  if (dam2 > tmp->stats.dam) {
1244  tmp->stats.dam = dam2;
1245  }
1246  }
1247 }
1248 
1255 void
1257 {
1258  HARD_ASSERT(op != NULL);
1259 
1260  static archetype_t *at = NULL;
1261  if (at == NULL) {
1262  at = arch_find("slowness");
1263  SOFT_ASSERT(at != NULL, "Could not find slowness archetype");
1264  }
1265 
1266  /* Save some work if we know it isn't going to affect the player */
1267  if (op->protection[ATNR_SLOW] == 100) {
1268  return;
1269  }
1270 
1271  object *tmp = object_find_arch(op, at);
1272  if (tmp == NULL) {
1273  tmp = arch_to_object(at);
1274  SET_FLAG(tmp, FLAG_APPLIED);
1275  tmp = object_insert_into(tmp, op, 0);
1276  SOFT_ASSERT(tmp != NULL, "Failed to insert slowness into %s",
1277  object_get_str(op));
1278  draw_info(COLOR_WHITE, op, "The world suddenly moves very fast!");
1279  } else {
1280  tmp->stats.food++;
1281  esrv_update_item(UPD_EXTRA, tmp);
1282  }
1283 
1284  tmp->speed_left = 0;
1285 }
1286 
1293 void
1295 {
1296  HARD_ASSERT(op != NULL);
1297 
1298  static archetype_t *at = NULL;
1299  if (at == NULL) {
1300  at = arch_find("confusion");
1301  SOFT_ASSERT(at != NULL, "Could not find confusion archetype");
1302  }
1303 
1304  /* Save some work if we know it isn't going to affect the player */
1305  if (op->protection[ATNR_CONFUSION] == 100) {
1306  return;
1307  }
1308 
1309  object *tmp = object_find_arch(op, at);
1310  if (tmp == NULL) {
1311  tmp = arch_to_object(at);
1312  SET_FLAG(tmp, FLAG_APPLIED);
1313  tmp = object_insert_into(tmp, op, 0);
1314  SOFT_ASSERT(tmp != NULL, "Could not insert confusion into %s",
1315  object_get_str(op));
1316  }
1317 
1318  /* Duration added per hit and max. duration of confusion both depend
1319  * on the player's resistance */
1320  tmp->stats.food += MAX(1, 5 * (100 - op->protection[ATNR_CONFUSION]) / 100);
1321  int maxduration = MAX(2, 30 * (100 - op->protection[ATNR_CONFUSION]) / 100);
1322 
1323  if (tmp->stats.food > maxduration) {
1324  tmp->stats.food = maxduration;
1325  }
1326 
1327  tmp->speed_left = 0;
1328  esrv_update_item(UPD_EXTRA, tmp);
1329 }
1330 
1341 void
1342 attack_perform_blind (object *op, object *hitter, double dam)
1343 {
1344  HARD_ASSERT(op != NULL);
1345  HARD_ASSERT(hitter != NULL);
1346 
1347  static archetype_t *at = NULL;
1348  if (at == NULL) {
1349  at = arch_find("blindness");
1350  SOFT_ASSERT(at != NULL, "Could not find blindness archetype");
1351  }
1352 
1353  /* Save some work if we know it isn't going to affect the player */
1354  if (op->protection[ATNR_BLIND] == 100) {
1355  return;
1356  }
1357 
1358  object *tmp = object_find_arch(op, at);
1359  if (tmp == NULL) {
1360  tmp = arch_to_object(at);
1361  SET_FLAG(tmp, FLAG_APPLIED);
1362  tmp->speed = tmp->speed * (100.0 - op->protection[ATNR_BLIND]) / 100.0;
1363  tmp = object_insert_into(tmp, op, 0);
1364  SOFT_ASSERT(tmp != NULL, "Failed to insert blindness into %s",
1365  object_get_str(op));
1366 
1367  if (hitter != op) {
1368  object *owner = object_owner(hitter);
1369  if (owner == NULL) {
1370  owner = hitter;
1371  }
1372 
1373  char *name = object_get_name_s(op, owner);
1374  draw_info_format(COLOR_WHITE, owner, "Your attack blinds %s!",
1375  name);
1376  efree(name);
1377  }
1378  }
1379 
1380  tmp->stats.food += dam;
1381 
1382  if (tmp->stats.food > 10) {
1383  tmp->stats.food = 10;
1384  }
1385 
1386  tmp->speed_left = 0;
1387  esrv_update_item(UPD_EXTRA, tmp);
1388 }
1389 
1398 void
1399 attack_peform_paralyze (object *op, double dam)
1400 {
1401  HARD_ASSERT(op != NULL);
1402 
1403  /* Save some work if we know it isn't going to affect the player */
1404  if (op->protection[ATNR_PARALYZE] == 100) {
1405  return;
1406  }
1407 
1408  double effect = dam * 3.0;
1409  effect *= (100.0 - (double) op->protection[ATNR_PARALYZE]) / 100.0;
1410  if (DBL_EQUAL(effect, 0.0)) {
1411  return;
1412  }
1413 
1414  /* We mark this object as paralyzed */
1415  SET_FLAG(op, FLAG_PARALYZED);
1416 
1417  op->speed_left -= FABS(op->speed) * effect;
1418 
1419  /* Max number of ticks to be affected for. */
1420  double max = (100.0 - op->protection[ATNR_PARALYZE]) / 2.0;
1421  if (op->speed_left < -(FABS(op->speed) * max)) {
1422  op->speed_left = -(FABS(op->speed) * max);
1423  }
1424 }
1425 
1434 void
1435 attack_perform_fall (object *op, int fall_floors)
1436 {
1437  HARD_ASSERT(op != NULL);
1438  SOFT_ASSERT(IS_LIVE(op), "Object is not alive: %s", object_get_str(op));
1439 
1440  int8_t dex = op->type == PLAYER ? op->stats.Dex : MAX_STAT / 2;
1441 
1442  int roll = MAX_STAT - dex + MAX_STAT - rndm(1, dex);
1443  roll *= MAX(1, fall_floors - 1);
1444  if (roll < MAX_STAT / 4) {
1445  return;
1446  }
1447 
1448  object *damager = arch_get("falling");
1449  damager->level = op->level;
1450 
1451  double dam = (op->weight + op->carrying) / 1250.00 * MIN(10.0, fall_floors);
1452  dam *= falling_mitigation[dex];
1453  if (dam < 1.0) {
1454  dam = 1.0;
1455  }
1456 
1457  damager->stats.dam = dam;
1458  damager->stats.dam = rndm(damager->stats.dam / 2.0 + 1.0,
1459  damager->stats.dam);
1460 
1461  attack_hit(op, damager, damager->stats.dam);
1462  object_destroy(damager);
1463 }
1464 
1475 bool
1476 attack_is_melee_range (object *hitter, object *enemy)
1477 {
1478  HARD_ASSERT(hitter != NULL);
1479  HARD_ASSERT(enemy != NULL);
1480 
1481  SOFT_ASSERT_RC(hitter->head == NULL, false, "Called on tail part: %s",
1482  object_get_str(hitter));
1483  SOFT_ASSERT_RC(enemy->head == NULL, false, "Called on tail part: %s",
1484  object_get_str(enemy));
1485 
1486  for (object *hitter_part = hitter;
1487  hitter_part != NULL;
1488  hitter_part = hitter_part->more) {
1489  for (int i = 0; i <= SIZEOFFREE1; i++) {
1490  int x = hitter_part->x + freearr_x[i];
1491  int y = hitter_part->y + freearr_y[i];
1492  mapstruct *m = get_map_from_coord(hitter_part->map, &x, &y);
1493  if (m == NULL) {
1494  continue;
1495  }
1496 
1497  for (object *enemy_part = enemy;
1498  enemy_part != NULL;
1499  enemy_part = enemy_part->more) {
1500  if (enemy_part->map == m &&
1501  enemy_part->x == x &&
1502  enemy_part->y == y) {
1503  return true;
1504  }
1505  }
1506  }
1507  }
1508 
1509  return false;
1510 }
const char *const attack_save[NROFATTACKS]
Definition: attack.c:46
#define CONE
Definition: define.h:385
static void share_kill_exp(object *op, int64_t exp_gain, object *skill)
Definition: attack.c:914
void object_destroy(object *ob)
Definition: object.c:1441
#define MONSTER
Definition: define.h:353
#define FLAG_HITBACK
Definition: define.h:1000
const char *const attack_name[NROFATTACKS]
Definition: attack.c:57
int16_t ac
Definition: living.h:93
tag_t ownercount
Definition: object.h:219
uint8_t quick_pos
Definition: object.h:357
static bool attack_check_abort(object *op, object *hitter, bool attack_map)
Definition: attack.c:123
int get_rangevector(object *op1, object *op2, rv_vector *retval, int flags)
Definition: map.c:2238
static int attack_check_sanity(object *op, object *hitter, bool *attack_map)
Definition: attack.c:80
int direction
Definition: map.h:787
Definition: object.h:488
const char * race
Definition: object.h:174
tag_t enemy_count
Definition: object.h:216
void attack_peform_paralyze(object *op, double dam)
Definition: attack.c:1399
int64_t calc_skill_exp(object *who, object *op, int level)
Definition: skill_util.c:158
int trigger_event(int event_type, object *const activator, object *const me, object *const other, const char *msg, int parm1, int parm2, int parm3, int flags)
Definition: plugins.c:510
#define FLAG_SLEEP
Definition: define.h:868
double speed_left
Definition: object.h:472
#define SIZEOFFREE1
Definition: define.h:654
mapstruct * get_map_from_coord(mapstruct *m, int *x, int *y)
Definition: map.c:1869
void statistic_update(const char *type, object *op, int64_t i, const char *buf)
Definition: statistics.c:83
shstr * object_get_value(const object *op, const char *const key)
Definition: object.c:2515
#define FLAG_CORPSE_FORCED
Definition: define.h:1293
void attack_perform_blind(object *op, object *hitter, double dam)
Definition: attack.c:1342
void faction_update_kill(faction_t faction, player *pl)
Definition: faction.c:527
const char * slaying
Definition: object.h:180
bool attack_kill(object *op, object *hitter)
Definition: attack.c:981
#define FLAG_BLIND
Definition: define.h:884
#define OBJECT_IS_PROJECTILE(ob)
Definition: object.h:823
object * arch_get(const char *name)
Definition: arch.c:430
#define FLAG_INVULNERABLE
Definition: define.h:1229
void object_destruct(object *op)
Definition: object.c:1521
static int absdir(int d)
Definition: define.h:1838
#define IS_LIVE(op)
Definition: define.h:841
#define PLAYER
Definition: define.h:122
#define FLAG_PARALYZED
Definition: define.h:876
void kill_player(object *op)
Definition: player.c:765
int16_t sp
Definition: living.h:78
#define OBJECT_FREE(_ob_)
Definition: object.h:554
#define OBJECTS_DESTROYED_BEGIN(...)
Definition: object.h:765
object * object_find_arch(object *op, archetype_t *at)
Definition: object.c:2258
static void send_attack_msg(object *op, object *hitter, atnr_t atnr, double dam_done, double dam_orig)
Definition: attack.c:471
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
uint8_t absorb
Definition: object.h:421
struct obj * enemy
Definition: object.h:196
#define POISONING
Definition: define.h:437
uint8_t attack[NROFATTACKS]
Definition: object.h:436
int32_t hp
Definition: living.h:72
#define FLAG_STARTEQUIP
Definition: define.h:1004
char * object_get_name_s(const object *op, const object *caller)
Definition: item.c:398
#define FLAG_IS_ASSASSINATION
Definition: define.h:1335
int64_t add_exp(object *op, int64_t exp_gain, int skill_nr, int exact)
Definition: exp.c:304
struct obj * chosen_skill
Definition: object.h:210
int16_t y
Definition: object.h:276
enum atnr atnr_t
int32_t maxhp
Definition: living.h:75
#define OBJECTS_DESTROYED_ANY(...)
Definition: object.h:782
void set_npc_enemy(object *npc, object *enemy, rv_vector *rv)
Definition: monster.c:85
const char * object_get_str(const object *op)
Definition: object.c:3151
uint8_t sub_layer
Definition: object.h:408
int8_t direction
Definition: object.h:350
Definition: arch.h:40
int is_friend_of(object *op, object *obj)
Definition: monster.c:1685
#define EVENT_DEATH
Definition: plugin.h:79
uint32_t weight
Definition: object.h:246
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
void attack_hit_map(object *op, int dir, bool multi_reduce)
Definition: attack.c:830
struct mapdef * map
Definition: object.h:139
#define FLAG_SCARED
Definition: define.h:880
int16_t last_damage
Definition: object.h:292
int16_t dam
Definition: living.h:87
uint32_t carrying
Definition: object.h:157
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
struct obj * env
Definition: object.h:130
#define LAYER_LIVING
Definition: map.h:59
faction_t faction_find(shstr *name)
Definition: faction.c:382
object * arch_to_object(archetype_t *at)
Definition: arch.c:446
int attack_object(object *op, object *hitter)
Definition: attack.c:260
atnr
Definition: attack.h:36
void disease_physically_infect(object *op, object *hitter)
Definition: disease.c:483
#define SKILL_IS_MELEE(nr)
Definition: skills.h:99
static double attack_hit_attacktype(object *op, object *hitter, double dam, double dam_orig, atnr_t atnr)
Definition: attack.c:522
uint32_t damage_round_tag
Definition: object.h:149
uint8_t wc_range
Definition: living.h:100
#define IS_INVISIBLE(__ob_, __player_)
Definition: define.h:1356
void player_set_killer(player *pl, const char *killer)
Definition: player.c:2696
#define ARROW
Definition: define.h:170
#define FLAG_FLYING
Definition: define.h:918
#define FOR_MAP_LAYER_END
Definition: define.h:1657
double speed
Definition: object.h:469
#define HEAD(op)
Definition: object.h:657
uint8_t block
Definition: object.h:416
bool attack_is_melee_range(object *hitter, object *enemy)
Definition: attack.c:1476
int16_t x
Definition: object.h:273
#define EVENT_ATTACK
Definition: plugin.h:77
int pvp_area(object *attacker, object *victim)
Definition: player.c:977
#define FOR_MAP_LAYER_BEGIN(_m, _x, _y, _layer, _sub_layer, _obj)
Definition: define.h:1630
#define FLAG_REMOVED
Definition: define.h:930
int16_t wc
Definition: living.h:90
void play_sound_map(mapstruct *map, int type, const char *filename, int x, int y, int loop, int volume)
Definition: sounds.c:159
#define MAXLEVEL
Definition: global.h:221
void attack_perform_slow(object *op)
Definition: attack.c:1256
#define FLAG_APPLIED
Definition: define.h:1182
int8_t protection[NROFATTACKS]
Definition: object.h:439
double falling_mitigation[MAX_STAT+1]
Definition: living.c:110
void object_owner_set(object *op, object *owner)
Definition: object.c:788
#define OBJECTS_DESTROYED_END()
Definition: object.h:816
int on_same_map(object *op1, object *op2)
Definition: map.c:2414
struct obj * owner
Definition: object.h:207
static bool attack_block_hit(object *op, object *hitter, double *damage)
Definition: attack.c:417
tag_t count
Definition: object.h:142
int get_rangevector_from_mapcoords(mapstruct *map1, int x, int y, mapstruct *map2, int x2, int y2, rv_vector *retval, int flags)
Definition: map.c:2297
living stats
Definition: object.h:481
int8_t Dex
Definition: living.h:106
struct oblnk * next
Definition: object.h:500
void esrv_update_item(int flags, object *op)
Definition: item.c:639
int freearr_x[SIZEOFFREE]
Definition: object.c:84
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
object * object_owner(object *op)
Definition: object.c:857
#define FLAG_MONSTER
Definition: define.h:922
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
uint8_t anim_flags
Definition: object.h:390
struct obj * head
Definition: object.h:136
#define FLAG_UNDEAD
Definition: define.h:1012
#define FLAG_UNAGGRESSIVE
Definition: define.h:1020
uint8_t run_away
Definition: object.h:402
#define FLAG_CONFUSED
Definition: define.h:872
void attack_perform_fall(object *op, int fall_floors)
Definition: attack.c:1435
static int attack_roll_adjust(object *op, object *hitter)
Definition: attack.c:154
objectlink * members
Definition: party.h:112
void object_update_speed(object *op)
Definition: object.c:1043
Definition: map.h:536
bool monster_data_enemy_get_coords(object *op, mapstruct **map, uint16_t *x, uint16_t *y)
Definition: monster_data.c:120
#define FLAG_RUN_AWAY
Definition: define.h:1049
int8_t level
Definition: object.h:347
int freearr_y[SIZEOFFREE]
Definition: object.c:99
void attack_perform_poison(object *op, object *hitter, double dam)
Definition: attack.c:1169
struct obj * more
Definition: object.h:133
void attack_perform_confusion(object *op)
Definition: attack.c:1294
static void share_kill_exp_one(object *op, int64_t exp_gain, object *skill)
Definition: attack.c:888
int savethrow[MAXLEVEL+1]
Definition: living.c:165
static object * OWNER(object *op)
Definition: object.h:979
archetype_t * arch_find(const char *name)
Definition: arch.c:407
int attack_hit(object *op, object *hitter, int dam)
Definition: attack.c:669
#define MAX_STAT
Definition: define.h:47
int16_t food
Definition: living.h:84