ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/attack.C
Revision: 1.39
Committed: Mon Jan 8 01:19:03 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.38: +2 -2 lines
Log Message:
more preperations for player eviction

File Contents

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