ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/button.C
Revision: 1.21
Committed: Sat Dec 30 10:16:10 2006 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.20: +0 -30 lines
Log Message:
preliminary snapshot check-in, DO NOT USE IN PRODUCTION SYSTEMS
See the Changes file for details

File Contents

# Content
1 /*
2 CrossFire, A Multiplayer game for X-windows
3
4 Copyright (C) 2002 Mark Wedel & Crossfire Development Team
5 Copyright (C) 1992 Frank Tore Johansen
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 The authors can be reached via e-mail at <crossfire@schmorp.de>
22 */
23
24 #include <global.h>
25 #include <funcpoint.h>
26
27 /*
28 * This code is no longer highly inefficient 8)
29 */
30
31 /*
32 * elmex:
33 * This function takes a objectlink list with all the objects are going to be activated.
34 * state is a true/false flag that will actiavte objects that have FLAG_ACTIVATE_ON_PUSH/RELEASE set.
35 * The source argument can be 0 or the source object for this activation.
36 */
37 void
38 activate_connection_link (objectlink * ol, bool state, object *source = 0)
39 {
40 for (; ol; ol = ol->next)
41 {
42 if (!ol->ob)
43 {
44 LOG (llevError, "Internal error in activate_connection_link.\n");
45 continue;
46 }
47
48 /* a button link object can become freed when the map is saving. As
49 * a map is saved, objects are removed and freed, and if an object is
50 * on top of a button, this function is eventually called. If a map
51 * is getting moved out of memory, the status of buttons and levers
52 * probably isn't important - it will get sorted out when the map is
53 * re-loaded. As such, just exit this function if that is the case.
54 */
55
56 if (QUERY_FLAG (ol->ob, FLAG_FREED))
57 return;
58
59 object *tmp = ol->ob;
60
61 /* if the criteria isn't appropriate, don't do anything */
62 if (state && !QUERY_FLAG (tmp, FLAG_ACTIVATE_ON_PUSH))
63 continue;
64 if (!state && !QUERY_FLAG (tmp, FLAG_ACTIVATE_ON_RELEASE))
65 continue;
66
67 switch (tmp->type)
68 {
69 case GATE:
70 case HOLE:
71 tmp->value = tmp->stats.maxsp ? !state : state;
72 tmp->set_speed (0.5);
73 break;
74
75 case CF_HANDLE:
76 SET_ANIMATION (tmp, (tmp->value = tmp->stats.maxsp ? !state : state));
77 update_object (tmp, UP_OBJ_FACE);
78 break;
79
80 case SIGN:
81 if (!tmp->stats.food || tmp->last_eat < tmp->stats.food)
82 {
83 new_info_map (NDI_UNIQUE | NDI_NAVY, tmp->map, tmp->msg);
84 if (tmp->stats.food)
85 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 = state;
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, source);
104 break;
105
106 case TIMED_GATE:
107 tmp->set_speed (tmp->arch->clone.speed);
108 tmp->value = tmp->arch->clone.value;
109 tmp->stats.sp = 1;
110 tmp->stats.hp = tmp->stats.maxhp;
111 /* Handle multipart gates. We copy the value for the other parts
112 * from the head - this ensures that the data will consistent
113 */
114 for (tmp = tmp->more; tmp; tmp = tmp->more)
115 {
116 tmp->value = tmp->head->value;
117 tmp->stats.sp = tmp->head->stats.sp;
118 tmp->stats.hp = tmp->head->stats.hp;
119 tmp->set_speed (tmp->head->speed);
120 }
121 break;
122
123 case DIRECTOR:
124 case FIREWALL:
125 if (!QUERY_FLAG (tmp, FLAG_ANIMATE) && tmp->type == FIREWALL)
126 move_firewall (tmp);
127 else
128 {
129 if ((tmp->stats.sp += tmp->stats.maxsp) > 8) /* next direction */
130 tmp->stats.sp = ((tmp->stats.sp - 1) % 8) + 1;
131 animate_turning (tmp);
132 }
133 break;
134
135 case TELEPORTER:
136 move_teleporter (tmp);
137 break;
138
139 case CREATOR:
140 move_creator (tmp);
141 break;
142
143 case TRIGGER_MARKER:
144 move_marker (tmp);
145 break;
146
147 case DUPLICATOR:
148 move_duplicator (tmp);
149 break;
150 }
151 }
152 }
153
154 /*
155 * elmex:
156 * This is the new push_button function, it got split up so that
157 * you can activate connections without a button now!
158 * old but still valid comment:
159 *
160 * Push the specified object. This can affect other buttons/gates/handles
161 * altars/pedestals/holes in the whole map.
162 * Changed the routine to loop through _all_ objects.
163 * Better hurry with that linked list...
164 *
165 */
166 void
167 push_button (object *op)
168 {
169 oblinkpt *obp = get_button_links (op);
170
171 if (!obp)
172 return;
173
174 if (INVOKE_MAP (TRIGGER, op->map, ARG_INT64 (obp->value), ARG_INT (op->value)))
175 return;
176
177 activate_connection_link (obp->link, op->value, op);
178 }
179
180 /*
181 * elmex:
182 * This activates a connection, similar to push_button (object *op) but it takes
183 * only a map, a connection value and a true or false flag that indicated whether
184 * the connection was 'state' or 'released'. So that you can activate objects
185 * who have FLAG_ACTIVATE_ON_PUSH/RELEASE set properly.
186 *
187 */
188 void
189 activate_connection (maptile *map, long connection, bool state)
190 {
191 if (INVOKE_MAP (TRIGGER, map, ARG_INT64 (connection), ARG_INT (state)))
192 return;
193
194 oblinkpt *obp = get_connection_links (map, connection);
195
196 if (obp)
197 activate_connection_link (obp->link, state);
198 }
199
200 /*
201 * Updates everything connected with the button op.
202 * After changing the state of a button, this function must be called
203 * to make sure that all gates and other buttons connected to the
204 * button reacts to the (eventual) change of state.
205 */
206 void
207 update_button (object *op)
208 {
209 object *ab, *tmp, *head;
210 int tot, any_down = 0, old_value = op->value;
211 oblinkpt *obp = 0;
212 objectlink *ol;
213
214 obp = get_button_links (op);
215 /* LOG(llevDebug, "update_button: %s (%d)\n", op->name, op->count); */
216 if (obp)
217 for (ol = obp->link; ol; ol = ol->next)
218 {
219 if (!ol->ob)
220 {
221 LOG (llevDebug, "Internal error in update_button (%s).\n", &op->name);
222 continue;
223 }
224
225 tmp = ol->ob;
226 if (tmp->type == BUTTON)
227 {
228 for (ab = tmp->above, tot = 0; ab != NULL; ab = ab->above)
229 /* Bug? The pedestal code below looks for the head of
230 * the object, this bit doesn't. I'd think we should check
231 * for head here also. Maybe it also makese sense to
232 * make the for ab=tmp->above loop common, and alter
233 * behaviour based on object within that loop?
234 */
235
236 /* Basically, if the move_type matches that on what the
237 * button wants, we count it. The second check is so that
238 * objects don't move (swords, etc) will count. Note that
239 * this means that more work is needed to make buttons
240 * that are only triggered by flying objects.
241 */
242 if ((ab->move_type & tmp->move_on) || ab->move_type == 0)
243 tot += ab->weight * (ab->nrof ? ab->nrof : 1) + ab->carrying;
244
245 tmp->value = (tot >= tmp->weight) ? 1 : 0;
246 if (tmp->value)
247 any_down = 1;
248 }
249 else if (tmp->type == PEDESTAL)
250 {
251 tmp->value = 0;
252 for (ab = tmp->above; ab != NULL; ab = ab->above)
253 {
254 head = ab->head ? ab->head : ab;
255 /* Same note regarding move_type for buttons above apply here. */
256 if (((head->move_type & tmp->move_on) || ab->move_type == 0) &&
257 (head->race == tmp->slaying ||
258 ((head->type == SPECIAL_KEY) && (head->slaying == tmp->slaying)) ||
259 (!strcmp (tmp->slaying, "player") && head->type == PLAYER)))
260 tmp->value = 1;
261 }
262 if (tmp->value)
263 any_down = 1;
264 }
265 }
266 if (any_down) /* If any other buttons were down, force this to remain down */
267 op->value = 1;
268
269 /* If this button hasn't changed, don't do anything */
270 if (op->value != old_value)
271 {
272 SET_ANIMATION (op, op->value);
273 update_object (op, UP_OBJ_FACE);
274 push_button (op); /* Make all other buttons the same */
275 }
276 }
277
278 void
279 use_trigger (object *op)
280 {
281 /* Toggle value */
282 op->value = !op->value;
283 push_button (op);
284 }
285
286 /*
287 * Note: animate_object should be used instead of this,
288 * but it can't handle animations in the 8 directions
289 */
290 void
291 animate_turning (object *op) /* only one part objects */
292 {
293 if (++op->state >= NUM_ANIMATIONS (op) / 8)
294 op->state = 0;
295 SET_ANIMATION (op, (op->stats.sp - 1) * NUM_ANIMATIONS (op) / 8 + op->state);
296 update_object (op, UP_OBJ_FACE);
297 }
298
299 #define ARCH_SACRIFICE(xyz) ((xyz)->slaying)
300 #define NROF_SACRIFICE(xyz) ((uint32)(xyz)->stats.food)
301
302 /* Returns true if the sacrifice meets the needs of the altar.
303 *
304 * Function put in (0.92.1) so that identify altars won't grab money
305 * unnecessarily - we can see if there is sufficient money, see if something
306 * needs to be identified, and then remove money if needed.
307 *
308 * 0.93.4: Linked objects (ie, objects that are connected) can not be
309 * sacrificed. This fixes a bug of trying to put multiple altars/related
310 * objects on the same space that take the same sacrifice.
311 */
312
313 int
314 check_altar_sacrifice (const object *altar, const object *sacrifice)
315 {
316 if (!QUERY_FLAG (sacrifice, FLAG_ALIVE) && !QUERY_FLAG (sacrifice, FLAG_IS_LINKED) && sacrifice->type != PLAYER)
317 {
318 if ((ARCH_SACRIFICE (altar) == sacrifice->arch->name ||
319 ARCH_SACRIFICE (altar) == sacrifice->name ||
320 ARCH_SACRIFICE (altar) == sacrifice->slaying ||
321 (!strcmp (ARCH_SACRIFICE (altar), query_base_name (sacrifice, 0))))
322 && NROF_SACRIFICE (altar) <= (sacrifice->nrof ? sacrifice->nrof : 1))
323 return 1;
324
325 if (strcmp (ARCH_SACRIFICE (altar), "money") == 0
326 && sacrifice->type == MONEY && sacrifice->nrof * sacrifice->value >= NROF_SACRIFICE (altar))
327 return 1;
328 }
329
330 return 0;
331 }
332
333 /*
334 * operate_altar checks if sacrifice was accepted and removes sacrificed
335 * objects. If sacrifice was succeed return 1 else 0. Might be better to
336 * call check_altar_sacrifice (above) than depend on the return value,
337 * since operate_altar will remove the sacrifice also.
338 *
339 * If this function returns 1, '*sacrifice' is modified to point to the
340 * remaining sacrifice, or is set to NULL if the sacrifice was used up.
341 */
342 int
343 operate_altar (object *altar, object **sacrifice)
344 {
345 if (!altar->map)
346 {
347 LOG (llevError, "BUG: operate_altar(): altar has no map\n");
348 return 0;
349 }
350
351 if (!altar->slaying || altar->value)
352 return 0;
353
354 if (!check_altar_sacrifice (altar, *sacrifice))
355 return 0;
356
357 /* check_altar_sacrifice should have already verified that enough money
358 * has been dropped.
359 */
360 if (!strcmp (ARCH_SACRIFICE (altar), "money"))
361 {
362 int number = NROF_SACRIFICE (altar) / (*sacrifice)->value;
363
364 /* Round up any sacrifices. Altars don't make change either */
365 if (NROF_SACRIFICE (altar) % (*sacrifice)->value)
366 number++;
367
368 *sacrifice = decrease_ob_nr (*sacrifice, number);
369 }
370 else
371 *sacrifice = decrease_ob_nr (*sacrifice, NROF_SACRIFICE (altar));
372
373 if (altar->msg)
374 new_info_map (NDI_BLACK, altar->map, altar->msg);
375
376 return 1;
377 }
378
379 void
380 trigger_move (object *op, int state) /* 1 down and 0 up */
381 {
382 op->stats.wc = state;
383 if (state)
384 {
385 use_trigger (op);
386 op->set_speed (op->stats.exp > 0 ? 1. / op->stats.exp : 1.);
387 op->speed_left = -1;
388 }
389 else
390 {
391 use_trigger (op);
392 op->set_speed (0);
393 }
394 }
395
396
397 /*
398 * cause != NULL: something has moved on top of op
399 *
400 * cause == NULL: nothing has moved, we have been called from
401 * animate_trigger().
402 *
403 * TRIGGER_ALTAR: Returns 1 if 'cause' was destroyed, 0 if not.
404 *
405 * TRIGGER: Returns 1 if handle could be moved, 0 if not.
406 *
407 * TRIGGER_BUTTON, TRIGGER_PEDESTAL: Returns 0.
408 */
409 int
410 check_trigger (object *op, object *cause)
411 {
412 object *tmp;
413 int push = 0, tot = 0;
414 int in_movement = op->stats.wc || op->speed;
415
416 switch (op->type)
417 {
418 case TRIGGER_BUTTON:
419 if (op->weight > 0)
420 {
421 if (cause)
422 {
423 for (tmp = op->above; tmp; tmp = tmp->above)
424 /* Comment reproduced from update_buttons(): */
425 /* Basically, if the move_type matches that on what the
426 * button wants, we count it. The second check is so that
427 * objects that don't move (swords, etc) will count. Note that
428 * this means that more work is needed to make buttons
429 * that are only triggered by flying objects.
430 */
431
432 if ((tmp->move_type & op->move_on) || tmp->move_type == 0)
433 {
434 tot += tmp->weight * (tmp->nrof ? tmp->nrof : 1) + tmp->carrying;
435 }
436 if (tot >= op->weight)
437 push = 1;
438 if (op->stats.ac == push)
439 return 0;
440 op->stats.ac = push;
441 if (NUM_ANIMATIONS (op) > 1)
442 {
443 SET_ANIMATION (op, push);
444 update_object (op, UP_OBJ_FACE);
445 }
446 if (in_movement || !push)
447 return 0;
448 }
449 trigger_move (op, push);
450 }
451 return 0;
452
453 case TRIGGER_PEDESTAL:
454 if (cause)
455 {
456 for (tmp = op->above; tmp; tmp = tmp->above)
457 {
458 object *head = tmp->head ? tmp->head : tmp;
459
460 /* See comment in TRIGGER_BUTTON about move_types */
461 if (((head->move_type & op->move_on) || head->move_type == 0)
462 && (head->race == op->slaying || (!strcmp (op->slaying, "player") && head->type == PLAYER)))
463 {
464 push = 1;
465 break;
466 }
467 }
468 if (op->stats.ac == push)
469 return 0;
470 op->stats.ac = push;
471 if (NUM_ANIMATIONS (op) > 1)
472 {
473 SET_ANIMATION (op, push);
474 update_object (op, UP_OBJ_FACE);
475 }
476 update_object (op, UP_OBJ_FACE);
477 if (in_movement || !push)
478 return 0;
479 }
480 trigger_move (op, push);
481 return 0;
482
483 case TRIGGER_ALTAR:
484 if (cause)
485 {
486 if (in_movement)
487 return 0;
488 if (operate_altar (op, &cause))
489 {
490 if (NUM_ANIMATIONS (op) > 1)
491 {
492 SET_ANIMATION (op, 1);
493 update_object (op, UP_OBJ_FACE);
494 }
495 if (op->last_sp >= 0)
496 {
497 trigger_move (op, 1);
498 if (op->last_sp > 0)
499 op->last_sp = -op->last_sp;
500 }
501 else
502 {
503 /* for trigger altar with last_sp, the ON/OFF
504 * status (-> +/- value) is "simulated":
505 */
506 op->value = !op->value;
507 trigger_move (op, 1);
508 op->last_sp = -op->last_sp;
509 op->value = !op->value;
510 }
511 return cause == NULL;
512 }
513 else
514 {
515 return 0;
516 }
517 }
518 else
519 {
520 if (NUM_ANIMATIONS (op) > 1)
521 {
522 SET_ANIMATION (op, 0);
523 update_object (op, UP_OBJ_FACE);
524 }
525
526 /* If trigger_altar has "last_sp > 0" set on the map,
527 * it will push the connected value only once per sacrifice.
528 * Otherwise (default), the connected value will be
529 * pushed twice: First by sacrifice, second by reset! -AV
530 */
531 if (!op->last_sp)
532 trigger_move (op, 0);
533 else
534 {
535 op->stats.wc = 0;
536 op->value = !op->value;
537 op->set_speed (0);
538 }
539 }
540 return 0;
541
542 case TRIGGER:
543 if (cause)
544 {
545 if (in_movement)
546 return 0;
547
548 push = 1;
549 }
550
551 if (NUM_ANIMATIONS (op) > 1)
552 {
553 SET_ANIMATION (op, push);
554 update_object (op, UP_OBJ_FACE);
555 }
556
557 trigger_move (op, push);
558 return 1;
559
560 default:
561 LOG (llevDebug, "Unknown trigger type: %s (%d)\n", &op->name, op->type);
562 return 0;
563 }
564 }
565
566 void
567 add_button_link (object *button, maptile *map, int connected)
568 {
569 oblinkpt *obp;
570 objectlink *ol = get_objectlink ();
571
572 if (!map)
573 {
574 LOG (llevError, "Tried to add button-link without map.\n");
575 return;
576 }
577
578 button->path_attuned = connected; /* peterm: I need this so I can rebuild
579 a connected map from a template map. */
580
581 SET_FLAG (button, FLAG_IS_LINKED);
582
583 ol->ob = button;
584
585 for (obp = map->buttons; obp && obp->value != connected; obp = obp->next)
586 ;
587
588 if (obp)
589 {
590 ol->next = obp->link;
591 obp->link = ol;
592 }
593 else
594 {
595 obp = get_objectlinkpt ();
596 obp->value = connected;
597
598 obp->next = map->buttons;
599 map->buttons = obp;
600 obp->link = ol;
601 }
602 }
603
604 /*
605 * Remove the object from the linked lists of buttons in the map.
606 * This is only needed by editors.
607 */
608
609 void
610 remove_button_link (object *op)
611 {
612 oblinkpt *obp;
613 objectlink **olp, *ol;
614
615 if (op->map == NULL)
616 {
617 LOG (llevError, "remove_button_link() in object without map.\n");
618 return;
619 }
620
621 if (!QUERY_FLAG (op, FLAG_IS_LINKED))
622 {
623 LOG (llevError, "remove_button_linked() in unlinked object.\n");
624 return;
625 }
626
627 for (obp = op->map->buttons; obp; obp = obp->next)
628 for (olp = &obp->link; (ol = *olp); olp = &ol->next)
629 if (ol->ob == op)
630 {
631
632 /* LOG(llevDebug, "Removed link %d in button %s and map %s.\n",
633 obp->value, op->name, op->map->path);
634 */
635 *olp = ol->next;
636 delete ol;
637 return;
638 }
639
640 LOG (llevError, "remove_button_linked(): couldn't find object.\n");
641 CLEAR_FLAG (op, FLAG_IS_LINKED);
642 }
643
644 /*
645 * Gets the objectlink for this connection from the map.
646 */
647 oblinkpt *
648 get_connection_links (maptile *map, long connection)
649 {
650 for (oblinkpt * obp = map->buttons; obp; obp = obp->next)
651 if (obp->value == connection)
652 return obp;
653
654 return 0;
655 }
656
657 /*
658 * Return the first objectlink in the objects linked to this one
659 */
660
661 oblinkpt *
662 get_button_links (const object *button)
663 {
664 oblinkpt *obp;
665 objectlink *ol;
666
667 if (!button->map)
668 return NULL;
669
670 for (obp = button->map->buttons; obp; obp = obp->next)
671 for (ol = obp->link; ol; ol = ol->next)
672 if (ol->ob == button)
673 return obp;
674
675 return NULL;
676 }
677
678 /*
679 * Made as a separate function to increase efficiency
680 */
681
682 int
683 get_button_value (const object *button)
684 {
685 oblinkpt *obp;
686 objectlink *ol;
687
688 if (!button->map)
689 return 0;
690 for (obp = button->map->buttons; obp; obp = obp->next)
691 for (ol = obp->link; ol; ol = ol->next)
692 if (ol->ob == button)
693 return obp->value;
694 return 0;
695 }
696
697 /* This routine makes monsters who are
698 * standing on the 'mood floor' change their
699 * disposition if it is different.
700 * If floor is to be triggered must have
701 * a speed of zero (default is 1 for all
702 * but the charm floor type).
703 * by b.t. thomas@nomad.astro.psu.edu
704 */
705
706 void
707 do_mood_floor (object *op, object *source)
708 {
709 object *tmp;
710 object *tmp2;
711
712 if (!source)
713 source = op;
714
715 for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = tmp->above)
716 if (QUERY_FLAG (tmp, FLAG_MONSTER))
717 break;
718
719 /* doesn't effect players, and if there is a player on this space, won't also
720 * be a monster here.
721 */
722 if (!tmp || tmp->type == PLAYER)
723 return;
724
725 switch (op->last_sp)
726 {
727 case 0: /* furious--make all monsters mad */
728 if (QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE))
729 CLEAR_FLAG (tmp, FLAG_UNAGGRESSIVE);
730 if (QUERY_FLAG (tmp, FLAG_FRIENDLY))
731 {
732 CLEAR_FLAG (tmp, FLAG_FRIENDLY);
733 remove_friendly_object (tmp);
734 tmp->attack_movement = 0;
735 /* lots of checks here, but want to make sure we don't
736 * dereference a null value
737 */
738 if (tmp->type == GOLEM && tmp->owner && tmp->owner->type == PLAYER && tmp->owner->contr->ranges[range_golem] == tmp)
739 tmp->owner->contr->ranges[range_golem] = 0;
740
741 tmp->owner = 0;
742 }
743 break;
744 case 1: /* angry -- get neutral monsters mad */
745 if (QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (tmp, FLAG_FRIENDLY))
746 CLEAR_FLAG (tmp, FLAG_UNAGGRESSIVE);
747 break;
748 case 2: /* calm -- pacify unfriendly monsters */
749 if (!QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE))
750 SET_FLAG (tmp, FLAG_UNAGGRESSIVE);
751 break;
752 case 3: /* make all monsters fall asleep */
753 if (!QUERY_FLAG (tmp, FLAG_SLEEP))
754 SET_FLAG (tmp, FLAG_SLEEP);
755 break;
756 case 4: /* charm all monsters */
757 if (op == source)
758 break; /* only if 'connected' */
759
760 for (tmp2 = GET_MAP_OB (source->map, source->x, source->y); /* finding an owner */
761 tmp2->type != PLAYER; tmp2 = tmp2->above)
762 if (tmp2->above == NULL)
763 break;
764
765 if (tmp2->type != PLAYER)
766 break;
767
768 tmp->set_owner (tmp2);
769 SET_FLAG (tmp, FLAG_MONSTER);
770
771 tmp->stats.exp = 0;
772 SET_FLAG (tmp, FLAG_FRIENDLY);
773
774 add_friendly_object (tmp);
775 tmp->attack_movement = PETMOVE;
776 break;
777
778 default:
779 break;
780 }
781 }
782
783 /* this function returns the object it matches, or NULL if non.
784 * It will descend through containers to find the object.
785 * slaying = match object slaying flag
786 * race = match object archetype name flag
787 * hp = match object type (excpt type '0'== PLAYER)
788 */
789 object *
790 check_inv_recursive (object *op, const object *trig)
791 {
792 object *tmp, *ret = NULL;
793
794 /* First check the object itself. */
795 if ((trig->stats.hp && (op->type == trig->stats.hp))
796 || (trig->slaying && (op->slaying == trig->slaying))
797 || (trig->race && (op->arch->name == trig->race)))
798 return op;
799
800 for (tmp = op->inv; tmp; tmp = tmp->below)
801 {
802 if (tmp->inv)
803 {
804 ret = check_inv_recursive (tmp, trig);
805 if (ret)
806 return ret;
807 }
808 else if ((trig->stats.hp && (tmp->type == trig->stats.hp))
809 || (trig->slaying && (tmp->slaying == trig->slaying))
810 || (trig->race && (tmp->arch->name == trig->race)))
811 return tmp;
812 }
813 return NULL;
814 }
815
816 /* check_inv(), a function to search the inventory,
817 * of a player and then based on a set of conditions,
818 * the square will activate connected items.
819 * Monsters can't trigger this square (for now)
820 * Values are: last_sp = 1/0 obj/no obj triggers
821 * last_heal = 1/0 remove/dont remove obj if triggered
822 * -b.t. (thomas@nomad.astro.psu.edu
823 *
824 * Tue Dec 19 15:34:00 CET 2006 elmex: changed the function to ignore op
825 * because the check-inventory semantic essentially only applies when
826 * something is above the inventory checker.
827 * The semantic prior this change was: trigger if something has moved on or off
828 * and has a matching item. Imagine what happens if someone steps on the inventory
829 * checker with a matching item, has it, activates the connection, throws the item
830 * away, and then leaves the inventory checker. That would've caused an always-enabled
831 * state in the inventory checker. This won't happen anymore now.
832 *
833 */
834 void
835 check_inv (object *op, object *trig)
836 {
837 trig->value = 0; // deactivate if none of the following conditions apply
838
839 if (object *pl = trig->ms ().player ())
840 {
841 object *match = check_inv_recursive (pl, trig);
842
843 if (match && trig->last_sp) // match == having
844 {
845 if (trig->last_heal)
846 decrease_ob (match);
847
848 trig->value = 1;
849 }
850 else if (!match && !trig->last_sp) // match == not having
851 trig->value = 1;
852 }
853
854 push_button (trig);
855 }
856