ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/attack.C
Revision: 1.69
Committed: Sun Jul 1 05:00:19 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.68: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT 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 3 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, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 #include <assert.h>
25 #include <global.h>
26 #include <living.h>
27 #include <material.h>
28 #include <skills.h>
29 #include <sounds.h>
30 #include <sproto.h>
31
32 typedef struct att_msg_str
33 {
34 char *msg1;
35 char *msg2;
36 } att_msg;
37
38 /*#define ATTACK_DEBUG*/
39
40 /* cancels object *op. Cancellation basically means an object loses
41 * its magical benefits.
42 */
43 void
44 cancellation (object *op)
45 {
46 if (op->invisible)
47 return;
48
49 if (QUERY_FLAG (op, FLAG_ALIVE) || op->type == CONTAINER || op->type == THROWN_OBJ)
50 {
51 /* Recurse through the inventory */
52 for (object *tmp = op->inv; tmp; tmp = tmp->below)
53 if (!did_make_save_item (tmp, AT_CANCELLATION, op))
54 cancellation (tmp);
55 }
56 else if (FABS (op->magic) <= rndm (0, 5))
57 {
58 /* Nullify this object. This code could probably be more complete */
59 /* in what abilities it should cancel */
60 op->magic = 0;
61
62 CLEAR_FLAG (op, FLAG_DAMNED);
63 CLEAR_FLAG (op, FLAG_CURSED);
64 CLEAR_FLAG (op, FLAG_KNOWN_MAGICAL);
65 CLEAR_FLAG (op, FLAG_KNOWN_CURSED);
66
67 if (op->env && op->env->type == PLAYER)
68 esrv_send_item (op->env, op);
69 }
70 }
71
72 /* did_make_save_item just checks to make sure the item actually
73 * made its saving throw based on the tables. It does not take
74 * any further action (like destroying the item).
75 */
76 int
77 did_make_save_item (object *op, int type, object *originator)
78 {
79 int i, roll, saves = 0, attacks = 0, number;
80 materialtype_t *mt;
81
82 if (op->materialname == NULL)
83 {
84 for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next)
85 if (op->materials & mt->material)
86 break;
87 }
88 else
89 mt = name_to_material (op->materialname);
90
91 if (mt == NULL)
92 return TRUE;
93
94 roll = rndm (1, 20);
95
96 /* the attacktypes have no meaning for object saves
97 * If the type is only magic, don't adjust type - basically, if
98 * pure magic is hitting an object, it should save. However, if it
99 * is magic teamed with something else, then strip out the
100 * magic type, and instead let the fire, cold, or whatever component
101 * destroy the item. Otherwise, you get the case of poisoncloud
102 * destroying objects because it has magic attacktype.
103 */
104 if (type != AT_MAGIC)
105 type &= ~(AT_CONFUSION | AT_DRAIN | AT_GHOSTHIT | AT_POISON | AT_SLOW |
106 AT_PARALYZE | AT_TURN_UNDEAD | AT_FEAR | AT_DEPLETE | AT_DEATH |
107 AT_COUNTERSPELL | AT_HOLYWORD | AT_BLIND | AT_LIFE_STEALING | AT_MAGIC);
108
109 if (type == 0)
110 return TRUE;
111 if (roll == 20)
112 return TRUE;
113 if (roll == 1)
114 return FALSE;
115
116 for (number = 0; number < NROFATTACKS; number++)
117 {
118 i = 1 << number;
119 if (!(i & type))
120 continue;
121 attacks++;
122 if (op->resist[number] == 100)
123 saves++;
124 else if (roll >= mt->save[number] - op->magic - op->resist[number] / 100)
125 saves++;
126 else if ((20 - mt->save[number]) / 3 > originator->stats.dam)
127 saves++;
128 }
129
130 if (saves == attacks || attacks == 0)
131 return TRUE;
132
133 if (saves == 0 || (rndm (1, attacks) > saves))
134 return FALSE;
135
136 return TRUE;
137 }
138
139 /* This function calls did_make_save_item. It then performs the
140 * appropriate actions to the item (such as burning the item up,
141 * calling cancellation, etc.)
142 */
143 void
144 save_throw_object (object *op, int type, object *originator)
145 {
146 if (!did_make_save_item (op, type, originator))
147 {
148 object *env = op->env;
149 int x = op->x, y = op->y;
150 maptile *m = op->map;
151
152 op = stop_item (op);
153 if (op == NULL)
154 return;
155
156 /* Hacked the following so that type LIGHTER will work.
157 * Also, objects which are potenital "lights" that are hit by
158 * flame/elect attacks will be set to glow. "lights" are any
159 * object with +/- glow_radius and an "other_arch" to change to.
160 * (and please note that we cant fail our save and reach this
161 * function if the object doesnt contain a material that can burn.
162 * So forget lighting magical swords on fire with this!) -b.t.
163 */
164 if (type & (AT_FIRE | AT_ELECTRICITY) && op->other_arch && QUERY_FLAG (op, FLAG_IS_LIGHTABLE))
165 {
166 const char *arch = op->other_arch->archname;
167
168 op = decrease_ob_nr (op, 1);
169
170 if (op)
171 fix_stopped_item (op, m, originator);
172
173 if ((op = get_archetype (arch)) != NULL)
174 {
175 if (env)
176 {
177 op->x = env->x, op->y = env->y;
178 insert_ob_in_ob (op, env);
179 if (env->contr)
180 esrv_send_item (env, op);
181 }
182 else
183 {
184 op->x = x, op->y = y;
185 insert_ob_in_map (op, m, originator, 0);
186 }
187 }
188
189 return;
190 }
191
192 if (type & AT_CANCELLATION)
193 { /* Cancellation. */
194 cancellation (op);
195 fix_stopped_item (op, m, originator);
196 return;
197 }
198
199 if (op->nrof > 1)
200 {
201 op = decrease_ob_nr (op, rndm (0, op->nrof - 1));
202
203 if (op)
204 fix_stopped_item (op, m, originator);
205 }
206 else
207 {
208 if (op->env)
209 {
210 object *tmp = op->in_player ();
211
212 if (tmp)
213 esrv_del_item (tmp->contr, op->count);
214 }
215
216 op->destroy ();
217 }
218
219 if (type & (AT_FIRE | AT_ELECTRICITY))
220 if (env)
221 {
222 op = get_archetype ("burnout");
223 op->x = env->x, op->y = env->y;
224 insert_ob_in_ob (op, env);
225 }
226 else
227 replace_insert_ob_in_map ("burnout", originator);
228
229 return;
230 }
231
232 /* The value of 50 is arbitrary. */
233 if (type & AT_COLD && (op->resist[ATNR_COLD] < 50) && !QUERY_FLAG (op, FLAG_NO_PICK) && (RANDOM () & 2))
234 {
235 object *tmp;
236 archetype *at = archetype::find ("icecube");
237
238 if (at == NULL)
239 return;
240
241 op = stop_item (op);
242 if (op == NULL)
243 return;
244
245 if ((tmp = present_arch (at, op->map, op->x, op->y)) == NULL)
246 {
247 tmp = arch_to_object (at);
248 tmp->x = op->x, tmp->y = op->y;
249 /* This was in the old (pre new movement code) -
250 * icecubes have slow_move set to 1 - don't want
251 * that for ones we create.
252 */
253 tmp->move_slow_penalty = 0;
254 tmp->move_slow = 0;
255 insert_ob_in_map (tmp, op->map, originator, 0);
256 }
257
258 if (!QUERY_FLAG (op, FLAG_REMOVED))
259 op->remove ();
260
261 insert_ob_in_ob (op, tmp);
262 return;
263 }
264 }
265
266 /* Object op is hitting the map.
267 * op is going in direction 'dir'
268 * type is the attacktype of the object.
269 * full_hit is set if monster area does not matter.
270 * returns 1 if it hits something, 0 otherwise.
271 */
272 int
273 hit_map (object *op, int dir, int type, int full_hit)
274 {
275 maptile *map;
276 sint16 x, y;
277 int retflag = 0; /* added this flag.. will return 1 if it hits a monster */
278
279 if (QUERY_FLAG (op, FLAG_FREED))
280 {
281 LOG (llevError, "BUG: hit_map(): free object\n");
282 return 0;
283 }
284
285 if (QUERY_FLAG (op, FLAG_REMOVED) || op->env != NULL)
286 {
287 LOG (llevError, "BUG: hit_map(): hitter (arch %s, name %s) not on a map\n", &op->arch->archname, &op->name);
288 return 0;
289 }
290
291 if (!op->map)
292 {
293 LOG (llevError, "BUG: hit_map(): %s has no map\n", &op->name);
294 return 0;
295 }
296
297 if (op->head)
298 op = op->head;
299
300 map = op->map;
301 x = op->x + freearr_x[dir];
302 y = op->y + freearr_y[dir];
303
304 if (!xy_normalise (map, x, y))
305 return 0;
306
307 // elmex: a safe map tile can't be hit!
308 // this should prevent most harmful effects on items and players there.
309 mapspace &ms = map->at (x, y);
310
311 if (ms.flags () & P_SAFE)
312 return 0;
313
314 /* peterm: a few special cases for special attacktypes --counterspell
315 * must be out here because it strikes things which are not alive
316 */
317 if (type & (AT_COUNTERSPELL | AT_CHAOS))
318 {
319 if (type & AT_COUNTERSPELL)
320 {
321 counterspell (op, dir); /* see spell_effect.c */
322
323 /* If the only attacktype is counterspell or magic, don't need
324 * to do any further processing.
325 */
326 if (!(type & ~(AT_COUNTERSPELL | AT_MAGIC)))
327 return 0;
328
329 type &= ~AT_COUNTERSPELL;
330 }
331
332 if (type & AT_CHAOS)
333 {
334 shuffle_attack (op, 1); /* flag tells it to change the face */
335 update_object (op, UP_OBJ_FACE);
336 type &= ~AT_CHAOS;
337 }
338 }
339
340 /* There may still be objects that were above 'next', but there is no
341 * simple way to find out short of copying all object references and
342 * tags into a temporary array before we start processing the first
343 * object. That's why we just abort on destroy.
344 *
345 * This happens whenever attack spells (like fire) hit a pile
346 * of objects. This is not a bug - nor an error.
347 */
348 for (object *next = ms.bot; next && !next->destroyed (); )
349 {
350 object *tmp = next;
351 next = tmp->above;
352
353 /* Something could have happened to 'tmp' while 'tmp->below' was processed.
354 * For example, 'tmp' was put in an icecube.
355 * This is one of the few cases where on_same_map should not be used.
356 */
357 if (tmp->map != map || tmp->x != x || tmp->y != y)
358 continue;
359
360 if (QUERY_FLAG (tmp, FLAG_ALIVE))
361 {
362 hit_player (tmp, op->stats.dam, op, type, full_hit);
363 retflag |= 1;
364
365 if (op->destroyed ())
366 break;
367 }
368 /* Here we are potentially destroying an object. If the object has
369 * NO_PASS set, it is also immune - you can't destroy walls. Note
370 * that weak walls have is_alive set, which prevent objects from
371 * passing over/through them. We don't care what type of movement
372 * the wall blocks - if it blocks any type of movement, can't be
373 * destroyed right now.
374 */
375 else if (tmp->materialname && op->stats.dam > 0 && !tmp->move_block)
376 {
377 save_throw_object (tmp, type, op);
378
379 if (op->destroyed ())
380 break;
381 }
382 }
383
384 return 0;
385 }
386
387 void
388 attack_message (int dam, int type, object *op, object *hitter)
389 {
390 char buf[MAX_BUF], buf1[MAX_BUF], buf2[MAX_BUF];
391 int i, found = 0;
392 maptile *map;
393 object *next, *tmp;
394
395 /* put in a few special messages for some of the common attacktypes
396 * a player might have. For example, fire, electric, cold, etc
397 * [garbled 20010919]
398 */
399
400 if (dam == 9998 && op->type == DOOR)
401 {
402 sprintf (buf1, "unlock %s", &op->name);
403 sprintf (buf2, " unlocks");
404 found++;
405 }
406 if (dam < 0)
407 {
408 sprintf (buf1, "hit %s", &op->name);
409 sprintf (buf2, " hits");
410 found++;
411 }
412 else if (dam == 0)
413 {
414 sprintf (buf1, "missed %s", &op->name);
415 sprintf (buf2, " misses");
416 found++;
417 }
418 else if ((hitter->type == DISEASE || hitter->type == SYMPTOM ||
419 hitter->type == POISONING || (type & AT_POISON && op->is_alive ())) && !found)
420 {
421 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_SUFFER][i].level != -1; i++)
422 if (dam < attack_mess[ATM_SUFFER][i].level || attack_mess[ATM_SUFFER][i + 1].level == -1)
423 {
424 sprintf (buf1, "%s %s%s", attack_mess[ATM_SUFFER][i].buf1, &op->name, attack_mess[ATM_SUFFER][i].buf2);
425 strcpy (buf2, attack_mess[ATM_SUFFER][i].buf3);
426 found++;
427 break;
428 }
429 }
430 else if (op->type == DOOR && !found)
431 {
432 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_DOOR][i].level != -1; i++)
433 if (dam < attack_mess[ATM_DOOR][i].level || attack_mess[ATM_DOOR][i + 1].level == -1)
434 {
435 sprintf (buf1, "%s %s%s", attack_mess[ATM_DOOR][i].buf1, &op->name, attack_mess[ATM_DOOR][i].buf2);
436 strcpy (buf2, attack_mess[ATM_DOOR][i].buf3);
437 found++;
438 break;
439 }
440 }
441 else if (hitter->type == PLAYER && op->is_alive ())
442 {
443 if (USING_SKILL (hitter, SK_KARATE))
444 {
445 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_KARATE][i].level != -1; i++)
446 if (dam < attack_mess[ATM_KARATE][i].level || attack_mess[ATM_KARATE][i + 1].level == -1)
447 {
448 sprintf (buf1, "%s %s%s", attack_mess[ATM_KARATE][i].buf1, &op->name, attack_mess[ATM_KARATE][i].buf2);
449 strcpy (buf2, attack_mess[ATM_KARATE][i].buf3);
450 found++;
451 break;
452 }
453 }
454 else if (USING_SKILL (hitter, SK_CLAWING))
455 {
456 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_CLAW][i].level != -1; i++)
457 if (dam < attack_mess[ATM_CLAW][i].level || attack_mess[ATM_CLAW][i + 1].level == -1)
458 {
459 sprintf (buf1, "%s %s%s", attack_mess[ATM_CLAW][i].buf1, &op->name, attack_mess[ATM_CLAW][i].buf2);
460 strcpy (buf2, attack_mess[ATM_CLAW][i].buf3);
461 found++;
462 break;
463 }
464 }
465 else if (USING_SKILL (hitter, SK_PUNCHING))
466 {
467 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_PUNCH][i].level != -1; i++)
468 if (dam < attack_mess[ATM_PUNCH][i].level || attack_mess[ATM_PUNCH][i + 1].level == -1)
469 {
470 sprintf (buf1, "%s %s%s", attack_mess[ATM_PUNCH][i].buf1, &op->name, attack_mess[ATM_PUNCH][i].buf2);
471 strcpy (buf2, attack_mess[ATM_PUNCH][i].buf3);
472 found++;
473 break;
474 }
475 }
476 }
477
478 if (found)
479 {
480 /* done */
481 }
482 else if (hitter->is_arrow () && (type == AT_PHYSICAL || type == AT_MAGIC))
483 {
484 sprintf (buf1, "hit"); /* just in case */
485 for (i = 0; i < MAXATTACKMESS; i++)
486 if (dam < attack_mess[ATM_ARROW][i].level || attack_mess[ATM_ARROW][i + 1].level == -1)
487 {
488 strcpy (buf2, attack_mess[ATM_ARROW][i].buf3);
489 found++;
490 break;
491 }
492 }
493 else if (type & AT_DRAIN && op->is_alive ())
494 {
495 /* drain is first, because some items have multiple attypes */
496 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_DRAIN][i].level != -1; i++)
497 if (dam < attack_mess[ATM_DRAIN][i].level || attack_mess[ATM_DRAIN][i + 1].level == -1)
498 {
499 sprintf (buf1, "%s %s%s", attack_mess[ATM_DRAIN][i].buf1, &op->name, attack_mess[ATM_DRAIN][i].buf2);
500 strcpy (buf2, attack_mess[ATM_DRAIN][i].buf3);
501 found++;
502 break;
503 }
504 }
505 else if (type & AT_ELECTRICITY && op->is_alive ())
506 {
507 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_ELEC][i].level != -1; i++)
508 if (dam < attack_mess[ATM_ELEC][i].level || attack_mess[ATM_ELEC][i + 1].level == -1)
509 {
510 sprintf (buf1, "%s %s%s", attack_mess[ATM_ELEC][i].buf1, &op->name, attack_mess[ATM_ELEC][i].buf2);
511 strcpy (buf2, attack_mess[ATM_ELEC][i].buf3);
512 found++;
513 break;
514 }
515 }
516 else if (type & AT_COLD && op->is_alive ())
517 {
518 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_COLD][i].level != -1; i++)
519 if (dam < attack_mess[ATM_COLD][i].level || attack_mess[ATM_COLD][i + 1].level == -1)
520 {
521 sprintf (buf1, "%s %s%s", attack_mess[ATM_COLD][i].buf1, &op->name, attack_mess[ATM_COLD][i].buf2);
522 strcpy (buf2, attack_mess[ATM_COLD][i].buf3);
523 found++;
524 break;
525 }
526 }
527 else if (type & AT_FIRE)
528 {
529 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_FIRE][i].level != -1; i++)
530 if (dam < attack_mess[ATM_FIRE][i].level || attack_mess[ATM_FIRE][i + 1].level == -1)
531 {
532 sprintf (buf1, "%s %s%s", attack_mess[ATM_FIRE][i].buf1, &op->name, attack_mess[ATM_FIRE][i].buf2);
533 strcpy (buf2, attack_mess[ATM_FIRE][i].buf3);
534 found++;
535 break;
536 }
537 }
538 else if (hitter->current_weapon != NULL)
539 {
540 int mtype;
541
542 switch (hitter->current_weapon->weapontype)
543 {
544 case WEAP_HIT:
545 mtype = ATM_BASIC;
546 break;
547 case WEAP_SLASH:
548 mtype = ATM_SLASH;
549 break;
550 case WEAP_PIERCE:
551 mtype = ATM_PIERCE;
552 break;
553 case WEAP_CLEAVE:
554 mtype = ATM_CLEAVE;
555 break;
556 case WEAP_SLICE:
557 mtype = ATM_SLICE;
558 break;
559 case WEAP_STAB:
560 mtype = ATM_STAB;
561 break;
562 case WEAP_WHIP:
563 mtype = ATM_WHIP;
564 break;
565 case WEAP_CRUSH:
566 mtype = ATM_CRUSH;
567 break;
568 case WEAP_BLUD:
569 mtype = ATM_BLUD;
570 break;
571 default:
572 mtype = ATM_BASIC;
573 break;
574 }
575 for (i = 0; i < MAXATTACKMESS && attack_mess[mtype][i].level != -1; i++)
576 if (dam < attack_mess[mtype][i].level || attack_mess[mtype][i + 1].level == -1)
577 {
578 sprintf (buf1, "%s %s%s", attack_mess[mtype][i].buf1, &op->name, attack_mess[mtype][i].buf2);
579 strcpy (buf2, attack_mess[mtype][i].buf3);
580 found++;
581 break;
582 }
583 }
584 else
585 {
586 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_BASIC][i].level != -1; i++)
587 if (dam < attack_mess[ATM_BASIC][i].level || attack_mess[ATM_BASIC][i + 1].level == -1)
588 {
589 sprintf (buf1, "%s %s%s", attack_mess[ATM_BASIC][i].buf1, &op->name, attack_mess[ATM_BASIC][i].buf2);
590 strcpy (buf2, attack_mess[ATM_BASIC][i].buf3);
591 found++;
592 break;
593 }
594 }
595
596 if (!found)
597 {
598 strcpy (buf1, "hit");
599 strcpy (buf2, " hits");
600 }
601
602 /* bail out if a monster is casting spells */
603 if (!(hitter->type == PLAYER || (hitter->owner != NULL && hitter->owner->type == PLAYER)))
604 return;
605
606 /* scale down magic considerably. */
607 if (type & AT_MAGIC && rndm (0, 5))
608 return;
609
610 /* Did a player hurt another player? Inform both! */
611 if (op->type == PLAYER && (hitter->owner == NULL ? hitter->type : hitter->owner->type) == PLAYER)
612 {
613 if (hitter->owner != NULL)
614 sprintf (buf, "%s's %s%s you.", &hitter->owner->name, &hitter->name, buf2);
615 else
616 {
617 sprintf (buf, "%s%s you.", &hitter->name, buf2);
618 if (dam != 0)
619 {
620 if (dam < 10)
621 play_sound_player_only (op->contr, SOUND_PLAYER_IS_HIT1, 0, 0);
622 else if (dam < 20)
623 play_sound_player_only (op->contr, SOUND_PLAYER_IS_HIT2, 0, 0);
624 else
625 play_sound_player_only (op->contr, SOUND_PLAYER_IS_HIT3, 0, 0);
626 }
627 }
628 new_draw_info (NDI_BLACK, 0, op, buf);
629 } /* end of player hitting player */
630
631 if (hitter->type == PLAYER)
632 {
633 sprintf (buf, "You %s.", buf1);
634 if (dam != 0)
635 {
636 if (dam < 10)
637 play_sound_player_only (hitter->contr, SOUND_PLAYER_HITS1, 0, 0);
638 else if (dam < 20)
639 play_sound_player_only (hitter->contr, SOUND_PLAYER_HITS2, 0, 0);
640 else
641 play_sound_player_only (hitter->contr, SOUND_PLAYER_HITS3, 0, 0);
642 }
643 new_draw_info (NDI_BLACK, 0, hitter, buf);
644 }
645 else if (hitter->owner != NULL && hitter->owner->type == PLAYER)
646 {
647 /* look for stacked spells and start reducing the message chances */
648 if (hitter->type == SPELL_EFFECT && (hitter->subtype == SP_EXPLOSION || hitter->subtype == SP_BULLET || hitter->subtype == SP_CONE))
649 {
650 i = 4;
651 map = hitter->map;
652 if (out_of_map (map, hitter->x, hitter->y))
653 return;
654
655 next = GET_MAP_OB (map, hitter->x, hitter->y);
656 if (next)
657 while (next)
658 {
659 if (next->type == SPELL_EFFECT && (next->subtype == SP_EXPLOSION || next->subtype == SP_BULLET || next->subtype == SP_CONE))
660 i *= 3;
661 tmp = next;
662 next = tmp->above;
663 }
664
665 if (i < 0)
666 return;
667
668 if (rndm (0, i) != 0)
669 return;
670 }
671 else if (rndm (0, 5) != 0)
672 return;
673
674 sprintf (buf, "Your %s%s %s.", &hitter->name, buf2, &op->name);
675 play_sound_map (op->map, op->x, op->y, SOUND_PLAYER_HITS4);
676 new_draw_info (NDI_BLACK, 0, hitter->owner, buf);
677 }
678 }
679
680
681 static int
682 get_attack_mode (object **target, object **hitter, int *simple_attack)
683 {
684 if (QUERY_FLAG (*target, FLAG_FREED) || QUERY_FLAG (*hitter, FLAG_FREED))
685 {
686 LOG (llevError, "BUG: get_attack_mode(): freed object\n");
687 return 1;
688 }
689 if ((*target)->head)
690 *target = (*target)->head;
691 if ((*hitter)->head)
692 *hitter = (*hitter)->head;
693 if ((*hitter)->env != NULL || (*target)->env != NULL)
694 {
695 *simple_attack = 1;
696 return 0;
697 }
698 if (QUERY_FLAG (*target, FLAG_REMOVED)
699 || QUERY_FLAG (*hitter, FLAG_REMOVED) || (*hitter)->map == NULL || !on_same_map ((*hitter), (*target)))
700 {
701 LOG (llevError, "BUG: hitter (arch %s, name %s) with no relation to " "target\n", &(*hitter)->arch->archname, &(*hitter)->name);
702 return 1;
703 }
704 *simple_attack = 0;
705 return 0;
706 }
707
708 static int
709 abort_attack (object *target, object *hitter, int simple_attack)
710 {
711
712 /* Check if target and hitter are still in a relation similar to the one
713 * determined by get_attack_mode(). Returns true if the relation has changed.
714 */
715 int new_mode;
716
717 if (hitter->env == target || target->env == hitter)
718 new_mode = 1;
719 else if (QUERY_FLAG (hitter, FLAG_REMOVED) || QUERY_FLAG (target, FLAG_REMOVED) || hitter->map == NULL || !on_same_map (hitter, target))
720 return 1;
721 else
722 new_mode = 0;
723 return new_mode != simple_attack;
724 }
725
726 static void thrown_item_effect (object *, object *);
727
728 static int
729 attack_ob_simple (object *op, object *hitter, int base_dam, int base_wc)
730 {
731 int simple_attack, roll, dam = 0;
732 uint32 type;
733 shstr op_name;
734
735 if (get_attack_mode (&op, &hitter, &simple_attack))
736 goto error;
737
738 if (hitter->current_weapon)
739 if (INVOKE_OBJECT (WEAPON_ATTACK, hitter->current_weapon, ARG_OBJECT (hitter), ARG_OBJECT (op)))
740 return RESULT_INT (0);
741
742 if (INVOKE_OBJECT (ATTACK, op, ARG_OBJECT (hitter)))
743 return RESULT_INT (0);
744
745 /*
746 * A little check to make it more difficult to dance forward and back
747 * to avoid ever being hit by monsters.
748 */
749 if (!simple_attack && QUERY_FLAG (op, FLAG_MONSTER) && op->speed_left > abs (op->speed) * -0.3f)
750 {
751 /* Decrease speed BEFORE calling process_object. Otherwise, an
752 * infinite loop occurs, with process_object calling move_monster,
753 * which then gets here again. By decreasing the speed before
754 * we call process_object, the 'if' statement above will fail.
755 */
756 --op->speed_left;
757 process_object (op);
758
759 if (op->destroyed () || hitter->destroyed () || abort_attack (op, hitter, simple_attack))
760 goto error;
761 }
762
763 op_name = op->name;
764
765 roll = random_roll (1, 20, hitter, PREFER_HIGH);
766
767 /* Adjust roll for various situations. */
768 if (!simple_attack)
769 roll += adj_attackroll (hitter, op);
770
771 /* See if we hit the creature */
772 if (roll == 20 || op->stats.ac >= base_wc - roll)
773 {
774 int hitdam = base_dam;
775
776 if (!simple_attack)
777 {
778 /* If you hit something, the victim should *always* wake up.
779 * Before, invisible hitters could avoid doing this.
780 * -b.t. */
781 if (QUERY_FLAG (op, FLAG_SLEEP))
782 CLEAR_FLAG (op, FLAG_SLEEP);
783
784 /* If the victim can't see the attacker, it may alert others
785 * for help. */
786 if (op->type != PLAYER && !can_see_enemy (op, hitter) && !op->owner && rndm (0, op->stats.Int))
787 npc_call_help (op);
788
789 /* if you were hidden and hit by a creature, you are discovered */
790 if (op->hide && QUERY_FLAG (hitter, FLAG_ALIVE))
791 {
792 make_visible (op);
793
794 if (op->type == PLAYER)
795 new_draw_info (NDI_UNIQUE, 0, op, "You were hit by a wild attack. " "You are no longer hidden!");
796 }
797
798 /* thrown items (hitter) will have various effects
799 * when they hit the victim. For things like thrown daggers,
800 * this sets 'hitter' to the actual dagger, and not the
801 * wrapper object.
802 */
803 thrown_item_effect (hitter, op);
804
805 if (hitter->destroyed () || op->destroyed () || abort_attack (op, hitter, simple_attack))
806 goto leave;
807 }
808
809 /* Need to do at least 1 damage, otherwise there is no point
810 * to go further and it will cause FPE's below.
811 */
812 if (hitdam <= 0)
813 hitdam = 1;
814
815 type = hitter->attacktype;
816
817 if (!type)
818 type = AT_PHYSICAL;
819
820 /* Handle monsters that hit back */
821 if (!simple_attack && QUERY_FLAG (op, FLAG_HITBACK) && QUERY_FLAG (hitter, FLAG_ALIVE))
822 {
823 if (op->attacktype & AT_ACID && hitter->type == PLAYER)
824 new_draw_info (NDI_UNIQUE, 0, hitter, "You are splashed by acid!\n");
825
826 hit_player (hitter, random_roll (0, (op->stats.dam), hitter, PREFER_LOW), op, op->attacktype, 1);
827
828 if (op->destroyed () || hitter->destroyed () || abort_attack (op, hitter, simple_attack))
829 goto leave;
830 }
831
832 /* In the new attack code, it should handle multiple attack
833 * types in its area, so remove it from here.
834 */
835 dam = hit_player (op, random_roll (1, hitdam, hitter, PREFER_HIGH), hitter, type, 1);
836
837 if (op->destroyed () || hitter->destroyed () || abort_attack (op, hitter, simple_attack))
838 goto leave;
839 } /* end of if hitter hit op */
840 /* if we missed, dam=0 */
841
842 /*attack_message(dam, type, op, hitter); */
843
844 goto leave;
845
846 error:
847 dam = 1;
848
849 leave:
850
851 return dam;
852 }
853
854 int
855 attack_ob (object *op, object *hitter)
856 {
857 hitter = hitter->head_ ();
858
859 return attack_ob_simple (op, hitter, hitter->stats.dam, hitter->stats.wc);
860 }
861
862 /* op is the arrow, tmp is what is stopping the arrow.
863 *
864 * Returns 1 if op was inserted into tmp's inventory, 0 otherwise.
865 */
866 static int
867 stick_arrow (object *op, object *tmp)
868 {
869 /* If the missile hit a player, we insert it in their inventory.
870 * However, if the missile is heavy, we don't do so (assume it falls
871 * to the ground after a hit). What a good value for this is up to
872 * debate - 5000 is 5 kg, so arrows, knives, and other light weapons
873 * stick around.
874 */
875 if (op->weight <= 5000 && tmp->stats.hp >= 0)
876 {
877 tmp = tmp->head_ ();
878
879 op->remove ();
880 op = insert_ob_in_ob (op, tmp);
881
882 if (tmp->type == PLAYER)
883 esrv_send_item (tmp, op);
884
885 return 1;
886 }
887 else
888 return 0;
889 }
890
891 /* hit_with_arrow() disassembles the missile, attacks the victim and
892 * reassembles the missile.
893 *
894 * It returns a pointer to the reassembled missile, or NULL if the missile
895 * isn't available anymore.
896 */
897 object *
898 hit_with_arrow (object *op, object *victim)
899 {
900 object *container, *hitter;
901 int hit_something = 0;
902
903 /* Disassemble missile */
904 if (op->inv)
905 {
906 container = op;
907 hitter = op->inv;
908 hitter->remove ();
909 insert_ob_in_map (hitter, container->map, hitter, INS_NO_MERGE | INS_NO_WALK_ON);
910 /* Note that we now have an empty THROWN_OBJ on the map. Code that
911 * might be called until this THROWN_OBJ is either reassembled or
912 * removed at the end of this function must be able to deal with empty
913 * THROWN_OBJs. */
914 }
915 else
916 {
917 container = 0;
918 hitter = op;
919 }
920
921 /* Try to hit victim */
922 hit_something = attack_ob_simple (victim, hitter, op->stats.dam, op->stats.wc);
923
924 /* Arrow attacks door, rune of summoning is triggered, demon is put on
925 * arrow, move_apply() calls this function, arrow sticks in demon,
926 * attack_ob_simple() returns, and we've got an arrow that still exists
927 * but is no longer on the map. Ugh. (Beware: Such things can happen at
928 * other places as well!)
929 */
930 if (hitter->destroyed () || hitter->env != NULL)
931 {
932 if (container)
933 {
934 container->remove ();
935 container->destroy ();
936 }
937
938 return 0;
939 }
940
941 /* Missile hit victim */
942 /* if the speed is > 10, then this is a fast moving arrow, we go straight
943 * through the target
944 */
945 if (hit_something && op->speed <= 10.0)
946 {
947 /* Stop arrow */
948 if (!container)
949 {
950 hitter = fix_stopped_arrow (hitter);
951 if (!hitter)
952 return 0;
953 }
954 else
955 container->destroy ();
956
957 /* Try to stick arrow into victim */
958 if (!victim->destroyed () && stick_arrow (hitter, victim))
959 return 0;
960
961 /* Else try to put arrow on victim's map square
962 * remove check for P_WALL here. If the arrow got to this
963 * space, that is good enough - with the new movement code,
964 * there is now the potential for lots of spaces where something
965 * can fly over but not otherwise move over. What is the correct
966 * way to handle those otherwise?
967 */
968 if (victim->x != hitter->x || victim->y != hitter->y)
969 {
970 hitter->remove ();
971 hitter->x = victim->x;
972 hitter->y = victim->y;
973 insert_ob_in_map (hitter, victim->map, hitter, 0);
974 }
975 else
976 /* Else leave arrow where it is */
977 merge_ob (hitter, NULL);
978
979 return 0;
980 }
981
982 if (hit_something && op->speed >= 10.0)
983 op->speed -= 1.0;
984
985 /* Missile missed victim - reassemble missile */
986 if (container)
987 {
988 hitter->remove ();
989 insert_ob_in_ob (hitter, container);
990 }
991
992 return op;
993 }
994
995 void
996 tear_down_wall (object *op)
997 {
998 int perc = 0;
999
1000 if (!op->stats.maxhp)
1001 {
1002 LOG (llevError, "TEAR_DOWN wall %s had no maxhp.\n", &op->name);
1003 perc = 1;
1004 }
1005 else if (!GET_ANIM_ID (op))
1006 {
1007 /* Object has been called - no animations, so remove it */
1008 if (op->stats.hp < 0)
1009 op->destroy ();
1010
1011 return; /* no animations, so nothing more to do */
1012 }
1013
1014 perc = NUM_ANIMATIONS (op) - ((int) NUM_ANIMATIONS (op) * op->stats.hp) / op->stats.maxhp;
1015
1016 if (perc >= (int) NUM_ANIMATIONS (op))
1017 perc = NUM_ANIMATIONS (op) - 1;
1018 else if (perc < 1)
1019 perc = 1;
1020
1021 SET_ANIMATION (op, perc);
1022 update_object (op, UP_OBJ_FACE);
1023
1024 if (perc == NUM_ANIMATIONS (op) - 1)
1025 { /* Reached the last animation */
1026 if (op->face == blank_face)
1027 /* If the last face is blank, remove the ob */
1028 op->destroy ();
1029 else
1030 { /* The last face was not blank, leave an image */
1031 CLEAR_FLAG (op, FLAG_BLOCKSVIEW);
1032 update_all_los (op->map, op->x, op->y);
1033 op->move_block = 0;
1034 CLEAR_FLAG (op, FLAG_ALIVE);
1035 }
1036 }
1037 }
1038
1039 void
1040 scare_creature (object *target, object *hitter)
1041 {
1042 object *owner = hitter->owner;
1043
1044 if (!owner)
1045 owner = hitter;
1046
1047 SET_FLAG (target, FLAG_SCARED);
1048 if (!target->enemy)
1049 target->enemy = owner;
1050 }
1051
1052 /* This returns the amount of damage hitter does to op with the
1053 * appropriate attacktype. Only 1 attacktype should be set at a time.
1054 * This doesn't damage the player, but returns how much it should
1055 * take. However, it will do other effects (paralyzation, slow, etc.)
1056 * Note - changed for PR code - we now pass the attack number and not
1057 * the attacktype. Makes it easier for the PR code. */
1058 int
1059 hit_player_attacktype (object *op, object *hitter, int dam, uint32 attacknum, int magic)
1060 {
1061 int doesnt_slay = 1;
1062
1063 /* Catch anyone that may be trying to send us a bitmask instead of the number */
1064 if (attacknum >= NROFATTACKS)
1065 {
1066 LOG (llevError, "hit_player_attacktype: Invalid attacknumber passed: %u\n", attacknum);
1067 return 0;
1068 }
1069
1070 if (dam < 0)
1071 {
1072 LOG (llevError, "hit_player_attacktype called with negative damage %d (hitter %s, target %s)\n",
1073 dam, hitter->debug_desc (), op->debug_desc ());
1074 return 0;
1075 }
1076
1077 /* AT_INTERNAL is supposed to do exactly dam. Put a case here so
1078 * people can't mess with that or it otherwise get confused. */
1079 if (attacknum == ATNR_INTERNAL)
1080 return dam;
1081
1082 if (hitter->slaying)
1083 {
1084 if ((op->race && strstr (hitter->slaying, op->race))
1085 || (op->arch && op->arch->archname && strstr (op->arch->archname, hitter->slaying)))
1086 {
1087 doesnt_slay = 0;
1088 dam *= 3;
1089 }
1090 }
1091
1092 /* Adjust the damage for resistance. Note that neg. values increase damage. */
1093 if (op->resist[attacknum])
1094 {
1095 /* basically: dam = dam*(100-op->resist[attacknum])/100;
1096 * in case 0>dam>1, we try to "simulate" a float value-effect */
1097 dam *= (100 - op->resist[attacknum]);
1098 if (dam >= 100)
1099 dam /= 100;
1100 else
1101 dam = (dam > (random_roll (0, 99, op, PREFER_LOW))) ? 1 : 0;
1102 }
1103
1104 /* Special hack. By default, if immune to something, you
1105 * shouldn't need to worry. However, acid is an exception, since
1106 * it can still damage your items. Only include attacktypes if
1107 * special processing is needed */
1108
1109 if (op->resist[attacknum] >= 100
1110 && doesnt_slay
1111 && attacknum != ATNR_ACID)
1112 return 0;
1113
1114 /* Keep this in order - makes things easier to find */
1115
1116 switch (attacknum)
1117 {
1118 case ATNR_PHYSICAL:
1119 /* here also check for diseases */
1120 check_physically_infect (op, hitter);
1121 break;
1122
1123 /* Don't need to do anything for:
1124 magic,
1125 fire,
1126 electricity,
1127 cold */
1128
1129 case ATNR_CONFUSION:
1130 case ATNR_POISON:
1131 case ATNR_SLOW:
1132 case ATNR_PARALYZE:
1133 case ATNR_FEAR:
1134 case ATNR_CANCELLATION:
1135 case ATNR_DEPLETE:
1136 case ATNR_BLIND:
1137 {
1138 /* chance for inflicting a special attack depends on the
1139 * difference between attacker's and defender's level
1140 */
1141 int level_diff = MIN (110, MAX (0, op->level - hitter->level));
1142
1143 /* First, only creatures/players with speed can be affected.
1144 * Second, just getting hit doesn't mean it always affects
1145 * you. Third, you still get a saving through against the
1146 * effect.
1147 */
1148 if (op->speed &&
1149 (QUERY_FLAG (op, FLAG_MONSTER) || op->type == PLAYER) &&
1150 !(rndm (0, (attacknum == ATNR_SLOW ? 6 : 3) - 1)) && !did_make_save (op, level_diff, op->resist[attacknum] / 10))
1151 {
1152
1153 /* Player has been hit by something */
1154 if (attacknum == ATNR_CONFUSION)
1155 confuse_player (op, hitter, dam);
1156 else if (attacknum == ATNR_POISON)
1157 poison_player (op, hitter, dam);
1158 else if (attacknum == ATNR_SLOW)
1159 slow_player (op, hitter, dam);
1160 else if (attacknum == ATNR_PARALYZE)
1161 paralyze_player (op, hitter, dam);
1162 else if (attacknum == ATNR_FEAR)
1163 scare_creature (op, hitter);
1164 else if (attacknum == ATNR_CANCELLATION)
1165 cancellation (op);
1166 else if (attacknum == ATNR_DEPLETE)
1167 op->drain_stat ();
1168 else if (attacknum == ATNR_BLIND && !QUERY_FLAG (op, FLAG_UNDEAD) && !QUERY_FLAG (op, FLAG_GENERATOR))
1169 blind_player (op, hitter, dam);
1170 }
1171
1172 dam = 0; /* These are all effects and don't do real damage */
1173 }
1174 break;
1175
1176 case ATNR_ACID:
1177 {
1178 int flag = 0;
1179
1180 /* Items only get corroded if you're not on a battleground and
1181 * if your acid resistance is below 50%. */
1182 if (!op_on_battleground (op, NULL, NULL) && (op->resist[ATNR_ACID] < 50))
1183 {
1184 for (object *tmp = op->inv; tmp; tmp = tmp->below)
1185 {
1186 if (tmp->invisible)
1187 continue;
1188 if (!QUERY_FLAG (tmp, FLAG_APPLIED) || (tmp->resist[ATNR_ACID] >= 10))
1189 /* >= 10% acid res. on items will protect these */
1190 continue;
1191 if (!(tmp->materials & M_IRON))
1192 continue;
1193 if (tmp->magic < -4) /* Let's stop at -5 */
1194 continue;
1195 if (tmp->type == RING
1196 /* removed boots and gloves from exclusion list in PR */
1197 || tmp->type == GIRDLE
1198 || tmp->type == AMULET
1199 || tmp->type == WAND
1200 || tmp->type == ROD
1201 || tmp->type == HORN)
1202 continue; /* To avoid some strange effects */
1203
1204 /* High damage acid has better chance of corroding
1205 objects */
1206 if (rndm (0, dam + 4) > random_roll (0, 39, op, PREFER_HIGH) + 2 * tmp->magic)
1207 {
1208 if (op->type == PLAYER)
1209 /* Make this more visible */
1210 new_draw_info_format (NDI_UNIQUE | NDI_RED, 0, op,
1211 "The %s's acid corrodes your %s!", query_name (hitter), query_name (tmp));
1212 flag = 1;
1213 tmp->magic--;
1214 if (op->type == PLAYER)
1215 esrv_send_item (op, tmp);
1216 }
1217 }
1218
1219 if (flag)
1220 op->update_stats (); /* Something was corroded */
1221 }
1222 }
1223 break;
1224
1225 case ATNR_DRAIN:
1226 {
1227 /* rate is the proportion of exp drained. High rate means
1228 * not much is drained, low rate means a lot is drained.
1229 */
1230 int rate;
1231
1232 if (op->resist[ATNR_DRAIN] >= 0)
1233 rate = 400 + op->resist[ATNR_DRAIN] * 3;
1234 else
1235 rate = 400 * 100 / (100 - op->resist[ATNR_DRAIN]);
1236
1237 if (op->stats.exp <= rate)
1238 {
1239 if (op->type == GOLEM)
1240 dam = 999; /* Its force is "sucked" away. 8) */
1241 else
1242 /* If we can't drain, lets try to do physical damage */
1243 dam = hit_player_attacktype (op, hitter, dam, ATNR_PHYSICAL, magic);
1244 }
1245 else
1246 {
1247 /* Randomly give the hitter some hp */
1248 if (hitter->stats.hp < hitter->stats.maxhp &&
1249 (op->level > hitter->level) && random_roll (0, (op->level - hitter->level + 2), hitter, PREFER_HIGH) > 3)
1250 hitter->stats.hp++;
1251
1252 /* Can't do drains on battleground spaces.
1253 * Move the wiz check up here - before, the hitter wouldn't gain exp
1254 * exp, but the wiz would still lose exp! If drainee is a wiz,
1255 * nothing happens.
1256 * Try to credit the owner. We try to display player -> player drain
1257 * attacks, hence all the != PLAYER checks.
1258 */
1259 if (!op_on_battleground (hitter, NULL, NULL) && !QUERY_FLAG (op, FLAG_WIZ))
1260 {
1261 object *owner = hitter->owner;
1262
1263 if (owner && owner != hitter)
1264 {
1265 if (op->type != PLAYER || owner->type != PLAYER)
1266 change_exp (owner, op->stats.exp / (rate * 2),
1267 hitter->chosen_skill ? hitter->chosen_skill->skill : (const char *) 0, SK_EXP_TOTAL);
1268 }
1269 else if (op->type != PLAYER || hitter->type != PLAYER)
1270 change_exp (hitter, op->stats.exp / (rate * 2),
1271 hitter->chosen_skill ? hitter->chosen_skill->skill : (const char *) 0, 0);
1272
1273 change_exp (op, -op->stats.exp / rate, NULL, 0);
1274 }
1275
1276 dam = 1; /* Drain is an effect. Still return 1 - otherwise, if you have pure
1277 * drain attack, you won't know that you are actually sucking out EXP,
1278 * as the messages will say you missed
1279 */
1280 }
1281 }
1282 break;
1283
1284 case ATNR_TURN_UNDEAD:
1285 {
1286 if (QUERY_FLAG (op, FLAG_UNDEAD))
1287 {
1288 object *owner = hitter->owner ? (object *)hitter->owner : hitter;
1289 object *god = find_god (determine_god (owner));
1290 int div = 1;
1291
1292 /* if undead are not an enemy of your god, you turn them
1293 * at half strength */
1294 if (!god || !god->slaying || strstr (god->slaying, undead_name) == NULL)
1295 div = 2;
1296
1297 /* Give a bonus if you resist turn undead */
1298 if (op->level * div < (turn_bonus[owner->stats.Wis] + owner->level + (op->resist[ATNR_TURN_UNDEAD] / 100)))
1299 scare_creature (op, owner);
1300 }
1301 else
1302 dam = 0; /* don't damage non undead - should we damage
1303 undead? */
1304 }
1305 break;
1306
1307 case ATNR_DEATH:
1308 deathstrike_player (op, hitter, &dam);
1309 break;
1310
1311 case ATNR_CHAOS:
1312 LOG (llevError, "%s was hit by %s with non-specific chaos.\n", op->debug_desc (), hitter->debug_desc ());
1313 dam = 0;
1314 break;
1315
1316 case ATNR_COUNTERSPELL:
1317 LOG (llevError, "%s was hit by %s with counterspell attack.\n", op->debug_desc (), hitter->debug_desc ());
1318 dam = 0;
1319 /* This should never happen. Counterspell is handled
1320 * seperately and filtered out. If this does happen,
1321 * Counterspell has no effect on anything but spells, so it
1322 * does no damage. */
1323 break;
1324
1325 case ATNR_HOLYWORD:
1326 {
1327 /* This has already been handled by hit_player,
1328 * no need to check twice -- DAMN */
1329 object *owner = hitter->owner ? (object *)hitter->owner : hitter;
1330
1331 /* As with turn undead above, give a bonus on the saving throw */
1332 if ((op->level + (op->resist[ATNR_HOLYWORD] / 100)) < owner->level + turn_bonus[owner->stats.Wis])
1333 scare_creature (op, owner);
1334 }
1335 break;
1336
1337 case ATNR_LIFE_STEALING:
1338 {
1339 int new_hp;
1340
1341 /* this is replacement to drain for players, instead of taking
1342 * exp it takes hp. It is geared for players, probably not
1343 * much use giving it to monsters
1344 *
1345 * life stealing doesn't do a lot of damage, but it gives the
1346 * damage it does do to the player. Given that,
1347 * it only does 1/10'th normal damage (hence the divide by
1348 * 1000).
1349 */
1350 /* You can't steal life from something undead */
1351 if ((op->type == GOLEM) || (QUERY_FLAG (op, FLAG_UNDEAD)))
1352 return 0;
1353
1354 /* If drain protection is higher than life stealing, use that */
1355 if (op->resist[ATNR_DRAIN] >= op->resist[ATNR_LIFE_STEALING])
1356 dam = (dam * (100 - op->resist[ATNR_DRAIN])) / 3000;
1357 else
1358 dam = (dam * (100 - op->resist[ATNR_LIFE_STEALING])) / 3000;
1359
1360 /* You die at -1 hp, not zero. */
1361 if (dam > (op->stats.hp + 1))
1362 dam = op->stats.hp + 1;
1363
1364 new_hp = hitter->stats.hp + dam;
1365 if (new_hp > hitter->stats.maxhp)
1366 new_hp = hitter->stats.maxhp;
1367
1368 if (new_hp > hitter->stats.hp)
1369 hitter->stats.hp = new_hp;
1370 }
1371 }
1372
1373 return dam;
1374 }
1375
1376 /* GROS: This code comes from hit_player. It has been made external to
1377 * allow script procedures to "kill" objects in a combat-like fashion.
1378 * It was initially used by (kill-object) developed for the Collector's
1379 * Sword. Note that nothing has been changed from the original version
1380 * of the following code.
1381 * op is what is being killed.
1382 * dam is the damage done to it.
1383 * hitter is what is hitting it.
1384 * type is the attacktype.
1385 *
1386 * This function was a bit of a mess with hitter getting changed,
1387 * values being stored away but not used, etc. I've cleaned it up
1388 * a bit - I think it should be functionally equivalant.
1389 * MSW 2002-07-17
1390 */
1391 int
1392 kill_object (object *op, int dam, object *hitter, int type)
1393 {
1394 char buf[MAX_BUF];
1395 const char *skill;
1396 int maxdam = 0;
1397 int battleg = 0; /* true if op standing on battleground */
1398 int pk = 0; /* true if op and what controls hitter are both players */
1399 object *owner = 0;
1400 object *skop = 0;
1401
1402 if (op->stats.hp >= 0)
1403 return -1;
1404
1405 if (INVOKE_OBJECT (KILL, op, ARG_OBJECT (hitter)))
1406 return 0;
1407
1408 /* maxdam needs to be the amount of damage it took to kill
1409 * this creature. The function(s) that call us have already
1410 * adjusted the creatures HP total, so that is negative.
1411 */
1412 maxdam = dam + op->stats.hp + 1;
1413
1414 if (QUERY_FLAG (op, FLAG_BLOCKSVIEW))
1415 update_all_los (op->map, op->x, op->y); /* makes sure los will be recalculated */
1416
1417 if (op->type == DOOR)
1418 {
1419 op->set_speed (0.1f);
1420 op->speed_left = -0.05f;
1421 return maxdam;
1422 }
1423
1424 if (QUERY_FLAG (op, FLAG_FRIENDLY) && op->type != PLAYER)
1425 {
1426 op->destroy ();
1427 return maxdam;
1428 }
1429
1430 /* Now lets start dealing with experience we get for killing something */
1431
1432 owner = hitter->owner;
1433 if (!owner)
1434 owner = hitter;
1435
1436 /* is the victim (op) standing on battleground? */
1437 if (op_on_battleground (op, NULL, NULL))
1438 battleg = 1;
1439
1440 /* is this player killing? */
1441 if (op->type == PLAYER && owner->type == PLAYER)
1442 pk = 1;
1443
1444 /* Player killed something */
1445 if (owner->type == PLAYER)
1446 {
1447 Log_Kill (owner->name,
1448 query_name (op), op->type, (owner != hitter) ? query_name (hitter) : NULL, (owner != hitter) ? hitter->type : 0);
1449
1450 /* Log players killing other players - makes it easier to detect
1451 * and filter out malicious player killers - that is why the
1452 * ip address is included.
1453 */
1454 if (op->type == PLAYER && !battleg)
1455 {
1456 time_t t = time (NULL);
1457 struct tm *tmv;
1458 char buf[256];
1459
1460 tmv = localtime (&t);
1461 strftime (buf, 256, "%a %b %d %H:%M:%S %Y", tmv);
1462
1463 LOG (llevInfo, "%s PLAYER_KILL_PLAYER: %s (%s) killed %s\n", buf, &owner->name, owner->contr->ns->host, query_name (op));
1464 }
1465
1466 /* try to filter some things out - basically, if you are
1467 * killing a level 1 creature and your level 20, you
1468 * probably don't want to see that.
1469 */
1470 if (owner->level < op->level * 2 || op->stats.exp > 1000)
1471 {
1472 if (owner != hitter)
1473 new_draw_info_format (NDI_BLACK, 0, owner, "You killed %s with %s.", query_name (op), query_name (hitter));
1474 else
1475 new_draw_info_format (NDI_BLACK, 0, owner, "You killed %s.", query_name (op));
1476
1477 /* Only play sounds for melee kills */
1478 if (hitter->type == PLAYER)
1479 play_sound_map (owner->map, owner->x, owner->y, SOUND_PLAYER_KILLS);
1480 }
1481
1482 /* If a player kills another player, not on
1483 * battleground, the "killer" looses 1 luck. Since this is
1484 * not reversible, it's actually quite a pain IMHO. -AV
1485 * Fix bug in that we were changing the luck of the hitter, not
1486 * player that the object belonged to - so if you killed another player
1487 * with spells, pets, whatever, there was no penalty.
1488 * Changed to make luck penalty configurable in settings.
1489 */
1490 if (op->type == PLAYER && owner != op && !battleg)
1491 owner->change_luck (-settings.pk_luck_penalty);
1492
1493 /* This code below deals with finding the appropriate skill
1494 * to credit exp to. This is a bit problematic - we should
1495 * probably never really have to look at current_weapon->skill
1496 */
1497 skill = 0;
1498
1499 if (hitter->skill && hitter->type != PLAYER)
1500 skill = hitter->skill;
1501 else if (owner->chosen_skill)
1502 {
1503 skop = owner->chosen_skill;
1504 skill = skop->skill;
1505 }
1506 else if (QUERY_FLAG (owner, FLAG_READY_WEAPON))
1507 skill = owner->current_weapon->skill;
1508 else
1509 LOG (llevError, "kill_object - unable to find skill that killed monster\n");
1510
1511 /* We have the skill we want to credit to - now find the object this goes
1512 * to. Make sure skop is an actual skill, and not a skill tool!
1513 */
1514 if ((!skop || skop->type != SKILL) && skill)
1515 {
1516 int i;
1517
1518 for (i = 0; i < NUM_SKILLS; i++)
1519 if (owner->contr->last_skill_ob[i] && !strcmp (owner->contr->last_skill_ob[i]->skill, skill))
1520 {
1521 skop = owner->contr->last_skill_ob[i];
1522 break;
1523 }
1524 }
1525 } /* Was it a player that hit somethign */
1526 else
1527 skill = 0;
1528
1529 /* Pet (or spell) killed something. */
1530 if (owner != hitter)
1531 sprintf (buf, "%s killed %s with %s%s%s.", &owner->name,
1532 query_name (op), query_name (hitter), battleg ? " (duel)" : "", pk ? " (pk)" : "");
1533 else
1534 sprintf (buf, "%s killed %s%s%s%s.", &hitter->name, &op->name,
1535 (QUERY_FLAG (hitter, FLAG_MONSTER)) || hitter->type == PLAYER ?
1536 " in hand to hand combat" : "", battleg ? " (duel)" : "", pk ? " (pk)" : "");
1537
1538 /* These may have been set in the player code section above */
1539 if (!skop)
1540 skop = hitter->chosen_skill;
1541
1542 if (!skill && skop)
1543 skill = skop->skill;
1544
1545 new_draw_info (NDI_ALL, op->type == PLAYER ? 1 : 10, NULL, buf);
1546
1547 /* If you didn't kill yourself, and your not the wizard */
1548 if (owner != op && !QUERY_FLAG (op, FLAG_WIZ))
1549 {
1550 int exp;
1551
1552 /* Really don't give much experience for killing other players */
1553 // schmorp: temporarily? reduce the amount of exp gained for pking enourmously
1554 if (op->type == PLAYER)
1555 {
1556 if (battleg)
1557 {
1558 new_draw_info (NDI_UNIQUE, 0, owner, "Your foe has fallen!");
1559 new_draw_info (NDI_UNIQUE, 0, owner, "VICTORY!!!");
1560 }
1561 else
1562 exp = op->stats.exp / 1000;
1563 }
1564 else
1565 exp = calc_skill_exp (owner, op, skop);
1566
1567 /* if op is standing on "battleground" (arena), no way to gain
1568 * exp by killing him
1569 */
1570 if (battleg)
1571 exp = 0;
1572
1573 /* Don't know why this is set this way - doesn't make
1574 * sense to just divide everything by two for no reason.
1575 */
1576
1577 if (!settings.simple_exp)
1578 exp = exp / 2;
1579
1580 if (owner->type != PLAYER || owner->contr->party == NULL)
1581 change_exp (owner, exp, skill, 0);
1582 else
1583 {
1584 int shares = 0, count = 0;
1585 partylist *party = owner->contr->party;
1586
1587 add_kill_to_party (party, query_name (owner), query_name (op), exp);
1588
1589 for_all_players (pl)
1590 if (party && pl->ob->contr->party == party && on_same_map (pl->ob, owner))
1591 {
1592 count++;
1593 shares += (pl->ob->level + 4);
1594 }
1595
1596 if (count == 1 || shares > exp || !shares)
1597 change_exp (owner, exp, skill, SK_EXP_TOTAL);
1598 else
1599 {
1600 int share = exp / shares, given = 0, nexp;
1601
1602 for_all_players (pl)
1603 if (party && pl->ob->contr->party == party && on_same_map (pl->ob, owner))
1604 {
1605 nexp = (pl->ob->level + 4) * share;
1606 change_exp (pl->ob, nexp, skill, SK_EXP_TOTAL);
1607 given += nexp;
1608 }
1609
1610 exp -= given;
1611 /* give any remainder to the player */
1612 change_exp (owner, exp, skill, SK_EXP_ADD_SKILL);
1613 }
1614 } /* else part of a party */
1615 } /* end if person didn't kill himself */
1616
1617 if (op->type != PLAYER)
1618 {
1619 if (QUERY_FLAG (op, FLAG_FRIENDLY))
1620 {
1621 object *owner1 = op->owner;
1622
1623 if (owner1 && owner1->type == PLAYER)
1624 {
1625 play_sound_player_only (owner1->contr, SOUND_PET_IS_KILLED, 0, 0);
1626 /* Maybe we should include the owner that killed this, maybe not */
1627 new_draw_info_format (NDI_UNIQUE, 0, owner1, "Your pet, the %s, is killed by %s.", &op->name, &hitter->name);
1628 }
1629
1630 remove_friendly_object (op);
1631 }
1632
1633 op->destroy ();
1634 }
1635 else
1636 {
1637 /* Player has been killed! */
1638 if (owner->type == PLAYER)
1639 snprintf (op->contr->killer, sizeof (op->contr->killer), "%s the %s", &owner->name, owner->contr->title);
1640 else
1641 assign (op->contr->killer, hitter->name);
1642 }
1643
1644 /* This was return -1 - that doesn't seem correct - if we return -1, process
1645 * continues in the calling function.
1646 */
1647 return maxdam;
1648 }
1649
1650 /* Find out if this is friendly fire (PVP and attacker is peaceful) or not
1651 * Returns 0 this is not friendly fire
1652 */
1653 int
1654 friendly_fire (object *op, object *hitter)
1655 {
1656 object *owner;
1657 int friendlyfire;
1658
1659 if (hitter->head)
1660 hitter = hitter->head;
1661
1662 friendlyfire = 0;
1663
1664 if (op->type == PLAYER)
1665 {
1666 if (op_on_battleground (hitter, 0, 0))
1667 return 0;
1668
1669 if (hitter->type == PLAYER && hitter->contr->peaceful == 1)
1670 return 1;
1671
1672 if ((owner = hitter->owner) != NULL)
1673 {
1674 if (owner->type == PLAYER && owner->contr->peaceful == 1)
1675 friendlyfire = 2;
1676 }
1677
1678 if (hitter->type == SPELL || hitter->type == POISONING || hitter->type == DISEASE || hitter->type == RUNE)
1679 friendlyfire = 0;
1680 }
1681
1682 return friendlyfire;
1683 }
1684
1685 /* This isn't used just for players, but in fact most objects.
1686 * op is the object to be hit, dam is the amount of damage, hitter
1687 * is what is hitting the object, type is the attacktype, and
1688 * full_hit is set if monster area does not matter.
1689 * dam is base damage - protections/vulnerabilities/slaying matches can
1690 * modify it.
1691 */
1692 /* Oct 95 - altered the following slightly for MULTIPLE_GODS hack
1693 * which needs new attacktype AT_HOLYWORD to work . b.t. */
1694 int
1695 hit_player (object *op, int dam, object *hitter, int type, int full_hit)
1696 {
1697 int maxdam = 0, ndam = 0, attacktype = 1, magic = (type & AT_MAGIC);
1698 int maxattacktype, attacknum;
1699 int body_attack = op && op->head; /* Did we hit op's head? */
1700 int simple_attack;
1701 int rtn_kill = 0;
1702 int friendlyfire;
1703
1704 if (get_attack_mode (&op, &hitter, &simple_attack))
1705 return 0;
1706
1707 /* very simple: if our target has no_damage 1 set or is wiz, we can't hurt him */
1708 if (QUERY_FLAG (op, FLAG_WIZ) || QUERY_FLAG (op, FLAG_NO_DAMAGE))
1709 return 0;
1710
1711 // only allow pk for hostile players
1712 if (op->type == PLAYER)
1713 {
1714 object *owner = hitter->owner;
1715
1716 if (!owner)
1717 owner = hitter;
1718
1719 if (owner->type == PLAYER
1720 && (!op_on_battleground (op, 0, 0)
1721 && (op->contr->peaceful || owner->contr->peaceful))
1722 && op != owner)
1723 return 0;
1724 }
1725
1726 if (body_attack)
1727 {
1728 /* slow and paralyze must hit the head. But we don't want to just
1729 * return - we still need to process other attacks the spell still
1730 * might have. So just remove the paralyze and slow attacktypes,
1731 * and keep on processing if we have other attacktypes.
1732 * return if only magic or nothing is left - under normal code
1733 * we don't attack with pure magic if there is another attacktype.
1734 * Only do processing if the initial attacktype includes one of those
1735 * attack so we don't cancel out things like magic bullet.
1736 */
1737 if (type & (AT_PARALYZE | AT_SLOW))
1738 {
1739 type &= ~(AT_PARALYZE | AT_SLOW);
1740
1741 if (!type || type == AT_MAGIC)
1742 return 0;
1743 }
1744 }
1745
1746 if (!simple_attack && op->type == DOOR)
1747 {
1748 for (object *tmp = op->inv; tmp; tmp = tmp->below)
1749 if (tmp->type == RUNE || tmp->type == TRAP)
1750 {
1751 spring_trap (tmp, hitter);
1752
1753 if (hitter->destroyed () || op->destroyed () || abort_attack (op, hitter, simple_attack))
1754 return 0;
1755
1756 break;
1757 }
1758 }
1759
1760 if (!QUERY_FLAG (op, FLAG_ALIVE) || op->stats.hp < 0)
1761 {
1762 /* FIXME: If a player is killed by a rune in a door, the
1763 * destroyed() check above doesn't return, and might get here.
1764 */
1765
1766 /* FIXME: This for example happens when a dead door is on a mover and
1767 gets it's speed_left raised on each mover-tick.
1768 Doors are removed in a kinda funny way by giving them speed and speed_left
1769 and waiting for that to run out.
1770 */
1771 LOG (llevDebug, "victim %s (%d) already dead in hit_player()\n", op->debug_desc (), op->stats.hp);
1772 return 0;
1773 }
1774
1775 #ifdef ATTACK_DEBUG
1776 LOG (llevDebug, "hit player: attacktype %d, dam %d\n", type, dam);
1777 #endif
1778
1779 if (magic)
1780 {
1781 /* basically: dam = dam*(100-op->resist[attacknum])/100;
1782 * in case 0>dam>1, we try to "simulate" a float value-effect */
1783 dam = dam * (100 - op->resist[ATNR_MAGIC]);
1784 if (dam >= 100)
1785 dam /= 100;
1786 else
1787 dam = (dam > rndm (0, 99)) ? 1 : 0;
1788 }
1789
1790 /* AT_CHAOS here is a weapon or monster. Spells are handled by hit_map
1791 * If the attacktype still has chaos, shuffle it, then clear the Chaos bit
1792 */
1793 if (type & AT_CHAOS)
1794 {
1795 shuffle_attack (op, 0); /*0 flag tells it to not change the face */
1796 update_object (op, UP_OBJ_FACE);
1797 type &= ~AT_CHAOS;
1798 }
1799
1800 /* Holyword is really an attacktype modifier (like magic is). If
1801 * holyword is part of an attacktype, then make sure the creature is
1802 * a proper match, otherwise no damage.
1803 */
1804 if (type & AT_HOLYWORD)
1805 {
1806 object *god;
1807
1808 if ((!hitter->slaying ||
1809 (!(op->race && strstr (hitter->slaying, op->race)) &&
1810 !(op->name && strstr (hitter->slaying, op->name)))) &&
1811 (!QUERY_FLAG (op, FLAG_UNDEAD) ||
1812 (hitter->title != NULL
1813 && (god = find_god (determine_god (hitter))) != NULL && god->race != NULL && strstr (god->race, undead_name) != NULL)))
1814 return 0;
1815 }
1816
1817 maxattacktype = type; /* initialise this to something */
1818 for (attacknum = 0; attacknum < NROFATTACKS; attacknum++, attacktype = 1 << attacknum)
1819 {
1820 /* Magic isn't really a true attack type - it gets combined with other
1821 * attack types. As such, skip it over. However, if magic is
1822 * the only attacktype in the group, then still attack with it
1823 */
1824 if ((attacktype == AT_MAGIC) && (type & ~AT_MAGIC))
1825 continue;
1826
1827 /* Go through and hit the player with each attacktype, one by one.
1828 * hit_player_attacktype only figures out the damage, doesn't inflict
1829 * it. It will do the appropriate action for attacktypes with
1830 * effects (slow, paralization, etc.
1831 */
1832 if (type & attacktype)
1833 {
1834 ndam = hit_player_attacktype (op, hitter, dam, attacknum, magic);
1835 /* the >= causes us to prefer messages from special attacks, if
1836 * the damage is equal.
1837 */
1838 if (ndam >= maxdam)
1839 {
1840 maxdam = ndam;
1841 maxattacktype = 1 << attacknum;
1842 }
1843 }
1844 }
1845
1846 /* if this is friendly fire then do a set % of damage only
1847 * Note - put a check in to make sure this attack is actually
1848 * doing damage - otherwise, the +1 in the code below will make
1849 * an attack do damage before when it otherwise didn't
1850 */
1851 friendlyfire = friendly_fire (op, hitter);
1852 if (friendlyfire && maxdam)
1853 {
1854 maxdam = ((dam * settings.set_friendly_fire) / 100);
1855
1856 #ifdef ATTACK_DEBUG
1857 LOG (llevDebug, "Friendly fire (type:%d setting: %d%) did %d damage dropped to %d\n",
1858 friendlyfire, settings.set_friendly_fire, dam, maxdam);
1859 #endif
1860 }
1861
1862 if (!full_hit)
1863 {
1864 int area;
1865 int remainder;
1866
1867 area = 0;
1868
1869 for (archetype *at = op->arch; at; at = (archetype *)at->more)
1870 area++;
1871
1872 assert (area > 0);
1873
1874 /* basically: maxdam /= area; we try to "simulate" a float
1875 value-effect */
1876 remainder = 100 * (maxdam % area) / area;
1877 maxdam /= area;
1878 if (rndm (100) < remainder)
1879 maxdam++;
1880 }
1881
1882 #ifdef ATTACK_DEBUG
1883 LOG (llevDebug, "Attacktype %d did %d damage\n", type, maxdam);
1884 #endif
1885
1886 // for now, only do this for active objects, otherwise they
1887 // keep a refcount for a long time and I see no usefulness
1888 // for an non-active objetc to know its enemy.
1889 if (op->active)
1890 if (hitter->owner)
1891 op->enemy = hitter->owner;
1892 else if (QUERY_FLAG (hitter, FLAG_ALIVE))
1893 op->enemy = hitter;
1894
1895 if (QUERY_FLAG (op, FLAG_UNAGGRESSIVE) && op->type != PLAYER)
1896 {
1897 /* The unaggressives look after themselves 8) */
1898 CLEAR_FLAG (op, FLAG_UNAGGRESSIVE);
1899 npc_call_help (op);
1900 }
1901
1902 if (magic && did_make_save (op, op->level, 0))
1903 maxdam = maxdam / 2;
1904
1905 attack_message (maxdam, maxattacktype, op, hitter);
1906
1907 op->stats.hp -= maxdam;
1908
1909 /* Eneq(@csd.uu.se): Check to see if monster runs away. */
1910 if ((op->stats.hp >= 0) &&
1911 (QUERY_FLAG (op, FLAG_MONSTER) || op->type == PLAYER) &&
1912 op->stats.hp < (signed short) (((float) op->run_away / (float) 100) * (float) op->stats.maxhp))
1913 {
1914
1915 if (QUERY_FLAG (op, FLAG_MONSTER))
1916 SET_FLAG (op, FLAG_RUN_AWAY);
1917 else
1918 scare_creature (op, hitter);
1919 }
1920
1921 if (QUERY_FLAG (op, FLAG_TEAR_DOWN))
1922 {
1923 if (maxdam)
1924 tear_down_wall (op);
1925
1926 return maxdam; /* nothing more to do for wall */
1927 }
1928
1929 /* See if the creature has been killed */
1930 rtn_kill = kill_object (op, maxdam, hitter, type);
1931 if (rtn_kill != -1)
1932 return rtn_kill;
1933
1934
1935 /* Used to be ghosthit removal - we now use the ONE_HIT flag. Note
1936 * that before if the player was immune to ghosthit, the monster
1937 * remained - that is no longer the case.
1938 */
1939 if (QUERY_FLAG (hitter, FLAG_ONE_HIT))
1940 hitter->destroy ();
1941
1942 /* Lets handle creatures that are splitting now */
1943 else if (type & AT_PHYSICAL && !QUERY_FLAG (op, FLAG_FREED) && QUERY_FLAG (op, FLAG_SPLITTING))
1944 {
1945 int i;
1946 int friendly = QUERY_FLAG (op, FLAG_FRIENDLY);
1947 int unaggressive = QUERY_FLAG (op, FLAG_UNAGGRESSIVE);
1948 object *owner = op->owner;
1949
1950 if (!op->other_arch)
1951 {
1952 LOG (llevError, "SPLITTING without other_arch error.\n");
1953 return maxdam;
1954 }
1955
1956 op->remove ();
1957
1958 for (i = 0; i < NROFNEWOBJS (op); i++)
1959 { /* This doesn't handle op->more yet */
1960 object *tmp = arch_to_object (op->other_arch);
1961 int j;
1962
1963 tmp->stats.hp = op->stats.hp;
1964
1965 if (friendly)
1966 {
1967 add_friendly_object (tmp);
1968 tmp->attack_movement = PETMOVE;
1969
1970 if (owner)
1971 tmp->set_owner (owner);
1972 }
1973
1974 if (unaggressive)
1975 SET_FLAG (tmp, FLAG_UNAGGRESSIVE);
1976
1977 j = find_first_free_spot (tmp, op->map, op->x, op->y);
1978
1979 if (j == -1) /* No spot to put this monster */
1980 tmp->destroy ();
1981 else
1982 {
1983 tmp->x = op->x + freearr_x[j], tmp->y = op->y + freearr_y[j];
1984 insert_ob_in_map (tmp, op->map, NULL, 0);
1985 }
1986 }
1987
1988 op->destroy ();
1989 }
1990 else if (type & AT_DRAIN && hitter->type == GRIMREAPER && hitter->value++ > 10)
1991 hitter->destroy ();
1992
1993 return maxdam;
1994 }
1995
1996 void
1997 poison_player (object *op, object *hitter, int dam)
1998 {
1999 archetype *at = archetype::find ("poisoning");
2000 object *tmp = present_arch_in_ob (at, op);
2001
2002 if (tmp == NULL)
2003 {
2004 if ((tmp = arch_to_object (at)) == NULL)
2005 LOG (llevError, "Failed to clone arch poisoning.\n");
2006 else
2007 {
2008 tmp = insert_ob_in_ob (tmp, op);
2009 /* peterm: give poisoning some teeth. It should
2010 * be able to kill things better than it does:
2011 * damage should be dependent something--I choose to
2012 * do this: if it's a monster, the damage from the
2013 * poisoning goes as the level of the monster/2.
2014 * If anything else, goes as damage.
2015 */
2016
2017 if (QUERY_FLAG (hitter, FLAG_ALIVE))
2018 tmp->stats.dam += hitter->level / 2;
2019 else
2020 tmp->stats.dam = dam;
2021
2022 tmp->set_owner (hitter); /* so we get credit for poisoning kills */
2023 if (hitter->skill && hitter->skill != tmp->skill)
2024 {
2025 tmp->skill = hitter->skill;
2026 }
2027
2028 tmp->stats.food += dam; /* more damage, longer poisoning */
2029
2030 if (op->type == PLAYER)
2031 {
2032 /* player looses stats, maximum is -10 of each */
2033 tmp->stats.Con = MAX (-(dam / 4 + 1), -10);
2034 tmp->stats.Str = MAX (-(dam / 3 + 2), -10);
2035 tmp->stats.Dex = MAX (-(dam / 6 + 1), -10);
2036 tmp->stats.Int = MAX (-dam / 7, -10);
2037 SET_FLAG (tmp, FLAG_APPLIED);
2038 op->update_stats ();
2039 new_draw_info (NDI_UNIQUE, 0, op, "You suddenly feel very ill.");
2040 }
2041
2042 if (hitter->type == PLAYER)
2043 new_draw_info_format (NDI_UNIQUE, 0, hitter, "You poison %s.", &op->name);
2044 else if (hitter->owner != NULL && hitter->owner->type == PLAYER)
2045 new_draw_info_format (NDI_UNIQUE, 0, hitter->owner, "Your %s poisons %s.", &hitter->name, &op->name);
2046 }
2047
2048 tmp->speed_left = 0;
2049 }
2050 else
2051 tmp->stats.food++;
2052 }
2053
2054 void
2055 slow_player (object *op, object *hitter, int dam)
2056 {
2057 archetype *at = archetype::find ("slowness");
2058 object *tmp;
2059
2060 if (at == NULL)
2061 LOG (llevError, "Can't find slowness archetype.\n");
2062
2063 if ((tmp = present_arch_in_ob (at, op)) == NULL)
2064 {
2065 tmp = arch_to_object (at);
2066 tmp = insert_ob_in_ob (tmp, op);
2067 new_draw_info (NDI_UNIQUE, 0, op, "The world suddenly moves very fast!");
2068 }
2069 else
2070 tmp->stats.food++;
2071
2072 SET_FLAG (tmp, FLAG_APPLIED);
2073 tmp->speed_left = 0;
2074 op->update_stats ();
2075 }
2076
2077 void
2078 confuse_player (object *op, object *hitter, int dam)
2079 {
2080 object *tmp;
2081 int maxduration;
2082
2083 tmp = present_in_ob_by_name (FORCE, "confusion", op);
2084 if (!tmp)
2085 {
2086 tmp = get_archetype (FORCE_NAME);
2087 tmp = insert_ob_in_ob (tmp, op);
2088 }
2089
2090 /* Duration added per hit and max. duration of confusion both depend
2091 * on the player's resistance
2092 */
2093 tmp->speed = 0.05;
2094 tmp->subtype = FORCE_CONFUSION;
2095 tmp->duration = 8 + MAX (1, 5 * (100 - op->resist[ATNR_CONFUSION]) / 100);
2096 tmp->name = "confusion";
2097 maxduration = MAX (2, 30 * (100 - op->resist[ATNR_CONFUSION]) / 100);
2098
2099 if (tmp->duration > maxduration)
2100 tmp->duration = maxduration;
2101
2102 if (op->type == PLAYER && !QUERY_FLAG (op, FLAG_CONFUSED))
2103 new_draw_info (NDI_UNIQUE, 0, op, "You suddenly feel very confused!");
2104
2105 SET_FLAG (op, FLAG_CONFUSED);
2106 }
2107
2108 void
2109 blind_player (object *op, object *hitter, int dam)
2110 {
2111 object *tmp, *owner;
2112
2113 /* Save some work if we know it isn't going to affect the player */
2114 if (op->resist[ATNR_BLIND] == 100)
2115 return;
2116
2117 tmp = present_in_ob (BLINDNESS, op);
2118 if (!tmp)
2119 {
2120 tmp = get_archetype ("blindness");
2121 SET_FLAG (tmp, FLAG_BLIND);
2122 SET_FLAG (tmp, FLAG_APPLIED);
2123 /* use floats so we don't lose too much precision due to rounding errors.
2124 * speed is a float anyways.
2125 */
2126 tmp->speed = tmp->speed * (100.0 - (float) op->resist[ATNR_BLIND]) / 100;
2127
2128 tmp = insert_ob_in_ob (tmp, op);
2129 change_abil (op, tmp); /* Mostly to display any messages */
2130 op->update_stats (); /* This takes care of some other stuff */
2131
2132 if (hitter->owner)
2133 owner = hitter->owner;
2134 else
2135 owner = hitter;
2136
2137 new_draw_info_format (NDI_UNIQUE, 0, owner, "Your attack blinds %s!", query_name (op));
2138 }
2139 tmp->stats.food += dam;
2140 if (tmp->stats.food > 10)
2141 tmp->stats.food = 10;
2142 }
2143
2144 void
2145 paralyze_player (object *op, object *hitter, int dam)
2146 {
2147 float effect, max;
2148
2149 /* object *tmp; */
2150
2151 /* This is strange stuff... someone knows for what this is
2152 * written? Well, i think this can and should be removed
2153 */
2154
2155 /*
2156 if((tmp=present(PARAIMAGE,op->map,op->x,op->y))==NULL) {
2157 tmp=clone_arch(PARAIMAGE);
2158 tmp->x=op->x,tmp->y=op->y;
2159 insert_ob_in_map(tmp,op->map,tmp,INS_NO_MERGE | INS_NO_WALK_ON);
2160 }
2161 */
2162
2163 /* Do this as a float - otherwise, rounding might very well reduce this to 0 */
2164 effect = (float) dam *3.0 * (100.0 - op->resist[ATNR_PARALYZE]) / 100;
2165
2166 if (effect == 0)
2167 return;
2168
2169 op->speed_left -= FABS (op->speed) * effect;
2170 /* tmp->stats.food+=(signed short) effect/op->speed; */
2171
2172 /* max number of ticks to be affected for. */
2173 max = (100 - op->resist[ATNR_PARALYZE]) / 2;
2174 if (op->speed_left < -(FABS (op->speed) * max))
2175 op->speed_left = (float) -(FABS (op->speed) * max);
2176
2177 /* tmp->stats.food = (signed short) (max/FABS(op->speed)); */
2178 }
2179
2180 /* Attempts to kill 'op'. hitter is the attack object, dam is
2181 * the computed damaged.
2182 */
2183 void
2184 deathstrike_player (object *op, object *hitter, int *dam)
2185 {
2186 /* The intention of a death attack is to kill outright things
2187 ** that are a lot weaker than the attacker, have a chance of killing
2188 ** things somewhat weaker than the caster, and no chance of
2189 ** killing something equal or stronger than the attacker.
2190 ** Also, if a deathstrike attack has a slaying, any monster
2191 ** whose name or race matches a comma-delimited list in the slaying
2192 ** field of the deathstriking object */
2193
2194 int atk_lev, def_lev, kill_lev;
2195
2196 if (hitter->slaying)
2197 if (!((QUERY_FLAG (op, FLAG_UNDEAD) && strstr (hitter->slaying, undead_name)) || (op->race && strstr (hitter->slaying, op->race))))
2198 return;
2199
2200 def_lev = op->level;
2201 if (def_lev < 1)
2202 {
2203 LOG (llevError, "BUG: arch %s, name %s with level < 1\n", &op->arch->archname, &op->name);
2204 def_lev = 1;
2205 }
2206
2207 atk_lev = (hitter->chosen_skill ? hitter->chosen_skill->level : hitter->level) / 2;
2208 /* LOG(llevDebug,"Deathstrike - attack level %d, defender level %d\n",
2209 atk_lev, def_lev); */
2210
2211 if (atk_lev >= def_lev)
2212 {
2213 kill_lev = random_roll (0, atk_lev - 1, hitter, PREFER_HIGH);
2214
2215 /* Note that the below effectively means the ratio of the atk vs
2216 * defener level is important - if level 52 character has very little
2217 * chance of killing a level 50 monster. This should probably be
2218 * redone.
2219 */
2220 if (kill_lev >= def_lev)
2221 {
2222 *dam = op->stats.hp + 10; /* take all hp. they can still save for 1/2 */
2223 /* I think this doesn't really do much. Because of
2224 * integer rounding, this only makes any difference if the
2225 * attack level is double the defender level.
2226 */
2227 *dam *= kill_lev / def_lev;
2228 }
2229 }
2230 else
2231 *dam = 0; /* no harm done */
2232 }
2233
2234 /* thrown_item_effect() - handles any special effects of thrown
2235 * items (like attacking living creatures--a potion thrown at a
2236 * monster).
2237 */
2238 static void
2239 thrown_item_effect (object *hitter, object *victim)
2240 {
2241 if (!QUERY_FLAG (hitter, FLAG_ALIVE))
2242 {
2243 /* May not need a switch for just 2 types, but this makes it
2244 * easier for expansion.
2245 */
2246 switch (hitter->type)
2247 {
2248 case POTION:
2249 /* should player get a save throw instead of checking magic protection? */
2250 if (QUERY_FLAG (victim, FLAG_ALIVE) && !QUERY_FLAG (victim, FLAG_UNDEAD) && (victim->resist[ATNR_MAGIC] < 60))
2251 (void) apply_potion (victim, hitter);
2252 break;
2253
2254 case POISON: /* poison drinks */
2255 /* As with potions, should monster get a save? */
2256 if (QUERY_FLAG (victim, FLAG_ALIVE) && !QUERY_FLAG (victim, FLAG_UNDEAD) && (victim->resist[ATNR_POISON] < 60))
2257 apply_poison (victim, hitter);
2258 break;
2259
2260 /* Removed case statements that did nothing.
2261 * food may be poisonous, but monster must be willing to eat it,
2262 * so we don't handle it here.
2263 * Containers should perhaps break open, but that code was disabled.
2264 */
2265 }
2266 }
2267 }
2268
2269 /* adj_attackroll() - adjustments to attacks by various conditions */
2270 int
2271 adj_attackroll (object *hitter, object *target)
2272 {
2273 object *attacker = hitter;
2274 int adjust = 0;
2275
2276 /* safety */
2277 if (!target || !hitter || !hitter->map || !target->map || !on_same_map (hitter, target))
2278 {
2279 LOG (llevError, "BUG: adj_attackroll(): hitter and target not on same " "map\n");
2280 return 0;
2281 }
2282
2283 /* aimed missiles use the owning object's sight */
2284 if (is_aimed_missile (hitter))
2285 {
2286 if ((attacker = hitter->owner) == NULL)
2287 attacker = hitter;
2288 /* A player who saves but hasn't quit still could have objects
2289 * owned by him - need to handle that case to avoid crashes.
2290 */
2291 if (QUERY_FLAG (attacker, FLAG_REMOVED))
2292 attacker = hitter;
2293 }
2294 else if (!QUERY_FLAG (hitter, FLAG_ALIVE))
2295 return 0;
2296
2297 /* determine the condtions under which we make an attack.
2298 * Add more cases, as the need occurs. */
2299
2300 if (!can_see_enemy (attacker, target))
2301 {
2302 /* target is unseen */
2303 if (target->invisible || QUERY_FLAG (attacker, FLAG_BLIND))
2304 adjust -= 10;
2305 /* dark map penalty for the hitter (lacks infravision if we got here). */
2306 else if (target->map && target->map->darkness > 0 && !stand_in_light (target))
2307 adjust -= target->map->darkness;
2308 }
2309
2310 if (QUERY_FLAG (attacker, FLAG_SCARED))
2311 adjust -= 3;
2312
2313 if (QUERY_FLAG (target, FLAG_UNAGGRESSIVE))
2314 adjust += 1;
2315
2316 if (QUERY_FLAG (target, FLAG_SCARED))
2317 adjust += 1;
2318
2319 if (QUERY_FLAG (attacker, FLAG_CONFUSED))
2320 adjust -= 3;
2321
2322 /* if we attack at a different 'altitude' its harder */
2323 if ((attacker->move_type & target->move_type) == 0)
2324 adjust -= 2;
2325
2326 #if 0
2327 /* slower attacks are less likely to succeed. We should use a
2328 * comparison between attacker/target speeds BUT, players have
2329 * a generally faster speed, so this will wind up being a HUGE
2330 * disadantage for the monsters! Too bad, because missiles which
2331 * fly fast should have a better chance of hitting a slower target.
2332 */
2333 if (hitter->speed < target->speed)
2334 adjust += ((float) hitter->speed - target->speed);
2335 #endif
2336
2337 #if 0
2338 LOG (llevDebug, "adj_attackroll() returns %d (%d)\n", adjust, attacker->count);
2339 #endif
2340
2341 return adjust;
2342 }
2343
2344 /* determine if the object is an 'aimed' missile */
2345 int
2346 is_aimed_missile (object *op)
2347 {
2348
2349 /* I broke what used to be one big if into a few nested
2350 * ones so that figuring out the logic is at least possible.
2351 */
2352 if (op && (op->move_type & MOVE_FLYING))
2353 if (op->type == ARROW || op->type == THROWN_OBJ)
2354 return 1;
2355 else if (op->type == SPELL_EFFECT && (op->subtype == SP_BULLET || op->subtype == SP_EXPLOSION))
2356 return 1;
2357
2358 return 0;
2359 }