ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.64
Committed: Thu Aug 23 15:20:34 2007 UTC (16 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.63: +0 -1 lines
Log Message:
do not regenerate hp and sp when food is zero, this is ridiculous. the logic seems rather convoluted and broken, too

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