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

# Content
1 /*
2 * static char *rcsid_button_c =
3 * "$Id$";
4 */
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 int check_altar_sacrifice (const object *altar, const object *sacrifice)
283 {
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 if (NUM_ANIMATIONS(op) > 1) {
405 SET_ANIMATION (op, push);
406 update_object (op, UP_OBJ_FACE);
407 }
408 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 if (NUM_ANIMATIONS(op) > 1) {
432 SET_ANIMATION (op, push);
433 update_object (op, UP_OBJ_FACE);
434 }
435 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 if (NUM_ANIMATIONS(op) > 1) {
448 SET_ANIMATION (op, 1);
449 update_object(op, UP_OBJ_FACE);
450 }
451 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 if (NUM_ANIMATIONS(op) > 1) {
471 SET_ANIMATION (op, 0);
472 update_object(op, UP_OBJ_FACE);
473 }
474
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 if (NUM_ANIMATIONS(op) > 1) {
498 SET_ANIMATION (op, push);
499 update_object(op, UP_OBJ_FACE);
500 }
501 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 objectlink *get_button_links(const object *button) {
578 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 int get_button_value(const object *button) {
595 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 object * check_inv_recursive(object *op, const object *trig)
692 {
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 void verify_button_links(const mapstruct *map) {
743 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 }