ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.8
Committed: Sun Sep 3 00:18:42 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.7: +14 -21 lines
Log Message:
THIS CODE WILL NOT COMPILE
use the STABLE tag instead.

- major changes in object lifetime and memory management
- replaced manual refcounting by shstr class
- removed quest system
- many optimisations
- major changes

File Contents

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