ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.70
Committed: Mon Oct 22 20:59:25 2007 UTC (16 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.69: +11 -6 lines
Log Message:
correctly check wether enoughbourign map spaces exist when removing doors

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 /*
25 * Routines that is executed from objects based on their speed have been
26 * collected in this file.
27 */
28 #include <global.h>
29 #include <spells.h>
30 #include <sproto.h>
31
32 /* The following removes doors. The functions check to see if similar
33 * doors are next to the one that is being removed, and if so, set it
34 * so those will be removed shortly (in a cascade like fashion.)
35 */
36 void
37 remove_door (object *op)
38 {
39 int i;
40 object *tmp;
41
42 for (i = 1; i < SIZEOFFREE1 + 1; i += 2)
43 {
44 mapxy pos (op);
45 pos.move (i);
46 if (pos.normalise ()
47 && present (DOOR, pos.m, pos.x, pos.y))
48 {
49 tmp->set_speed (0.1f);
50 tmp->speed_left = -0.2f;
51 }
52 }
53
54 if (op->other_arch)
55 {
56 tmp = arch_to_object (op->other_arch);
57 tmp->x = op->x;
58 tmp->y = op->y;
59 tmp->map = op->map;
60 tmp->level = op->level;
61 insert_ob_in_map (tmp, op->map, op, 0);
62 }
63
64 op->destroy ();
65 }
66
67 void
68 remove_door2 (object *op)
69 {
70 int i;
71 object *tmp;
72
73 for (i = 1; i < 9; i += 2)
74 {
75 tmp = present (LOCKED_DOOR, op->map, op->x + freearr_x[i], op->y + freearr_y[i]);
76 if (tmp && tmp->slaying == op->slaying)
77 { /* same key both doors */
78 tmp->set_speed (0.1f);
79 tmp->speed_left = -0.2f;
80 }
81 }
82
83 if (op->other_arch)
84 {
85 tmp = arch_to_object (op->other_arch);
86 tmp->x = op->x;
87 tmp->y = op->y;
88 tmp->map = op->map;
89 tmp->level = op->level;
90 insert_ob_in_map (tmp, op->map, op, 0);
91 }
92
93 op->destroy ();
94 }
95
96 void
97 generate_monster (object *gen)
98 {
99 if (!gen->map)
100 return;
101
102 if (GENERATE_SPEED (gen) && rndm (0, GENERATE_SPEED (gen) - 1))
103 return;
104
105 object *op;
106 int dir;
107
108 if (QUERY_FLAG (gen, FLAG_CONTENT_ON_GEN))
109 {
110 // either copy one item from the inventory...
111 if (!gen->inv)
112 return;
113
114 // first select one item from the inventory
115 int index = 0;
116 for (object *tmp = gen->inv; tmp; tmp = tmp->below)
117 if (!rndm (++index))
118 op = tmp;
119
120 dir = find_free_spot (op, gen->map, gen->x, gen->y, 1, SIZEOFFREE1 + 1);
121 if (dir < 0)
122 return;
123
124 op = object_create_clone (op);
125
126 CLEAR_FLAG (op, FLAG_IS_A_TEMPLATE);
127 unflag_inv (op, FLAG_IS_A_TEMPLATE);
128 }
129 else if (gen->other_arch)
130 {
131 // ...or use other_arch
132 dir = find_free_spot (gen->other_arch, gen->map, gen->x, gen->y, 1, SIZEOFFREE1 + 1);
133 if (dir < 0)
134 return;
135
136 op = arch_to_object (gen->other_arch);
137 }
138 else
139 return;
140
141 op->expand_tail ();
142
143 mapxy pos (gen); pos.move (dir);
144
145 if (pos.insert (op, gen))
146 {
147 if (rndm (0, 9))
148 generate_artifact (op, gen->map->difficulty);
149
150 if (op->has_random_items ())
151 create_treasure (op->randomitems, op, GT_APPLY, gen->map->difficulty);
152
153 return;
154 }
155
156 op->destroy ();
157 }
158
159 void
160 remove_force (object *op)
161 {
162 if (--op->duration > 0)
163 return;
164
165 if (op->env)
166 switch (op->subtype)
167 {
168 case FORCE_CONFUSION:
169 CLEAR_FLAG (op->env, FLAG_CONFUSED);
170 new_draw_info (NDI_UNIQUE, 0, op->env, "You regain your senses.\n");
171
172 default:
173 CLEAR_FLAG (op, FLAG_APPLIED);
174 change_abil (op->env, op);
175 op->env->update_stats ();
176 }
177
178 op->destroy ();
179 }
180
181 void
182 remove_blindness (object *op)
183 {
184 if (--op->stats.food > 0)
185 return;
186
187 CLEAR_FLAG (op, FLAG_APPLIED);
188
189 if (op->env)
190 {
191 change_abil (op->env, op);
192 op->env->update_stats ();
193 }
194
195 op->destroy ();
196 }
197
198 void
199 poison_more (object *op)
200 {
201 if (op->env == NULL || !QUERY_FLAG (op->env, FLAG_ALIVE) || op->env->stats.hp < 0)
202 {
203 op->destroy ();
204 return;
205 }
206
207 if (op->stats.food == 1)
208 {
209 /* need to unapply the object before update_stats is called, else fix_player
210 * will not do anything.
211 */
212 if (op->env->type == PLAYER)
213 {
214 CLEAR_FLAG (op, FLAG_APPLIED);
215 op->env->update_stats ();
216 new_draw_info (NDI_UNIQUE, 0, op->env, "You feel much better now.");
217 }
218
219 op->destroy ();
220 return;
221 }
222
223 if (op->env->type == PLAYER)
224 {
225 op->env->stats.food--;
226 new_draw_info (NDI_UNIQUE, 0, op->env, "You feel very sick...");
227 }
228
229 hit_player (op->env, op->stats.dam, op, AT_INTERNAL, 1);
230 }
231
232
233 void
234 move_gate (object *op)
235 { /* 1 = going down, 0 = going up */
236 object *tmp;
237
238 if (op->stats.wc < 0 || (int) op->stats.wc >= NUM_ANIMATIONS (op))
239 {
240 LOG (llevError, "Gate error: animation was %d, max=%d\n", op->stats.wc, NUM_ANIMATIONS (op));
241 op->stats.wc = 0;
242 }
243
244 /* We're going down */
245 if (op->value)
246 {
247 if (--op->stats.wc <= 0)
248 { /* Reached bottom, let's stop */
249 op->stats.wc = 0;
250 if (op->arch->speed)
251 op->value = 0;
252 else
253 op->set_speed (0);
254 }
255
256 if ((int) op->stats.wc < (NUM_ANIMATIONS (op) / 2 + 1))
257 {
258 op->move_block = 0;
259 CLEAR_FLAG (op, FLAG_BLOCKSVIEW);
260 update_all_los (op->map, op->x, op->y);
261 }
262
263 SET_ANIMATION (op, op->stats.wc);
264 update_object (op, UP_OBJ_CHANGE);
265 return;
266 }
267
268 /* We're going up */
269
270 /* First, lets see if we are already at the top */
271 if ((unsigned char) op->stats.wc == (NUM_ANIMATIONS (op) - 1))
272 {
273
274 /* Check to make sure that only non pickable and non rollable
275 * objects are above the gate. If so, we finish closing the gate,
276 * otherwise, we fall through to the code below which should lower
277 * the gate slightly.
278 */
279
280 for (tmp = op->above; tmp; tmp = tmp->above)
281 if (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || QUERY_FLAG (tmp, FLAG_ALIVE))
282 break;
283
284 if (!tmp)
285 {
286 if (op->arch->speed)
287 op->value = 1;
288 else
289 op->set_speed (0);
290
291 return;
292 }
293 }
294
295 if (op->stats.food)
296 { /* The gate is going temporarily down */
297 if (--op->stats.wc <= 0)
298 { /* Gone all the way down? */
299 op->stats.food = 0; /* Then let's try again */
300 op->stats.wc = 0;
301 }
302 }
303 else
304 { /* The gate is still going up */
305 op->stats.wc++;
306
307 if (op->stats.wc >= NUM_ANIMATIONS (op))
308 op->stats.wc = NUM_ANIMATIONS (op) - 1;
309
310 /* If there is something on top of the gate, we try to roll it off.
311 * If a player/monster, we don't roll, we just hit them with damage
312 */
313 if (op->stats.wc >= NUM_ANIMATIONS (op) / 2)
314 {
315 /* Halfway or further, check blocks */
316 /* First, get the top object on the square. */
317 for (tmp = op->above; tmp && tmp->above; tmp = tmp->above)
318 ;
319
320 if (tmp)
321 {
322 if (QUERY_FLAG (tmp, FLAG_ALIVE))
323 {
324 hit_player (tmp, random_roll (0, op->stats.dam, tmp, PREFER_LOW), op, AT_PHYSICAL, 1);
325 op->play_sound (sound_find ("blocked_gate"));
326
327 if (tmp->type == PLAYER)
328 new_draw_info_format (NDI_UNIQUE, 0, tmp, "You are crushed by the %s!", &op->name);
329 }
330 /* If the object is not alive, and the object either can
331 * be picked up or the object rolls, move the object
332 * off the gate.
333 */
334 else if (!QUERY_FLAG (tmp, FLAG_ALIVE) && (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL)))
335 {
336 /* If it has speed, it should move itself, otherwise: */
337 int i = find_free_spot (tmp, op->map, op->x, op->y, 1, SIZEOFFREE1 + 1);
338
339 /* If there is a free spot, move the object someplace */
340 if (i > 0)
341 {
342 mapxy pos (tmp);
343 pos.move (i);
344 if (pos.normalise ())
345 tmp->move_to (pos);
346 }
347 }
348 }
349
350 /* See if there is still anything blocking the gate */
351 for (tmp = op->above; tmp; tmp = tmp->above)
352 if (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || QUERY_FLAG (tmp, FLAG_ALIVE))
353 break;
354
355 /* IF there is, start putting the gate down */
356 if (tmp)
357 op->stats.food = 1;
358 else
359 {
360 op->move_block = MOVE_ALL;
361
362 if (!op->arch->stats.ac)
363 SET_FLAG (op, FLAG_BLOCKSVIEW);
364 update_all_los (op->map, op->x, op->y);
365 }
366 } /* gate is halfway up */
367
368 SET_ANIMATION (op, op->stats.wc);
369 update_object (op, UP_OBJ_CHANGE);
370 } /* gate is going up */
371 }
372
373 /* hp : how long door is open/closed
374 * maxhp : initial value for hp
375 * sp : 1 = open, 0 = close
376 */
377 void
378 move_timed_gate (object *op)
379 {
380 int v = op->value;
381
382 if (op->stats.sp)
383 {
384 move_gate (op);
385
386 if (op->value != v) /* change direction ? */
387 op->stats.sp = 0;
388 return;
389 }
390
391 if (--op->stats.hp <= 0)
392 { /* keep gate down */
393 move_gate (op);
394
395 if (op->value != v)
396 op->set_speed (0);
397 }
398 }
399
400 /* slaying: name of the thing the detector is to look for
401 * speed: frequency of 'glances'
402 * connected: connected value of detector
403 * sp: 1 if detection sets buttons
404 * -1 if detection unsets buttons
405 */
406
407 void
408 move_detector (object *op)
409 {
410 object *tmp;
411 int last = op->value;
412 int detected;
413
414 detected = 0;
415
416 for (tmp = op->ms ().bot; tmp && !detected; tmp = tmp->above)
417 {
418 object *tmp2;
419
420 if (op->stats.hp)
421 {
422 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
423 {
424 if (op->slaying && op->slaying == tmp->name)
425 detected = 1;
426
427 if (tmp2->type == FORCE && tmp2->slaying && tmp2->slaying == op->slaying)
428 detected = 1;
429 }
430 }
431
432 if (op->slaying && op->slaying == tmp->name)
433 detected = 1;
434 else if (tmp->type == SPECIAL_KEY && tmp->slaying == op->slaying)
435 detected = 1;
436 }
437
438 /* the detector sets the button if detection is found */
439 if (op->stats.sp == 1)
440 {
441 if (detected && last == 0)
442 {
443 op->value = 1;
444 push_button (op);
445 }
446
447 if (!detected && last == 1)
448 {
449 op->value = 0;
450 push_button (op);
451 }
452 }
453 else
454 { /* in this case, we unset buttons */
455 if (detected && last == 1)
456 {
457 op->value = 0;
458 push_button (op);
459 }
460
461 if (!detected && last == 0)
462 {
463 op->value = 1;
464 push_button (op);
465 }
466 }
467 }
468
469 void
470 animate_trigger (object *op)
471 {
472 if ((unsigned char) ++op->stats.wc >= NUM_ANIMATIONS (op))
473 {
474 op->stats.wc = 0;
475 check_trigger (op, NULL);
476 }
477 else
478 {
479 SET_ANIMATION (op, op->stats.wc);
480 update_object (op, UP_OBJ_FACE);
481 }
482 }
483
484 void
485 move_hole (object *op)
486 { /* 1 = opening, 0 = closing */
487 object *next, *tmp;
488
489 if (op->value)
490 { /* We're opening */
491 if (--op->stats.wc <= 0)
492 { /* Opened, let's stop */
493 op->stats.wc = 0;
494 op->set_speed (0);
495
496 /* Hard coding this makes sense for holes I suppose */
497 op->move_on = MOVE_WALK;
498 for (tmp = op->above; tmp != NULL; tmp = next)
499 {
500 next = tmp->above;
501 move_apply (op, tmp, tmp);
502 }
503 }
504
505 SET_ANIMATION (op, op->stats.wc);
506 update_object (op, UP_OBJ_FACE);
507 return;
508 }
509
510 /* We're closing */
511 op->move_on = 0;
512
513 op->stats.wc++;
514 if ((int) op->stats.wc >= NUM_ANIMATIONS (op))
515 op->stats.wc = NUM_ANIMATIONS (op) - 1;
516
517 SET_ANIMATION (op, op->stats.wc);
518 update_object (op, UP_OBJ_FACE);
519 if ((unsigned char) op->stats.wc == (NUM_ANIMATIONS (op) - 1))
520 op->set_speed (0); /* closed, let's stop */
521 }
522
523
524 /* stop_item() returns a pointer to the stopped object. The stopped object
525 * may or may not have been removed from maps or inventories. It will not
526 * have been merged with other items.
527 *
528 * This function assumes that only items on maps need special treatment.
529 *
530 * If the object can't be stopped, or it was destroyed while trying to stop
531 * it, NULL is returned.
532 *
533 * fix_stopped_item() should be used if the stopped item should be put on
534 * the map.
535 */
536 object *
537 stop_item (object *op)
538 {
539 if (op->map == NULL)
540 return op;
541
542 switch (op->type)
543 {
544 case THROWN_OBJ:
545 {
546 object *payload = op->inv;
547
548 if (payload == NULL)
549 return NULL;
550 payload->remove ();
551 op->destroy ();
552 return payload;
553 }
554
555 case ARROW:
556 if (op->has_active_speed ())
557 op = fix_stopped_arrow (op);
558 return op;
559
560 default:
561 return op;
562 }
563 }
564
565 /* fix_stopped_item() - put stopped item where stop_item() had found it.
566 * Inserts item into the old map, or merges it if it already is on the map.
567 *
568 * 'map' must be the value of op->map before stop_item() was called.
569 */
570 void
571 fix_stopped_item (object *op, maptile *map, object *originator)
572 {
573 if (map == NULL)
574 return;
575
576 if (QUERY_FLAG (op, FLAG_REMOVED))
577 insert_ob_in_map (op, map, originator, 0);
578 else if (op->type == ARROW)
579 merge_ob (op, NULL); /* only some arrows actually need this */
580 }
581
582 object *
583 fix_stopped_arrow (object *op)
584 {
585 if (rndm (0, 99) < op->stats.food)
586 {
587 /* Small chance of breaking */
588 op->destroy ();
589 return NULL;
590 }
591
592 op->set_speed (0);
593 op->direction = 0;
594 op->move_on = 0;
595 op->move_type = 0;
596 op->skill = 0; // really?
597
598 // restore original wc, dam, attacktype and slaying
599 op->stats.wc = op->stats.sp;
600 op->stats.dam = op->stats.hp;
601 op->attacktype = op->stats.grace;
602
603 if (op->spellarg)
604 {
605 op->slaying = op->spellarg;
606 free (op->spellarg);
607 op->spellarg = 0;
608 }
609 else
610 op->slaying = 0;
611
612 /* Reset these to zero, so that object::can_merge will work properly */
613 op->spellarg = NULL;
614 op->stats.sp = 0;
615 op->stats.hp = 0;
616 op->stats.grace = 0;
617 op->level = 0;
618 op->face = op->arch->face;
619 op->owner = NULL; /* So that stopped arrows will be saved */
620 update_object (op, UP_OBJ_CHANGE);
621 return op;
622 }
623
624 /* stop_arrow() - what to do when a non-living flying object
625 * has to stop. Sept 96 - I added in thrown object code in
626 * here too. -b.t.
627 *
628 * Returns a pointer to the stopped object (which will have been removed
629 * from maps or inventories), or NULL if was destroyed.
630 */
631 static void
632 stop_arrow (object *op)
633 {
634 if (INVOKE_OBJECT (STOP, op))
635 return;
636
637 if (op->inv)
638 {
639 object *payload = op->inv;
640
641 payload->remove ();
642 payload->owner = 0;
643 insert_ob_in_map (payload, op->map, payload, 0);
644 op->destroy ();
645 }
646 else
647 {
648 op = fix_stopped_arrow (op);
649
650 if (op)
651 merge_ob (op, 0);
652 }
653 }
654
655 /* Move an arrow along its course. op is the arrow or thrown object.
656 */
657 void
658 move_arrow (object *op)
659 {
660 int was_reflected;
661
662 if (!op->map)
663 {
664 LOG (llevError, "BUG: Arrow had no map.\n");
665 op->destroy ();
666 return;
667 }
668
669 /* we need to stop thrown objects at some point. Like here. */
670 if (op->type == THROWN_OBJ)
671 {
672 /* If the object that the THROWN_OBJ encapsulates disappears,
673 * we need to have this object go away also - otherwise, you get
674 * left over remnants on the map. Where this currently happens
675 * is if the player throws a bomb - the bomb explodes on its own,
676 * but this object sticks around. We could handle the cleanup in the
677 * bomb code, but there are potential other cases where that could happen,
678 * and it is easy enough to clean it up here.
679 */
680 if (op->inv == NULL)
681 {
682 op->destroy ();
683 return;
684 }
685
686 if (op->last_sp-- < 0)
687 {
688 stop_arrow (op);
689 return;
690 }
691 }
692
693 /* if the arrow is moving too slow.. stop it. 0.5 was chosen as lower
694 values look rediculous. */
695 if (op->speed < 0.5 && op->type == ARROW)
696 {
697 stop_arrow (op);
698 return;
699 }
700
701 /* Calculate target map square */
702 was_reflected = 0;
703
704 mapxy pos (op); pos.move (op->direction);
705
706 if (!pos.normalise ())
707 {
708 stop_arrow (op);
709 return;
710 }
711
712 /* only need to look for living creatures if this flag is set */
713 if (pos->flags () & P_IS_ALIVE)
714 {
715 object *tmp;
716
717 for (tmp = pos->bot; tmp; tmp = tmp->above)
718 if (QUERY_FLAG (tmp, FLAG_ALIVE))
719 break;
720
721 /* Not really fair, but don't let monsters hit themselves with
722 * their own arrow - this can be because they fire it then
723 * move into it.
724 */
725 if (tmp && tmp != op->owner)
726 {
727 /* Found living object, but it is reflecting the missile. Update
728 * as below. (Note that for living creatures there is a small
729 * chance that reflect_missile fails.)
730 */
731 if (QUERY_FLAG (tmp, FLAG_REFL_MISSILE) && (rndm (0, 99)) < (90 - op->level / 10))
732 {
733 int number = op->face;
734
735 op->direction = absdir (op->direction + 4);
736 update_turn_face (op);
737 was_reflected = 1; /* skip normal movement calculations */
738 }
739 else
740 {
741 /* Attack the object. */
742 op = hit_with_arrow (op, tmp);
743
744 if (!op)
745 return;
746 }
747 } /* if this is not hitting its owner */
748 } /* if there is something alive on this space */
749
750 if (OB_TYPE_MOVE_BLOCK (op, pos->move_block))
751 {
752 int retry = 0;
753
754 /* if the object doesn't reflect, stop the arrow from moving
755 * note that this code will now catch cases where a monster is
756 * on a wall but has reflecting - the arrow won't reflect.
757 * Mapmakers shouldn't put monsters on top of wall in the first
758 * place, so I don't consider that a problem.
759 */
760 if (!QUERY_FLAG (op, FLAG_REFLECTING) || !(rndm (0, 19)))
761 {
762 stop_arrow (op);
763 return;
764 }
765 else
766 {
767 /* If one of the major directions (n,s,e,w), just reverse it */
768 if (op->direction & 1)
769 {
770 op->direction = absdir (op->direction + 4);
771 retry = 1;
772 }
773
774 /* There were two blocks with identical code -
775 * use this retry here to make this one block
776 * that did the same thing.
777 */
778 while (retry < 2)
779 {
780 retry++;
781
782 /* Need to check for P_OUT_OF_MAP: if the arrow is travelling
783 * over a corner in a tiled map, it is possible that
784 * op->direction is within an adjacent map but either
785 * op->direction-1 or op->direction+1 does not exist.
786 */
787 mapxy pos1 (pos); pos1.move (absdir (op->direction - 1));
788 bool left = pos1.normalise () && OB_TYPE_MOVE_BLOCK (op, pos1->move_block);
789
790 mapxy pos2 (pos); pos2.move (absdir (op->direction + 1));
791 bool right = pos2.normalise () && OB_TYPE_MOVE_BLOCK (op, pos2->move_block);
792
793 if (left == right)
794 op->direction = absdir (op->direction + 4);
795 else if (left)
796 op->direction = absdir (op->direction + 2);
797 else if (right)
798 op->direction = absdir (op->direction - 2);
799
800 /* If this space is not out of the map and not blocked, valid space -
801 * don't need to retry again.
802 */
803 mapxy pos3 (pos); pos3.move (op->direction);
804 if (pos3.normalise () && !OB_TYPE_MOVE_BLOCK (op, pos3->move_block))
805 break;
806 }
807
808 /* Couldn't find a direction to move the arrow to - just
809 * stop it from moving.
810 */
811 if (retry == 2)
812 {
813 stop_arrow (op);
814 return;
815 }
816
817 /* update object image for new facing */
818 /* many thrown objects *don't* have more than one face */
819 if (GET_ANIM_ID (op))
820 SET_ANIMATION (op, op->direction);
821 } /* object is reflected */
822 } /* object ran into a wall */
823
824 /* decrease the speed as it flies. 0.05 means a standard bow will shoot
825 * about 17 squares. Tune as needed.
826 */
827 op->speed -= 0.05;
828
829 /* Move the arrow. */
830 op->move_to (pos);
831 }
832
833 void
834 change_object (object *op)
835 { /* Doesn`t handle linked objs yet */
836 int i, j;
837
838 if (!op->other_arch)
839 {
840 LOG (llevError, "Change object (%s) without other_arch error.\n", op->debug_desc ());
841 return;
842 }
843
844 /* In non-living items only change when food value is 0 */
845 if (!QUERY_FLAG (op, FLAG_ALIVE))
846 {
847 if (op->stats.food-- > 0)
848 return;
849
850 op->stats.food = 1; /* so 1 other_arch is made */
851 }
852
853 object *env = op->env;
854
855 op->remove ();
856 for (i = 0; i < op->stats.food; i++)
857 {
858 object *tmp = arch_to_object (op->other_arch);
859
860 if (op->type == LAMP)
861 tmp->stats.food = op->stats.food - 1;
862
863 tmp->stats.hp = op->stats.hp; /* The only variable it keeps. */
864 if (env)
865 {
866 tmp = env->insert (tmp);
867
868 /* If this object is the players inventory, we need to tell the
869 * client of the change. Insert_ob_in_map takes care of the
870 * updating the client, so we don't need to do that below.
871 */
872 if (object *pl = op->in_player ())
873 {
874 esrv_del_item (pl->contr, op->count);
875 esrv_send_item (pl, tmp);
876 }
877 }
878 else
879 {
880 j = find_first_free_spot (tmp, op->map, op->x, op->y);
881 if (j < 0) /* No free spot */
882 tmp->destroy ();
883 else
884 {
885 mapxy pos (op); pos.move (j);
886
887 if (pos.normalise ())
888 pos.insert (tmp, op);
889 }
890 }
891 }
892
893 op->destroy ();
894 }
895
896 void
897 move_teleporter (object *op)
898 {
899 object *tmp, *head = op;
900
901 /* if this is a multipart teleporter, handle the other parts
902 * The check for speed isn't strictly needed - basically, if
903 * there is an old multipart teleporter in which the other parts
904 * have speed, we don't really want to call it twice for the same
905 * function - in fact, as written below, part N would get called
906 * N times without the speed check.
907 */
908 if (op->more && !op->more->has_active_speed ())
909 move_teleporter (op->more);
910
911 if (op->head)
912 head = op->head;
913
914 for (tmp = op->above; tmp; tmp = tmp->above)
915 if (!QUERY_FLAG (tmp, FLAG_IS_FLOOR))
916 break;
917
918 /* If nothing above us to move, nothing to do */
919 if (!tmp || QUERY_FLAG (tmp, FLAG_WIZPASS))
920 return;
921
922 if (EXIT_PATH (head))
923 {
924 if (tmp->type == PLAYER)
925 {
926 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
927 return;
928
929 tmp->enter_exit (head);
930 }
931 else
932 /* Currently only players can transfer maps */
933 return;
934 }
935 else if (EXIT_X (head) || EXIT_Y (head))
936 {
937 if (out_of_map (head->map, EXIT_X (head), EXIT_Y (head)))
938 {
939 LOG (llevError, "Removed illegal teleporter.\n");
940 head->destroy ();
941 return;
942 }
943
944 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
945 return;
946
947 transfer_ob (tmp, EXIT_X (head), EXIT_Y (head), 0, head);
948 }
949 else
950 {
951 /* Random teleporter */
952 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (tmp)))
953 return;
954 teleport (head, TELEPORTER, tmp);
955 }
956 }
957
958 /* This object will teleport someone to a different map
959 and will also apply changes to the player from its inventory.
960 This was invented for giving classes, but there's no reason it
961 can't be generalized.
962 */
963 void
964 move_player_changer (object *op)
965 {
966 object *player;
967 object *walk;
968
969 if (!op->above || !EXIT_PATH (op))
970 return;
971
972 /* This isn't all that great - means that the player_mover
973 * needs to be on top.
974 */
975 if (op->above->type == PLAYER)
976 {
977 if (INVOKE_OBJECT (TRIGGER, op, ARG_OBJECT (player)))
978 return;
979
980 player = op->above;
981
982 for (walk = op->inv; walk; walk = walk->below)
983 apply_changes_to_player (player, walk);
984
985 player->update_stats ();
986
987 esrv_send_inventory (op->above, op->above);
988 esrv_update_item (UPD_FACE, op->above, op->above);
989
990 /* update players death & WoR home-position */
991 if (*EXIT_PATH (op) == '/')
992 {
993 player->contr->savebed_map = EXIT_PATH (op);
994 player->contr->bed_x = EXIT_X (op);
995 player->contr->bed_y = EXIT_Y (op);
996 }
997 else
998 LOG (llevDebug, "WARNING: destination '%s' in player_changer must be an absolute path!\n", &EXIT_PATH (op));
999
1000 op->above->enter_exit (op);
1001 }
1002 }
1003
1004 /* firewalls fire other spells.
1005 * The direction of the wall is stored in op->stats.sp.
1006 * walls can have hp, so they can be torn down.
1007 */
1008 void
1009 move_firewall (object *op)
1010 {
1011 object *spell;
1012
1013 if (!op->map)
1014 return; /* dm has created a firewall in his inventory */
1015
1016 spell = op->inv;
1017
1018 if (!spell || spell->type != SPELL)
1019 spell = op->other_arch;
1020
1021 if (!spell)
1022 {
1023 LOG (llevError, "move_firewall: no spell specified (%s, %s, %d, %d)\n", &op->name, &op->map->name, op->x, op->y);
1024 return;
1025 }
1026
1027 cast_spell (op, op, op->stats.sp ? op->stats.sp : rndm (1, 8), spell, NULL);
1028 }
1029
1030 /* move_player_mover: this function takes a "player mover" as an
1031 * argument, and performs the function of a player mover, which is:
1032 *
1033 * a player mover finds any players that are sitting on it. It
1034 * moves them in the op->stats.sp direction. speed is how often it'll move.
1035 * If attacktype is nonzero it will paralyze the player. If lifesave is set,
1036 * it'll dissapear after hp+1 moves. If hp is set and attacktype is set,
1037 * it'll paralyze the victim for hp*his speed/op->speed
1038 */
1039 void
1040 move_player_mover (object *op)
1041 {
1042 int dir = op->stats.sp;
1043 sint16 nx, ny;
1044 maptile *m;
1045
1046 /* Determine direction now for random movers so we do the right thing */
1047 if (!dir)
1048 dir = rndm (1, 8);
1049
1050 for (object *victim = op->ms ().bot; victim; victim = victim->above)
1051 {
1052 if (QUERY_FLAG (victim, FLAG_ALIVE) && !QUERY_FLAG (victim, FLAG_WIZPASS) &&
1053 (victim->move_type & op->move_type || !victim->move_type))
1054 {
1055
1056 if (victim->head)
1057 victim = victim->head;
1058
1059 if (QUERY_FLAG (op, FLAG_LIFESAVE) && op->stats.hp-- < 0)
1060 {
1061 op->remove ();
1062 return;
1063 }
1064
1065 nx = op->x + freearr_x[dir];
1066 ny = op->y + freearr_y[dir];
1067 m = op->map;
1068 if (get_map_flags (m, &m, nx, ny, &nx, &ny) & P_OUT_OF_MAP)
1069 {
1070 LOG (llevError, "move_player_mover: Trying to push player off the map! map=%s (%d, %d)\n", &m->path, op->x, op->y);
1071 return;
1072 }
1073
1074 if (should_director_abort (op, victim))
1075 return;
1076
1077 for (object *nextmover = m->at (nx, ny).bot; nextmover; nextmover = nextmover->above)
1078 {
1079 if (nextmover->type == PLAYERMOVER)
1080 nextmover->speed_left = -.99f;
1081
1082 if (QUERY_FLAG (nextmover, FLAG_ALIVE))
1083 op->speed_left = -1.1f; /* wait until the next thing gets out of the way */
1084 }
1085
1086 if (victim->type == PLAYER)
1087 {
1088 /* only level >=1 movers move people */
1089 if (op->level)
1090 {
1091 /* Following is a bit of hack. We need to make sure it
1092 * is cleared, otherwise the player will get stuck in
1093 * place. This can happen if the player used a spell to
1094 * get to this space.
1095 */
1096 victim->contr->fire_on = 0;
1097 victim->speed_left = 1.f;
1098 move_player (victim, dir);
1099 }
1100 else
1101 return;
1102 }
1103 else
1104 move_object (victim, dir);
1105
1106 if (!op->stats.maxsp && op->attacktype)
1107 op->stats.maxsp = 2;
1108
1109 if (op->attacktype)
1110 { /* flag to paralyze the player */
1111 victim->speed_left = max (-5.f, -FABS (op->stats.maxsp * victim->speed / op->speed));
1112 }
1113 }
1114 }
1115 }
1116
1117 /*
1118 * Will duplicate a specified object placed on top of it.
1119 * connected: what will trigger it.
1120 * level: multiplier. 0 to destroy.
1121 * other_arch: the object to look for and duplicate.
1122 */
1123
1124 void
1125 move_duplicator (object *op)
1126 {
1127 object *tmp;
1128
1129 if (!op->other_arch)
1130 {
1131 LOG (llevInfo, "Duplicator with no other_arch! %d %d %s\n", op->x, op->y, op->map ? &op->map->path : "nullmap");
1132 return;
1133 }
1134
1135 if (op->above == NULL)
1136 return;
1137
1138 for (tmp = op->above; tmp; tmp = tmp->above)
1139 {
1140 if (op->other_arch->archname == tmp->arch->archname)
1141 {
1142 if (op->level <= 0)
1143 tmp->destroy ();
1144 else
1145 {
1146 uint64 new_nrof = (uint64) tmp->nrof * op->level;
1147
1148 if (new_nrof >= 1UL << 31)
1149 new_nrof = 1UL << 31;
1150
1151 tmp->nrof = new_nrof;
1152 }
1153
1154 break;
1155 }
1156 }
1157 }
1158
1159 /* move_creator (by peterm)
1160 * it has the creator object create it's other_arch right on top of it.
1161 * connected: what will trigger it
1162 * hp: how many times it may create before stopping
1163 * lifesave: if set, it'll never disappear but will go on creating
1164 * everytime it's triggered
1165 * other_arch: the object to create
1166 * Note this can create large objects, however, in that case, it
1167 * has to make sure that there is in fact space for the object.
1168 * It should really do this for small objects also, but there is
1169 * more concern with large objects, most notably a part being placed
1170 * outside of the map which would cause the server to crash
1171 */
1172 void
1173 move_creator (object *creator)
1174 {
1175 object *new_ob;
1176
1177 if (!QUERY_FLAG (creator, FLAG_LIFESAVE) && --creator->stats.hp < 0)
1178 {
1179 creator->stats.hp = -1;
1180 return;
1181 }
1182
1183 if (creator->inv != NULL)
1184 {
1185 object *ob;
1186 int i;
1187 object *ob_to_copy;
1188
1189 /* select random object from inventory to copy */
1190 ob_to_copy = creator->inv;
1191 for (ob = creator->inv->below, i = 1; ob != NULL; ob = ob->below, i++)
1192 {
1193 if (rndm (0, i) == 0)
1194 {
1195 ob_to_copy = ob;
1196 }
1197 }
1198 new_ob = object_create_clone (ob_to_copy);
1199 CLEAR_FLAG (new_ob, FLAG_IS_A_TEMPLATE);
1200 unflag_inv (new_ob, FLAG_IS_A_TEMPLATE);
1201 }
1202 else
1203 {
1204 if (creator->other_arch == NULL)
1205 {
1206 LOG (llevError, "move_creator: Creator doesn't have other arch set: %s (%s, %d, %d)\n",
1207 &creator->name, &creator->map->path, creator->x, creator->y);
1208 return;
1209 }
1210
1211 new_ob = object_create_arch (creator->other_arch);
1212 fix_generated_item (new_ob, creator, 0, 0, GT_MINIMAL);
1213 }
1214
1215 /* Make sure this multipart object fits */
1216 if (new_ob->arch->more && new_ob->blocked (creator->map, creator->x, creator->y))
1217 {
1218 new_ob->destroy ();
1219 return;
1220 }
1221
1222 // for now lets try to identify everything generated here, it mostly
1223 // happens automated, so this will at least fix many identify-experience holes
1224 SET_FLAG (new_ob, FLAG_IDENTIFIED);
1225
1226 insert_ob_in_map_at (new_ob, creator->map, creator, 0, creator->x, creator->y);
1227 if (QUERY_FLAG (new_ob, FLAG_FREED))
1228 return;
1229
1230 if (creator->slaying)
1231 {
1232 new_ob->name = new_ob->title = creator->slaying;
1233 }
1234 }
1235
1236 /* move_marker --peterm@soda.csua.berkeley.edu
1237 when moved, a marker will search for a player sitting above
1238 it, and insert an invisible, weightless force into him
1239 with a specific code as the slaying field.
1240 At that time, it writes the contents of its own message
1241 field to the player. The marker will decrement hp to
1242 0 and then delete itself every time it grants a mark.
1243 unless hp was zero to start with, in which case it is infinite.*/
1244 void
1245 move_marker (object *op)
1246 {
1247 if (object *tmp = op->ms ().player ())
1248 {
1249 /* remove an old force with a slaying field == op->name */
1250 if (object *force = tmp->force_find (op->name))
1251 force->destroy ();
1252
1253 if (!tmp->force_find (op->slaying))
1254 {
1255 tmp->force_add (op->slaying, op->stats.food);
1256
1257 if (op->msg)
1258 new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, tmp, op->msg);
1259
1260 if (op->stats.hp > 0)
1261 {
1262 op->stats.hp--;
1263
1264 if (op->stats.hp == 0)
1265 {
1266 /* marker expires--granted mark number limit */
1267 op->destroy ();
1268 return;
1269 }
1270 }
1271 }
1272 }
1273 }
1274
1275 void
1276 process_object (object *op)
1277 {
1278 if (expect_false (QUERY_FLAG (op, FLAG_IS_A_TEMPLATE)))
1279 return;
1280
1281 if (expect_false (INVOKE_OBJECT (TICK, op)))
1282 return;
1283
1284 if (QUERY_FLAG (op, FLAG_MONSTER))
1285 if (move_monster (op) || QUERY_FLAG (op, FLAG_FREED))
1286 return;
1287
1288 if (QUERY_FLAG (op, FLAG_ANIMATE) && op->anim_speed == 0)
1289 {
1290 animate_object (op, op->contr ? op->facing : op->direction);
1291
1292 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1293 make_sure_seen (op);
1294 }
1295
1296 if (expect_false (
1297 op->flag [FLAG_GENERATOR]
1298 || op->flag [FLAG_CHANGING]
1299 || op->flag [FLAG_IS_USED_UP]
1300 ))
1301 {
1302 if (QUERY_FLAG (op, FLAG_CHANGING) && !op->state)
1303 {
1304 change_object (op);
1305 return;
1306 }
1307
1308 if (QUERY_FLAG (op, FLAG_GENERATOR) && !QUERY_FLAG (op, FLAG_FRIENDLY))
1309 generate_monster (op);
1310
1311 if (QUERY_FLAG (op, FLAG_IS_USED_UP) && --op->stats.food <= 0)
1312 {
1313 if (QUERY_FLAG (op, FLAG_APPLIED))
1314 remove_force (op);
1315 else
1316 {
1317 /* If necessary, delete the item from the players inventory */
1318 if (object *pl = op->in_player ())
1319 esrv_del_item (pl->contr, op->count);
1320
1321 op->remove ();
1322
1323 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1324 make_sure_not_seen (op);
1325
1326 op->destroy ();
1327 }
1328
1329 return;
1330 }
1331 }
1332
1333 switch (op->type)
1334 {
1335 case SPELL_EFFECT:
1336 move_spell_effect (op);
1337 break;
1338
1339 case ROD:
1340 case HORN:
1341 regenerate_rod (op);
1342 break;
1343
1344 case FORCE:
1345 case POTION_EFFECT:
1346 remove_force (op);
1347 break;
1348
1349 case BLINDNESS:
1350 remove_blindness (op);
1351 break;
1352
1353 case POISONING:
1354 poison_more (op);
1355 break;
1356
1357 case DISEASE:
1358 move_disease (op);
1359 break;
1360
1361 case SYMPTOM:
1362 move_symptom (op);
1363 break;
1364
1365 case THROWN_OBJ:
1366 case ARROW:
1367 move_arrow (op);
1368 break;
1369
1370 case DOOR:
1371 remove_door (op);
1372 break;
1373
1374 case LOCKED_DOOR:
1375 remove_door2 (op);
1376 break;
1377
1378 case TELEPORTER:
1379 move_teleporter (op);
1380 break;
1381
1382 case GOLEM:
1383 move_golem (op);
1384 break;
1385
1386 case EARTHWALL:
1387 hit_player (op, 2, op, AT_PHYSICAL, 1);
1388 break;
1389
1390 case FIREWALL:
1391 move_firewall (op);
1392 if (op->stats.maxsp)
1393 animate_turning (op);
1394 break;
1395
1396 case MOOD_FLOOR:
1397 do_mood_floor (op);
1398 break;
1399
1400 case GATE:
1401 move_gate (op);
1402 break;
1403
1404 case TIMED_GATE:
1405 move_timed_gate (op);
1406 break;
1407
1408 case TRIGGER:
1409 case TRIGGER_BUTTON:
1410 case TRIGGER_PEDESTAL:
1411 case TRIGGER_ALTAR:
1412 animate_trigger (op);
1413 break;
1414
1415 case DETECTOR:
1416 move_detector (op);
1417
1418 case DIRECTOR:
1419 if (op->stats.maxsp)
1420 animate_turning (op);
1421 break;
1422
1423 case HOLE:
1424 move_hole (op);
1425 break;
1426
1427 case DEEP_SWAMP:
1428 move_deep_swamp (op);
1429 break;
1430
1431 case RUNE:
1432 case TRAP:
1433 move_rune (op);
1434 break;
1435
1436 case PLAYERMOVER:
1437 move_player_mover (op);
1438 break;
1439
1440 case CREATOR:
1441 move_creator (op);
1442 break;
1443
1444 case MARKER:
1445 move_marker (op);
1446 break;
1447
1448 case PLAYER_CHANGER:
1449 move_player_changer (op);
1450 break;
1451
1452 case PEACEMAKER:
1453 move_peacemaker (op);
1454 break;
1455
1456 case PLAYER:
1457 // players have their own speed-management, so undo the --speed_left
1458 ++op->speed_left;
1459 break;
1460 }
1461 }
1462