ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.24
Committed: Mon Dec 25 11:25:50 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.23: +16 -13 lines
Log Message:
- small, but subtle, rewrite of object management
- perl will now keep attachable objects alive
- objects are now refcounted
- refcouts need to be tested explicitly (refcnt_chk)
- explicit destroy is required current
- explicit destroy asks "nicely" for the object to self destruct, if possible
- refcounts will be used during mortal killing
- minor bugfixes, optimisations etc.
- some former hacks removed.

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
5     Copyright (C) 1992 Frank Tore Johansen
6    
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 root 1.12 The authors can be reached via e-mail at <crossfire@schmorp.de>
22 elmex 1.1 */
23    
24     /*
25     * Routines that is executed from objects based on their speed have been
26     * collected in this file.
27     */
28    
29     #include <global.h>
30     #include <spells.h>
31     #ifndef __CEXTRACT__
32 root 1.9 # include <sproto.h>
33 elmex 1.1 #endif
34    
35     /* The following removes doors. The functions check to see if similar
36     * doors are next to the one that is being removed, and if so, set it
37     * so those will be removed shortly (in a cascade like fashion.)
38     */
39    
40 root 1.9 void
41     remove_door (object *op)
42     {
43 elmex 1.1 int i;
44     object *tmp;
45 root 1.9
46     for (i = 1; i < 9; i += 2)
47     if ((tmp = present (DOOR, op->map, op->x + freearr_x[i], op->y + freearr_y[i])) != NULL)
48     {
49     tmp->speed = 0.1;
50     update_ob_speed (tmp);
51     tmp->speed_left = -0.2;
52     }
53    
54     if (op->other_arch)
55     {
56     tmp = arch_to_object (op->other_arch);
57     tmp->x = op->x;
58     tmp->y = op->y;
59     tmp->map = op->map;
60     tmp->level = op->level;
61     insert_ob_in_map (tmp, op->map, op, 0);
62     }
63 root 1.17
64     op->destroy ();
65 elmex 1.1 }
66    
67 root 1.9 void
68     remove_door2 (object *op)
69     {
70 elmex 1.1 int i;
71     object *tmp;
72 root 1.9
73     for (i = 1; i < 9; i += 2)
74     {
75     tmp = present (LOCKED_DOOR, op->map, op->x + freearr_x[i], op->y + freearr_y[i]);
76     if (tmp && tmp->slaying == op->slaying)
77     { /* same key both doors */
78     tmp->speed = 0.1;
79     update_ob_speed (tmp);
80     tmp->speed_left = -0.2;
81     }
82     }
83     if (op->other_arch)
84     {
85     tmp = arch_to_object (op->other_arch);
86     tmp->x = op->x;
87     tmp->y = op->y;
88     tmp->map = op->map;
89     tmp->level = op->level;
90     insert_ob_in_map (tmp, op->map, op, 0);
91     }
92 root 1.17
93     op->destroy ();
94 elmex 1.1 }
95    
96     /* Will generate a monster according to content
97     * of generator.
98     */
99 root 1.9 void
100     generate_monster_inv (object *gen)
101     {
102     int i;
103     object *op, *head = NULL;
104    
105     int qty = 0;
106    
107     /* Code below assumes the generator is on a map, as it tries
108     * to place the monster on the map. So if the generator
109     * isn't on a map, complain and exit.
110     */
111     if (gen->map == NULL)
112     {
113     //LOG(llevError,"Generator (%s) not on a map?\n", gen->name);
114     return;
115     }
116     /*First count numer of objects in inv */
117     for (op = gen->inv; op; op = op->below)
118     qty++;
119     if (!qty)
120     {
121     LOG (llevError, "Generator (%s) has no inventory in generate_monster_inv?\n", &gen->name);
122     return; /*No inventory */
123 elmex 1.1 }
124 root 1.9 qty = rndm (0, qty - 1);
125     for (op = gen->inv; qty; qty--)
126     op = op->below;
127     i = find_free_spot (op, gen->map, gen->x, gen->y, 1, 9);
128     if (i == -1)
129     return;
130     head = object_create_clone (op);
131     CLEAR_FLAG (head, FLAG_IS_A_TEMPLATE);
132     unflag_inv (head, FLAG_IS_A_TEMPLATE);
133     if (rndm (0, 9))
134     generate_artifact (head, gen->map->difficulty);
135     insert_ob_in_map_at (head, gen->map, gen, 0, gen->x + freearr_x[i], gen->y + freearr_y[i]);
136     if (QUERY_FLAG (head, FLAG_FREED))
137     return;
138 root 1.19 if (head->has_random_items ())
139 root 1.9 create_treasure (head->randomitems, head, GT_APPLY, gen->map->difficulty, 0);
140     }
141    
142     void
143     generate_monster_arch (object *gen)
144     {
145     int i;
146     object *op, *head = NULL, *prev = NULL;
147     archetype *at = gen->other_arch;
148 elmex 1.1
149 root 1.24 if (!gen->other_arch)
150     return;
151    
152 root 1.9 /* Code below assumes the generator is on a map, as it tries
153     * to place the monster on the map. So if the generator
154     * isn't on a map, complain and exit.
155     */
156 root 1.24 if (!gen->map)
157     return;
158    
159 root 1.9 i = find_free_spot (&at->clone, gen->map, gen->x, gen->y, 1, 9);
160     if (i == -1)
161     return;
162 root 1.24
163     while (at)
164 root 1.9 {
165     op = arch_to_object (at);
166     op->x = gen->x + freearr_x[i] + at->clone.x;
167     op->y = gen->y + freearr_y[i] + at->clone.y;
168    
169 root 1.24 if (head)
170 root 1.9 op->head = head, prev->more = op;
171    
172     if (rndm (0, 9))
173     generate_artifact (op, gen->map->difficulty);
174 root 1.24
175 root 1.9 insert_ob_in_map (op, gen->map, gen, 0);
176     if (QUERY_FLAG (op, FLAG_FREED))
177 root 1.6 return;
178 root 1.24
179 root 1.19 if (op->has_random_items ())
180 root 1.9 create_treasure (op->randomitems, op, GT_APPLY, gen->map->difficulty, 0);
181 root 1.24
182 root 1.9 if (head == NULL)
183     head = op;
184 root 1.24
185 root 1.9 prev = op;
186     at = at->more;
187 elmex 1.1 }
188     }
189    
190 root 1.9 void
191     generate_monster (object *gen)
192     {
193 elmex 1.1
194 root 1.9 if (GENERATE_SPEED (gen) && rndm (0, GENERATE_SPEED (gen) - 1))
195     return;
196 root 1.24
197 root 1.9 if (QUERY_FLAG (gen, FLAG_CONTENT_ON_GEN))
198     generate_monster_inv (gen);
199     else
200     generate_monster_arch (gen);
201 elmex 1.1
202     }
203    
204 root 1.9 void
205     remove_force (object *op)
206     {
207     if (--op->duration > 0)
208     return;
209 elmex 1.1
210 root 1.9 switch (op->subtype)
211     {
212 root 1.10 case FORCE_CONFUSION:
213     if (op->env != NULL)
214     {
215     CLEAR_FLAG (op->env, FLAG_CONFUSED);
216     new_draw_info (NDI_UNIQUE, 0, op->env, "You regain your senses.\n");
217     }
218 root 1.6
219 root 1.10 default:
220     if (op->env != NULL)
221     {
222     CLEAR_FLAG (op, FLAG_APPLIED);
223     change_abil (op->env, op);
224 root 1.23 op->env->update_stats ();
225 root 1.10 }
226 elmex 1.1 }
227 root 1.17
228     op->destroy ();
229 elmex 1.1 }
230    
231 root 1.9 void
232     remove_blindness (object *op)
233     {
234     if (--op->stats.food > 0)
235 elmex 1.1 return;
236 root 1.17
237 root 1.9 CLEAR_FLAG (op, FLAG_APPLIED);
238 root 1.17
239 root 1.9 if (op->env != NULL)
240     {
241     change_abil (op->env, op);
242 root 1.23 op->env->update_stats ();
243 root 1.9 }
244 root 1.17
245     op->destroy ();
246 elmex 1.1 }
247    
248 root 1.9 void
249     poison_more (object *op)
250     {
251     if (op->env == NULL || !QUERY_FLAG (op->env, FLAG_ALIVE) || op->env->stats.hp < 0)
252     {
253 root 1.17 op->destroy ();
254 root 1.9 return;
255     }
256 root 1.17
257 root 1.9 if (op->stats.food == 1)
258     {
259     /* need to remove the object before fix_player is called, else fix_player
260     * will not do anything.
261     */
262     if (op->env->type == PLAYER)
263     {
264     CLEAR_FLAG (op, FLAG_APPLIED);
265 root 1.23 op->env->update_stats ();
266 root 1.9 new_draw_info (NDI_UNIQUE, 0, op->env, "You feel much better now.");
267     }
268 root 1.17
269     op->destroy ();
270 root 1.9 return;
271     }
272 root 1.17
273 root 1.9 if (op->env->type == PLAYER)
274     {
275     op->env->stats.food--;
276     new_draw_info (NDI_UNIQUE, 0, op->env, "You feel very sick...");
277 elmex 1.1 }
278 root 1.9 (void) hit_player (op->env, op->stats.dam, op, AT_INTERNAL, 1);
279 elmex 1.1 }
280    
281    
282 root 1.9 void
283     move_gate (object *op)
284     { /* 1 = going down, 0 = goind up */
285     object *tmp;
286    
287     if (op->stats.wc < 0 || (int) op->stats.wc >= NUM_ANIMATIONS (op))
288     {
289     LOG (llevError, "Gate error: animation was %d, max=%d\n", op->stats.wc, NUM_ANIMATIONS (op));
290     op->stats.wc = 0;
291     }
292    
293     /* We're going down */
294     if (op->value)
295     {
296     if (--op->stats.wc <= 0)
297     { /* Reached bottom, let's stop */
298     op->stats.wc = 0;
299     if (op->arch->clone.speed)
300     op->value = 0;
301     else
302     {
303     op->speed = 0;
304     update_ob_speed (op);
305     }
306 root 1.6 }
307 root 1.9 if ((int) op->stats.wc < (NUM_ANIMATIONS (op) / 2 + 1))
308     {
309     op->move_block = 0;
310     CLEAR_FLAG (op, FLAG_BLOCKSVIEW);
311     update_all_los (op->map, op->x, op->y);
312     }
313     SET_ANIMATION (op, op->stats.wc);
314     update_object (op, UP_OBJ_CHANGE);
315     return;
316 elmex 1.1 }
317    
318 root 1.9 /* We're going up */
319 elmex 1.1
320 root 1.9 /* First, lets see if we are already at the top */
321     if ((unsigned char) op->stats.wc == (NUM_ANIMATIONS (op) - 1))
322     {
323 elmex 1.1
324 root 1.9 /* Check to make sure that only non pickable and non rollable
325     * objects are above the gate. If so, we finish closing the gate,
326     * otherwise, we fall through to the code below which should lower
327     * the gate slightly.
328     */
329    
330     for (tmp = op->above; tmp != NULL; tmp = tmp->above)
331     if (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || QUERY_FLAG (tmp, FLAG_ALIVE))
332     break;
333 root 1.6
334 root 1.9 if (tmp == NULL)
335     {
336     if (op->arch->clone.speed)
337     op->value = 1;
338     else
339     {
340     op->speed = 0;
341     update_ob_speed (op); /* Reached top, let's stop */
342 root 1.6 }
343 root 1.9 return;
344     }
345     }
346    
347     if (op->stats.food)
348     { /* The gate is going temporarily down */
349     if (--op->stats.wc <= 0)
350     { /* Gone all the way down? */
351     op->stats.food = 0; /* Then let's try again */
352     op->stats.wc = 0;
353 root 1.6 }
354 elmex 1.1 }
355 root 1.9 else
356     { /* The gate is still going up */
357     op->stats.wc++;
358 elmex 1.1
359 root 1.9 if ((int) op->stats.wc >= (NUM_ANIMATIONS (op)))
360     op->stats.wc = (signed char) NUM_ANIMATIONS (op) - 1;
361    
362     /* If there is something on top of the gate, we try to roll it off.
363     * If a player/monster, we don't roll, we just hit them with damage
364     */
365     if ((int) op->stats.wc >= NUM_ANIMATIONS (op) / 2)
366     {
367     /* Halfway or further, check blocks */
368     /* First, get the top object on the square. */
369     for (tmp = op->above; tmp != NULL && tmp->above != NULL; tmp = tmp->above);
370    
371     if (tmp != NULL)
372     {
373     if (QUERY_FLAG (tmp, FLAG_ALIVE))
374     {
375     hit_player (tmp, random_roll (1, op->stats.dam, tmp, PREFER_LOW), op, AT_PHYSICAL, 1);
376     if (tmp->type == PLAYER)
377     new_draw_info_format (NDI_UNIQUE, 0, tmp, "You are crushed by the %s!", &op->name);
378     }
379     else
380     /* If the object is not alive, and the object either can
381     * be picked up or the object rolls, move the object
382     * off the gate.
383     */
384     if (!QUERY_FLAG (tmp, FLAG_ALIVE) && (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL)))
385     {
386     /* If it has speed, it should move itself, otherwise: */
387     int i = find_free_spot (tmp, op->map, op->x, op->y, 1, 9);
388    
389     /* If there is a free spot, move the object someplace */
390     if (i != -1)
391     {
392 root 1.16 tmp->remove ();
393 root 1.9 tmp->x += freearr_x[i], tmp->y += freearr_y[i];
394     insert_ob_in_map (tmp, op->map, op, 0);
395 root 1.6 }
396     }
397     }
398    
399 root 1.9 /* See if there is still anything blocking the gate */
400     for (tmp = op->above; tmp != NULL; tmp = tmp->above)
401     if (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || QUERY_FLAG (tmp, FLAG_ALIVE))
402     break;
403    
404     /* IF there is, start putting the gate down */
405     if (tmp)
406     {
407     op->stats.food = 1;
408     }
409     else
410     {
411     op->move_block = MOVE_ALL;
412     if (!op->arch->clone.stats.ac)
413     SET_FLAG (op, FLAG_BLOCKSVIEW);
414     update_all_los (op->map, op->x, op->y);
415     }
416     } /* gate is halfway up */
417    
418     SET_ANIMATION (op, op->stats.wc);
419     update_object (op, UP_OBJ_CHANGE);
420     } /* gate is going up */
421 elmex 1.1 }
422    
423     /* hp : how long door is open/closed
424     * maxhp : initial value for hp
425     * sp : 1 = open, 0 = close
426     */
427 root 1.9 void
428     move_timed_gate (object *op)
429 elmex 1.1 {
430     int v = op->value;
431    
432 root 1.9 if (op->stats.sp)
433     {
434     move_gate (op);
435     if (op->value != v) /* change direction ? */
436     op->stats.sp = 0;
437     return;
438     }
439     if (--op->stats.hp <= 0)
440     { /* keep gate down */
441     move_gate (op);
442     if (op->value != v)
443     { /* ready ? */
444     op->speed = 0;
445     update_ob_speed (op);
446     }
447 elmex 1.1 }
448     }
449    
450     /* slaying: name of the thing the detector is to look for
451     * speed: frequency of 'glances'
452     * connected: connected value of detector
453     * sp: 1 if detection sets buttons
454     * -1 if detection unsets buttons
455     */
456    
457 root 1.9 void
458     move_detector (object *op)
459 elmex 1.1 {
460 root 1.9 object *tmp;
461     int last = op->value;
462     int detected;
463    
464     detected = 0;
465    
466 root 1.20 for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp != NULL && !detected; tmp = tmp->above)
467 root 1.9 {
468     object *tmp2;
469    
470     if (op->stats.hp)
471     {
472     for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
473     {
474     if (op->slaying && !strcmp (op->slaying, tmp->name))
475     detected = 1;
476     if (tmp2->type == FORCE && tmp2->slaying && !strcmp (tmp2->slaying, op->slaying))
477     detected = 1;
478     }
479     }
480     if (op->slaying && !strcmp (op->slaying, tmp->name))
481     {
482 root 1.6 detected = 1;
483     }
484 root 1.9 else if (tmp->type == SPECIAL_KEY && tmp->slaying == op->slaying)
485     detected = 1;
486 elmex 1.1 }
487    
488 root 1.9 /* the detector sets the button if detection is found */
489     if (op->stats.sp == 1)
490     {
491     if (detected && last == 0)
492     {
493     op->value = 1;
494     push_button (op);
495 root 1.6 }
496 root 1.9 if (!detected && last == 1)
497     {
498     op->value = 0;
499     push_button (op);
500 root 1.6 }
501 elmex 1.1 }
502 root 1.9 else
503     { /* in this case, we unset buttons */
504     if (detected && last == 1)
505     {
506     op->value = 0;
507     push_button (op);
508 root 1.6 }
509 root 1.9 if (!detected && last == 0)
510     {
511     op->value = 1;
512     push_button (op);
513 root 1.6 }
514 elmex 1.1 }
515     }
516    
517    
518 root 1.9 void
519     animate_trigger (object *op)
520 elmex 1.1 {
521 root 1.9 if ((unsigned char) ++op->stats.wc >= NUM_ANIMATIONS (op))
522     {
523     op->stats.wc = 0;
524     check_trigger (op, NULL);
525     }
526     else
527     {
528     SET_ANIMATION (op, op->stats.wc);
529     update_object (op, UP_OBJ_FACE);
530     }
531 elmex 1.1 }
532    
533 root 1.9 void
534     move_hole (object *op)
535     { /* 1 = opening, 0 = closing */
536     object *next, *tmp;
537    
538     if (op->value)
539     { /* We're opening */
540     if (--op->stats.wc <= 0)
541     { /* Opened, let's stop */
542     op->stats.wc = 0;
543     op->speed = 0;
544     update_ob_speed (op);
545    
546     /* Hard coding this makes sense for holes I suppose */
547     op->move_on = MOVE_WALK;
548     for (tmp = op->above; tmp != NULL; tmp = next)
549     {
550     next = tmp->above;
551     move_apply (op, tmp, tmp);
552 root 1.6 }
553     }
554 root 1.9 SET_ANIMATION (op, op->stats.wc);
555     update_object (op, UP_OBJ_FACE);
556     return;
557 elmex 1.1 }
558 root 1.9 /* We're closing */
559     op->move_on = 0;
560 elmex 1.1
561 root 1.9 op->stats.wc++;
562     if ((int) op->stats.wc >= NUM_ANIMATIONS (op))
563     op->stats.wc = NUM_ANIMATIONS (op) - 1;
564     SET_ANIMATION (op, op->stats.wc);
565     update_object (op, UP_OBJ_FACE);
566     if ((unsigned char) op->stats.wc == (NUM_ANIMATIONS (op) - 1))
567     {
568     op->speed = 0;
569     update_ob_speed (op); /* closed, let's stop */
570     return;
571 elmex 1.1 }
572     }
573    
574    
575     /* stop_item() returns a pointer to the stopped object. The stopped object
576     * may or may not have been removed from maps or inventories. It will not
577     * have been merged with other items.
578     *
579     * This function assumes that only items on maps need special treatment.
580     *
581     * If the object can't be stopped, or it was destroyed while trying to stop
582     * it, NULL is returned.
583     *
584     * fix_stopped_item() should be used if the stopped item should be put on
585     * the map.
586     */
587 root 1.9 object *
588     stop_item (object *op)
589 elmex 1.1 {
590 root 1.9 if (op->map == NULL)
591     return op;
592 elmex 1.1
593 root 1.9 switch (op->type)
594 elmex 1.1 {
595 root 1.10 case THROWN_OBJ:
596     {
597     object *payload = op->inv;
598 root 1.9
599 root 1.10 if (payload == NULL)
600     return NULL;
601 root 1.16 payload->remove ();
602 root 1.17 op->destroy ();
603 root 1.10 return payload;
604     }
605 elmex 1.1
606 root 1.10 case ARROW:
607     if (op->speed >= MIN_ACTIVE_SPEED)
608     op = fix_stopped_arrow (op);
609     return op;
610 elmex 1.1
611 root 1.10 default:
612     return op;
613 elmex 1.1 }
614     }
615    
616     /* fix_stopped_item() - put stopped item where stop_item() had found it.
617     * Inserts item into the old map, or merges it if it already is on the map.
618     *
619     * 'map' must be the value of op->map before stop_item() was called.
620     */
621 root 1.9 void
622 root 1.13 fix_stopped_item (object *op, maptile *map, object *originator)
623 elmex 1.1 {
624 root 1.9 if (map == NULL)
625     return;
626     if (QUERY_FLAG (op, FLAG_REMOVED))
627     insert_ob_in_map (op, map, originator, 0);
628     else if (op->type == ARROW)
629     merge_ob (op, NULL); /* only some arrows actually need this */
630 elmex 1.1 }
631    
632    
633 root 1.9 object *
634     fix_stopped_arrow (object *op)
635     {
636     if (rndm (0, 99) < op->stats.food)
637     {
638     /* Small chance of breaking */
639 root 1.17 op->destroy ();
640 root 1.9 return NULL;
641     }
642    
643     op->direction = 0;
644     op->move_on = 0;
645     op->move_type = 0;
646     op->speed = 0;
647     update_ob_speed (op);
648     op->stats.wc = op->stats.sp;
649     op->stats.dam = op->stats.hp;
650     op->attacktype = op->stats.grace;
651     op->slaying = 0;
652     op->skill = 0;
653    
654     if (op->spellarg != NULL)
655     {
656     op->slaying = op->spellarg;
657     free (op->spellarg);
658     op->spellarg = NULL;
659     }
660     else
661     op->slaying = NULL;
662    
663 root 1.18 /* Reset these to zero, so that object::can_merge will work properly */
664 root 1.9 op->spellarg = NULL;
665     op->stats.sp = 0;
666     op->stats.hp = 0;
667     op->stats.grace = 0;
668     op->level = 0;
669     op->face = op->arch->clone.face;
670     op->owner = NULL; /* So that stopped arrows will be saved */
671     update_object (op, UP_OBJ_FACE);
672     return op;
673 elmex 1.1 }
674    
675     /* stop_arrow() - what to do when a non-living flying object
676     * has to stop. Sept 96 - I added in thrown object code in
677     * here too. -b.t.
678     *
679     * Returns a pointer to the stopped object (which will have been removed
680     * from maps or inventories), or NULL if was destroyed.
681     */
682    
683 root 1.9 static void
684     stop_arrow (object *op)
685 elmex 1.1 {
686 root 1.9 if (INVOKE_OBJECT (STOP, op))
687     return;
688    
689     if (op->inv)
690     {
691     object *payload = op->inv;
692 root 1.4
693 root 1.16 payload->remove ();
694 root 1.18 payload->owner = 0;
695 root 1.9 insert_ob_in_map (payload, op->map, payload, 0);
696 root 1.17 op->destroy ();
697 root 1.9 }
698     else
699     {
700     op = fix_stopped_arrow (op);
701     if (op)
702     merge_ob (op, NULL);
703 elmex 1.1 }
704     }
705    
706     /* Move an arrow along its course. op is the arrow or thrown object.
707     */
708    
709 root 1.9 void
710     move_arrow (object *op)
711     {
712     object *tmp;
713     sint16 new_x, new_y;
714     int was_reflected, mflags;
715 root 1.13 maptile *m;
716 root 1.9
717     if (op->map == NULL)
718     {
719     LOG (llevError, "BUG: Arrow had no map.\n");
720 root 1.17 op->destroy ();
721 root 1.9 return;
722 elmex 1.1 }
723    
724 root 1.9 /* we need to stop thrown objects at some point. Like here. */
725     if (op->type == THROWN_OBJ)
726     {
727     /* If the object that the THROWN_OBJ encapsulates disappears,
728     * we need to have this object go away also - otherwise, you get
729     * left over remnants on the map. Where this currently happens
730     * is if the player throws a bomb - the bomb explodes on its own,
731     * but this object sticks around. We could handle the cleanup in the
732     * bomb code, but there are potential other cases where that could happen,
733     * and it is easy enough to clean it up here.
734     */
735     if (op->inv == NULL)
736     {
737 root 1.17 op->destroy ();
738 root 1.9 return;
739 root 1.6 }
740 root 1.14
741 root 1.9 if (op->last_sp-- < 0)
742     {
743     stop_arrow (op);
744     return;
745 root 1.6 }
746 elmex 1.1 }
747    
748 root 1.9 /* if the arrow is moving too slow.. stop it. 0.5 was chosen as lower
749     values look rediculous. */
750     if (op->speed < 0.5 && op->type == ARROW)
751     {
752     stop_arrow (op);
753     return;
754 elmex 1.1 }
755    
756 root 1.9 /* Calculate target map square */
757     new_x = op->x + DIRX (op);
758     new_y = op->y + DIRY (op);
759     was_reflected = 0;
760 elmex 1.1
761 root 1.9 m = op->map;
762     mflags = get_map_flags (m, &m, new_x, new_y, &new_x, &new_y);
763 elmex 1.1
764 root 1.9 if (mflags & P_OUT_OF_MAP)
765     {
766     stop_arrow (op);
767     return;
768 elmex 1.1 }
769    
770 root 1.9 /* only need to look for living creatures if this flag is set */
771     if (mflags & P_IS_ALIVE)
772     {
773 root 1.20 for (tmp = GET_MAP_OB (m, new_x, new_y); tmp != NULL; tmp = tmp->above)
774 root 1.9 if (QUERY_FLAG (tmp, FLAG_ALIVE))
775     break;
776    
777     /* Not really fair, but don't let monsters hit themselves with
778     * their own arrow - this can be because they fire it then
779     * move into it.
780     */
781 root 1.14 if (tmp && tmp != op->owner)
782 root 1.9 {
783     /* Found living object, but it is reflecting the missile. Update
784     * as below. (Note that for living creatures there is a small
785     * chance that reflect_missile fails.)
786     */
787     if (QUERY_FLAG (tmp, FLAG_REFL_MISSILE) && (rndm (0, 99)) < (90 - op->level / 10))
788     {
789     int number = op->face->number;
790    
791     op->direction = absdir (op->direction + 4);
792     op->state = 0;
793 root 1.14
794 root 1.9 if (GET_ANIM_ID (op))
795     {
796     number += 4;
797 root 1.14
798 root 1.9 if (number > GET_ANIMATION (op, 8))
799     number -= 8;
800 root 1.14
801 root 1.9 op->face = &new_faces[number];
802 root 1.6 }
803 root 1.14
804 root 1.9 was_reflected = 1; /* skip normal movement calculations */
805 root 1.6 }
806 root 1.9 else
807     {
808     /* Attack the object. */
809     op = hit_with_arrow (op, tmp);
810 root 1.14
811     if (!op)
812 root 1.9 return;
813     }
814     } /* if this is not hitting its owner */
815     } /* if there is something alive on this space */
816 root 1.6
817 root 1.9 if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y)))
818     {
819     int retry = 0;
820    
821     /* if the object doesn't reflect, stop the arrow from moving
822     * note that this code will now catch cases where a monster is
823     * on a wall but has reflecting - the arrow won't reflect.
824     * Mapmakers shouldn't put monsters on top of wall in the first
825     * place, so I don't consider that a problem.
826     */
827     if (!QUERY_FLAG (op, FLAG_REFLECTING) || !(rndm (0, 19)))
828     {
829     stop_arrow (op);
830     return;
831     }
832     else
833     {
834     /* If one of the major directions (n,s,e,w), just reverse it */
835     if (op->direction & 1)
836     {
837     op->direction = absdir (op->direction + 4);
838     retry = 1;
839     }
840     /* There were two blocks with identical code -
841     * use this retry here to make this one block
842     * that did the same thing.
843     */
844     while (retry < 2)
845     {
846     int left, right, mflags;
847 root 1.13 maptile *m1;
848 root 1.9 sint16 x1, y1;
849    
850     retry++;
851    
852     /* Need to check for P_OUT_OF_MAP: if the arrow is tavelling
853     * over a corner in a tiled map, it is possible that
854     * op->direction is within an adjacent map but either
855     * op->direction-1 or op->direction+1 does not exist.
856     */
857     mflags = get_map_flags (op->map, &m1, op->x + freearr_x[absdir (op->direction - 1)],
858     op->y + freearr_y[absdir (op->direction - 1)], &x1, &y1);
859     left = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, (GET_MAP_MOVE_BLOCK (m1, x1, y1)));
860    
861     mflags = get_map_flags (op->map, &m1, op->x + freearr_x[absdir (op->direction + 1)],
862     op->y + freearr_y[absdir (op->direction + 1)], &x1, &y1);
863     right = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, (GET_MAP_MOVE_BLOCK (m1, x1, y1)));
864    
865     if (left == right)
866     op->direction = absdir (op->direction + 4);
867     else if (left)
868     op->direction = absdir (op->direction + 2);
869     else if (right)
870     op->direction = absdir (op->direction - 2);
871    
872     mflags = get_map_flags (op->map, &m1, op->x + DIRX (op), op->y + DIRY (op), &x1, &y1);
873    
874     /* If this space is not out of the map and not blocked, valid space -
875     * don't need to retry again.
876     */
877     if (!(mflags & P_OUT_OF_MAP) && !OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m1, x1, y1)))
878     break;
879 root 1.6
880     }
881 root 1.9 /* Couldn't find a direction to move the arrow to - just
882     * top it from moving.
883     */
884     if (retry == 2)
885     {
886     stop_arrow (op);
887     return;
888     }
889     /* update object image for new facing */
890     /* many thrown objects *don't* have more than one face */
891     if (GET_ANIM_ID (op))
892     SET_ANIMATION (op, op->direction);
893     } /* object is reflected */
894     } /* object ran into a wall */
895    
896     /* Move the arrow. */
897 root 1.16 op->remove ();
898 root 1.9 op->x = new_x;
899     op->y = new_y;
900    
901     /* decrease the speed as it flies. 0.05 means a standard bow will shoot
902     * about 17 squares. Tune as needed.
903     */
904     op->speed -= 0.05;
905     insert_ob_in_map (op, m, op, 0);
906 elmex 1.1 }
907    
908     /* This routine doesnt seem to work for "inanimate" objects that
909     * are being carried, ie a held torch leaps from your hands!.
910     * Modified this routine to allow held objects. b.t. */
911    
912 root 1.9 void
913     change_object (object *op)
914     { /* Doesn`t handle linked objs yet */
915     int i, j;
916    
917     if (op->other_arch == NULL)
918     {
919     LOG (llevError, "Change object (%s) without other_arch error.\n", &op->name);
920     return;
921     }
922    
923     /* In non-living items only change when food value is 0 */
924     if (!QUERY_FLAG (op, FLAG_ALIVE))
925     {
926     if (op->stats.food-- > 0)
927     return;
928     else
929     op->stats.food = 1; /* so 1 other_arch is made */
930     }
931 root 1.20
932     object *pl = op->in_player ();
933     object *env = op->env;
934    
935 root 1.16 op->remove ();
936 root 1.9 for (i = 0; i < NROFNEWOBJS (op); i++)
937     {
938 root 1.20 object *tmp = arch_to_object (op->other_arch);
939    
940 root 1.9 if (op->type == LAMP)
941     tmp->stats.food = op->stats.food - 1;
942 root 1.20
943 root 1.9 tmp->stats.hp = op->stats.hp; /* The only variable it keeps. */
944     if (env)
945     {
946     tmp->x = env->x, tmp->y = env->y;
947     tmp = insert_ob_in_ob (tmp, env);
948 root 1.20
949 root 1.9 /* If this object is the players inventory, we need to tell the
950     * client of the change. Insert_ob_in_map takes care of the
951     * updating the client, so we don't need to do that below.
952     */
953 root 1.20 if (pl)
954 root 1.9 {
955     esrv_del_item (pl->contr, op->count);
956     esrv_send_item (pl, tmp);
957     }
958     }
959     else
960     {
961     j = find_first_free_spot (tmp, op->map, op->x, op->y);
962     if (j == -1) /* No free spot */
963 root 1.17 tmp->destroy ();
964 root 1.9 else
965     {
966     tmp->x = op->x + freearr_x[j], tmp->y = op->y + freearr_y[j];
967     insert_ob_in_map (tmp, op->map, op, 0);
968     }
969     }
970     }
971 root 1.17
972     op->destroy ();
973 root 1.9 }
974    
975     void
976     move_teleporter (object *op)
977     {
978     object *tmp, *head = op;
979 elmex 1.1
980 root 1.9 /* if this is a multipart teleporter, handle the other parts
981     * The check for speed isn't strictly needed - basically, if
982     * there is an old multipart teleporter in which the other parts
983     * have speed, we don't really want to call it twice for the same
984     * function - in fact, as written below, part N would get called
985     * N times without the speed check.
986     */
987     if (op->more && FABS (op->more->speed) < MIN_ACTIVE_SPEED)
988     move_teleporter (op->more);
989    
990     if (op->head)
991     head = op->head;
992    
993     for (tmp = op->above; tmp != NULL; tmp = tmp->above)
994     if (!QUERY_FLAG (tmp, FLAG_IS_FLOOR))
995     break;
996    
997     /* If nothing above us to move, nothing to do */
998     if (!tmp || QUERY_FLAG (tmp, FLAG_WIZPASS))
999 elmex 1.1 return;
1000    
1001 root 1.9 if (EXIT_PATH (head))
1002     {
1003     if (tmp->type == PLAYER)
1004     {
1005     if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
1006     return;
1007 root 1.3
1008 root 1.9 enter_exit (tmp, head);
1009 root 1.6 }
1010 root 1.9 else
1011     /* Currently only players can transfer maps */
1012     return;
1013 elmex 1.1 }
1014 root 1.9 else if (EXIT_X (head) || EXIT_Y (head))
1015     {
1016     if (out_of_map (head->map, EXIT_X (head), EXIT_Y (head)))
1017     {
1018     LOG (llevError, "Removed illegal teleporter.\n");
1019 root 1.17 head->destroy ();
1020 root 1.9 return;
1021 root 1.6 }
1022 root 1.17
1023 root 1.9 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
1024     return;
1025 root 1.17
1026 root 1.9 transfer_ob (tmp, EXIT_X (head), EXIT_Y (head), 0, head);
1027 elmex 1.1 }
1028 root 1.9 else
1029     {
1030     /* Random teleporter */
1031     if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
1032     return;
1033     teleport (head, TELEPORTER, tmp);
1034 elmex 1.1 }
1035     }
1036    
1037    
1038     /* This object will teleport someone to a different map
1039     and will also apply changes to the player from its inventory.
1040     This was invented for giving classes, but there's no reason it
1041     can't be generalized.
1042     */
1043    
1044 root 1.9 void
1045     move_player_changer (object *op)
1046     {
1047     object *player;
1048     object *walk;
1049     char c;
1050    
1051     if (!op->above || !EXIT_PATH (op))
1052     return;
1053    
1054     /* This isn't all that great - means that the player_mover
1055     * needs to be on top.
1056     */
1057     if (op->above->type == PLAYER)
1058     {
1059     if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (player)))
1060     return;
1061     player = op->above;
1062    
1063     for (walk = op->inv; walk != NULL; walk = walk->below)
1064     apply_changes_to_player (player, walk);
1065    
1066 root 1.23 player->update_stats ();
1067 root 1.9
1068     esrv_send_inventory (op->above, op->above);
1069     esrv_update_item (UPD_FACE, op->above, op->above);
1070    
1071     /* update players death & WoR home-position */
1072     sscanf (EXIT_PATH (op), "%c", &c);
1073     if (c == '/')
1074     {
1075     strcpy (player->contr->savebed_map, EXIT_PATH (op));
1076     player->contr->bed_x = EXIT_X (op);
1077     player->contr->bed_y = EXIT_Y (op);
1078     }
1079     else
1080     LOG (llevDebug, "WARNING: destination '%s' in player_changer must be an absolute path!\n", &EXIT_PATH (op));
1081    
1082     enter_exit (op->above, op);
1083 root 1.23 player->contr->save ();
1084 elmex 1.1 }
1085     }
1086    
1087     /* firewalls fire other spells.
1088     * The direction of the wall is stored in op->stats.sp.
1089     * walls can have hp, so they can be torn down.
1090     */
1091 root 1.9 void
1092     move_firewall (object *op)
1093     {
1094     object *spell;
1095 elmex 1.1
1096 root 1.9 if (!op->map)
1097     return; /* dm has created a firewall in his inventory */
1098 elmex 1.1
1099 root 1.9 spell = op->inv;
1100 root 1.24
1101 root 1.9 if (!spell || spell->type != SPELL)
1102     spell = &op->other_arch->clone;
1103 root 1.24
1104 root 1.9 if (!spell)
1105     {
1106     LOG (llevError, "move_firewall: no spell specified (%s, %s, %d, %d)\n", &op->name, op->map->name, op->x, op->y);
1107     return;
1108 elmex 1.1 }
1109    
1110 root 1.9 cast_spell (op, op, op->stats.sp ? op->stats.sp : rndm (1, 8), spell, NULL);
1111 elmex 1.1 }
1112    
1113     /* move_player_mover: this function takes a "player mover" as an
1114     * argument, and performs the function of a player mover, which is:
1115     *
1116     * a player mover finds any players that are sitting on it. It
1117     * moves them in the op->stats.sp direction. speed is how often it'll move.
1118     * If attacktype is nonzero it will paralyze the player. If lifesave is set,
1119     * it'll dissapear after hp+1 moves. If hp is set and attacktype is set,
1120     * it'll paralyze the victim for hp*his speed/op->speed
1121     */
1122 root 1.9 void
1123     move_player_mover (object *op)
1124     {
1125     object *victim, *nextmover;
1126     int dir = op->stats.sp;
1127     sint16 nx, ny;
1128 root 1.13 maptile *m;
1129 root 1.9
1130     /* Determine direction now for random movers so we do the right thing */
1131     if (!dir)
1132     dir = rndm (1, 8);
1133    
1134 root 1.20 for (victim = GET_MAP_OB (op->map, op->x, op->y); victim != NULL; victim = victim->above)
1135 root 1.9 {
1136     if (QUERY_FLAG (victim, FLAG_ALIVE) && !QUERY_FLAG (victim, FLAG_WIZPASS) &&
1137     (victim->move_type & op->move_type || !victim->move_type))
1138     {
1139    
1140     if (victim->head)
1141     victim = victim->head;
1142    
1143     if (QUERY_FLAG (op, FLAG_LIFESAVE) && op->stats.hp-- < 0)
1144     {
1145 root 1.16 op->remove ();
1146 root 1.9 return;
1147     }
1148 root 1.17
1149 root 1.9 nx = op->x + freearr_x[dir];
1150     ny = op->y + freearr_y[dir];
1151     m = op->map;
1152     if (get_map_flags (m, &m, nx, ny, &nx, &ny) & P_OUT_OF_MAP)
1153     {
1154     LOG (llevError, "move_player_mover: Trying to push player off the map! map=%s (%d, %d)\n", m->path, op->x, op->y);
1155     return;
1156 root 1.6 }
1157 root 1.9
1158     if (should_director_abort (op, victim))
1159     return;
1160    
1161 root 1.20 for (nextmover = GET_MAP_OB (m, nx, ny); nextmover != NULL; nextmover = nextmover->above)
1162 root 1.9 {
1163     if (nextmover->type == PLAYERMOVER)
1164     nextmover->speed_left = -.99;
1165     if (QUERY_FLAG (nextmover, FLAG_ALIVE))
1166     {
1167     op->speed_left = -1.1; /* wait until the next thing gets out of the way */
1168 root 1.6 }
1169     }
1170    
1171 root 1.9 if (victim->type == PLAYER)
1172     {
1173     /* only level >=1 movers move people */
1174     if (op->level)
1175     {
1176     /* Following is a bit of hack. We need to make sure it
1177     * is cleared, otherwise the player will get stuck in
1178     * place. This can happen if the player used a spell to
1179     * get to this space.
1180     */
1181     victim->contr->fire_on = 0;
1182     victim->speed_left = -FABS (victim->speed);
1183     move_player (victim, dir);
1184 root 1.6 }
1185 root 1.9 else
1186     return;
1187 root 1.6 }
1188 root 1.9 else
1189     move_object (victim, dir);
1190 root 1.6
1191 root 1.9 if (!op->stats.maxsp && op->attacktype)
1192     op->stats.maxsp = 2;
1193 root 1.6
1194 root 1.9 if (op->attacktype)
1195     { /* flag to paralyze the player */
1196 root 1.6
1197 root 1.9 victim->speed_left = -FABS (op->stats.maxsp * victim->speed / op->speed);
1198     /* Not sure why, but for some chars on metalforge, they
1199     * would sometimes get -inf speed_left, and from the
1200     * description, it could only happen here, so just put
1201     * a lower sanity limit. My only guess is that the
1202     * mover has 0 speed.
1203     */
1204     if (victim->speed_left < -5.0)
1205     victim->speed_left = -5.0;
1206 root 1.6 }
1207     }
1208 elmex 1.1 }
1209     }
1210    
1211     /*
1212     * Will duplicate a specified object placed on top of it.
1213     * connected: what will trigger it.
1214     * level: multiplier. 0 to destroy.
1215     * other_arch: the object to look for and duplicate.
1216     */
1217    
1218 root 1.9 void
1219     move_duplicator (object *op)
1220     {
1221     object *tmp;
1222 elmex 1.1
1223 root 1.9 if (!op->other_arch)
1224     {
1225     LOG (llevInfo, "Duplicator with no other_arch! %d %d %s\n", op->x, op->y, op->map ? op->map->path : "nullmap");
1226     return;
1227 elmex 1.1 }
1228    
1229 root 1.9 if (op->above == NULL)
1230     return;
1231     for (tmp = op->above; tmp != NULL; tmp = tmp->above)
1232     {
1233     if (strcmp (op->other_arch->name, tmp->arch->name) == 0)
1234     {
1235     if (op->level <= 0)
1236 root 1.17 tmp->destroy ();
1237 root 1.9 else
1238     {
1239     uint64 new_nrof = (uint64) tmp->nrof * op->level;
1240    
1241     if (new_nrof >= 1UL << 31)
1242     new_nrof = 1UL << 31;
1243 root 1.17
1244 root 1.9 tmp->nrof = new_nrof;
1245 root 1.6 }
1246 root 1.17
1247 root 1.9 break;
1248 root 1.6 }
1249 elmex 1.1 }
1250     }
1251    
1252     /* move_creator (by peterm)
1253     * it has the creator object create it's other_arch right on top of it.
1254     * connected: what will trigger it
1255     * hp: how many times it may create before stopping
1256     * lifesave: if set, it'll never disappear but will go on creating
1257     * everytime it's triggered
1258     * other_arch: the object to create
1259     * Note this can create large objects, however, in that case, it
1260     * has to make sure that there is in fact space for the object.
1261     * It should really do this for small objects also, but there is
1262     * more concern with large objects, most notably a part being placed
1263     * outside of the map which would cause the server to crash
1264     */
1265    
1266 root 1.9 void
1267     move_creator (object *creator)
1268     {
1269     object *new_ob;
1270 elmex 1.1
1271 root 1.9 if (!QUERY_FLAG (creator, FLAG_LIFESAVE) && --creator->stats.hp < 0)
1272     {
1273     creator->stats.hp = -1;
1274     return;
1275 elmex 1.1 }
1276    
1277 root 1.9 if (creator->inv != NULL)
1278     {
1279     object *ob;
1280     int i;
1281     object *ob_to_copy;
1282    
1283     /* select random object from inventory to copy */
1284     ob_to_copy = creator->inv;
1285     for (ob = creator->inv->below, i = 1; ob != NULL; ob = ob->below, i++)
1286     {
1287     if (rndm (0, i) == 0)
1288     {
1289     ob_to_copy = ob;
1290     }
1291     }
1292     new_ob = object_create_clone (ob_to_copy);
1293     CLEAR_FLAG (new_ob, FLAG_IS_A_TEMPLATE);
1294     unflag_inv (new_ob, FLAG_IS_A_TEMPLATE);
1295     }
1296     else
1297     {
1298     if (creator->other_arch == NULL)
1299     {
1300     LOG (llevError, "move_creator: Creator doesn't have other arch set: %s (%s, %d, %d)\n", &creator->name, creator->map->path,
1301     creator->x, creator->y);
1302     return;
1303 root 1.6 }
1304 elmex 1.1
1305 root 1.9 new_ob = object_create_arch (creator->other_arch);
1306     fix_generated_item (new_ob, creator, 0, 0, GT_MINIMAL);
1307 elmex 1.1 }
1308    
1309 root 1.9 /* Make sure this multipart object fits */
1310     if (new_ob->arch->more && ob_blocked (new_ob, creator->map, creator->x, creator->y))
1311     {
1312 root 1.17 new_ob->destroy ();
1313 root 1.9 return;
1314 elmex 1.1 }
1315    
1316 root 1.9 insert_ob_in_map_at (new_ob, creator->map, creator, 0, creator->x, creator->y);
1317     if (QUERY_FLAG (new_ob, FLAG_FREED))
1318     return;
1319 elmex 1.1
1320 root 1.9 if (creator->slaying)
1321     {
1322     new_ob->name = new_ob->title = creator->slaying;
1323 elmex 1.1 }
1324     }
1325    
1326     /* move_marker --peterm@soda.csua.berkeley.edu
1327     when moved, a marker will search for a player sitting above
1328     it, and insert an invisible, weightless force into him
1329     with a specific code as the slaying field.
1330     At that time, it writes the contents of its own message
1331     field to the player. The marker will decrement hp to
1332     0 and then delete itself every time it grants a mark.
1333     unless hp was zero to start with, in which case it is infinite.*/
1334    
1335 root 1.9 void
1336     move_marker (object *op)
1337     {
1338 root 1.22 if (object *tmp = op->ms ().player ())
1339     {
1340     object *tmp2;
1341 root 1.9
1342 root 1.22 /* remove an old force with a slaying field == op->name */
1343     for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
1344     if (tmp2->type == FORCE && tmp2->slaying && !strcmp (tmp2->slaying, op->name))
1345     {
1346     tmp2->destroy ();
1347     break;
1348     }
1349 root 1.9
1350 root 1.22 /* cycle through his inventory to look for the MARK we want to
1351     * place
1352     */
1353     for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
1354     if (tmp2->type == FORCE && tmp2->slaying && !strcmp (tmp2->slaying, op->slaying))
1355     break;
1356 root 1.9
1357 root 1.22 /* if we didn't find our own MARK */
1358     if (tmp2 == NULL)
1359     {
1360     object *force = get_archetype (FORCE_NAME);
1361 root 1.9
1362 root 1.22 force->speed = 0;
1363     if (op->stats.food)
1364 root 1.9 {
1365 root 1.22 force->speed = 0.01;
1366     force->speed_left = -op->stats.food;
1367 root 1.9 }
1368 root 1.22 update_ob_speed (force);
1369     /* put in the lock code */
1370     force->slaying = op->slaying;
1371    
1372     if (op->lore)
1373     force->lore = op->lore;
1374    
1375     insert_ob_in_ob (force, tmp);
1376     if (op->msg)
1377     new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, tmp, op->msg);
1378 root 1.9
1379 root 1.22 if (op->stats.hp > 0)
1380 root 1.9 {
1381 root 1.22 op->stats.hp--;
1382     if (op->stats.hp == 0)
1383 root 1.9 {
1384 root 1.22 /* marker expires--granted mark number limit */
1385     op->destroy ();
1386     return;
1387 root 1.6 }
1388 root 1.22 }
1389     }
1390     }
1391 root 1.9 }
1392    
1393     int
1394     process_object (object *op)
1395     {
1396     if (QUERY_FLAG (op, FLAG_IS_A_TEMPLATE))
1397     return 0;
1398    
1399     if (INVOKE_OBJECT (TICK, op))
1400     return 0;
1401    
1402     if (QUERY_FLAG (op, FLAG_MONSTER))
1403     if (move_monster (op) || QUERY_FLAG (op, FLAG_FREED))
1404     return 1;
1405    
1406     if (QUERY_FLAG (op, FLAG_ANIMATE) && op->anim_speed == 0)
1407     {
1408     if (op->type == PLAYER)
1409     animate_object (op, op->facing);
1410     else
1411     animate_object (op, op->direction);
1412    
1413     if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1414     make_sure_seen (op);
1415     }
1416 root 1.10
1417 root 1.9 if (QUERY_FLAG (op, FLAG_CHANGING) && !op->state)
1418     {
1419     change_object (op);
1420     return 1;
1421     }
1422 root 1.10
1423 root 1.9 if (QUERY_FLAG (op, FLAG_GENERATOR) && !QUERY_FLAG (op, FLAG_FRIENDLY))
1424     generate_monster (op);
1425    
1426     if (QUERY_FLAG (op, FLAG_IS_USED_UP) && --op->stats.food <= 0)
1427     {
1428     if (QUERY_FLAG (op, FLAG_APPLIED))
1429     remove_force (op);
1430     else
1431     {
1432     /* IF necessary, delete the item from the players inventory */
1433 root 1.20 object *pl = op->in_player ();
1434 root 1.9
1435     if (pl)
1436     esrv_del_item (pl->contr, op->count);
1437 root 1.10
1438 root 1.16 op->remove ();
1439 root 1.10
1440 root 1.9 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1441     make_sure_not_seen (op);
1442 root 1.10
1443 root 1.17 op->destroy ();
1444 root 1.6 }
1445 root 1.10
1446 root 1.9 return 1;
1447 elmex 1.1 }
1448 root 1.11
1449 root 1.9 switch (op->type)
1450     {
1451 root 1.10 case SPELL_EFFECT:
1452     move_spell_effect (op);
1453     return 1;
1454    
1455     case ROD:
1456     case HORN:
1457     regenerate_rod (op);
1458     return 1;
1459    
1460     case FORCE:
1461     case POTION_EFFECT:
1462     remove_force (op);
1463     return 1;
1464 elmex 1.1
1465 root 1.10 case BLINDNESS:
1466     remove_blindness (op);
1467     return 0;
1468    
1469     case POISONING:
1470     poison_more (op);
1471     return 0;
1472    
1473     case DISEASE:
1474     move_disease (op);
1475     return 0;
1476    
1477     case SYMPTOM:
1478     move_symptom (op);
1479     return 0;
1480    
1481     case THROWN_OBJ:
1482     case ARROW:
1483     move_arrow (op);
1484     return 0;
1485    
1486     case DOOR:
1487     remove_door (op);
1488     return 0;
1489    
1490     case LOCKED_DOOR:
1491     remove_door2 (op);
1492     return 0;
1493    
1494     case TELEPORTER:
1495     move_teleporter (op);
1496     return 0;
1497    
1498     case GOLEM:
1499     move_golem (op);
1500     return 0;
1501    
1502     case EARTHWALL:
1503     hit_player (op, 2, op, AT_PHYSICAL, 1);
1504     return 0;
1505    
1506     case FIREWALL:
1507     move_firewall (op);
1508     if (op->stats.maxsp)
1509     animate_turning (op);
1510     return 0;
1511    
1512     case MOOD_FLOOR:
1513     do_mood_floor (op);
1514     return 0;
1515    
1516     case GATE:
1517     move_gate (op);
1518     return 0;
1519    
1520     case TIMED_GATE:
1521     move_timed_gate (op);
1522     return 0;
1523    
1524     case TRIGGER:
1525     case TRIGGER_BUTTON:
1526     case TRIGGER_PEDESTAL:
1527     case TRIGGER_ALTAR:
1528     animate_trigger (op);
1529     return 0;
1530    
1531     case DETECTOR:
1532     move_detector (op);
1533    
1534     case DIRECTOR:
1535     if (op->stats.maxsp)
1536     animate_turning (op);
1537     return 0;
1538    
1539     case HOLE:
1540     move_hole (op);
1541     return 0;
1542    
1543     case DEEP_SWAMP:
1544     move_deep_swamp (op);
1545     return 0;
1546    
1547     case RUNE:
1548     case TRAP:
1549     move_rune (op);
1550     return 0;
1551    
1552     case PLAYERMOVER:
1553     move_player_mover (op);
1554     return 0;
1555    
1556     case CREATOR:
1557     move_creator (op);
1558     return 0;
1559    
1560     case MARKER:
1561     move_marker (op);
1562     return 0;
1563    
1564     case PLAYER_CHANGER:
1565     move_player_changer (op);
1566     return 0;
1567    
1568     case PEACEMAKER:
1569     move_peacemaker (op);
1570     return 0;
1571 elmex 1.1 }
1572    
1573 root 1.9 return 0;
1574 elmex 1.1 }