ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/button.C
Revision: 1.31
Committed: Mon Apr 30 04:25:29 2007 UTC (17 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.30: +2 -2 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 2002 Mark Wedel & Crossfire Development Team
6 * Copyright (C) 1992 Frank Tore Johansen
7 *
8 * This program 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 2 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, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * The authors can be reached via e-mail at <crossfire@schmorp.de>
23 */
24
25 #include <global.h>
26 #include <funcpoint.h>
27
28 /*
29 * This code is no longer highly inefficient 8)
30 */
31
32 /*
33 * elmex:
34 * This function takes a objectlink list with all the objects are going to be activated.
35 * state is a true/false flag that will actiavte objects that have FLAG_ACTIVATE_ON_PUSH/RELEASE set.
36 * The source argument can be 0 or the source object for this activation.
37 */
38 void
39 activate_connection_link (objectlink * ol, bool state, object *source = 0)
40 {
41 for (; ol; ol = ol->next)
42 {
43 if (!ol->ob)
44 {
45 LOG (llevError, "Internal error in activate_connection_link.\n");
46 continue;
47 }
48
49 /* a button link object can become freed when the map is saving. As
50 * a map is saved, objects are removed and freed, and if an object is
51 * on top of a button, this function is eventually called. If a map
52 * is getting moved out of memory, the status of buttons and levers
53 * probably isn't important - it will get sorted out when the map is
54 * re-loaded. As such, just exit this function if that is the case.
55 */
56
57 if (QUERY_FLAG (ol->ob, FLAG_FREED))
58 return;
59
60 object *tmp = ol->ob;
61
62 /* if the criteria isn't appropriate, don't do anything */
63 if (state && !QUERY_FLAG (tmp, FLAG_ACTIVATE_ON_PUSH))
64 continue;
65 if (!state && !QUERY_FLAG (tmp, FLAG_ACTIVATE_ON_RELEASE))
66 continue;
67
68 switch (tmp->type)
69 {
70 case GATE:
71 case HOLE:
72 tmp->value = tmp->stats.maxsp ? !state : state;
73 tmp->set_speed (0.5);
74 break;
75
76 case CF_HANDLE:
77 SET_ANIMATION (tmp, (tmp->value = tmp->stats.maxsp ? !state : state));
78 update_object (tmp, UP_OBJ_FACE);
79 break;
80
81 case SIGN:
82 if (!tmp->stats.food || tmp->last_eat < tmp->stats.food)
83 {
84 new_info_map (NDI_UNIQUE | NDI_NAVY, tmp->map, tmp->msg);
85 if (tmp->stats.food)
86 tmp->last_eat++;
87 }
88 break;
89
90 case ALTAR:
91 tmp->value = 1;
92 SET_ANIMATION (tmp, tmp->value);
93 update_object (tmp, UP_OBJ_FACE);
94 break;
95
96 case BUTTON:
97 case PEDESTAL:
98 tmp->value = state;
99 SET_ANIMATION (tmp, tmp->value);
100 update_object (tmp, UP_OBJ_FACE);
101 break;
102
103 case MOOD_FLOOR:
104 do_mood_floor (tmp, source);
105 break;
106
107 case TIMED_GATE:
108 tmp->set_speed (tmp->arch->clone.speed);
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; tmp = tmp->more)
116 {
117 tmp->value = tmp->head->value;
118 tmp->stats.sp = tmp->head->stats.sp;
119 tmp->stats.hp = tmp->head->stats.hp;
120 tmp->set_speed (tmp->head->speed);
121 }
122 break;
123
124 case DIRECTOR:
125 case FIREWALL:
126 if (!QUERY_FLAG (tmp, FLAG_ANIMATE) && tmp->type == FIREWALL)
127 move_firewall (tmp);
128 else
129 {
130 if ((tmp->stats.sp += tmp->stats.maxsp) > 8) /* next direction */
131 tmp->stats.sp = ((tmp->stats.sp - 1) % 8) + 1;
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 ? ab->head : ab;
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 if (tmp->value)
264 any_down = 1;
265 }
266 }
267 if (any_down) /* If any other buttons were down, force this to remain down */
268 op->value = 1;
269
270 /* If this button hasn't changed, don't do anything */
271 if (op->value != old_value)
272 {
273 SET_ANIMATION (op, op->value);
274 update_object (op, UP_OBJ_FACE);
275 push_button (op); /* Make all other buttons the same */
276 }
277 }
278
279 void
280 use_trigger (object *op)
281 {
282 /* Toggle value */
283 op->value = !op->value;
284 push_button (op);
285 }
286
287 /*
288 * Note: animate_object should be used instead of this,
289 * but it can't handle animations in the 8 directions
290 */
291 void
292 animate_turning (object *op) /* only one part objects */
293 {
294 if (++op->state >= NUM_ANIMATIONS (op) / 8)
295 op->state = 0;
296 SET_ANIMATION (op, (op->stats.sp - 1) * NUM_ANIMATIONS (op) / 8 + op->state);
297 update_object (op, UP_OBJ_FACE);
298 }
299
300 #define ARCH_SACRIFICE(xyz) ((xyz)->slaying)
301 #define NROF_SACRIFICE(xyz) ((uint32)(xyz)->stats.food)
302
303 /* Returns true if the sacrifice meets the needs of the altar.
304 *
305 * Function put in (0.92.1) so that identify altars won't grab money
306 * unnecessarily - we can see if there is sufficient money, see if something
307 * needs to be identified, and then remove money if needed.
308 *
309 * 0.93.4: Linked objects (ie, objects that are connected) can not be
310 * sacrificed. This fixes a bug of trying to put multiple altars/related
311 * objects on the same space that take the same sacrifice.
312 */
313
314 int
315 check_altar_sacrifice (const object *altar, const object *sacrifice)
316 {
317 if (!QUERY_FLAG (sacrifice, FLAG_ALIVE) && !QUERY_FLAG (sacrifice, FLAG_IS_LINKED) && sacrifice->type != PLAYER)
318 {
319 if ((ARCH_SACRIFICE (altar) == sacrifice->arch->name ||
320 ARCH_SACRIFICE (altar) == sacrifice->name ||
321 ARCH_SACRIFICE (altar) == sacrifice->slaying ||
322 (!strcmp (ARCH_SACRIFICE (altar), query_base_name (sacrifice, 0))))
323 && NROF_SACRIFICE (altar) <= (sacrifice->nrof ? sacrifice->nrof : 1))
324 return 1;
325
326 if (strcmp (ARCH_SACRIFICE (altar), "money") == 0
327 && sacrifice->type == MONEY && sacrifice->nrof * sacrifice->value >= NROF_SACRIFICE (altar))
328 return 1;
329 }
330
331 return 0;
332 }
333
334 /*
335 * operate_altar checks if sacrifice was accepted and removes sacrificed
336 * objects. If sacrifice was succeed return 1 else 0. Might be better to
337 * call check_altar_sacrifice (above) than depend on the return value,
338 * since operate_altar will remove the sacrifice also.
339 *
340 * If this function returns 1, '*sacrifice' is modified to point to the
341 * remaining sacrifice, or is set to NULL if the sacrifice was used up.
342 */
343 int
344 operate_altar (object *altar, object **sacrifice)
345 {
346 if (!altar->map)
347 {
348 LOG (llevError, "BUG: operate_altar(): altar has no map\n");
349 return 0;
350 }
351
352 if (!altar->slaying || altar->value)
353 return 0;
354
355 if (!check_altar_sacrifice (altar, *sacrifice))
356 return 0;
357
358 /* check_altar_sacrifice should have already verified that enough money
359 * has been dropped.
360 */
361 if (!strcmp (ARCH_SACRIFICE (altar), "money"))
362 {
363 int number = NROF_SACRIFICE (altar) / (*sacrifice)->value;
364
365 /* Round up any sacrifices. Altars don't make change either */
366 if (NROF_SACRIFICE (altar) % (*sacrifice)->value)
367 number++;
368
369 *sacrifice = decrease_ob_nr (*sacrifice, number);
370 }
371 else
372 *sacrifice = decrease_ob_nr (*sacrifice, NROF_SACRIFICE (altar));
373
374 if (altar->msg)
375 new_info_map (NDI_BLACK, altar->map, altar->msg);
376
377 return 1;
378 }
379
380 void
381 trigger_move (object *op, int state) /* 1 down and 0 up */
382 {
383 op->stats.wc = state;
384 if (state)
385 {
386 use_trigger (op);
387 op->set_speed (op->stats.exp > 0 ? 1. / op->stats.exp : 1.);
388 op->speed_left = -1;
389 }
390 else
391 {
392 use_trigger (op);
393 op->set_speed (0);
394 }
395 }
396
397
398 /*
399 * cause != NULL: something has moved on top of op
400 *
401 * cause == NULL: nothing has moved, we have been called from
402 * animate_trigger().
403 *
404 * TRIGGER_ALTAR: Returns 1 if 'cause' was destroyed, 0 if not.
405 *
406 * TRIGGER: Returns 1 if handle could be moved, 0 if not.
407 *
408 * TRIGGER_BUTTON, TRIGGER_PEDESTAL: Returns 0.
409 */
410 int
411 check_trigger (object *op, object *cause)
412 {
413 object *tmp;
414 int push = 0, tot = 0;
415 int in_movement = op->stats.wc || op->speed;
416
417 switch (op->type)
418 {
419 case TRIGGER_BUTTON:
420 if (op->weight > 0)
421 {
422 if (cause)
423 {
424 for (tmp = op->above; tmp; tmp = tmp->above)
425 /* Comment reproduced from update_buttons(): */
426 /* Basically, if the move_type matches that on what the
427 * button wants, we count it. The second check is so that
428 * objects that don't move (swords, etc) will count. Note that
429 * this means that more work is needed to make buttons
430 * that are only triggered by flying objects.
431 */
432
433 if ((tmp->move_type & op->move_on) || tmp->move_type == 0)
434 {
435 tot += tmp->weight * (tmp->nrof ? tmp->nrof : 1) + tmp->carrying;
436 }
437 if (tot >= op->weight)
438 push = 1;
439 if (op->stats.ac == push)
440 return 0;
441 op->stats.ac = push;
442 if (NUM_ANIMATIONS (op) > 1)
443 {
444 SET_ANIMATION (op, push);
445 update_object (op, UP_OBJ_FACE);
446 }
447 if (in_movement || !push)
448 return 0;
449 }
450 trigger_move (op, push);
451 }
452 return 0;
453
454 case TRIGGER_PEDESTAL:
455 if (cause)
456 {
457 for (tmp = op->above; tmp; tmp = tmp->above)
458 {
459 object *head = tmp->head ? tmp->head : tmp;
460
461 /* See comment in TRIGGER_BUTTON about move_types */
462 if (((head->move_type & op->move_on) || head->move_type == 0)
463 && (head->race == op->slaying || (!strcmp (op->slaying, "player") && head->type == PLAYER)))
464 {
465 push = 1;
466 break;
467 }
468 }
469 if (op->stats.ac == push)
470 return 0;
471 op->stats.ac = push;
472 if (NUM_ANIMATIONS (op) > 1)
473 {
474 SET_ANIMATION (op, push);
475 update_object (op, UP_OBJ_FACE);
476 }
477 update_object (op, UP_OBJ_FACE);
478 if (in_movement || !push)
479 return 0;
480 }
481 trigger_move (op, push);
482 return 0;
483
484 case TRIGGER_ALTAR:
485 if (cause)
486 {
487 if (in_movement)
488 return 0;
489 if (operate_altar (op, &cause))
490 {
491 if (NUM_ANIMATIONS (op) > 1)
492 {
493 SET_ANIMATION (op, 1);
494 update_object (op, UP_OBJ_FACE);
495 }
496 if (op->last_sp >= 0)
497 {
498 trigger_move (op, 1);
499 if (op->last_sp > 0)
500 op->last_sp = -op->last_sp;
501 }
502 else
503 {
504 /* for trigger altar with last_sp, the ON/OFF
505 * status (-> +/- value) is "simulated":
506 */
507 op->value = !op->value;
508 trigger_move (op, 1);
509 op->last_sp = -op->last_sp;
510 op->value = !op->value;
511 }
512 return cause == NULL;
513 }
514 else
515 {
516 return 0;
517 }
518 }
519 else
520 {
521 if (NUM_ANIMATIONS (op) > 1)
522 {
523 SET_ANIMATION (op, 0);
524 update_object (op, UP_OBJ_FACE);
525 }
526
527 /* If trigger_altar has "last_sp > 0" set on the map,
528 * it will push the connected value only once per sacrifice.
529 * Otherwise (default), the connected value will be
530 * pushed twice: First by sacrifice, second by reset! -AV
531 */
532 if (!op->last_sp)
533 trigger_move (op, 0);
534 else
535 {
536 op->stats.wc = 0;
537 op->value = !op->value;
538 op->set_speed (0);
539 }
540 }
541 return 0;
542
543 case TRIGGER:
544 if (cause)
545 {
546 if (in_movement)
547 return 0;
548
549 push = 1;
550 }
551
552 if (NUM_ANIMATIONS (op) > 1)
553 {
554 SET_ANIMATION (op, push);
555 update_object (op, UP_OBJ_FACE);
556 }
557
558 trigger_move (op, push);
559 return 1;
560
561 default:
562 LOG (llevDebug, "Unknown trigger type: %s (%d)\n", &op->name, op->type);
563 return 0;
564 }
565 }
566
567 void
568 add_button_link (object *button, maptile *map, int connected)
569 {
570 oblinkpt *obp;
571 objectlink *ol = get_objectlink ();
572
573 if (!map)
574 {
575 LOG (llevError, "Tried to add button-link without map.\n");
576 return;
577 }
578
579 button->path_attuned = connected; /* peterm: I need this so I can rebuild
580 a connected map from a template map. */
581
582 SET_FLAG (button, FLAG_IS_LINKED);
583
584 ol->ob = button;
585
586 for (obp = map->buttons; obp && obp->value != connected; obp = obp->next)
587 ;
588
589 if (obp)
590 {
591 ol->next = obp->link;
592 obp->link = ol;
593 }
594 else
595 {
596 obp = get_objectlinkpt ();
597 obp->value = connected;
598
599 obp->next = map->buttons;
600 map->buttons = obp;
601 obp->link = ol;
602 }
603 }
604
605 /*
606 * Remove the object from the linked lists of buttons in the map.
607 * This is only needed by editors.
608 */
609
610 void
611 remove_button_link (object *op)
612 {
613 oblinkpt *obp;
614 objectlink **olp, *ol;
615
616 if (op->map == NULL)
617 {
618 LOG (llevError, "remove_button_link() in object without map.\n");
619 return;
620 }
621
622 if (!QUERY_FLAG (op, FLAG_IS_LINKED))
623 {
624 LOG (llevError, "remove_button_linked() in unlinked object.\n");
625 return;
626 }
627
628 for (obp = op->map->buttons; obp; obp = obp->next)
629 for (olp = &obp->link; (ol = *olp); olp = &ol->next)
630 if (ol->ob == op)
631 {
632
633 /* LOG(llevDebug, "Removed link %d in button %s and map %s.\n",
634 obp->value, op->name, op->map->path);
635 */
636 *olp = ol->next;
637 delete ol;
638 return;
639 }
640
641 LOG (llevError, "remove_button_linked(): couldn't find object.\n");
642 CLEAR_FLAG (op, FLAG_IS_LINKED);
643 }
644
645 /*
646 * Gets the objectlink for this connection from the map.
647 */
648 oblinkpt *
649 get_connection_links (maptile *map, long connection)
650 {
651 for (oblinkpt * obp = map->buttons; obp; obp = obp->next)
652 if (obp->value == connection)
653 return obp;
654
655 return 0;
656 }
657
658 /*
659 * Return the first objectlink in the objects linked to this one
660 */
661
662 oblinkpt *
663 get_button_links (const object *button)
664 {
665 oblinkpt *obp;
666 objectlink *ol;
667
668 if (!button->map)
669 return NULL;
670
671 for (obp = button->map->buttons; obp; obp = obp->next)
672 for (ol = obp->link; ol; ol = ol->next)
673 if (ol->ob == button)
674 return obp;
675
676 return NULL;
677 }
678
679 /*
680 * Made as a separate function to increase efficiency
681 */
682
683 int
684 get_button_value (const object *button)
685 {
686 oblinkpt *obp;
687 objectlink *ol;
688
689 if (!button->map)
690 return 0;
691 for (obp = button->map->buttons; obp; obp = obp->next)
692 for (ol = obp->link; ol; ol = ol->next)
693 if (ol->ob == button)
694 return obp->value;
695 return 0;
696 }
697
698 /* This routine makes monsters who are
699 * standing on the 'mood floor' change their
700 * disposition if it is different.
701 * If floor is to be triggered must have
702 * a speed of zero (default is 1 for all
703 * but the charm floor type).
704 * by b.t. thomas@nomad.astro.psu.edu
705 */
706
707 void
708 do_mood_floor (object *op, object *source)
709 {
710 if (!source)
711 source = op;
712
713 mapspace &ms = op->ms ();
714
715 if (!(ms.flags () & P_IS_ALIVE))
716 return;
717
718 object *tmp;
719
720 for (tmp = ms.top; tmp; tmp = tmp->below)
721 if (QUERY_FLAG (tmp, FLAG_MONSTER))
722 break;
723
724 /* doesn't effect players, and if there is a player on this space, won't also
725 * be a monster here.
726 */
727 //TODO: have players really FLAG_MONSTER? kept it for safety
728 if (!tmp || tmp->type == PLAYER)
729 return;
730
731 switch (op->last_sp)
732 {
733 case 0: /* furious--make all monsters mad */
734 if (QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE))
735 CLEAR_FLAG (tmp, FLAG_UNAGGRESSIVE);
736
737 if (QUERY_FLAG (tmp, FLAG_FRIENDLY))
738 {
739 tmp->attack_movement = 0;
740 /* lots of checks here, but want to make sure we don't
741 * dereference a null value
742 */
743 if (tmp->type == GOLEM
744 && tmp->owner
745 && tmp->owner->type == PLAYER
746 && tmp->owner->contr->golem == tmp)
747 tmp->owner->contr->golem = 0;
748
749 tmp->owner = 0;
750
751 remove_friendly_object (tmp);
752 }
753 break;
754
755 case 1: /* angry -- get neutral monsters mad */
756 if (QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (tmp, FLAG_FRIENDLY))
757 CLEAR_FLAG (tmp, FLAG_UNAGGRESSIVE);
758 break;
759
760 case 2: /* calm -- pacify unfriendly monsters */
761 SET_FLAG (tmp, FLAG_UNAGGRESSIVE);
762 break;
763
764 case 3: /* make all monsters fall asleep */
765 SET_FLAG (tmp, FLAG_SLEEP);
766 break;
767
768 case 4: /* charm all monsters */
769 if (op == source)
770 break; /* only if 'connected' */
771
772 if (object *pl = source->ms ().player ())
773 {
774 tmp->set_owner (pl);
775 SET_FLAG (tmp, FLAG_MONSTER);
776
777 tmp->stats.exp = 0;
778
779 add_friendly_object (tmp);
780 tmp->attack_movement = PETMOVE;
781 }
782 break;
783
784 case 6: // kill monsters
785 if (!QUERY_FLAG (tmp, FLAG_FRIENDLY))
786 break;
787
788 // FALL THROUGH
789 case 5: // kill all alives
790 if (!tmp->flag [FLAG_PRECIOUS])
791 {
792 get_archetype ("burnout")->insert_at (tmp, source);
793 tmp->destroy ();
794 }
795 break;
796
797 default:
798 break;
799 }
800 }
801
802 /* this function returns the object it matches, or NULL if non.
803 * It will descend through containers to find the object.
804 * slaying = match object slaying flag
805 * race = match object archetype name flag
806 * hp = match object type (excpt type '0'== PLAYER)
807 */
808 object *
809 check_inv_recursive (object *op, const object *trig)
810 {
811 object *tmp, *ret = NULL;
812
813 /* First check the object itself. */
814 if ((trig->stats.hp && (op->type == trig->stats.hp))
815 || (trig->slaying && (op->slaying == trig->slaying))
816 || (trig->race && (op->arch->name == trig->race)))
817 return op;
818
819 for (tmp = op->inv; tmp; tmp = tmp->below)
820 {
821 if (tmp->inv)
822 {
823 ret = check_inv_recursive (tmp, trig);
824 if (ret)
825 return ret;
826 }
827 else if ((trig->stats.hp && (tmp->type == trig->stats.hp))
828 || (trig->slaying && (tmp->slaying == trig->slaying))
829 || (trig->race && (tmp->arch->name == trig->race)))
830 return tmp;
831 }
832 return NULL;
833 }
834
835 /* check_inv(), a function to search the inventory,
836 * of a player and then based on a set of conditions,
837 * the square will activate connected items.
838 * Monsters can't trigger this square (for now)
839 * Values are: last_sp = 1/0 obj/no obj triggers
840 * last_heal = 1/0 remove/dont remove obj if triggered
841 * -b.t. (thomas@nomad.astro.psu.edu
842 *
843 * Tue Dec 19 15:34:00 CET 2006 elmex: changed the function to ignore op
844 * because the check-inventory semantic essentially only applies when
845 * something is above the inventory checker.
846 * The semantic prior this change was: trigger if something has moved on or off
847 * and has a matching item. Imagine what happens if someone steps on the inventory
848 * checker with a matching item, has it, activates the connection, throws the item
849 * away, and then leaves the inventory checker. That would've caused an always-enabled
850 * state in the inventory checker. This won't happen anymore now.
851 *
852 * Wed Jan 10 11:34:26 CET 2007 elmex: fixed this function, we now check
853 * whether op is on this mapspace or not, because the value (1|0) depends
854 * on this information. also make sure to only push_button if op has
855 * a matching item (because when we do a push_button with value=0 timed gates
856 * will still open)! (i hope i got the semantics right this time)
857 *
858 */
859 void
860 check_inv (object *op, object *trig)
861 {
862 trig->value = 0; // deactivate if none of the following conditions apply
863
864 object *pl = trig->ms ().player ();
865 object *match = check_inv_recursive (op, trig);
866
867 // elmex: a note about (pl == op):
868 // if pl == 0 then the player has left this space
869 // if pl != 0 then a player is on this mapspace, but then
870 // we still have to check whether it's the player that triggered
871 // this inv-checker, because if not, then the op left this inv-checker
872 // and we have to set the value to 0
873
874 if (match && trig->last_sp) // match == having
875 {
876 if (trig->last_heal)
877 decrease_ob (match);
878
879 trig->value = (pl == op ? 1 : 0); // 1 if matching player entered, and 0 if he left
880 push_button (trig);
881 }
882 else if (!match && !trig->last_sp) // match == not having
883 {
884 trig->value = (pl == op ? 1 : 0); // 1 if matching player entered, and 0 if he left
885 push_button (trig);
886 }
887 }
888