ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/button.C
Revision: 1.36
Committed: Sun Jul 1 05:00:17 2007 UTC (16 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.35: +11 -12 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

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