ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.81
Committed: Mon Sep 29 10:31:32 2008 UTC (15 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.80: +5 -9 lines
Log Message:
introduce drop_and_destroy and use it

File Contents

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