ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.58
Committed: Sat Jun 30 03:00:55 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.57: +5 -6 lines
Log Message:
*** empty log message ***

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 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 *
13 * 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 *
18 * 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 */
24
25 /*
26 * Routines that is executed from objects based on their speed have been
27 * collected in this file.
28 */
29 #include <global.h>
30 #include <spells.h>
31 #include <sproto.h>
32
33 /* The following removes doors. The functions check to see if similar
34 * doors are next to the one that is being removed, and if so, set it
35 * so those will be removed shortly (in a cascade like fashion.)
36 */
37 void
38 remove_door (object *op)
39 {
40 int i;
41 object *tmp;
42
43 for (i = 1; i < 9; i += 2)
44 if ((tmp = present (DOOR, op->map, op->x + freearr_x[i], op->y + freearr_y[i])) != NULL)
45 {
46 tmp->set_speed (0.1f);
47 tmp->speed_left = -0.2f;
48 }
49
50 if (op->other_arch)
51 {
52 tmp = arch_to_object (op->other_arch);
53 tmp->x = op->x;
54 tmp->y = op->y;
55 tmp->map = op->map;
56 tmp->level = op->level;
57 insert_ob_in_map (tmp, op->map, op, 0);
58 }
59
60 op->destroy ();
61 }
62
63 void
64 remove_door2 (object *op)
65 {
66 int i;
67 object *tmp;
68
69 for (i = 1; i < 9; i += 2)
70 {
71 tmp = present (LOCKED_DOOR, op->map, op->x + freearr_x[i], op->y + freearr_y[i]);
72 if (tmp && tmp->slaying == op->slaying)
73 { /* same key both doors */
74 tmp->set_speed (0.1f);
75 tmp->speed_left = -0.2f;
76 }
77 }
78
79 if (op->other_arch)
80 {
81 tmp = arch_to_object (op->other_arch);
82 tmp->x = op->x;
83 tmp->y = op->y;
84 tmp->map = op->map;
85 tmp->level = op->level;
86 insert_ob_in_map (tmp, op->map, op, 0);
87 }
88
89 op->destroy ();
90 }
91
92 void
93 generate_monster (object *gen)
94 {
95 if (!gen->map)
96 return;
97
98 if (GENERATE_SPEED (gen) && rndm (0, GENERATE_SPEED (gen) - 1))
99 return;
100
101 object *op;
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 op = object_create_clone (op);
116
117 CLEAR_FLAG (op, FLAG_IS_A_TEMPLATE);
118 unflag_inv (op, FLAG_IS_A_TEMPLATE);
119 }
120 else if (gen->other_arch)
121 {
122 // ...or use other_arch
123 op = arch_to_object (gen->other_arch);
124 }
125 else
126 return;
127
128 op->expand_tail ();
129
130 int i = find_free_spot (op, gen->map, gen->x, gen->y, 1, 9);
131 if (i >= 0)
132 {
133 if (insert_ob_in_map_at (op, gen->map, gen, 0, gen->x + freearr_x[i], gen->y + freearr_y[i]))
134 {
135 if (rndm (0, 9))
136 generate_artifact (op, gen->map->difficulty);
137
138 if (op->has_random_items ())
139 create_treasure (op->randomitems, op, GT_APPLY, gen->map->difficulty);
140
141 return;
142 }
143 }
144
145 op->destroy ();
146 }
147
148 void
149 remove_force (object *op)
150 {
151 if (--op->duration > 0)
152 return;
153
154 if (op->env)
155 switch (op->subtype)
156 {
157 case FORCE_CONFUSION:
158 CLEAR_FLAG (op->env, FLAG_CONFUSED);
159 new_draw_info (NDI_UNIQUE, 0, op->env, "You regain your senses.\n");
160
161 default:
162 CLEAR_FLAG (op, FLAG_APPLIED);
163 change_abil (op->env, op);
164 op->env->update_stats ();
165 }
166
167 op->destroy ();
168 }
169
170 void
171 remove_blindness (object *op)
172 {
173 if (--op->stats.food > 0)
174 return;
175
176 CLEAR_FLAG (op, FLAG_APPLIED);
177
178 if (op->env)
179 {
180 change_abil (op->env, op);
181 op->env->update_stats ();
182 }
183
184 op->destroy ();
185 }
186
187 void
188 poison_more (object *op)
189 {
190 if (op->env == NULL || !QUERY_FLAG (op->env, FLAG_ALIVE) || op->env->stats.hp < 0)
191 {
192 op->destroy ();
193 return;
194 }
195
196 if (op->stats.food == 1)
197 {
198 /* need to unapply the object before update_stats is called, else fix_player
199 * will not do anything.
200 */
201 if (op->env->type == PLAYER)
202 {
203 CLEAR_FLAG (op, FLAG_APPLIED);
204 op->env->update_stats ();
205 new_draw_info (NDI_UNIQUE, 0, op->env, "You feel much better now.");
206 }
207
208 op->destroy ();
209 return;
210 }
211
212 if (op->env->type == PLAYER)
213 {
214 op->env->stats.food--;
215 new_draw_info (NDI_UNIQUE, 0, op->env, "You feel very sick...");
216 }
217
218 hit_player (op->env, op->stats.dam, op, AT_INTERNAL, 1);
219 }
220
221
222 void
223 move_gate (object *op)
224 { /* 1 = going down, 0 = going up */
225 object *tmp;
226
227 if (op->stats.wc < 0 || (int) op->stats.wc >= NUM_ANIMATIONS (op))
228 {
229 LOG (llevError, "Gate error: animation was %d, max=%d\n", op->stats.wc, NUM_ANIMATIONS (op));
230 op->stats.wc = 0;
231 }
232
233 /* We're going down */
234 if (op->value)
235 {
236 if (--op->stats.wc <= 0)
237 { /* Reached bottom, let's stop */
238 op->stats.wc = 0;
239 if (op->arch->speed)
240 op->value = 0;
241 else
242 op->set_speed (0);
243 }
244
245 if ((int) op->stats.wc < (NUM_ANIMATIONS (op) / 2 + 1))
246 {
247 op->move_block = 0;
248 CLEAR_FLAG (op, FLAG_BLOCKSVIEW);
249 update_all_los (op->map, op->x, op->y);
250 }
251
252 SET_ANIMATION (op, op->stats.wc);
253 update_object (op, UP_OBJ_CHANGE);
254 return;
255 }
256
257 /* We're going up */
258
259 /* First, lets see if we are already at the top */
260 if ((unsigned char) op->stats.wc == (NUM_ANIMATIONS (op) - 1))
261 {
262
263 /* Check to make sure that only non pickable and non rollable
264 * objects are above the gate. If so, we finish closing the gate,
265 * otherwise, we fall through to the code below which should lower
266 * the gate slightly.
267 */
268
269 for (tmp = op->above; tmp != NULL; tmp = tmp->above)
270 if (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || QUERY_FLAG (tmp, FLAG_ALIVE))
271 break;
272
273 if (tmp == NULL)
274 {
275 if (op->arch->speed)
276 op->value = 1;
277 else
278 op->set_speed (0);
279
280 return;
281 }
282 }
283
284 if (op->stats.food)
285 { /* The gate is going temporarily down */
286 if (--op->stats.wc <= 0)
287 { /* Gone all the way down? */
288 op->stats.food = 0; /* Then let's try again */
289 op->stats.wc = 0;
290 }
291 }
292 else
293 { /* The gate is still going up */
294 op->stats.wc++;
295
296 if ((int) op->stats.wc >= (NUM_ANIMATIONS (op)))
297 op->stats.wc = (signed char) NUM_ANIMATIONS (op) - 1;
298
299 /* If there is something on top of the gate, we try to roll it off.
300 * If a player/monster, we don't roll, we just hit them with damage
301 */
302 if ((int) op->stats.wc >= NUM_ANIMATIONS (op) / 2)
303 {
304 /* Halfway or further, check blocks */
305 /* First, get the top object on the square. */
306 for (tmp = op->above; tmp && tmp->above; tmp = tmp->above)
307 ;
308
309 if (tmp)
310 {
311 if (QUERY_FLAG (tmp, FLAG_ALIVE))
312 {
313 hit_player (tmp, random_roll (0, op->stats.dam, tmp, PREFER_LOW), op, AT_PHYSICAL, 1);
314
315 if (tmp->type == PLAYER)
316 new_draw_info_format (NDI_UNIQUE, 0, tmp, "You are crushed by the %s!", &op->name);
317 }
318 else
319 /* If the object is not alive, and the object either can
320 * be picked up or the object rolls, move the object
321 * off the gate.
322 */
323 if (!QUERY_FLAG (tmp, FLAG_ALIVE) && (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL)))
324 {
325 /* If it has speed, it should move itself, otherwise: */
326 int i = find_free_spot (tmp, op->map, op->x, op->y, 1, 9);
327
328 /* If there is a free spot, move the object someplace */
329 if (i != -1)
330 {
331 tmp->remove ();
332 tmp->x += freearr_x[i], tmp->y += freearr_y[i];
333 insert_ob_in_map (tmp, op->map, op, 0);
334 }
335 }
336 }
337
338 /* See if there is still anything blocking the gate */
339 for (tmp = op->above; tmp != NULL; tmp = tmp->above)
340 if (!QUERY_FLAG (tmp, FLAG_NO_PICK) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || QUERY_FLAG (tmp, FLAG_ALIVE))
341 break;
342
343 /* IF there is, start putting the gate down */
344 if (tmp)
345 {
346 op->stats.food = 1;
347 }
348 else
349 {
350 op->move_block = MOVE_ALL;
351 if (!op->arch->stats.ac)
352 SET_FLAG (op, FLAG_BLOCKSVIEW);
353 update_all_los (op->map, op->x, op->y);
354 }
355 } /* gate is halfway up */
356
357 SET_ANIMATION (op, op->stats.wc);
358 update_object (op, UP_OBJ_CHANGE);
359 } /* gate is going up */
360 }
361
362 /* hp : how long door is open/closed
363 * maxhp : initial value for hp
364 * sp : 1 = open, 0 = close
365 */
366 void
367 move_timed_gate (object *op)
368 {
369 int v = op->value;
370
371 if (op->stats.sp)
372 {
373 move_gate (op);
374 if (op->value != v) /* change direction ? */
375 op->stats.sp = 0;
376 return;
377 }
378 if (--op->stats.hp <= 0)
379 { /* keep gate down */
380 move_gate (op);
381 if (op->value != v)
382 op->set_speed (0);
383 }
384 }
385
386 /* slaying: name of the thing the detector is to look for
387 * speed: frequency of 'glances'
388 * connected: connected value of detector
389 * sp: 1 if detection sets buttons
390 * -1 if detection unsets buttons
391 */
392
393 void
394 move_detector (object *op)
395 {
396 object *tmp;
397 int last = op->value;
398 int detected;
399
400 detected = 0;
401
402 for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp != NULL && !detected; tmp = tmp->above)
403 {
404 object *tmp2;
405
406 if (op->stats.hp)
407 {
408 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
409 {
410 if (op->slaying && op->slaying == tmp->name)
411 detected = 1;
412
413 if (tmp2->type == FORCE && tmp2->slaying && tmp2->slaying == op->slaying)
414 detected = 1;
415 }
416 }
417
418 if (op->slaying && op->slaying == tmp->name)
419 detected = 1;
420 else if (tmp->type == SPECIAL_KEY && tmp->slaying == op->slaying)
421 detected = 1;
422 }
423
424 /* the detector sets the button if detection is found */
425 if (op->stats.sp == 1)
426 {
427 if (detected && last == 0)
428 {
429 op->value = 1;
430 push_button (op);
431 }
432 if (!detected && last == 1)
433 {
434 op->value = 0;
435 push_button (op);
436 }
437 }
438 else
439 { /* in this case, we unset buttons */
440 if (detected && last == 1)
441 {
442 op->value = 0;
443 push_button (op);
444 }
445 if (!detected && last == 0)
446 {
447 op->value = 1;
448 push_button (op);
449 }
450 }
451 }
452
453
454 void
455 animate_trigger (object *op)
456 {
457 if ((unsigned char) ++op->stats.wc >= NUM_ANIMATIONS (op))
458 {
459 op->stats.wc = 0;
460 check_trigger (op, NULL);
461 }
462 else
463 {
464 SET_ANIMATION (op, op->stats.wc);
465 update_object (op, UP_OBJ_FACE);
466 }
467 }
468
469 void
470 move_hole (object *op)
471 { /* 1 = opening, 0 = closing */
472 object *next, *tmp;
473
474 if (op->value)
475 { /* We're opening */
476 if (--op->stats.wc <= 0)
477 { /* Opened, let's stop */
478 op->stats.wc = 0;
479 op->set_speed (0);
480
481 /* Hard coding this makes sense for holes I suppose */
482 op->move_on = MOVE_WALK;
483 for (tmp = op->above; tmp != NULL; tmp = next)
484 {
485 next = tmp->above;
486 move_apply (op, tmp, tmp);
487 }
488 }
489
490 SET_ANIMATION (op, op->stats.wc);
491 update_object (op, UP_OBJ_FACE);
492 return;
493 }
494 /* We're closing */
495 op->move_on = 0;
496
497 op->stats.wc++;
498 if ((int) op->stats.wc >= NUM_ANIMATIONS (op))
499 op->stats.wc = NUM_ANIMATIONS (op) - 1;
500
501 SET_ANIMATION (op, op->stats.wc);
502 update_object (op, UP_OBJ_FACE);
503 if ((unsigned char) op->stats.wc == (NUM_ANIMATIONS (op) - 1))
504 op->set_speed (0); /* closed, let's stop */
505 }
506
507
508 /* stop_item() returns a pointer to the stopped object. The stopped object
509 * may or may not have been removed from maps or inventories. It will not
510 * have been merged with other items.
511 *
512 * This function assumes that only items on maps need special treatment.
513 *
514 * If the object can't be stopped, or it was destroyed while trying to stop
515 * it, NULL is returned.
516 *
517 * fix_stopped_item() should be used if the stopped item should be put on
518 * the map.
519 */
520 object *
521 stop_item (object *op)
522 {
523 if (op->map == NULL)
524 return op;
525
526 switch (op->type)
527 {
528 case THROWN_OBJ:
529 {
530 object *payload = op->inv;
531
532 if (payload == NULL)
533 return NULL;
534 payload->remove ();
535 op->destroy ();
536 return payload;
537 }
538
539 case ARROW:
540 if (op->has_active_speed ())
541 op = fix_stopped_arrow (op);
542 return op;
543
544 default:
545 return op;
546 }
547 }
548
549 /* fix_stopped_item() - put stopped item where stop_item() had found it.
550 * Inserts item into the old map, or merges it if it already is on the map.
551 *
552 * 'map' must be the value of op->map before stop_item() was called.
553 */
554 void
555 fix_stopped_item (object *op, maptile *map, object *originator)
556 {
557 if (map == NULL)
558 return;
559
560 if (QUERY_FLAG (op, FLAG_REMOVED))
561 insert_ob_in_map (op, map, originator, 0);
562 else if (op->type == ARROW)
563 merge_ob (op, NULL); /* only some arrows actually need this */
564 }
565
566 object *
567 fix_stopped_arrow (object *op)
568 {
569 if (rndm (0, 99) < op->stats.food)
570 {
571 /* Small chance of breaking */
572 op->destroy ();
573 return NULL;
574 }
575
576 op->set_speed (0);
577 op->direction = 0;
578 op->move_on = 0;
579 op->move_type = 0;
580 op->skill = 0; // really?
581
582 // restore original wc, dam, attacktype and slaying
583 op->stats.wc = op->stats.sp;
584 op->stats.dam = op->stats.hp;
585 op->attacktype = op->stats.grace;
586
587 if (op->spellarg)
588 {
589 op->slaying = op->spellarg;
590 free (op->spellarg);
591 op->spellarg = 0;
592 }
593 else
594 op->slaying = 0;
595
596 /* Reset these to zero, so that object::can_merge will work properly */
597 op->spellarg = NULL;
598 op->stats.sp = 0;
599 op->stats.hp = 0;
600 op->stats.grace = 0;
601 op->level = 0;
602 op->face = op->arch->face;
603 op->owner = NULL; /* So that stopped arrows will be saved */
604 update_object (op, UP_OBJ_CHANGE);
605 return op;
606 }
607
608 /* stop_arrow() - what to do when a non-living flying object
609 * has to stop. Sept 96 - I added in thrown object code in
610 * here too. -b.t.
611 *
612 * Returns a pointer to the stopped object (which will have been removed
613 * from maps or inventories), or NULL if was destroyed.
614 */
615 static void
616 stop_arrow (object *op)
617 {
618 if (INVOKE_OBJECT (STOP, op))
619 return;
620
621 if (op->inv)
622 {
623 object *payload = op->inv;
624
625 payload->remove ();
626 payload->owner = 0;
627 insert_ob_in_map (payload, op->map, payload, 0);
628 op->destroy ();
629 }
630 else
631 {
632 op = fix_stopped_arrow (op);
633
634 if (op)
635 merge_ob (op, 0);
636 }
637 }
638
639 /* Move an arrow along its course. op is the arrow or thrown object.
640 */
641 void
642 move_arrow (object *op)
643 {
644 object *tmp;
645 sint16 new_x, new_y;
646 int was_reflected, mflags;
647 maptile *m;
648
649 if (op->map == NULL)
650 {
651 LOG (llevError, "BUG: Arrow had no map.\n");
652 op->destroy ();
653 return;
654 }
655
656 /* we need to stop thrown objects at some point. Like here. */
657 if (op->type == THROWN_OBJ)
658 {
659 /* If the object that the THROWN_OBJ encapsulates disappears,
660 * we need to have this object go away also - otherwise, you get
661 * left over remnants on the map. Where this currently happens
662 * is if the player throws a bomb - the bomb explodes on its own,
663 * but this object sticks around. We could handle the cleanup in the
664 * bomb code, but there are potential other cases where that could happen,
665 * and it is easy enough to clean it up here.
666 */
667 if (op->inv == NULL)
668 {
669 op->destroy ();
670 return;
671 }
672
673 if (op->last_sp-- < 0)
674 {
675 stop_arrow (op);
676 return;
677 }
678 }
679
680 /* if the arrow is moving too slow.. stop it. 0.5 was chosen as lower
681 values look rediculous. */
682 if (op->speed < 0.5 && op->type == ARROW)
683 {
684 stop_arrow (op);
685 return;
686 }
687
688 /* Calculate target map square */
689 new_x = op->x + DIRX (op);
690 new_y = op->y + DIRY (op);
691 was_reflected = 0;
692
693 m = op->map;
694 mflags = get_map_flags (m, &m, new_x, new_y, &new_x, &new_y);
695
696 if (mflags & P_OUT_OF_MAP)
697 {
698 stop_arrow (op);
699 return;
700 }
701
702 /* only need to look for living creatures if this flag is set */
703 if (mflags & P_IS_ALIVE)
704 {
705 for (tmp = GET_MAP_OB (m, new_x, new_y); tmp != NULL; tmp = tmp->above)
706 if (QUERY_FLAG (tmp, FLAG_ALIVE))
707 break;
708
709 /* Not really fair, but don't let monsters hit themselves with
710 * their own arrow - this can be because they fire it then
711 * move into it.
712 */
713 if (tmp && tmp != op->owner)
714 {
715 /* Found living object, but it is reflecting the missile. Update
716 * as below. (Note that for living creatures there is a small
717 * chance that reflect_missile fails.)
718 */
719 if (QUERY_FLAG (tmp, FLAG_REFL_MISSILE) && (rndm (0, 99)) < (90 - op->level / 10))
720 {
721 int number = op->face;
722
723 op->direction = absdir (op->direction + 4);
724 update_turn_face (op);
725 was_reflected = 1; /* skip normal movement calculations */
726 }
727 else
728 {
729 /* Attack the object. */
730 op = hit_with_arrow (op, tmp);
731
732 if (!op)
733 return;
734 }
735 } /* if this is not hitting its owner */
736 } /* if there is something alive on this space */
737
738 if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y)))
739 {
740 int retry = 0;
741
742 /* if the object doesn't reflect, stop the arrow from moving
743 * note that this code will now catch cases where a monster is
744 * on a wall but has reflecting - the arrow won't reflect.
745 * Mapmakers shouldn't put monsters on top of wall in the first
746 * place, so I don't consider that a problem.
747 */
748 if (!QUERY_FLAG (op, FLAG_REFLECTING) || !(rndm (0, 19)))
749 {
750 stop_arrow (op);
751 return;
752 }
753 else
754 {
755 /* If one of the major directions (n,s,e,w), just reverse it */
756 if (op->direction & 1)
757 {
758 op->direction = absdir (op->direction + 4);
759 retry = 1;
760 }
761 /* There were two blocks with identical code -
762 * use this retry here to make this one block
763 * that did the same thing.
764 */
765 while (retry < 2)
766 {
767 int left, right, mflags;
768 maptile *m1;
769 sint16 x1, y1;
770
771 retry++;
772
773 /* Need to check for P_OUT_OF_MAP: if the arrow is tavelling
774 * over a corner in a tiled map, it is possible that
775 * op->direction is within an adjacent map but either
776 * op->direction-1 or op->direction+1 does not exist.
777 */
778 mflags = get_map_flags (op->map, &m1, op->x + freearr_x[absdir (op->direction - 1)],
779 op->y + freearr_y[absdir (op->direction - 1)], &x1, &y1);
780 left = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, (GET_MAP_MOVE_BLOCK (m1, x1, y1)));
781
782 mflags = get_map_flags (op->map, &m1, op->x + freearr_x[absdir (op->direction + 1)],
783 op->y + freearr_y[absdir (op->direction + 1)], &x1, &y1);
784 right = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, (GET_MAP_MOVE_BLOCK (m1, x1, y1)));
785
786 if (left == right)
787 op->direction = absdir (op->direction + 4);
788 else if (left)
789 op->direction = absdir (op->direction + 2);
790 else if (right)
791 op->direction = absdir (op->direction - 2);
792
793 mflags = get_map_flags (op->map, &m1, op->x + DIRX (op), op->y + DIRY (op), &x1, &y1);
794
795 /* If this space is not out of the map and not blocked, valid space -
796 * don't need to retry again.
797 */
798 if (!(mflags & P_OUT_OF_MAP) && !OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m1, x1, y1)))
799 break;
800
801 }
802 /* Couldn't find a direction to move the arrow to - just
803 * top it from moving.
804 */
805 if (retry == 2)
806 {
807 stop_arrow (op);
808 return;
809 }
810 /* update object image for new facing */
811 /* many thrown objects *don't* have more than one face */
812 if (GET_ANIM_ID (op))
813 SET_ANIMATION (op, op->direction);
814 } /* object is reflected */
815 } /* object ran into a wall */
816
817 /* Move the arrow. */
818 op->remove ();
819 op->x = new_x;
820 op->y = new_y;
821
822 /* decrease the speed as it flies. 0.05 means a standard bow will shoot
823 * about 17 squares. Tune as needed.
824 */
825 op->speed -= 0.05;
826 insert_ob_in_map (op, m, op, 0);
827 }
828
829 /* This routine doesnt seem to work for "inanimate" objects that
830 * are being carried, ie a held torch leaps from your hands!.
831 * Modified this routine to allow held objects. b.t. */
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 == NULL)
839 {
840 LOG (llevError, "Change object (%s) without other_arch error.\n", &op->name);
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 else
850 op->stats.food = 1; /* so 1 other_arch is made */
851 }
852
853 object *pl = op->in_player ();
854 object *env = op->env;
855
856 op->remove ();
857 for (i = 0; i < NROFNEWOBJS (op); i++)
858 {
859 object *tmp = arch_to_object (op->other_arch);
860
861 if (op->type == LAMP)
862 tmp->stats.food = op->stats.food - 1;
863
864 tmp->stats.hp = op->stats.hp; /* The only variable it keeps. */
865 if (env)
866 {
867 tmp->x = env->x, tmp->y = env->y;
868 tmp = insert_ob_in_ob (tmp, env);
869
870 /* If this object is the players inventory, we need to tell the
871 * client of the change. Insert_ob_in_map takes care of the
872 * updating the client, so we don't need to do that below.
873 */
874 if (pl)
875 {
876 esrv_del_item (pl->contr, op->count);
877 esrv_send_item (pl, tmp);
878 }
879 }
880 else
881 {
882 j = find_first_free_spot (tmp, op->map, op->x, op->y);
883 if (j == -1) /* No free spot */
884 tmp->destroy ();
885 else
886 {
887 tmp->x = op->x + freearr_x[j], tmp->y = op->y + freearr_y[j];
888 insert_ob_in_map (tmp, op->map, op, 0);
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
1173 void
1174 move_creator (object *creator)
1175 {
1176 object *new_ob;
1177
1178 if (!QUERY_FLAG (creator, FLAG_LIFESAVE) && --creator->stats.hp < 0)
1179 {
1180 creator->stats.hp = -1;
1181 return;
1182 }
1183
1184 if (creator->inv != NULL)
1185 {
1186 object *ob;
1187 int i;
1188 object *ob_to_copy;
1189
1190 /* select random object from inventory to copy */
1191 ob_to_copy = creator->inv;
1192 for (ob = creator->inv->below, i = 1; ob != NULL; ob = ob->below, i++)
1193 {
1194 if (rndm (0, i) == 0)
1195 {
1196 ob_to_copy = ob;
1197 }
1198 }
1199 new_ob = object_create_clone (ob_to_copy);
1200 CLEAR_FLAG (new_ob, FLAG_IS_A_TEMPLATE);
1201 unflag_inv (new_ob, FLAG_IS_A_TEMPLATE);
1202 }
1203 else
1204 {
1205 if (creator->other_arch == NULL)
1206 {
1207 LOG (llevError, "move_creator: Creator doesn't have other arch set: %s (%s, %d, %d)\n",
1208 &creator->name, &creator->map->path, creator->x, creator->y);
1209 return;
1210 }
1211
1212 new_ob = object_create_arch (creator->other_arch);
1213 fix_generated_item (new_ob, creator, 0, 0, GT_MINIMAL);
1214 }
1215
1216 /* Make sure this multipart object fits */
1217 if (new_ob->arch->more && ob_blocked (new_ob, creator->map, creator->x, creator->y))
1218 {
1219 new_ob->destroy ();
1220 return;
1221 }
1222
1223 insert_ob_in_map_at (new_ob, creator->map, creator, 0, creator->x, creator->y);
1224 if (QUERY_FLAG (new_ob, FLAG_FREED))
1225 return;
1226
1227 if (creator->slaying)
1228 {
1229 new_ob->name = new_ob->title = creator->slaying;
1230 }
1231 }
1232
1233 /* move_marker --peterm@soda.csua.berkeley.edu
1234 when moved, a marker will search for a player sitting above
1235 it, and insert an invisible, weightless force into him
1236 with a specific code as the slaying field.
1237 At that time, it writes the contents of its own message
1238 field to the player. The marker will decrement hp to
1239 0 and then delete itself every time it grants a mark.
1240 unless hp was zero to start with, in which case it is infinite.*/
1241 void
1242 move_marker (object *op)
1243 {
1244 if (object *tmp = op->ms ().player ())
1245 {
1246 object *tmp2;
1247
1248 /* remove an old force with a slaying field == op->name */
1249 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
1250 if (tmp2->type == FORCE && tmp2->slaying && tmp2->slaying == op->name)
1251 {
1252 tmp2->destroy ();
1253 break;
1254 }
1255
1256 /* cycle through his inventory to look for the MARK we want to
1257 * place
1258 */
1259 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
1260 if (tmp2->type == FORCE && tmp2->slaying && tmp2->slaying == op->slaying)
1261 break;
1262
1263 /* if we didn't find our own MARK */
1264 if (!tmp2)
1265 {
1266 object *force = get_archetype (FORCE_NAME);
1267
1268 if (op->stats.food)
1269 {
1270 force->set_speed (0.01);
1271 force->speed_left = -op->stats.food;
1272 }
1273 else
1274 force->set_speed (0);
1275
1276 /* put in the lock code */
1277 force->slaying = op->slaying;
1278
1279 if (op->lore)
1280 force->lore = op->lore;
1281
1282 insert_ob_in_ob (force, tmp);
1283 if (op->msg)
1284 new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, tmp, op->msg);
1285
1286 if (op->stats.hp > 0)
1287 {
1288 op->stats.hp--;
1289 if (op->stats.hp == 0)
1290 {
1291 /* marker expires--granted mark number limit */
1292 op->destroy ();
1293 return;
1294 }
1295 }
1296 }
1297 }
1298 }
1299
1300 void
1301 process_object (object *op)
1302 {
1303 if (expect_false (QUERY_FLAG (op, FLAG_IS_A_TEMPLATE)))
1304 return;
1305
1306 if (expect_false (INVOKE_OBJECT (TICK, op)))
1307 return;
1308
1309 if (QUERY_FLAG (op, FLAG_MONSTER))
1310 if (move_monster (op) || QUERY_FLAG (op, FLAG_FREED))
1311 return;
1312
1313 if (QUERY_FLAG (op, FLAG_ANIMATE) && op->anim_speed == 0)
1314 {
1315 animate_object (op, op->contr ? op->facing : op->direction);
1316
1317 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1318 make_sure_seen (op);
1319 }
1320
1321 if (expect_false (
1322 op->flag [FLAG_GENERATOR]
1323 || op->flag [FLAG_CHANGING]
1324 || op->flag [FLAG_IS_USED_UP]
1325 ))
1326 {
1327 if (QUERY_FLAG (op, FLAG_CHANGING) && !op->state)
1328 {
1329 change_object (op);
1330 return;
1331 }
1332
1333 if (QUERY_FLAG (op, FLAG_GENERATOR) && !QUERY_FLAG (op, FLAG_FRIENDLY))
1334 generate_monster (op);
1335
1336 if (QUERY_FLAG (op, FLAG_IS_USED_UP) && --op->stats.food <= 0)
1337 {
1338 if (QUERY_FLAG (op, FLAG_APPLIED))
1339 remove_force (op);
1340 else
1341 {
1342 /* If necessary, delete the item from the players inventory */
1343 object *pl = op->in_player ();
1344
1345 if (pl)
1346 esrv_del_item (pl->contr, op->count);
1347
1348 op->remove ();
1349
1350 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1351 make_sure_not_seen (op);
1352
1353 op->destroy ();
1354 }
1355
1356 return;
1357 }
1358 }
1359
1360 switch (op->type)
1361 {
1362 case SPELL_EFFECT:
1363 move_spell_effect (op);
1364 break;
1365
1366 case ROD:
1367 case HORN:
1368 regenerate_rod (op);
1369 break;
1370
1371 case FORCE:
1372 case POTION_EFFECT:
1373 remove_force (op);
1374 break;
1375
1376 case BLINDNESS:
1377 remove_blindness (op);
1378 break;
1379
1380 case POISONING:
1381 poison_more (op);
1382 break;
1383
1384 case DISEASE:
1385 move_disease (op);
1386 break;
1387
1388 case SYMPTOM:
1389 move_symptom (op);
1390 break;
1391
1392 case THROWN_OBJ:
1393 case ARROW:
1394 move_arrow (op);
1395 break;
1396
1397 case DOOR:
1398 remove_door (op);
1399 break;
1400
1401 case LOCKED_DOOR:
1402 remove_door2 (op);
1403 break;
1404
1405 case TELEPORTER:
1406 move_teleporter (op);
1407 break;
1408
1409 case GOLEM:
1410 move_golem (op);
1411 break;
1412
1413 case EARTHWALL:
1414 hit_player (op, 2, op, AT_PHYSICAL, 1);
1415 break;
1416
1417 case FIREWALL:
1418 move_firewall (op);
1419 if (op->stats.maxsp)
1420 animate_turning (op);
1421 break;
1422
1423 case MOOD_FLOOR:
1424 do_mood_floor (op);
1425 break;
1426
1427 case GATE:
1428 move_gate (op);
1429 break;
1430
1431 case TIMED_GATE:
1432 move_timed_gate (op);
1433 break;
1434
1435 case TRIGGER:
1436 case TRIGGER_BUTTON:
1437 case TRIGGER_PEDESTAL:
1438 case TRIGGER_ALTAR:
1439 animate_trigger (op);
1440 break;
1441
1442 case DETECTOR:
1443 move_detector (op);
1444
1445 case DIRECTOR:
1446 if (op->stats.maxsp)
1447 animate_turning (op);
1448 break;
1449
1450 case HOLE:
1451 move_hole (op);
1452 break;
1453
1454 case DEEP_SWAMP:
1455 move_deep_swamp (op);
1456 break;
1457
1458 case RUNE:
1459 case TRAP:
1460 move_rune (op);
1461 break;
1462
1463 case PLAYERMOVER:
1464 move_player_mover (op);
1465 break;
1466
1467 case CREATOR:
1468 move_creator (op);
1469 break;
1470
1471 case MARKER:
1472 move_marker (op);
1473 break;
1474
1475 case PLAYER_CHANGER:
1476 move_player_changer (op);
1477 break;
1478
1479 case PEACEMAKER:
1480 move_peacemaker (op);
1481 break;
1482
1483 case PLAYER:
1484 // players have their own speed-management, so undo the --speed_left
1485 ++op->speed_left;
1486 break;
1487 }
1488 }
1489