ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.2
Committed: Sat Aug 26 23:36:34 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +4 -3 lines
Log Message:
intermediate check-in, per-object events work

File Contents

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