ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.5
Committed: Tue Aug 29 07:34:01 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.4: +1 -9 lines
Log Message:
nuke transports

File Contents

# User Rev Content
1 elmex 1.1 /*
2     * static char *rcsid_time_c =
3 root 1.5 * "$Id: time.C,v 1.4 2006-08-29 05:03:55 root 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     //LOG(llevError,"Generator (%s) not on a map?\n", gen->name);
100     return;
101     }
102     /*First count numer of objects in inv*/
103     for (op=gen->inv;op;op=op->below)
104     qty++;
105     if (!qty){
106     LOG(llevError,"Generator (%s) has no inventory in generate_monster_inv?\n", gen->name);
107     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     //LOG(llevError,"Generator without other_arch: %s\n",gen->name);
134     return;
135     }
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     //LOG(llevError,"Generator (%s) not on a map?\n", gen->name);
142     return;
143     }
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     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     gen->map->difficulty,0);
160     if(head==NULL)
161     head=op;
162     prev=op;
163     at=at->more;
164     }
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     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     }
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     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     }
250    
251     /* We're going down */
252     if(op->value) {
253     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     }
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     /* 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     }
299    
300     if(op->stats.food) { /* The gate is going temporarily down */
301     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     } else { /* The gate is still going up */
306     op->stats.wc++;
307    
308     if((int)op->stats.wc >= (NUM_ANIMATIONS(op)))
309     op->stats.wc=(signed char)NUM_ANIMATIONS(op)-1;
310    
311     /* 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     "You are crushed by the %s!",op->name);
325     } 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    
363     SET_ANIMATION(op, op->stats.wc);
364     update_object(op,UP_OBJ_CHANGE);
365     } /* 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     op->speed = 0;
386     update_ob_speed(op);
387     }
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     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     }
419    
420     /* the detector sets the button if detection is found */
421     if(op->stats.sp == 1) {
422     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     }
431     else { /* in this case, we unset buttons */
432     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     }
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     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     }
475     /* We're closing */
476     op->move_on = 0;
477    
478     op->stats.wc++;
479     if((int)op->stats.wc >= NUM_ANIMATIONS(op))
480     op->stats.wc=NUM_ANIMATIONS(op)-1;
481     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     op->speed = 0;
485     update_ob_speed(op); /* closed, let's stop */
486     return;
487     }
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     /* Small chance of breaking */
551     remove_ob (op);
552     free_object(op);
553     return NULL;
554     }
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     if (op->slaying != NULL)
565     FREE_AND_CLEAR_STR(op->slaying);
566    
567     if (op->skill != NULL)
568     FREE_AND_CLEAR_STR(op->skill);
569    
570     if (op->spellarg != NULL) {
571     op->slaying = add_string(op->spellarg);
572     free(op->spellarg);
573     op->spellarg = NULL;
574     } else
575     op->slaying = NULL;
576    
577     /* Reset these to zero, so that CAN_MERGE will work properly */
578     op->spellarg = NULL;
579     op->stats.sp = 0;
580     op->stats.hp = 0;
581     op->stats.grace = 0;
582     op->level = 0;
583     op->face=op->arch->clone.face;
584     op->owner=NULL; /* So that stopped arrows will be saved */
585     update_object (op,UP_OBJ_FACE);
586     return op;
587     }
588    
589     /* stop_arrow() - what to do when a non-living flying object
590     * has to stop. Sept 96 - I added in thrown object code in
591     * here too. -b.t.
592     *
593     * Returns a pointer to the stopped object (which will have been removed
594     * from maps or inventories), or NULL if was destroyed.
595     */
596    
597     static void stop_arrow (object *op)
598     {
599 root 1.4 if (INVOKE_OBJECT (STOP, op))
600     return;
601    
602 elmex 1.1 if (op->inv) {
603     object *payload = op->inv;
604     remove_ob (payload);
605     clear_owner(payload);
606     insert_ob_in_map (payload, op->map, payload,0);
607     remove_ob (op);
608     free_object (op);
609     } else {
610     op = fix_stopped_arrow (op);
611     if (op)
612     merge_ob (op, NULL);
613     }
614     }
615    
616     /* Move an arrow along its course. op is the arrow or thrown object.
617     */
618    
619     void move_arrow(object *op) {
620     object *tmp;
621     sint16 new_x, new_y;
622     int was_reflected, mflags;
623     mapstruct *m;
624    
625     if(op->map==NULL) {
626     LOG (llevError, "BUG: Arrow had no map.\n");
627     remove_ob(op);
628     free_object(op);
629     return;
630     }
631    
632     /* we need to stop thrown objects at some point. Like here. */
633     if(op->type==THROWN_OBJ) {
634     /* If the object that the THROWN_OBJ encapsulates disappears,
635     * we need to have this object go away also - otherwise, you get
636     * left over remnants on the map. Where this currently happens
637     * is if the player throws a bomb - the bomb explodes on its own,
638     * but this object sticks around. We could handle the cleanup in the
639     * bomb code, but there are potential other cases where that could happen,
640     * and it is easy enough to clean it up here.
641     */
642     if (op->inv == NULL) {
643     remove_ob(op);
644     free_object(op);
645     return;
646     }
647     if(op->last_sp-- < 0) {
648     stop_arrow (op);
649     return;
650     }
651     }
652    
653     /* if the arrow is moving too slow.. stop it. 0.5 was chosen as lower
654     values look rediculous. */
655     if (op->speed < 0.5 && op->type==ARROW) {
656     stop_arrow(op);
657     return;
658     }
659    
660     /* Calculate target map square */
661     new_x = op->x + DIRX(op);
662     new_y = op->y + DIRY(op);
663     was_reflected = 0;
664    
665     m = op->map;
666     mflags = get_map_flags(m, &m, new_x, new_y, &new_x, &new_y);
667    
668     if (mflags & P_OUT_OF_MAP) {
669     stop_arrow(op);
670     return;
671     }
672    
673     /* only need to look for living creatures if this flag is set */
674     if (mflags & P_IS_ALIVE) {
675     for (tmp = get_map_ob(m, new_x, new_y); tmp != NULL; tmp=tmp->above)
676     if (QUERY_FLAG(tmp, FLAG_ALIVE)) break;
677    
678    
679     /* Not really fair, but don't let monsters hit themselves with
680     * their own arrow - this can be because they fire it then
681     * move into it.
682     */
683    
684     if (tmp != NULL && tmp != op->owner) {
685     /* Found living object, but it is reflecting the missile. Update
686     * as below. (Note that for living creatures there is a small
687     * chance that reflect_missile fails.)
688     */
689    
690     if (QUERY_FLAG (tmp, FLAG_REFL_MISSILE) &&
691     (rndm(0, 99)) < (90-op->level/10)) {
692    
693     int number = op->face->number;
694    
695     op->direction = absdir (op->direction + 4);
696     op->state = 0;
697     if (GET_ANIM_ID (op)) {
698     number += 4;
699     if (number > GET_ANIMATION (op, 8))
700     number -= 8;
701     op->face = &new_faces[number];
702     }
703     was_reflected = 1; /* skip normal movement calculations */
704     }
705     else {
706     /* Attack the object. */
707     op = hit_with_arrow (op, tmp);
708     if (op == NULL)
709     return;
710     }
711     } /* if this is not hitting its owner */
712     } /* if there is something alive on this space */
713    
714    
715     if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y))) {
716     int retry=0;
717    
718     /* if the object doesn't reflect, stop the arrow from moving
719     * note that this code will now catch cases where a monster is
720     * on a wall but has reflecting - the arrow won't reflect.
721     * Mapmakers shouldn't put monsters on top of wall in the first
722     * place, so I don't consider that a problem.
723     */
724     if(!QUERY_FLAG(op, FLAG_REFLECTING) || !(rndm(0, 19))) {
725     stop_arrow (op);
726     return;
727     } else {
728     /* If one of the major directions (n,s,e,w), just reverse it */
729     if(op->direction&1) {
730     op->direction=absdir(op->direction+4);
731     retry=1;
732     }
733     /* There were two blocks with identical code -
734     * use this retry here to make this one block
735     * that did the same thing.
736     */
737     while (retry<2) {
738     int left, right, mflags;
739     mapstruct *m1;
740     sint16 x1, y1;
741    
742     retry++;
743    
744     /* Need to check for P_OUT_OF_MAP: if the arrow is tavelling
745     * over a corner in a tiled map, it is possible that
746     * op->direction is within an adjacent map but either
747     * op->direction-1 or op->direction+1 does not exist.
748     */
749     mflags = get_map_flags(op->map,&m1, op->x+freearr_x[absdir(op->direction-1)],
750     op->y+freearr_y[absdir(op->direction-1)], &x1, &y1);
751     left = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, (GET_MAP_MOVE_BLOCK(m1, x1, y1)));
752    
753     mflags = get_map_flags(op->map,&m1, op->x+freearr_x[absdir(op->direction+1)],
754     op->y+freearr_y[absdir(op->direction+1)], &x1, &y1);
755     right = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, (GET_MAP_MOVE_BLOCK(m1, x1, y1)));
756    
757     if(left==right)
758     op->direction=absdir(op->direction+4);
759     else if(left)
760     op->direction=absdir(op->direction+2);
761     else if(right)
762     op->direction=absdir(op->direction-2);
763    
764     mflags = get_map_flags(op->map,&m1, op->x+DIRX(op),op->y+DIRY(op), &x1, &y1);
765    
766     /* If this space is not out of the map and not blocked, valid space -
767     * don't need to retry again.
768     */
769     if (!(mflags & P_OUT_OF_MAP) &&
770     !OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m1, x1, y1))) break;
771    
772     }
773     /* Couldn't find a direction to move the arrow to - just
774     * top it from moving.
775     */
776     if (retry==2) {
777     stop_arrow (op);
778     return;
779     }
780     /* update object image for new facing */
781     /* many thrown objects *don't* have more than one face */
782     if(GET_ANIM_ID(op))
783     SET_ANIMATION(op, op->direction);
784     } /* object is reflected */
785     } /* object ran into a wall */
786    
787     /* Move the arrow. */
788     remove_ob (op);
789     op->x = new_x;
790     op->y = new_y;
791    
792     /* decrease the speed as it flies. 0.05 means a standard bow will shoot
793     * about 17 squares. Tune as needed.
794     */
795     op->speed -= 0.05;
796     insert_ob_in_map (op, m, op,0);
797     }
798    
799     /* This routine doesnt seem to work for "inanimate" objects that
800     * are being carried, ie a held torch leaps from your hands!.
801     * Modified this routine to allow held objects. b.t. */
802    
803     void change_object(object *op) { /* Doesn`t handle linked objs yet */
804     object *tmp,*env,*pl;
805     int i,j;
806    
807     if(op->other_arch==NULL) {
808     LOG(llevError,"Change object (%s) without other_arch error.\n", op->name);
809     return;
810     }
811    
812     /* In non-living items only change when food value is 0 */
813     if(!QUERY_FLAG(op,FLAG_ALIVE)) {
814     if(op->stats.food-- > 0) return;
815     else op->stats.food=1; /* so 1 other_arch is made */
816     }
817     env=op->env;
818     remove_ob(op);
819     for(i=0;i<NROFNEWOBJS(op);i++) {
820     tmp=arch_to_object(op->other_arch);
821     if (op->type == LAMP)
822     tmp->stats.food = op->stats.food-1;
823     tmp->stats.hp=op->stats.hp; /* The only variable it keeps. */
824     if(env) {
825     tmp->x=env->x,tmp->y=env->y;
826     tmp=insert_ob_in_ob(tmp,env);
827     /* If this object is the players inventory, we need to tell the
828     * client of the change. Insert_ob_in_map takes care of the
829     * updating the client, so we don't need to do that below.
830     */
831     if ((pl=is_player_inv(env))!=NULL) {
832     esrv_del_item(pl->contr, op->count);
833     esrv_send_item(pl, tmp);
834     }
835     } else {
836     j=find_first_free_spot(tmp,op->map,op->x,op->y);
837     if (j==-1) /* No free spot */
838     free_object(tmp);
839     else {
840     tmp->x=op->x+freearr_x[j],tmp->y=op->y+freearr_y[j];
841     insert_ob_in_map(tmp,op->map,op,0);
842     }
843     }
844     }
845     free_object(op);
846     }
847    
848     void move_teleporter(object *op) {
849     object *tmp, *head=op;
850    
851     /* if this is a multipart teleporter, handle the other parts
852     * The check for speed isn't strictly needed - basically, if
853     * there is an old multipart teleporter in which the other parts
854     * have speed, we don't really want to call it twice for the same
855     * function - in fact, as written below, part N would get called
856     * N times without the speed check.
857     */
858     if (op->more && FABS(op->more->speed)<MIN_ACTIVE_SPEED) move_teleporter(op->more);
859    
860     if (op->head) head=op->head;
861    
862     for (tmp=op->above; tmp!=NULL; tmp=tmp->above)
863     if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) break;
864    
865     /* If nothing above us to move, nothing to do */
866     if (!tmp || QUERY_FLAG(tmp, FLAG_WIZPASS)) return;
867    
868     if(EXIT_PATH(head)) {
869     if(tmp->type==PLAYER) {
870 root 1.3 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
871 elmex 1.1 return;
872 root 1.3
873 elmex 1.1 enter_exit(tmp, head);
874     }
875     else
876     /* Currently only players can transfer maps */
877     return;
878     }
879     else if(EXIT_X(head)||EXIT_Y(head)) {
880     if (out_of_map(head->map, EXIT_X(head), EXIT_Y(head))) {
881     LOG(llevError, "Removed illegal teleporter.\n");
882     remove_ob(head);
883     free_object(head);
884     return;
885     }
886 root 1.3 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
887     return;
888 elmex 1.1 transfer_ob(tmp,EXIT_X(head),EXIT_Y(head),0,head);
889     }
890     else {
891     /* Random teleporter */
892 root 1.3 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
893 elmex 1.1 return;
894     teleport(head, TELEPORTER, tmp);
895     }
896     }
897    
898    
899     /* This object will teleport someone to a different map
900     and will also apply changes to the player from its inventory.
901     This was invented for giving classes, but there's no reason it
902     can't be generalized.
903     */
904    
905     void move_player_changer(object *op) {
906     object *player;
907     object *walk;
908     char c;
909    
910     if (!op->above || !EXIT_PATH(op)) return;
911    
912     /* This isn't all that great - means that the player_mover
913     * needs to be on top.
914     */
915     if(op->above->type==PLAYER) {
916 root 1.3 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (player)))
917 elmex 1.1 return;
918     player=op->above;
919     for(walk=op->inv;walk!=NULL;walk=walk->below)
920     apply_changes_to_player(player,walk);
921    
922     fix_player(player);
923     esrv_send_inventory(op->above,op->above);
924     esrv_update_item(UPD_FACE, op->above, op->above);
925    
926     /* update players death & WoR home-position */
927     sscanf(EXIT_PATH(op), "%c", &c);
928     if (c == '/') {
929     strcpy(player->contr->savebed_map, EXIT_PATH(op));
930     player->contr->bed_x = EXIT_X(op);
931     player->contr->bed_y = EXIT_Y(op);
932     }
933     else
934     LOG(llevDebug,
935     "WARNING: destination '%s' in player_changer must be an absolute path!\n",
936     EXIT_PATH(op));
937    
938     enter_exit(op->above,op);
939     save_player(player, 1);
940     }
941     }
942    
943     /* firewalls fire other spells.
944     * The direction of the wall is stored in op->stats.sp.
945     * walls can have hp, so they can be torn down.
946     */
947     void move_firewall(object *op) {
948     object *spell;
949    
950     if ( ! op->map)
951     return; /* dm has created a firewall in his inventory */
952    
953     spell = op->inv;
954     if (!spell || spell->type != SPELL) spell=&op->other_arch->clone;
955     if (!spell) {
956     LOG(llevError,"move_firewall: no spell specified (%s, %s, %d, %d)\n",
957     op->name, op->map->name, op->x, op->y);
958     return;
959     }
960    
961     cast_spell(op,op,op->stats.sp?op->stats.sp:rndm(1, 8),spell, NULL);
962     }
963    
964    
965     /* move_player_mover: this function takes a "player mover" as an
966     * argument, and performs the function of a player mover, which is:
967     *
968     * a player mover finds any players that are sitting on it. It
969     * moves them in the op->stats.sp direction. speed is how often it'll move.
970     * If attacktype is nonzero it will paralyze the player. If lifesave is set,
971     * it'll dissapear after hp+1 moves. If hp is set and attacktype is set,
972     * it'll paralyze the victim for hp*his speed/op->speed
973     */
974     void move_player_mover(object *op) {
975     object *victim, *nextmover;
976     int dir = op->stats.sp;
977     sint16 nx, ny;
978     mapstruct *m;
979    
980     /* Determine direction now for random movers so we do the right thing */
981     if (!dir) dir=rndm(1, 8);
982    
983     for(victim=get_map_ob(op->map,op->x,op->y); victim !=NULL; victim=victim->above) {
984     if(QUERY_FLAG(victim, FLAG_ALIVE) && !QUERY_FLAG(victim, FLAG_WIZPASS) &&
985     (victim->move_type & op->move_type || !victim->move_type)) {
986    
987     if (victim->head) victim = victim->head;
988    
989     if(QUERY_FLAG(op,FLAG_LIFESAVE)&&op->stats.hp--<0) {
990     remove_ob(op);
991     free_object(op);
992     return;
993     }
994     nx = op->x+freearr_x[dir];
995     ny = op->y+freearr_y[dir];
996     m = op->map;
997     if (get_map_flags(m, &m, nx, ny, &nx, &ny) & P_OUT_OF_MAP) {
998     LOG(llevError,"move_player_mover: Trying to push player off the map! map=%s (%d, %d)\n",
999     m->path, op->x, op->y);
1000     return ;
1001     }
1002    
1003     if (should_director_abort(op, victim)) return ;
1004    
1005     for(nextmover=get_map_ob(m,nx, ny); nextmover !=NULL; nextmover=nextmover->above) {
1006     if(nextmover->type == PLAYERMOVER)
1007     nextmover->speed_left=-.99;
1008     if(QUERY_FLAG(nextmover,FLAG_ALIVE)) {
1009     op->speed_left=-1.1; /* wait until the next thing gets out of the way */
1010     }
1011     }
1012    
1013     if(victim->type==PLAYER) {
1014     /* only level >=1 movers move people */
1015     if(op->level) {
1016     /* Following is a bit of hack. We need to make sure it
1017     * is cleared, otherwise the player will get stuck in
1018     * place. This can happen if the player used a spell to
1019     * get to this space.
1020     */
1021     victim->contr->fire_on=0;
1022     victim->speed_left=-FABS(victim->speed);
1023     move_player(victim, dir);
1024     }
1025     else return;
1026     }
1027     else move_object(victim,dir);
1028    
1029     if(!op->stats.maxsp&&op->attacktype) op->stats.maxsp=2;
1030    
1031     if(op->attacktype) { /* flag to paralyze the player */
1032    
1033     victim->speed_left= -FABS(op->stats.maxsp*victim->speed/op->speed);
1034     /* Not sure why, but for some chars on metalforge, they
1035     * would sometimes get -inf speed_left, and from the
1036     * description, it could only happen here, so just put
1037     * a lower sanity limit. My only guess is that the
1038     * mover has 0 speed.
1039     */
1040     if (victim->speed_left < -5.0) victim->speed_left=-5.0;
1041     }
1042     }
1043     }
1044     }
1045    
1046     /*
1047     * Will duplicate a specified object placed on top of it.
1048     * connected: what will trigger it.
1049     * level: multiplier. 0 to destroy.
1050     * other_arch: the object to look for and duplicate.
1051     */
1052    
1053     void move_duplicator(object *op) {
1054     object *tmp;
1055    
1056     if ( !op->other_arch ) {
1057     LOG(llevInfo, "Duplicator with no other_arch! %d %d %s\n", op->x, op->y, op->map ? op->map->path : "nullmap");
1058     return;
1059     }
1060    
1061     if (op->above == NULL)
1062     return;
1063     for (tmp=op->above; tmp != NULL; tmp=tmp->above) {
1064     if (strcmp(op->other_arch->name, tmp->arch->name) == 0) {
1065     if (op->level <= 0) {
1066     remove_ob(tmp);
1067     free_object(tmp);
1068     } else {
1069     uint64 new_nrof = (uint64)tmp->nrof*op->level;
1070     if (new_nrof >= 1UL<<31)
1071     new_nrof = 1UL<<31;
1072     tmp->nrof = new_nrof;
1073     }
1074     break;
1075     }
1076     }
1077     }
1078    
1079     /* move_creator (by peterm)
1080     * it has the creator object create it's other_arch right on top of it.
1081     * connected: what will trigger it
1082     * hp: how many times it may create before stopping
1083     * lifesave: if set, it'll never disappear but will go on creating
1084     * everytime it's triggered
1085     * other_arch: the object to create
1086     * Note this can create large objects, however, in that case, it
1087     * has to make sure that there is in fact space for the object.
1088     * It should really do this for small objects also, but there is
1089     * more concern with large objects, most notably a part being placed
1090     * outside of the map which would cause the server to crash
1091     */
1092    
1093     void move_creator(object *creator) {
1094     object *new_ob;
1095    
1096     if(!QUERY_FLAG(creator, FLAG_LIFESAVE) && --creator->stats.hp < 0) {
1097     creator->stats.hp=-1;
1098     return;
1099     }
1100    
1101     if (creator->inv != NULL) {
1102     object *ob;
1103     int i;
1104     object *ob_to_copy;
1105    
1106     /* select random object from inventory to copy */
1107     ob_to_copy = creator->inv;
1108     for (ob = creator->inv->below, i = 1; ob != NULL; ob = ob->below, i++) {
1109     if (rndm(0, i) == 0) {
1110     ob_to_copy = ob;
1111     }
1112     }
1113     new_ob = object_create_clone(ob_to_copy);
1114     CLEAR_FLAG(new_ob, FLAG_IS_A_TEMPLATE);
1115     unflag_inv(new_ob, FLAG_IS_A_TEMPLATE);
1116     } else {
1117     if (creator->other_arch == NULL) {
1118     LOG(llevError,"move_creator: Creator doesn't have other arch set: %s (%s, %d, %d)\n", creator->name ? creator->name : "(null)", creator->map->path, creator->x, creator->y);
1119     return;
1120     }
1121    
1122     new_ob = object_create_arch(creator->other_arch);
1123     fix_generated_item(new_ob, creator, 0, 0, GT_MINIMAL);
1124     }
1125    
1126     /* Make sure this multipart object fits */
1127     if (new_ob->arch->more && ob_blocked(new_ob, creator->map, creator->x, creator->y)) {
1128     free_object(new_ob);
1129     return;
1130     }
1131    
1132     insert_ob_in_map_at(new_ob, creator->map, creator, 0, creator->x, creator->y);
1133     if (QUERY_FLAG(new_ob, FLAG_FREED))
1134     return;
1135    
1136     if (creator->slaying) {
1137     FREE_AND_COPY(new_ob->name, creator->slaying);
1138     FREE_AND_COPY(new_ob->title, creator->slaying);
1139     }
1140     }
1141    
1142     /* move_marker --peterm@soda.csua.berkeley.edu
1143     when moved, a marker will search for a player sitting above
1144     it, and insert an invisible, weightless force into him
1145     with a specific code as the slaying field.
1146     At that time, it writes the contents of its own message
1147     field to the player. The marker will decrement hp to
1148     0 and then delete itself every time it grants a mark.
1149     unless hp was zero to start with, in which case it is infinite.*/
1150    
1151     void move_marker(object *op) {
1152     object *tmp,*tmp2;
1153    
1154     for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) {
1155     if(tmp->type == PLAYER) { /* we've got someone to MARK */
1156    
1157     if ( quest_on_activate(op, tmp->contr) )
1158     return;
1159    
1160     /* remove an old force with a slaying field == op->name */
1161     for(tmp2=tmp->inv;tmp2 !=NULL; tmp2=tmp2->below) {
1162     if(tmp2->type == FORCE && tmp2->slaying && !strcmp(tmp2->slaying,op->name)) break;
1163     }
1164    
1165     if(tmp2) {
1166     remove_ob(tmp2);
1167     free_object(tmp2);
1168     }
1169    
1170     /* cycle through his inventory to look for the MARK we want to
1171     * place
1172     */
1173     for(tmp2=tmp->inv;tmp2 !=NULL; tmp2=tmp2->below) {
1174     if(tmp2->type == FORCE && tmp2->slaying && !strcmp(tmp2->slaying,op->slaying)) break;
1175     }
1176    
1177     /* if we didn't find our own MARK */
1178     if(tmp2==NULL) {
1179     object *force = get_archetype(FORCE_NAME);
1180    
1181     force->speed = 0;
1182     if(op->stats.food) {
1183     force->speed = 0.01;
1184     force->speed_left = -op->stats.food;
1185     }
1186     update_ob_speed (force);
1187     /* put in the lock code */
1188     force->slaying = add_string(op->slaying);
1189    
1190     if ( op->lore )
1191     force->lore = add_string( op->lore );
1192    
1193     insert_ob_in_ob(force,tmp);
1194     if(op->msg)
1195     new_draw_info(NDI_UNIQUE|NDI_NAVY,0,tmp,op->msg);
1196    
1197     if(op->stats.hp > 0) {
1198     op->stats.hp--;
1199     if(op->stats.hp==0) {
1200     /* marker expires--granted mark number limit */
1201     remove_ob(op);
1202     free_object(op);
1203     return;
1204     }
1205     }
1206     } /* if tmp2 == NULL */
1207     } /* if tmp->type == PLAYER */
1208     } /* For all objects on this space */
1209     }
1210    
1211     int process_object(object *op) {
1212     if (QUERY_FLAG(op, FLAG_IS_A_TEMPLATE))
1213     return 0;
1214    
1215 root 1.2 if (INVOKE_OBJECT (TICK, op))
1216     return 0;
1217    
1218 elmex 1.1 if(QUERY_FLAG(op, FLAG_MONSTER))
1219     if(move_monster(op) || QUERY_FLAG(op, FLAG_FREED))
1220     return 1;
1221    
1222     if(QUERY_FLAG(op, FLAG_ANIMATE) && op->anim_speed==0) {
1223     if (op->type == PLAYER)
1224     animate_object(op, op->facing);
1225     else
1226     animate_object(op, op->direction);
1227    
1228     if (QUERY_FLAG(op, FLAG_SEE_ANYWHERE))
1229     make_sure_seen(op);
1230     }
1231     if(QUERY_FLAG(op, FLAG_CHANGING)&&!op->state) {
1232     change_object(op);
1233     return 1;
1234     }
1235     if(QUERY_FLAG(op, FLAG_GENERATOR)&&!QUERY_FLAG(op, FLAG_FRIENDLY))
1236     generate_monster(op);
1237    
1238     if(QUERY_FLAG(op, FLAG_IS_USED_UP)&&--op->stats.food<=0) {
1239     if(QUERY_FLAG(op, FLAG_APPLIED))
1240     remove_force(op);
1241     else {
1242     /* IF necessary, delete the item from the players inventory */
1243     object *pl=is_player_inv(op);
1244     if (pl)
1245     esrv_del_item(pl->contr, op->count);
1246     remove_ob(op);
1247     if (QUERY_FLAG(op, FLAG_SEE_ANYWHERE))
1248     make_sure_not_seen(op);
1249     free_object(op);
1250     }
1251     return 1;
1252     }
1253     switch(op->type) {
1254    
1255     case SPELL_EFFECT:
1256     move_spell_effect(op);
1257     return 1;
1258    
1259     case ROD:
1260     case HORN:
1261     regenerate_rod(op);
1262     return 1;
1263    
1264     case FORCE:
1265     case POTION_EFFECT:
1266     remove_force(op);
1267     return 1;
1268    
1269     case BLINDNESS:
1270     remove_blindness(op);
1271     return 0;
1272    
1273     case POISONING:
1274     poison_more(op);
1275     return 0;
1276    
1277     case DISEASE:
1278     move_disease(op);
1279     return 0;
1280    
1281     case SYMPTOM:
1282     move_symptom(op);
1283     return 0;
1284    
1285     case THROWN_OBJ:
1286     case ARROW:
1287     move_arrow(op);
1288     return 0;
1289    
1290     case LIGHTNING: /* It now moves twice as fast */
1291     move_bolt(op);
1292     return 0;
1293    
1294     case DOOR:
1295     remove_door(op);
1296     return 0;
1297    
1298     case LOCKED_DOOR:
1299     remove_door2(op);
1300     return 0;
1301    
1302     case TELEPORTER:
1303     move_teleporter(op);
1304     return 0;
1305    
1306     case GOLEM:
1307     move_golem(op);
1308     return 0;
1309    
1310     case EARTHWALL:
1311     hit_player(op, 2, op, AT_PHYSICAL, 1);
1312     return 0;
1313    
1314     case FIREWALL:
1315     move_firewall(op);
1316     if (op->stats.maxsp)
1317     animate_turning(op);
1318     return 0;
1319    
1320     case MOOD_FLOOR:
1321     do_mood_floor(op, op);
1322     return 0;
1323    
1324     case GATE:
1325     move_gate(op);
1326     return 0;
1327    
1328     case TIMED_GATE:
1329     move_timed_gate(op);
1330     return 0;
1331    
1332     case TRIGGER:
1333     case TRIGGER_BUTTON:
1334     case TRIGGER_PEDESTAL:
1335     case TRIGGER_ALTAR:
1336     animate_trigger(op);
1337     return 0;
1338    
1339     case DETECTOR:
1340     move_detector(op);
1341    
1342     case DIRECTOR:
1343     if (op->stats.maxsp)
1344     animate_turning(op);
1345     return 0;
1346    
1347     case HOLE:
1348     move_hole(op);
1349     return 0;
1350    
1351     case DEEP_SWAMP:
1352     move_deep_swamp(op);
1353     return 0;
1354    
1355     case RUNE:
1356     case TRAP:
1357     move_rune(op);
1358     return 0;
1359    
1360     case PLAYERMOVER:
1361     move_player_mover(op);
1362     return 0;
1363    
1364     case CREATOR:
1365     move_creator(op);
1366     return 0;
1367    
1368     case MARKER:
1369     move_marker(op);
1370     return 0;
1371    
1372     case PLAYER_CHANGER:
1373     move_player_changer(op);
1374     return 0;
1375    
1376     case PEACEMAKER:
1377     move_peacemaker(op);
1378     return 0;
1379     }
1380    
1381     return 0;
1382     }