ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/button.c
Revision: 1.2
Committed: Sun Jul 9 11:11:13 2006 UTC (17 years, 10 months ago) by elmex
Content type: text/plain
Branch: MAIN
CVS Tags: LAST_C_VERSION, difficulty_fix_merge_060810_2300
Branch point for: difficulty_fix
Changes since 1.1: +26 -16 lines
Log Message:
Fix from crossfire upstream (by mwedel):
Fix for sourceforge bug common/button.c - harpsicord crashes
client.  The problem is that the trigger code presumed that the object
would be animated, and blindly set teh face to the second animation frame.
harpsicords are not animated, so this could amount to garbage.  So add
check for trigger objects to see if they are in fact animated before
blindly setting the face.

File Contents

# User Rev Content
1 root 1.1 /*
2     * static char *rcsid_button_c =
3 elmex 1.2 * "$Id$";
4 root 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     #include <global.h>
30     #include <funcpoint.h>
31    
32     /*
33     * This code is no longer highly inefficient 8)
34     */
35    
36     /*
37     * Push the specified object. This can affect other buttons/gates/handles
38     * altars/pedestals/holes in the whole map.
39     * Changed the routine to loop through _all_ objects.
40     * Better hurry with that linked list...
41     */
42    
43     void push_button(object *op) {
44     object *tmp;
45     objectlink *ol;
46    
47     /* LOG(llevDebug, "push_button: %s (%d)\n", op->name, op->count); */
48     for (ol = get_button_links(op); ol; ol = ol->next) {
49     if (!ol->ob || ol->ob->count != ol->id) {
50     LOG(llevError, "Internal error in push_button (%s).\n", op->name);
51     continue;
52     }
53     /* a button link object can become freed when the map is saving. As
54     * a map is saved, objects are removed and freed, and if an object is
55     * on top of a button, this function is eventually called. If a map
56     * is getting moved out of memory, the status of buttons and levers
57     * probably isn't important - it will get sorted out when the map is
58     * re-loaded. As such, just exit this function if that is the case.
59     */
60    
61     if (QUERY_FLAG(ol->ob, FLAG_FREED)) return;
62     tmp = ol->ob;
63    
64     /* if the criteria isn't appropriate, don't do anything */
65     if (op->value && !QUERY_FLAG(tmp, FLAG_ACTIVATE_ON_PUSH)) continue;
66     if (!op->value && !QUERY_FLAG(tmp, FLAG_ACTIVATE_ON_RELEASE)) continue;
67    
68     switch(tmp->type) {
69    
70     case GATE:
71     case HOLE:
72     tmp->value=tmp->stats.maxsp?!op->value:op->value;
73     tmp->speed=0.5;
74     update_ob_speed(tmp);
75     break;
76    
77     case CF_HANDLE:
78     SET_ANIMATION(tmp, (tmp->value=tmp->stats.maxsp?!op->value:op->value));
79     update_object(tmp,UP_OBJ_FACE);
80     break;
81    
82     case SIGN:
83     if (!tmp->stats.food || tmp->last_eat < tmp->stats.food) {
84     new_info_map(NDI_UNIQUE | NDI_NAVY,tmp->map,tmp->msg);
85     if (tmp->stats.food) tmp->last_eat++;
86     }
87     break;
88    
89     case ALTAR:
90     tmp->value = 1;
91     SET_ANIMATION(tmp, tmp->value);
92     update_object(tmp,UP_OBJ_FACE);
93     break;
94    
95     case BUTTON:
96     case PEDESTAL:
97     tmp->value=op->value;
98     SET_ANIMATION(tmp, tmp->value);
99     update_object(tmp,UP_OBJ_FACE);
100     break;
101    
102     case MOOD_FLOOR:
103     do_mood_floor(tmp, op);
104     break;
105    
106     case TIMED_GATE:
107     tmp->speed = tmp->arch->clone.speed;
108     update_ob_speed(tmp); /* original values */
109     tmp->value = tmp->arch->clone.value;
110     tmp->stats.sp = 1;
111     tmp->stats.hp = tmp->stats.maxhp;
112     /* Handle multipart gates. We copy the value for the other parts
113     * from the head - this ensures that the data will consistent
114     */
115     for (tmp=tmp->more; tmp!=NULL; tmp=tmp->more) {
116     tmp->speed = tmp->head->speed;
117     tmp->value = tmp->head->value;
118     tmp->stats.sp = tmp->head->stats.sp;
119     tmp->stats.hp = tmp->head->stats.hp;
120     update_ob_speed(tmp);
121     }
122     break;
123    
124     case DIRECTOR:
125     case FIREWALL:
126     if(!QUERY_FLAG(tmp,FLAG_ANIMATE)&&tmp->type==FIREWALL) move_firewall(tmp);
127     else {
128     if ((tmp->stats.sp += tmp->stats.maxsp) > 8) /* next direction */
129     tmp->stats.sp = ((tmp->stats.sp-1)%8)+1;
130     animate_turning(tmp);
131     }
132     break;
133    
134     case TELEPORTER:
135     move_teleporter(tmp);
136     break;
137    
138     case CREATOR:
139     move_creator(tmp);
140     break;
141    
142     case TRIGGER_MARKER:
143     move_marker(tmp);
144     break;
145    
146     case DUPLICATOR:
147     move_duplicator(tmp);
148     break;
149     }
150     }
151     }
152    
153     /*
154     * Updates everything connected with the button op.
155     * After changing the state of a button, this function must be called
156     * to make sure that all gates and other buttons connected to the
157     * button reacts to the (eventual) change of state.
158     */
159    
160     void update_button(object *op) {
161     object *ab,*tmp,*head;
162     int tot,any_down=0, old_value=op->value;
163     objectlink *ol;
164    
165     /* LOG(llevDebug, "update_button: %s (%d)\n", op->name, op->count); */
166     for (ol = get_button_links(op); ol; ol = ol->next) {
167     if (!ol->ob || ol->ob->count != ol->id) {
168     LOG(llevDebug, "Internal error in update_button (%s).\n", op->name);
169     continue;
170     }
171     tmp = ol->ob;
172     if (tmp->type==BUTTON) {
173     for(ab=tmp->above,tot=0;ab!=NULL;ab=ab->above)
174     /* Bug? The pedestal code below looks for the head of
175     * the object, this bit doesn't. I'd think we should check
176     * for head here also. Maybe it also makese sense to
177     * make the for ab=tmp->above loop common, and alter
178     * behaviour based on object within that loop?
179     */
180    
181     /* Basically, if the move_type matches that on what the
182     * button wants, we count it. The second check is so that
183     * objects don't move (swords, etc) will count. Note that
184     * this means that more work is needed to make buttons
185     * that are only triggered by flying objects.
186     */
187     if ((ab->move_type & tmp->move_on) || ab->move_type==0 )
188     tot+=ab->weight*(ab->nrof?ab->nrof:1)+ab->carrying;
189    
190     tmp->value=(tot>=tmp->weight)?1:0;
191     if(tmp->value)
192     any_down=1;
193     } else if (tmp->type == PEDESTAL) {
194     tmp->value = 0;
195     for(ab=tmp->above; ab!=NULL; ab=ab->above) {
196     head = ab->head ? ab->head : ab;
197     /* Same note regarding move_type for buttons above apply here. */
198     if ( ((head->move_type & tmp->move_on) || ab->move_type==0) &&
199     (head->race==tmp->slaying ||
200     ((head->type==SPECIAL_KEY) && (head->slaying==tmp->slaying)) ||
201     (!strcmp (tmp->slaying, "player") &&
202     head->type == PLAYER)))
203     tmp->value = 1;
204     }
205     if(tmp->value)
206     any_down=1;
207     }
208     }
209     if(any_down) /* If any other buttons were down, force this to remain down */
210     op->value=1;
211    
212     /* If this button hasn't changed, don't do anything */
213     if (op->value != old_value) {
214     SET_ANIMATION(op, op->value);
215     update_object(op, UP_OBJ_FACE);
216     push_button(op); /* Make all other buttons the same */
217     }
218     }
219    
220     /*
221     * Updates every button on the map (by calling update_button() for them).
222     */
223    
224     void update_buttons(mapstruct *m) {
225     objectlink *ol;
226     oblinkpt *obp;
227     for (obp = m->buttons; obp; obp = obp->next)
228     for (ol = obp->link; ol; ol = ol->next) {
229     if (!ol->ob || ol->ob->count != ol->id) {
230     LOG(llevError, "Internal error in update_button (%s (%dx%d):%d, connected %ld).\n",
231     ol->ob?ol->ob->name:"null",
232     ol->ob?ol->ob->x:-1,
233     ol->ob?ol->ob->y:-1,
234     ol->id,
235     obp->value);
236     continue;
237     }
238     if (ol->ob->type==BUTTON || ol->ob->type==PEDESTAL)
239     {
240     update_button(ol->ob);
241     break;
242     }
243     }
244     }
245    
246     void use_trigger(object *op)
247     {
248    
249     /* Toggle value */
250     op->value = !op->value;
251     push_button(op);
252     }
253    
254     /*
255     * Note: animate_object should be used instead of this,
256     * but it can't handle animations in the 8 directions
257     */
258    
259     void animate_turning(object *op) /* only one part objects */
260     {
261     if (++op->state >= NUM_ANIMATIONS(op)/8)
262     op->state=0;
263     SET_ANIMATION(op, (op->stats.sp-1) * NUM_ANIMATIONS(op) / 8 +
264     op->state);
265     update_object(op,UP_OBJ_FACE);
266     }
267    
268     #define ARCH_SACRIFICE(xyz) ((xyz)->slaying)
269     #define NROF_SACRIFICE(xyz) ((uint32)(xyz)->stats.food)
270    
271     /* Returns true if the sacrifice meets the needs of the altar.
272     *
273     * Function put in (0.92.1) so that identify altars won't grab money
274     * unnecessarily - we can see if there is sufficient money, see if something
275     * needs to be identified, and then remove money if needed.
276     *
277     * 0.93.4: Linked objects (ie, objects that are connected) can not be
278     * sacrificed. This fixes a bug of trying to put multiple altars/related
279     * objects on the same space that take the same sacrifice.
280     */
281    
282 elmex 1.2 int check_altar_sacrifice (const object *altar, const object *sacrifice)
283 root 1.1 {
284     if ( ! QUERY_FLAG (sacrifice, FLAG_ALIVE)
285     && ! QUERY_FLAG (sacrifice, FLAG_IS_LINKED)
286     && sacrifice->type != PLAYER)
287     {
288     if ((ARCH_SACRIFICE(altar) == sacrifice->arch->name ||
289     ARCH_SACRIFICE(altar) == sacrifice->name ||
290     ARCH_SACRIFICE(altar) == sacrifice->slaying ||
291     (!strcmp(ARCH_SACRIFICE(altar),query_base_name(sacrifice,0))))
292     && NROF_SACRIFICE(altar) <= (sacrifice->nrof?sacrifice->nrof:1))
293     return 1;
294     if (strcmp (ARCH_SACRIFICE(altar), "money") == 0
295     && sacrifice->type == MONEY
296     && sacrifice->nrof * sacrifice->value >= NROF_SACRIFICE(altar))
297     return 1;
298     }
299     return 0;
300     }
301    
302    
303     /*
304     * operate_altar checks if sacrifice was accepted and removes sacrificed
305     * objects. If sacrifice was succeed return 1 else 0. Might be better to
306     * call check_altar_sacrifice (above) than depend on the return value,
307     * since operate_altar will remove the sacrifice also.
308     *
309     * If this function returns 1, '*sacrifice' is modified to point to the
310     * remaining sacrifice, or is set to NULL if the sacrifice was used up.
311     */
312    
313     int operate_altar (object *altar, object **sacrifice)
314     {
315    
316     if ( ! altar->map) {
317     LOG (llevError, "BUG: operate_altar(): altar has no map\n");
318     return 0;
319     }
320    
321     if (!altar->slaying || altar->value)
322     return 0;
323    
324     if ( ! check_altar_sacrifice (altar, *sacrifice))
325     return 0;
326    
327     /* check_altar_sacrifice should have already verified that enough money
328     * has been dropped.
329     */
330     if (!strcmp(ARCH_SACRIFICE(altar), "money")) {
331     int number=NROF_SACRIFICE(altar) / (*sacrifice)->value;
332    
333     /* Round up any sacrifices. Altars don't make change either */
334     if (NROF_SACRIFICE(altar) % (*sacrifice)->value) number++;
335     *sacrifice = decrease_ob_nr (*sacrifice, number);
336     }
337     else
338     *sacrifice = decrease_ob_nr (*sacrifice, NROF_SACRIFICE(altar));
339    
340     if (altar->msg)
341     new_info_map(NDI_BLACK, altar->map, altar->msg);
342     return 1;
343     }
344    
345     void trigger_move (object *op, int state) /* 1 down and 0 up */
346     {
347     op->stats.wc = state;
348     if (state) {
349     use_trigger(op);
350     if (op->stats.exp > 0) /* check sanity */
351     op->speed = 1.0 / op->stats.exp;
352     else
353     op->speed = 1.0;
354     update_ob_speed(op);
355     op->speed_left = -1;
356     } else {
357     use_trigger(op);
358     op->speed = 0;
359     update_ob_speed(op);
360     }
361     }
362    
363    
364     /*
365     * cause != NULL: something has moved on top of op
366     *
367     * cause == NULL: nothing has moved, we have been called from
368     * animate_trigger().
369     *
370     * TRIGGER_ALTAR: Returns 1 if 'cause' was destroyed, 0 if not.
371     *
372     * TRIGGER: Returns 1 if handle could be moved, 0 if not.
373     *
374     * TRIGGER_BUTTON, TRIGGER_PEDESTAL: Returns 0.
375     */
376     int check_trigger (object *op, object *cause)
377     {
378     object *tmp;
379     int push = 0, tot = 0;
380     int in_movement = op->stats.wc || op->speed;
381    
382     switch (op->type) {
383     case TRIGGER_BUTTON:
384     if (op->weight > 0) {
385     if (cause) {
386     for (tmp = op->above; tmp; tmp = tmp->above)
387     /* Comment reproduced from update_buttons(): */
388     /* Basically, if the move_type matches that on what the
389     * button wants, we count it. The second check is so that
390     * objects that don't move (swords, etc) will count. Note that
391     * this means that more work is needed to make buttons
392     * that are only triggered by flying objects.
393     */
394    
395     if ((tmp->move_type & op->move_on) || tmp->move_type==0) {
396     tot += tmp->weight * (tmp->nrof ? tmp->nrof : 1)
397     + tmp->carrying;
398     }
399     if (tot >= op->weight)
400     push = 1;
401     if (op->stats.ac == push)
402     return 0;
403     op->stats.ac = push;
404 elmex 1.2 if (NUM_ANIMATIONS(op) > 1) {
405     SET_ANIMATION (op, push);
406     update_object (op, UP_OBJ_FACE);
407     }
408 root 1.1 if (in_movement || ! push)
409     return 0;
410     }
411     trigger_move (op, push);
412     }
413     return 0;
414    
415     case TRIGGER_PEDESTAL:
416     if (cause) {
417     for (tmp = op->above; tmp; tmp = tmp->above) {
418     object *head = tmp->head ? tmp->head : tmp;
419    
420     /* See comment in TRIGGER_BUTTON about move_types */
421     if (((head->move_type & op->move_on) || head->move_type==0)
422     && (head->race==op->slaying ||
423     (!strcmp (op->slaying, "player") && head->type == PLAYER))) {
424     push = 1;
425     break;
426     }
427     }
428     if (op->stats.ac == push)
429     return 0;
430     op->stats.ac = push;
431 elmex 1.2 if (NUM_ANIMATIONS(op) > 1) {
432     SET_ANIMATION (op, push);
433     update_object (op, UP_OBJ_FACE);
434     }
435 root 1.1 update_object(op,UP_OBJ_FACE);
436     if (in_movement || ! push)
437     return 0;
438     }
439     trigger_move (op, push);
440     return 0;
441    
442     case TRIGGER_ALTAR:
443     if (cause) {
444     if (in_movement)
445     return 0;
446     if (operate_altar (op, &cause)) {
447 elmex 1.2 if (NUM_ANIMATIONS(op) > 1) {
448     SET_ANIMATION (op, 1);
449     update_object(op, UP_OBJ_FACE);
450     }
451 root 1.1 if (op->last_sp >= 0) {
452     trigger_move (op, 1);
453     if (op->last_sp > 0)
454     op->last_sp = -op->last_sp;
455     }
456     else {
457     /* for trigger altar with last_sp, the ON/OFF
458     * status (-> +/- value) is "simulated":
459     */
460     op->value = !op->value;
461     trigger_move (op, 1);
462     op->last_sp = -op->last_sp;
463     op->value = !op->value;
464     }
465     return cause == NULL;
466     } else {
467     return 0;
468     }
469     } else {
470 elmex 1.2 if (NUM_ANIMATIONS(op) > 1) {
471     SET_ANIMATION (op, 0);
472     update_object(op, UP_OBJ_FACE);
473     }
474 root 1.1
475     /* If trigger_altar has "last_sp > 0" set on the map,
476     * it will push the connected value only once per sacrifice.
477     * Otherwise (default), the connected value will be
478     * pushed twice: First by sacrifice, second by reset! -AV
479     */
480     if (!op->last_sp)
481     trigger_move (op, 0);
482     else {
483     op->stats.wc = 0;
484     op->value = !op->value;
485     op->speed = 0;
486     update_ob_speed(op);
487     }
488     }
489     return 0;
490    
491     case TRIGGER:
492     if (cause) {
493     if (in_movement)
494     return 0;
495     push = 1;
496     }
497 elmex 1.2 if (NUM_ANIMATIONS(op) > 1) {
498     SET_ANIMATION (op, push);
499     update_object(op, UP_OBJ_FACE);
500     }
501 root 1.1 trigger_move (op, push);
502     return 1;
503    
504     default:
505     LOG(llevDebug, "Unknown trigger type: %s (%d)\n", op->name, op->type);
506     return 0;
507     }
508     }
509    
510     void add_button_link(object *button, mapstruct *map, int connected) {
511     oblinkpt *obp;
512     objectlink *ol = get_objectlink();
513    
514     if (!map) {
515     LOG(llevError, "Tried to add button-link without map.\n");
516     return;
517     }
518     if (!editor) button->path_attuned = connected; /* peterm: I need this so I can rebuild
519     a connected map from a template map. */
520     /* LOG(llevDebug,"adding button %s (%d)\n", button->name, connected);*/
521    
522     SET_FLAG(button,FLAG_IS_LINKED);
523    
524     ol->ob = button;
525     ol->id = button->count;
526    
527     for (obp = map->buttons; obp && obp->value != connected; obp = obp->next);
528    
529     if (obp) {
530     ol->next = obp->link;
531     obp->link = ol;
532     } else {
533     obp = get_objectlinkpt();
534     obp->value = connected;
535    
536     obp->next = map->buttons;
537     map->buttons = obp;
538     obp->link = ol;
539     }
540     }
541    
542     /*
543     * Remove the object from the linked lists of buttons in the map.
544     * This is only needed by editors.
545     */
546    
547     void remove_button_link(object *op) {
548     oblinkpt *obp;
549     objectlink **olp, *ol;
550    
551     if (op->map == NULL) {
552     LOG(llevError, "remove_button_link() in object without map.\n");
553     return;
554     }
555     if (!QUERY_FLAG(op,FLAG_IS_LINKED)) {
556     LOG(llevError, "remove_button_linked() in unlinked object.\n");
557     return;
558     }
559     for (obp = op->map->buttons; obp; obp = obp->next)
560     for (olp = &obp->link; (ol = *olp); olp = &ol->next)
561     if (ol->ob == op) {
562     /* LOG(llevDebug, "Removed link %d in button %s and map %s.\n",
563     obp->value, op->name, op->map->path);
564     */
565     *olp = ol->next;
566     free(ol);
567     return;
568     }
569     LOG(llevError, "remove_button_linked(): couldn't find object.\n");
570     CLEAR_FLAG(op,FLAG_IS_LINKED);
571     }
572    
573     /*
574     * Return the first objectlink in the objects linked to this one
575     */
576    
577 elmex 1.2 objectlink *get_button_links(const object *button) {
578 root 1.1 oblinkpt *obp;
579     objectlink *ol;
580    
581     if (!button->map)
582     return NULL;
583     for (obp = button->map->buttons; obp; obp = obp->next)
584     for (ol = obp->link; ol; ol = ol->next)
585     if (ol->ob == button && ol->id == button->count)
586     return obp->link;
587     return NULL;
588     }
589    
590     /*
591     * Made as a separate function to increase efficiency
592     */
593    
594 elmex 1.2 int get_button_value(const object *button) {
595 root 1.1 oblinkpt *obp;
596     objectlink *ol;
597    
598     if (!button->map)
599     return 0;
600     for (obp = button->map->buttons; obp; obp = obp->next)
601     for (ol = obp->link; ol; ol = ol->next)
602     if (ol->ob == button && ol->id == button->count)
603     return obp->value;
604     return 0;
605     }
606    
607     /* This routine makes monsters who are
608     * standing on the 'mood floor' change their
609     * disposition if it is different.
610     * If floor is to be triggered must have
611     * a speed of zero (default is 1 for all
612     * but the charm floor type).
613     * by b.t. thomas@nomad.astro.psu.edu
614     */
615    
616     void do_mood_floor(object *op, object *op2) {
617     object *tmp;
618     object *tmp2;
619    
620     for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp; tmp=tmp->above)
621     if (QUERY_FLAG(tmp, FLAG_MONSTER)) break;
622    
623     /* doesn't effect players, and if there is a player on this space, won't also
624     * be a monster here.
625     */
626     if (!tmp || tmp->type == PLAYER) return;
627    
628     switch(op->last_sp) {
629     case 0: /* furious--make all monsters mad */
630     if(QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
631     CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
632     if(QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
633     CLEAR_FLAG(tmp, FLAG_FRIENDLY);
634     remove_friendly_object(tmp);
635     tmp->attack_movement = 0;
636     /* lots of checks here, but want to make sure we don't
637     * dereference a null value
638     */
639     if (tmp->type == GOLEM && tmp->owner && tmp->owner->type==PLAYER &&
640     tmp->owner->contr->ranges[range_golem]==tmp) {
641     tmp->owner->contr->ranges[range_golem]=NULL;
642     tmp->owner->contr->golem_count = 0;
643     }
644     tmp->owner = 0;
645     }
646     break;
647     case 1: /* angry -- get neutral monsters mad */
648     if(QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)&&
649     !QUERY_FLAG(tmp, FLAG_FRIENDLY))
650     CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
651     break;
652     case 2: /* calm -- pacify unfriendly monsters */
653     if(!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
654     SET_FLAG(tmp, FLAG_UNAGGRESSIVE);
655     break;
656     case 3: /* make all monsters fall asleep */
657     if(!QUERY_FLAG(tmp, FLAG_SLEEP))
658     SET_FLAG(tmp, FLAG_SLEEP);
659     break;
660     case 4: /* charm all monsters */
661    
662     if(op == op2) break; /* only if 'connected' */
663    
664     for(tmp2=get_map_ob(op2->map,op2->x,op2->y); /* finding an owner */
665     tmp2->type!=PLAYER;tmp2=tmp2->above)
666     if(tmp2->above==NULL) break;
667    
668     if (tmp2->type != PLAYER)
669     break;
670     set_owner(tmp,tmp2);
671     SET_FLAG(tmp,FLAG_MONSTER);
672     tmp->stats.exp = 0;
673     SET_FLAG(tmp, FLAG_FRIENDLY);
674     add_friendly_object (tmp);
675     tmp->attack_movement = PETMOVE;
676     break;
677    
678     default:
679     break;
680    
681     }
682     }
683    
684     /* this function returns the object it matches, or NULL if non.
685     * It will descend through containers to find the object.
686     * slaying = match object slaying flag
687     * race = match object archetype name flag
688     * hp = match object type (excpt type '0'== PLAYER)
689     */
690    
691 elmex 1.2 object * check_inv_recursive(object *op, const object *trig)
692 root 1.1 {
693     object *tmp,*ret=NULL;
694    
695     /* First check the object itself. */
696     if((trig->stats.hp && (op->type == trig->stats.hp))
697     || (trig->slaying && (op->slaying == trig->slaying))
698     || (trig->race && (op->arch->name == trig->race)))
699     return op;
700    
701     for(tmp=op->inv; tmp; tmp=tmp->below) {
702     if (tmp->inv) {
703     ret=check_inv_recursive(tmp, trig);
704     if (ret) return ret;
705     }
706     else if((trig->stats.hp && (tmp->type == trig->stats.hp))
707     || (trig->slaying && (tmp->slaying == trig->slaying))
708     || (trig->race && (tmp->arch->name == trig->race)))
709     return tmp;
710     }
711     return NULL;
712     }
713    
714     /* check_inv(), a function to search the inventory,
715     * of a player and then based on a set of conditions,
716     * the square will activate connected items.
717     * Monsters can't trigger this square (for now)
718     * Values are: last_sp = 1/0 obj/no obj triggers
719     * last_heal = 1/0 remove/dont remove obj if triggered
720     * -b.t. (thomas@nomad.astro.psu.edu
721     */
722    
723     void check_inv (object *op, object *trig) {
724     object *match;
725    
726     if(op->type != PLAYER) return;
727     match = check_inv_recursive(op,trig);
728     if (match && trig->last_sp) {
729     if(trig->last_heal)
730     decrease_ob(match);
731     use_trigger(trig);
732     }
733     else if (!match && !trig->last_sp)
734     use_trigger(trig);
735     }
736    
737    
738     /* This does a minimal check of the button link consistency for object
739     * map. All it really does it much sure the object id link that is set
740     * matches what the object has.
741     */
742 elmex 1.2 void verify_button_links(const mapstruct *map) {
743 root 1.1 oblinkpt *obp;
744     objectlink *ol;
745    
746     if (!map) return;
747    
748     for (obp = map->buttons; obp; obp = obp->next) {
749     for (ol=obp->link; ol; ol=ol->next) {
750     if (ol->id!=ol->ob->count)
751     LOG(llevError,"verify_button_links: object %s on list is corrupt (%d!=%d)\n",ol->ob->name, ol->id, ol->ob->count);
752     }
753     }
754     }