ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/time.C
Revision: 1.128
Committed: Wed Dec 5 19:03:27 2018 UTC (5 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.127: +3 -3 lines
Log Message:
some bugfixes

File Contents

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