ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.68
Committed: Wed Sep 12 11:10:11 2007 UTC (16 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.67: +1 -1 lines
Log Message:
- rewrite/cleanup ob_blocked a little. its more efficient now, probably
  has less bugs and more bugs.
- get rid of now unused P_NO_PASS.

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