ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.60
Committed: Thu Jul 5 08:10:30 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.59: +6 -33 lines
Log Message:
- unbundle marker code into force_find/force_add
- use completely different (more efficient) speed logic
- make those methods available to perl
- to avoid numerical errors use an exact fp number for MIN_ACTIVE_SPEED.

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
102 if (QUERY_FLAG (gen, FLAG_CONTENT_ON_GEN))
103 {
104 // either copy one item from the inventory...
105 if (!gen->inv)
106 return;
107
108 // first select one item from the inventory
109 int index = 0;
110 for (object *tmp = gen->inv; tmp; tmp = tmp->below)
111 if (!rndm (++index))
112 op = tmp;
113
114 op = object_create_clone (op);
115
116 CLEAR_FLAG (op, FLAG_IS_A_TEMPLATE);
117 unflag_inv (op, FLAG_IS_A_TEMPLATE);
118 }
119 else if (gen->other_arch)
120 {
121 // ...or use other_arch
122 op = arch_to_object (gen->other_arch);
123 }
124 else
125 return;
126
127 op->expand_tail ();
128
129 int i = find_free_spot (op, gen->map, gen->x, gen->y, 1, 9);
130 if (i >= 0)
131 {
132 if (insert_ob_in_map_at (op, gen->map, gen, 0, gen->x + freearr_x[i], gen->y + freearr_y[i]))
133 {
134 if (rndm (0, 9))
135 generate_artifact (op, gen->map->difficulty);
136
137 if (op->has_random_items ())
138 create_treasure (op->randomitems, op, GT_APPLY, gen->map->difficulty);
139
140 return;
141 }
142 }
143
144 op->destroy ();
145 }
146
147 void
148 remove_force (object *op)
149 {
150 fprintf (stderr, "duration %s %d\n", &op->name, op->duration);//D
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 /* remove an old force with a slaying field == op->name */
1247 if (object *force = tmp->force_find (op->name))
1248 force->destroy ();
1249
1250 if (!tmp->force_find (op->slaying))
1251 {
1252 tmp->force_add (op->slaying, op->stats.food);
1253
1254 if (op->msg)
1255 new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, tmp, op->msg);
1256
1257 if (op->stats.hp > 0)
1258 {
1259 op->stats.hp--;
1260
1261 if (op->stats.hp == 0)
1262 {
1263 /* marker expires--granted mark number limit */
1264 op->destroy ();
1265 return;
1266 }
1267 }
1268 }
1269 }
1270 }
1271
1272 void
1273 process_object (object *op)
1274 {
1275 if (expect_false (QUERY_FLAG (op, FLAG_IS_A_TEMPLATE)))
1276 return;
1277
1278 if (expect_false (INVOKE_OBJECT (TICK, op)))
1279 return;
1280
1281 if (QUERY_FLAG (op, FLAG_MONSTER))
1282 if (move_monster (op) || QUERY_FLAG (op, FLAG_FREED))
1283 return;
1284
1285 if (QUERY_FLAG (op, FLAG_ANIMATE) && op->anim_speed == 0)
1286 {
1287 animate_object (op, op->contr ? op->facing : op->direction);
1288
1289 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1290 make_sure_seen (op);
1291 }
1292
1293 if (expect_false (
1294 op->flag [FLAG_GENERATOR]
1295 || op->flag [FLAG_CHANGING]
1296 || op->flag [FLAG_IS_USED_UP]
1297 ))
1298 {
1299 if (QUERY_FLAG (op, FLAG_CHANGING) && !op->state)
1300 {
1301 change_object (op);
1302 return;
1303 }
1304
1305 if (QUERY_FLAG (op, FLAG_GENERATOR) && !QUERY_FLAG (op, FLAG_FRIENDLY))
1306 generate_monster (op);
1307
1308 if (QUERY_FLAG (op, FLAG_IS_USED_UP) && --op->stats.food <= 0)
1309 {
1310 if (QUERY_FLAG (op, FLAG_APPLIED))
1311 remove_force (op);
1312 else
1313 {
1314 /* If necessary, delete the item from the players inventory */
1315 object *pl = op->in_player ();
1316
1317 if (pl)
1318 esrv_del_item (pl->contr, op->count);
1319
1320 op->remove ();
1321
1322 if (QUERY_FLAG (op, FLAG_SEE_ANYWHERE))
1323 make_sure_not_seen (op);
1324
1325 op->destroy ();
1326 }
1327
1328 return;
1329 }
1330 }
1331
1332 switch (op->type)
1333 {
1334 case SPELL_EFFECT:
1335 move_spell_effect (op);
1336 break;
1337
1338 case ROD:
1339 case HORN:
1340 regenerate_rod (op);
1341 break;
1342
1343 case FORCE:
1344 case POTION_EFFECT:
1345 remove_force (op);
1346 break;
1347
1348 case BLINDNESS:
1349 remove_blindness (op);
1350 break;
1351
1352 case POISONING:
1353 poison_more (op);
1354 break;
1355
1356 case DISEASE:
1357 move_disease (op);
1358 break;
1359
1360 case SYMPTOM:
1361 move_symptom (op);
1362 break;
1363
1364 case THROWN_OBJ:
1365 case ARROW:
1366 move_arrow (op);
1367 break;
1368
1369 case DOOR:
1370 remove_door (op);
1371 break;
1372
1373 case LOCKED_DOOR:
1374 remove_door2 (op);
1375 break;
1376
1377 case TELEPORTER:
1378 move_teleporter (op);
1379 break;
1380
1381 case GOLEM:
1382 move_golem (op);
1383 break;
1384
1385 case EARTHWALL:
1386 hit_player (op, 2, op, AT_PHYSICAL, 1);
1387 break;
1388
1389 case FIREWALL:
1390 move_firewall (op);
1391 if (op->stats.maxsp)
1392 animate_turning (op);
1393 break;
1394
1395 case MOOD_FLOOR:
1396 do_mood_floor (op);
1397 break;
1398
1399 case GATE:
1400 move_gate (op);
1401 break;
1402
1403 case TIMED_GATE:
1404 move_timed_gate (op);
1405 break;
1406
1407 case TRIGGER:
1408 case TRIGGER_BUTTON:
1409 case TRIGGER_PEDESTAL:
1410 case TRIGGER_ALTAR:
1411 animate_trigger (op);
1412 break;
1413
1414 case DETECTOR:
1415 move_detector (op);
1416
1417 case DIRECTOR:
1418 if (op->stats.maxsp)
1419 animate_turning (op);
1420 break;
1421
1422 case HOLE:
1423 move_hole (op);
1424 break;
1425
1426 case DEEP_SWAMP:
1427 move_deep_swamp (op);
1428 break;
1429
1430 case RUNE:
1431 case TRAP:
1432 move_rune (op);
1433 break;
1434
1435 case PLAYERMOVER:
1436 move_player_mover (op);
1437 break;
1438
1439 case CREATOR:
1440 move_creator (op);
1441 break;
1442
1443 case MARKER:
1444 move_marker (op);
1445 break;
1446
1447 case PLAYER_CHANGER:
1448 move_player_changer (op);
1449 break;
1450
1451 case PEACEMAKER:
1452 move_peacemaker (op);
1453 break;
1454
1455 case PLAYER:
1456 // players have their own speed-management, so undo the --speed_left
1457 ++op->speed_left;
1458 break;
1459 }
1460 }
1461