ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/button.C
Revision: 1.35
Committed: Mon Jun 4 13:04:00 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.34: +2 -2 lines
Log Message:
- archetype is now a subclass of object.
- store archetypes in an object_vector.
- use a different startegy for archetype loading
  (reloading is MOST CERTAINLY broken).

File Contents

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