1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
5 * Holger Schemel *
6 * Detmolder Strasse 189 *
7 * 33604 Bielefeld *
8 * Germany *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
11 * game.c *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
29
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
63
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
69
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y) \
83 DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH 0
95 #define DF_DIG 1
96 #define DF_SNAP 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
100 #define MP_MOVING 1
101 #define MP_ACTION 2
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* game panel display and control definitions */
124 #define GAME_PANEL_LEVEL_NUMBER 0
125 #define GAME_PANEL_GEMS 1
126 #define GAME_PANEL_INVENTORY_COUNT 2
127 #define GAME_PANEL_INVENTORY_FIRST_1 3
128 #define GAME_PANEL_INVENTORY_FIRST_2 4
129 #define GAME_PANEL_INVENTORY_FIRST_3 5
130 #define GAME_PANEL_INVENTORY_FIRST_4 6
131 #define GAME_PANEL_INVENTORY_FIRST_5 7
132 #define GAME_PANEL_INVENTORY_FIRST_6 8
133 #define GAME_PANEL_INVENTORY_FIRST_7 9
134 #define GAME_PANEL_INVENTORY_FIRST_8 10
135 #define GAME_PANEL_INVENTORY_LAST_1 11
136 #define GAME_PANEL_INVENTORY_LAST_2 12
137 #define GAME_PANEL_INVENTORY_LAST_3 13
138 #define GAME_PANEL_INVENTORY_LAST_4 14
139 #define GAME_PANEL_INVENTORY_LAST_5 15
140 #define GAME_PANEL_INVENTORY_LAST_6 16
141 #define GAME_PANEL_INVENTORY_LAST_7 17
142 #define GAME_PANEL_INVENTORY_LAST_8 18
143 #define GAME_PANEL_KEY_1 19
144 #define GAME_PANEL_KEY_2 20
145 #define GAME_PANEL_KEY_3 21
146 #define GAME_PANEL_KEY_4 22
147 #define GAME_PANEL_KEY_5 23
148 #define GAME_PANEL_KEY_6 24
149 #define GAME_PANEL_KEY_7 25
150 #define GAME_PANEL_KEY_8 26
151 #define GAME_PANEL_KEY_WHITE 27
152 #define GAME_PANEL_KEY_WHITE_COUNT 28
153 #define GAME_PANEL_SCORE 29
154 #define GAME_PANEL_HIGHSCORE 30
155 #define GAME_PANEL_TIME 31
156 #define GAME_PANEL_TIME_HH 32
157 #define GAME_PANEL_TIME_MM 33
158 #define GAME_PANEL_TIME_SS 34
159 #define GAME_PANEL_FRAME 35
160 #define GAME_PANEL_SHIELD_NORMAL 36
161 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
162 #define GAME_PANEL_SHIELD_DEADLY 38
163 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
164 #define GAME_PANEL_EXIT 40
165 #define GAME_PANEL_EMC_MAGIC_BALL 41
166 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
167 #define GAME_PANEL_LIGHT_SWITCH 43
168 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
169 #define GAME_PANEL_TIMEGATE_SWITCH 45
170 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
171 #define GAME_PANEL_SWITCHGATE_SWITCH 47
172 #define GAME_PANEL_EMC_LENSES 48
173 #define GAME_PANEL_EMC_LENSES_TIME 49
174 #define GAME_PANEL_EMC_MAGNIFIER 50
175 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
176 #define GAME_PANEL_BALLOON_SWITCH 52
177 #define GAME_PANEL_DYNABOMB_NUMBER 53
178 #define GAME_PANEL_DYNABOMB_SIZE 54
179 #define GAME_PANEL_DYNABOMB_POWER 55
180 #define GAME_PANEL_PENGUINS 56
181 #define GAME_PANEL_SOKOBAN_OBJECTS 57
182 #define GAME_PANEL_SOKOBAN_FIELDS 58
183 #define GAME_PANEL_ROBOT_WHEEL 59
184 #define GAME_PANEL_CONVEYOR_BELT_1 60
185 #define GAME_PANEL_CONVEYOR_BELT_2 61
186 #define GAME_PANEL_CONVEYOR_BELT_3 62
187 #define GAME_PANEL_CONVEYOR_BELT_4 63
188 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
189 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
190 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
191 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
192 #define GAME_PANEL_MAGIC_WALL 68
193 #define GAME_PANEL_MAGIC_WALL_TIME 69
194 #define GAME_PANEL_GRAVITY_STATE 70
195 #define GAME_PANEL_GRAPHIC_1 71
196 #define GAME_PANEL_GRAPHIC_2 72
197 #define GAME_PANEL_GRAPHIC_3 73
198 #define GAME_PANEL_GRAPHIC_4 74
199 #define GAME_PANEL_GRAPHIC_5 75
200 #define GAME_PANEL_GRAPHIC_6 76
201 #define GAME_PANEL_GRAPHIC_7 77
202 #define GAME_PANEL_GRAPHIC_8 78
203 #define GAME_PANEL_ELEMENT_1 79
204 #define GAME_PANEL_ELEMENT_2 80
205 #define GAME_PANEL_ELEMENT_3 81
206 #define GAME_PANEL_ELEMENT_4 82
207 #define GAME_PANEL_ELEMENT_5 83
208 #define GAME_PANEL_ELEMENT_6 84
209 #define GAME_PANEL_ELEMENT_7 85
210 #define GAME_PANEL_ELEMENT_8 86
211 #define GAME_PANEL_ELEMENT_COUNT_1 87
212 #define GAME_PANEL_ELEMENT_COUNT_2 88
213 #define GAME_PANEL_ELEMENT_COUNT_3 89
214 #define GAME_PANEL_ELEMENT_COUNT_4 90
215 #define GAME_PANEL_ELEMENT_COUNT_5 91
216 #define GAME_PANEL_ELEMENT_COUNT_6 92
217 #define GAME_PANEL_ELEMENT_COUNT_7 93
218 #define GAME_PANEL_ELEMENT_COUNT_8 94
219 #define GAME_PANEL_CE_SCORE_1 95
220 #define GAME_PANEL_CE_SCORE_2 96
221 #define GAME_PANEL_CE_SCORE_3 97
222 #define GAME_PANEL_CE_SCORE_4 98
223 #define GAME_PANEL_CE_SCORE_5 99
224 #define GAME_PANEL_CE_SCORE_6 100
225 #define GAME_PANEL_CE_SCORE_7 101
226 #define GAME_PANEL_CE_SCORE_8 102
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
235 #define GAME_PANEL_PLAYER_NAME 111
236 #define GAME_PANEL_LEVEL_NAME 112
237 #define GAME_PANEL_LEVEL_AUTHOR 113
238
239 #define NUM_GAME_PANEL_CONTROLS 114
240
241 struct GamePanelOrderInfo
242 {
243 int nr;
244 int sort_priority;
245 };
246
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
248
249 struct GamePanelControlInfo
250 {
251 int nr;
252
253 struct TextPosInfo *pos;
254 int type;
255
256 int value, last_value;
257 int frame, last_frame;
258 int gfx_frame;
259 int gfx_random;
260 };
261
262 static struct GamePanelControlInfo game_panel_controls[] =
263 {
264 {
265 GAME_PANEL_LEVEL_NUMBER,
266 &game.panel.level_number,
267 TYPE_INTEGER,
268 },
269 {
270 GAME_PANEL_GEMS,
271 &game.panel.gems,
272 TYPE_INTEGER,
273 },
274 {
275 GAME_PANEL_INVENTORY_COUNT,
276 &game.panel.inventory_count,
277 TYPE_INTEGER,
278 },
279 {
280 GAME_PANEL_INVENTORY_FIRST_1,
281 &game.panel.inventory_first[0],
282 TYPE_ELEMENT,
283 },
284 {
285 GAME_PANEL_INVENTORY_FIRST_2,
286 &game.panel.inventory_first[1],
287 TYPE_ELEMENT,
288 },
289 {
290 GAME_PANEL_INVENTORY_FIRST_3,
291 &game.panel.inventory_first[2],
292 TYPE_ELEMENT,
293 },
294 {
295 GAME_PANEL_INVENTORY_FIRST_4,
296 &game.panel.inventory_first[3],
297 TYPE_ELEMENT,
298 },
299 {
300 GAME_PANEL_INVENTORY_FIRST_5,
301 &game.panel.inventory_first[4],
302 TYPE_ELEMENT,
303 },
304 {
305 GAME_PANEL_INVENTORY_FIRST_6,
306 &game.panel.inventory_first[5],
307 TYPE_ELEMENT,
308 },
309 {
310 GAME_PANEL_INVENTORY_FIRST_7,
311 &game.panel.inventory_first[6],
312 TYPE_ELEMENT,
313 },
314 {
315 GAME_PANEL_INVENTORY_FIRST_8,
316 &game.panel.inventory_first[7],
317 TYPE_ELEMENT,
318 },
319 {
320 GAME_PANEL_INVENTORY_LAST_1,
321 &game.panel.inventory_last[0],
322 TYPE_ELEMENT,
323 },
324 {
325 GAME_PANEL_INVENTORY_LAST_2,
326 &game.panel.inventory_last[1],
327 TYPE_ELEMENT,
328 },
329 {
330 GAME_PANEL_INVENTORY_LAST_3,
331 &game.panel.inventory_last[2],
332 TYPE_ELEMENT,
333 },
334 {
335 GAME_PANEL_INVENTORY_LAST_4,
336 &game.panel.inventory_last[3],
337 TYPE_ELEMENT,
338 },
339 {
340 GAME_PANEL_INVENTORY_LAST_5,
341 &game.panel.inventory_last[4],
342 TYPE_ELEMENT,
343 },
344 {
345 GAME_PANEL_INVENTORY_LAST_6,
346 &game.panel.inventory_last[5],
347 TYPE_ELEMENT,
348 },
349 {
350 GAME_PANEL_INVENTORY_LAST_7,
351 &game.panel.inventory_last[6],
352 TYPE_ELEMENT,
353 },
354 {
355 GAME_PANEL_INVENTORY_LAST_8,
356 &game.panel.inventory_last[7],
357 TYPE_ELEMENT,
358 },
359 {
360 GAME_PANEL_KEY_1,
361 &game.panel.key[0],
362 TYPE_ELEMENT,
363 },
364 {
365 GAME_PANEL_KEY_2,
366 &game.panel.key[1],
367 TYPE_ELEMENT,
368 },
369 {
370 GAME_PANEL_KEY_3,
371 &game.panel.key[2],
372 TYPE_ELEMENT,
373 },
374 {
375 GAME_PANEL_KEY_4,
376 &game.panel.key[3],
377 TYPE_ELEMENT,
378 },
379 {
380 GAME_PANEL_KEY_5,
381 &game.panel.key[4],
382 TYPE_ELEMENT,
383 },
384 {
385 GAME_PANEL_KEY_6,
386 &game.panel.key[5],
387 TYPE_ELEMENT,
388 },
389 {
390 GAME_PANEL_KEY_7,
391 &game.panel.key[6],
392 TYPE_ELEMENT,
393 },
394 {
395 GAME_PANEL_KEY_8,
396 &game.panel.key[7],
397 TYPE_ELEMENT,
398 },
399 {
400 GAME_PANEL_KEY_WHITE,
401 &game.panel.key_white,
402 TYPE_ELEMENT,
403 },
404 {
405 GAME_PANEL_KEY_WHITE_COUNT,
406 &game.panel.key_white_count,
407 TYPE_INTEGER,
408 },
409 {
410 GAME_PANEL_SCORE,
411 &game.panel.score,
412 TYPE_INTEGER,
413 },
414 {
415 GAME_PANEL_HIGHSCORE,
416 &game.panel.highscore,
417 TYPE_INTEGER,
418 },
419 {
420 GAME_PANEL_TIME,
421 &game.panel.time,
422 TYPE_INTEGER,
423 },
424 {
425 GAME_PANEL_TIME_HH,
426 &game.panel.time_hh,
427 TYPE_INTEGER,
428 },
429 {
430 GAME_PANEL_TIME_MM,
431 &game.panel.time_mm,
432 TYPE_INTEGER,
433 },
434 {
435 GAME_PANEL_TIME_SS,
436 &game.panel.time_ss,
437 TYPE_INTEGER,
438 },
439 {
440 GAME_PANEL_FRAME,
441 &game.panel.frame,
442 TYPE_INTEGER,
443 },
444 {
445 GAME_PANEL_SHIELD_NORMAL,
446 &game.panel.shield_normal,
447 TYPE_ELEMENT,
448 },
449 {
450 GAME_PANEL_SHIELD_NORMAL_TIME,
451 &game.panel.shield_normal_time,
452 TYPE_INTEGER,
453 },
454 {
455 GAME_PANEL_SHIELD_DEADLY,
456 &game.panel.shield_deadly,
457 TYPE_ELEMENT,
458 },
459 {
460 GAME_PANEL_SHIELD_DEADLY_TIME,
461 &game.panel.shield_deadly_time,
462 TYPE_INTEGER,
463 },
464 {
465 GAME_PANEL_EXIT,
466 &game.panel.exit,
467 TYPE_ELEMENT,
468 },
469 {
470 GAME_PANEL_EMC_MAGIC_BALL,
471 &game.panel.emc_magic_ball,
472 TYPE_ELEMENT,
473 },
474 {
475 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
476 &game.panel.emc_magic_ball_switch,
477 TYPE_ELEMENT,
478 },
479 {
480 GAME_PANEL_LIGHT_SWITCH,
481 &game.panel.light_switch,
482 TYPE_ELEMENT,
483 },
484 {
485 GAME_PANEL_LIGHT_SWITCH_TIME,
486 &game.panel.light_switch_time,
487 TYPE_INTEGER,
488 },
489 {
490 GAME_PANEL_TIMEGATE_SWITCH,
491 &game.panel.timegate_switch,
492 TYPE_ELEMENT,
493 },
494 {
495 GAME_PANEL_TIMEGATE_SWITCH_TIME,
496 &game.panel.timegate_switch_time,
497 TYPE_INTEGER,
498 },
499 {
500 GAME_PANEL_SWITCHGATE_SWITCH,
501 &game.panel.switchgate_switch,
502 TYPE_ELEMENT,
503 },
504 {
505 GAME_PANEL_EMC_LENSES,
506 &game.panel.emc_lenses,
507 TYPE_ELEMENT,
508 },
509 {
510 GAME_PANEL_EMC_LENSES_TIME,
511 &game.panel.emc_lenses_time,
512 TYPE_INTEGER,
513 },
514 {
515 GAME_PANEL_EMC_MAGNIFIER,
516 &game.panel.emc_magnifier,
517 TYPE_ELEMENT,
518 },
519 {
520 GAME_PANEL_EMC_MAGNIFIER_TIME,
521 &game.panel.emc_magnifier_time,
522 TYPE_INTEGER,
523 },
524 {
525 GAME_PANEL_BALLOON_SWITCH,
526 &game.panel.balloon_switch,
527 TYPE_ELEMENT,
528 },
529 {
530 GAME_PANEL_DYNABOMB_NUMBER,
531 &game.panel.dynabomb_number,
532 TYPE_INTEGER,
533 },
534 {
535 GAME_PANEL_DYNABOMB_SIZE,
536 &game.panel.dynabomb_size,
537 TYPE_INTEGER,
538 },
539 {
540 GAME_PANEL_DYNABOMB_POWER,
541 &game.panel.dynabomb_power,
542 TYPE_ELEMENT,
543 },
544 {
545 GAME_PANEL_PENGUINS,
546 &game.panel.penguins,
547 TYPE_INTEGER,
548 },
549 {
550 GAME_PANEL_SOKOBAN_OBJECTS,
551 &game.panel.sokoban_objects,
552 TYPE_INTEGER,
553 },
554 {
555 GAME_PANEL_SOKOBAN_FIELDS,
556 &game.panel.sokoban_fields,
557 TYPE_INTEGER,
558 },
559 {
560 GAME_PANEL_ROBOT_WHEEL,
561 &game.panel.robot_wheel,
562 TYPE_ELEMENT,
563 },
564 {
565 GAME_PANEL_CONVEYOR_BELT_1,
566 &game.panel.conveyor_belt[0],
567 TYPE_ELEMENT,
568 },
569 {
570 GAME_PANEL_CONVEYOR_BELT_2,
571 &game.panel.conveyor_belt[1],
572 TYPE_ELEMENT,
573 },
574 {
575 GAME_PANEL_CONVEYOR_BELT_3,
576 &game.panel.conveyor_belt[2],
577 TYPE_ELEMENT,
578 },
579 {
580 GAME_PANEL_CONVEYOR_BELT_4,
581 &game.panel.conveyor_belt[3],
582 TYPE_ELEMENT,
583 },
584 {
585 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
586 &game.panel.conveyor_belt_switch[0],
587 TYPE_ELEMENT,
588 },
589 {
590 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
591 &game.panel.conveyor_belt_switch[1],
592 TYPE_ELEMENT,
593 },
594 {
595 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
596 &game.panel.conveyor_belt_switch[2],
597 TYPE_ELEMENT,
598 },
599 {
600 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
601 &game.panel.conveyor_belt_switch[3],
602 TYPE_ELEMENT,
603 },
604 {
605 GAME_PANEL_MAGIC_WALL,
606 &game.panel.magic_wall,
607 TYPE_ELEMENT,
608 },
609 {
610 GAME_PANEL_MAGIC_WALL_TIME,
611 &game.panel.magic_wall_time,
612 TYPE_INTEGER,
613 },
614 {
615 GAME_PANEL_GRAVITY_STATE,
616 &game.panel.gravity_state,
617 TYPE_STRING,
618 },
619 {
620 GAME_PANEL_GRAPHIC_1,
621 &game.panel.graphic[0],
622 TYPE_ELEMENT,
623 },
624 {
625 GAME_PANEL_GRAPHIC_2,
626 &game.panel.graphic[1],
627 TYPE_ELEMENT,
628 },
629 {
630 GAME_PANEL_GRAPHIC_3,
631 &game.panel.graphic[2],
632 TYPE_ELEMENT,
633 },
634 {
635 GAME_PANEL_GRAPHIC_4,
636 &game.panel.graphic[3],
637 TYPE_ELEMENT,
638 },
639 {
640 GAME_PANEL_GRAPHIC_5,
641 &game.panel.graphic[4],
642 TYPE_ELEMENT,
643 },
644 {
645 GAME_PANEL_GRAPHIC_6,
646 &game.panel.graphic[5],
647 TYPE_ELEMENT,
648 },
649 {
650 GAME_PANEL_GRAPHIC_7,
651 &game.panel.graphic[6],
652 TYPE_ELEMENT,
653 },
654 {
655 GAME_PANEL_GRAPHIC_8,
656 &game.panel.graphic[7],
657 TYPE_ELEMENT,
658 },
659 {
660 GAME_PANEL_ELEMENT_1,
661 &game.panel.element[0],
662 TYPE_ELEMENT,
663 },
664 {
665 GAME_PANEL_ELEMENT_2,
666 &game.panel.element[1],
667 TYPE_ELEMENT,
668 },
669 {
670 GAME_PANEL_ELEMENT_3,
671 &game.panel.element[2],
672 TYPE_ELEMENT,
673 },
674 {
675 GAME_PANEL_ELEMENT_4,
676 &game.panel.element[3],
677 TYPE_ELEMENT,
678 },
679 {
680 GAME_PANEL_ELEMENT_5,
681 &game.panel.element[4],
682 TYPE_ELEMENT,
683 },
684 {
685 GAME_PANEL_ELEMENT_6,
686 &game.panel.element[5],
687 TYPE_ELEMENT,
688 },
689 {
690 GAME_PANEL_ELEMENT_7,
691 &game.panel.element[6],
692 TYPE_ELEMENT,
693 },
694 {
695 GAME_PANEL_ELEMENT_8,
696 &game.panel.element[7],
697 TYPE_ELEMENT,
698 },
699 {
700 GAME_PANEL_ELEMENT_COUNT_1,
701 &game.panel.element_count[0],
702 TYPE_INTEGER,
703 },
704 {
705 GAME_PANEL_ELEMENT_COUNT_2,
706 &game.panel.element_count[1],
707 TYPE_INTEGER,
708 },
709 {
710 GAME_PANEL_ELEMENT_COUNT_3,
711 &game.panel.element_count[2],
712 TYPE_INTEGER,
713 },
714 {
715 GAME_PANEL_ELEMENT_COUNT_4,
716 &game.panel.element_count[3],
717 TYPE_INTEGER,
718 },
719 {
720 GAME_PANEL_ELEMENT_COUNT_5,
721 &game.panel.element_count[4],
722 TYPE_INTEGER,
723 },
724 {
725 GAME_PANEL_ELEMENT_COUNT_6,
726 &game.panel.element_count[5],
727 TYPE_INTEGER,
728 },
729 {
730 GAME_PANEL_ELEMENT_COUNT_7,
731 &game.panel.element_count[6],
732 TYPE_INTEGER,
733 },
734 {
735 GAME_PANEL_ELEMENT_COUNT_8,
736 &game.panel.element_count[7],
737 TYPE_INTEGER,
738 },
739 {
740 GAME_PANEL_CE_SCORE_1,
741 &game.panel.ce_score[0],
742 TYPE_INTEGER,
743 },
744 {
745 GAME_PANEL_CE_SCORE_2,
746 &game.panel.ce_score[1],
747 TYPE_INTEGER,
748 },
749 {
750 GAME_PANEL_CE_SCORE_3,
751 &game.panel.ce_score[2],
752 TYPE_INTEGER,
753 },
754 {
755 GAME_PANEL_CE_SCORE_4,
756 &game.panel.ce_score[3],
757 TYPE_INTEGER,
758 },
759 {
760 GAME_PANEL_CE_SCORE_5,
761 &game.panel.ce_score[4],
762 TYPE_INTEGER,
763 },
764 {
765 GAME_PANEL_CE_SCORE_6,
766 &game.panel.ce_score[5],
767 TYPE_INTEGER,
768 },
769 {
770 GAME_PANEL_CE_SCORE_7,
771 &game.panel.ce_score[6],
772 TYPE_INTEGER,
773 },
774 {
775 GAME_PANEL_CE_SCORE_8,
776 &game.panel.ce_score[7],
777 TYPE_INTEGER,
778 },
779 {
780 GAME_PANEL_CE_SCORE_1_ELEMENT,
781 &game.panel.ce_score_element[0],
782 TYPE_ELEMENT,
783 },
784 {
785 GAME_PANEL_CE_SCORE_2_ELEMENT,
786 &game.panel.ce_score_element[1],
787 TYPE_ELEMENT,
788 },
789 {
790 GAME_PANEL_CE_SCORE_3_ELEMENT,
791 &game.panel.ce_score_element[2],
792 TYPE_ELEMENT,
793 },
794 {
795 GAME_PANEL_CE_SCORE_4_ELEMENT,
796 &game.panel.ce_score_element[3],
797 TYPE_ELEMENT,
798 },
799 {
800 GAME_PANEL_CE_SCORE_5_ELEMENT,
801 &game.panel.ce_score_element[4],
802 TYPE_ELEMENT,
803 },
804 {
805 GAME_PANEL_CE_SCORE_6_ELEMENT,
806 &game.panel.ce_score_element[5],
807 TYPE_ELEMENT,
808 },
809 {
810 GAME_PANEL_CE_SCORE_7_ELEMENT,
811 &game.panel.ce_score_element[6],
812 TYPE_ELEMENT,
813 },
814 {
815 GAME_PANEL_CE_SCORE_8_ELEMENT,
816 &game.panel.ce_score_element[7],
817 TYPE_ELEMENT,
818 },
819 {
820 GAME_PANEL_PLAYER_NAME,
821 &game.panel.player_name,
822 TYPE_STRING,
823 },
824 {
825 GAME_PANEL_LEVEL_NAME,
826 &game.panel.level_name,
827 TYPE_STRING,
828 },
829 {
830 GAME_PANEL_LEVEL_AUTHOR,
831 &game.panel.level_author,
832 TYPE_STRING,
833 },
834
835 {
836 -1,
837 NULL,
838 -1,
839 }
840 };
841
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING 3
844 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION 2
846 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
847
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF -1
850 #define INITIAL_MOVE_DELAY_ON 0
851
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED 32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED 4
856 #define MOVE_DELAY_MAX_SPEED 1
857
858 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860
861 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
868
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
871
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
911 (e))
912
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
918 (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
924 (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
930 (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
934 (condition) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
938 IS_PLAYER(x, y) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define SOUND_CTRL_ID_MUSIC 3
1010 #define SOUND_CTRL_ID_LOOPS 4
1011 #define SOUND_CTRL_ID_SIMPLE 5
1012
1013 #define NUM_GAME_BUTTONS 6
1014
1015
1016 /* forward declaration for internal use */
1017
1018 static void CreateField(int, int, int);
1019
1020 static void ResetGfxAnimation(int, int);
1021
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1024
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1029
1030 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031 static boolean DigFieldByCE(int, int, int);
1032 static boolean SnapField(struct PlayerInfo *, int, int);
1033 static boolean DropElement(struct PlayerInfo *);
1034
1035 static void InitBeltMovement(void);
1036 static void CloseAllOpenTimegates(void);
1037 static void CheckGravityMovement(struct PlayerInfo *);
1038 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1039 static void KillPlayerUnlessEnemyProtected(int, int);
1040 static void KillPlayerUnlessExplosionProtected(int, int);
1041
1042 static void TestIfPlayerTouchesCustomElement(int, int);
1043 static void TestIfElementTouchesCustomElement(int, int);
1044 static void TestIfElementHitsCustomElement(int, int, int);
1045 #if 0
1046 static void TestIfElementSmashesCustomElement(int, int, int);
1047 #endif
1048
1049 static void HandleElementChange(int, int, int);
1050 static void ExecuteCustomElementAction(int, int, int, int);
1051 static boolean ChangeElement(int, int, int, int);
1052
1053 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1054 #define CheckTriggeredElementChange(x, y, e, ev) \
1055 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1056 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1057 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1058 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1059 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1060 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1061 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1062
1063 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1064 #define CheckElementChange(x, y, e, te, ev) \
1065 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1066 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1068 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1069 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1070
1071 static void PlayLevelSound(int, int, int);
1072 static void PlayLevelSoundNearest(int, int, int);
1073 static void PlayLevelSoundAction(int, int, int);
1074 static void PlayLevelSoundElementAction(int, int, int, int);
1075 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1076 static void PlayLevelSoundActionIfLoop(int, int, int);
1077 static void StopLevelSoundActionIfLoop(int, int, int);
1078 static void PlayLevelMusic();
1079
1080 static void MapGameButtons();
1081 static void HandleGameButtons(struct GadgetInfo *);
1082
1083 int AmoebeNachbarNr(int, int);
1084 void AmoebeUmwandeln(int, int);
1085 void ContinueMoving(int, int);
1086 void Bang(int, int);
1087 void InitMovDir(int, int);
1088 void InitAmoebaNr(int, int);
1089 int NewHiScore(void);
1090
1091 void TestIfGoodThingHitsBadThing(int, int, int);
1092 void TestIfBadThingHitsGoodThing(int, int, int);
1093 void TestIfPlayerTouchesBadThing(int, int);
1094 void TestIfPlayerRunsIntoBadThing(int, int, int);
1095 void TestIfBadThingTouchesPlayer(int, int);
1096 void TestIfBadThingRunsIntoPlayer(int, int, int);
1097 void TestIfFriendTouchesBadThing(int, int);
1098 void TestIfBadThingTouchesFriend(int, int);
1099 void TestIfBadThingTouchesOtherBadThing(int, int);
1100 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1101
1102 void KillPlayer(struct PlayerInfo *);
1103 void BuryPlayer(struct PlayerInfo *);
1104 void RemovePlayer(struct PlayerInfo *);
1105
1106 static int getInvisibleActiveFromInvisibleElement(int);
1107 static int getInvisibleFromInvisibleActiveElement(int);
1108
1109 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1110
1111 /* for detection of endless loops, caused by custom element programming */
1112 /* (using maximal playfield width x 10 is just a rough approximation) */
1113 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1114
1115 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1116 { \
1117 if (recursion_loop_detected) \
1118 return (rc); \
1119 \
1120 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1121 { \
1122 recursion_loop_detected = TRUE; \
1123 recursion_loop_element = (e); \
1124 } \
1125 \
1126 recursion_loop_depth++; \
1127 }
1128
1129 #define RECURSION_LOOP_DETECTION_END() \
1130 { \
1131 recursion_loop_depth--; \
1132 }
1133
1134 static int recursion_loop_depth;
1135 static boolean recursion_loop_detected;
1136 static boolean recursion_loop_element;
1137
1138 static int map_player_action[MAX_PLAYERS];
1139
1140
1141 /* ------------------------------------------------------------------------- */
1142 /* definition of elements that automatically change to other elements after */
1143 /* a specified time, eventually calling a function when changing */
1144 /* ------------------------------------------------------------------------- */
1145
1146 /* forward declaration for changer functions */
1147 static void InitBuggyBase(int, int);
1148 static void WarnBuggyBase(int, int);
1149
1150 static void InitTrap(int, int);
1151 static void ActivateTrap(int, int);
1152 static void ChangeActiveTrap(int, int);
1153
1154 static void InitRobotWheel(int, int);
1155 static void RunRobotWheel(int, int);
1156 static void StopRobotWheel(int, int);
1157
1158 static void InitTimegateWheel(int, int);
1159 static void RunTimegateWheel(int, int);
1160
1161 static void InitMagicBallDelay(int, int);
1162 static void ActivateMagicBall(int, int);
1163
1164 struct ChangingElementInfo
1165 {
1166 int element;
1167 int target_element;
1168 int change_delay;
1169 void (*pre_change_function)(int x, int y);
1170 void (*change_function)(int x, int y);
1171 void (*post_change_function)(int x, int y);
1172 };
1173
1174 static struct ChangingElementInfo change_delay_list[] =
1175 {
1176 {
1177 EL_NUT_BREAKING,
1178 EL_EMERALD,
1179 6,
1180 NULL,
1181 NULL,
1182 NULL
1183 },
1184 {
1185 EL_PEARL_BREAKING,
1186 EL_EMPTY,
1187 8,
1188 NULL,
1189 NULL,
1190 NULL
1191 },
1192 {
1193 EL_EXIT_OPENING,
1194 EL_EXIT_OPEN,
1195 29,
1196 NULL,
1197 NULL,
1198 NULL
1199 },
1200 {
1201 EL_EXIT_CLOSING,
1202 EL_EXIT_CLOSED,
1203 29,
1204 NULL,
1205 NULL,
1206 NULL
1207 },
1208 {
1209 EL_STEEL_EXIT_OPENING,
1210 EL_STEEL_EXIT_OPEN,
1211 29,
1212 NULL,
1213 NULL,
1214 NULL
1215 },
1216 {
1217 EL_STEEL_EXIT_CLOSING,
1218 EL_STEEL_EXIT_CLOSED,
1219 29,
1220 NULL,
1221 NULL,
1222 NULL
1223 },
1224 {
1225 EL_EM_EXIT_OPENING,
1226 EL_EM_EXIT_OPEN,
1227 29,
1228 NULL,
1229 NULL,
1230 NULL
1231 },
1232 {
1233 EL_EM_EXIT_CLOSING,
1234 #if 1
1235 EL_EMPTY,
1236 #else
1237 EL_EM_EXIT_CLOSED,
1238 #endif
1239 29,
1240 NULL,
1241 NULL,
1242 NULL
1243 },
1244 {
1245 EL_EM_STEEL_EXIT_OPENING,
1246 EL_EM_STEEL_EXIT_OPEN,
1247 29,
1248 NULL,
1249 NULL,
1250 NULL
1251 },
1252 {
1253 EL_EM_STEEL_EXIT_CLOSING,
1254 #if 1
1255 EL_STEELWALL,
1256 #else
1257 EL_EM_STEEL_EXIT_CLOSED,
1258 #endif
1259 29,
1260 NULL,
1261 NULL,
1262 NULL
1263 },
1264 {
1265 EL_SP_EXIT_OPENING,
1266 EL_SP_EXIT_OPEN,
1267 29,
1268 NULL,
1269 NULL,
1270 NULL
1271 },
1272 {
1273 EL_SP_EXIT_CLOSING,
1274 EL_SP_EXIT_CLOSED,
1275 29,
1276 NULL,
1277 NULL,
1278 NULL
1279 },
1280 {
1281 EL_SWITCHGATE_OPENING,
1282 EL_SWITCHGATE_OPEN,
1283 29,
1284 NULL,
1285 NULL,
1286 NULL
1287 },
1288 {
1289 EL_SWITCHGATE_CLOSING,
1290 EL_SWITCHGATE_CLOSED,
1291 29,
1292 NULL,
1293 NULL,
1294 NULL
1295 },
1296 {
1297 EL_TIMEGATE_OPENING,
1298 EL_TIMEGATE_OPEN,
1299 29,
1300 NULL,
1301 NULL,
1302 NULL
1303 },
1304 {
1305 EL_TIMEGATE_CLOSING,
1306 EL_TIMEGATE_CLOSED,
1307 29,
1308 NULL,
1309 NULL,
1310 NULL
1311 },
1312
1313 {
1314 EL_ACID_SPLASH_LEFT,
1315 EL_EMPTY,
1316 8,
1317 NULL,
1318 NULL,
1319 NULL
1320 },
1321 {
1322 EL_ACID_SPLASH_RIGHT,
1323 EL_EMPTY,
1324 8,
1325 NULL,
1326 NULL,
1327 NULL
1328 },
1329 {
1330 EL_SP_BUGGY_BASE,
1331 EL_SP_BUGGY_BASE_ACTIVATING,
1332 0,
1333 InitBuggyBase,
1334 NULL,
1335 NULL
1336 },
1337 {
1338 EL_SP_BUGGY_BASE_ACTIVATING,
1339 EL_SP_BUGGY_BASE_ACTIVE,
1340 0,
1341 InitBuggyBase,
1342 NULL,
1343 NULL
1344 },
1345 {
1346 EL_SP_BUGGY_BASE_ACTIVE,
1347 EL_SP_BUGGY_BASE,
1348 0,
1349 InitBuggyBase,
1350 WarnBuggyBase,
1351 NULL
1352 },
1353 {
1354 EL_TRAP,
1355 EL_TRAP_ACTIVE,
1356 0,
1357 InitTrap,
1358 NULL,
1359 ActivateTrap
1360 },
1361 {
1362 EL_TRAP_ACTIVE,
1363 EL_TRAP,
1364 31,
1365 NULL,
1366 ChangeActiveTrap,
1367 NULL
1368 },
1369 {
1370 EL_ROBOT_WHEEL_ACTIVE,
1371 EL_ROBOT_WHEEL,
1372 0,
1373 InitRobotWheel,
1374 RunRobotWheel,
1375 StopRobotWheel
1376 },
1377 {
1378 EL_TIMEGATE_SWITCH_ACTIVE,
1379 EL_TIMEGATE_SWITCH,
1380 0,
1381 InitTimegateWheel,
1382 RunTimegateWheel,
1383 NULL
1384 },
1385 {
1386 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1387 EL_DC_TIMEGATE_SWITCH,
1388 0,
1389 InitTimegateWheel,
1390 RunTimegateWheel,
1391 NULL
1392 },
1393 {
1394 EL_EMC_MAGIC_BALL_ACTIVE,
1395 EL_EMC_MAGIC_BALL_ACTIVE,
1396 0,
1397 InitMagicBallDelay,
1398 NULL,
1399 ActivateMagicBall
1400 },
1401 {
1402 EL_EMC_SPRING_BUMPER_ACTIVE,
1403 EL_EMC_SPRING_BUMPER,
1404 8,
1405 NULL,
1406 NULL,
1407 NULL
1408 },
1409 {
1410 EL_DIAGONAL_SHRINKING,
1411 EL_UNDEFINED,
1412 0,
1413 NULL,
1414 NULL,
1415 NULL
1416 },
1417 {
1418 EL_DIAGONAL_GROWING,
1419 EL_UNDEFINED,
1420 0,
1421 NULL,
1422 NULL,
1423 NULL,
1424 },
1425
1426 {
1427 EL_UNDEFINED,
1428 EL_UNDEFINED,
1429 -1,
1430 NULL,
1431 NULL,
1432 NULL
1433 }
1434 };
1435
1436 struct
1437 {
1438 int element;
1439 int push_delay_fixed, push_delay_random;
1440 }
1441 push_delay_list[] =
1442 {
1443 { EL_SPRING, 0, 0 },
1444 { EL_BALLOON, 0, 0 },
1445
1446 { EL_SOKOBAN_OBJECT, 2, 0 },
1447 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1448 { EL_SATELLITE, 2, 0 },
1449 { EL_SP_DISK_YELLOW, 2, 0 },
1450
1451 { EL_UNDEFINED, 0, 0 },
1452 };
1453
1454 struct
1455 {
1456 int element;
1457 int move_stepsize;
1458 }
1459 move_stepsize_list[] =
1460 {
1461 { EL_AMOEBA_DROP, 2 },
1462 { EL_AMOEBA_DROPPING, 2 },
1463 { EL_QUICKSAND_FILLING, 1 },
1464 { EL_QUICKSAND_EMPTYING, 1 },
1465 { EL_QUICKSAND_FAST_FILLING, 2 },
1466 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1467 { EL_MAGIC_WALL_FILLING, 2 },
1468 { EL_MAGIC_WALL_EMPTYING, 2 },
1469 { EL_BD_MAGIC_WALL_FILLING, 2 },
1470 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_DC_MAGIC_WALL_FILLING, 2 },
1472 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1473
1474 { EL_UNDEFINED, 0 },
1475 };
1476
1477 struct
1478 {
1479 int element;
1480 int count;
1481 }
1482 collect_count_list[] =
1483 {
1484 { EL_EMERALD, 1 },
1485 { EL_BD_DIAMOND, 1 },
1486 { EL_EMERALD_YELLOW, 1 },
1487 { EL_EMERALD_RED, 1 },
1488 { EL_EMERALD_PURPLE, 1 },
1489 { EL_DIAMOND, 3 },
1490 { EL_SP_INFOTRON, 1 },
1491 { EL_PEARL, 5 },
1492 { EL_CRYSTAL, 8 },
1493
1494 { EL_UNDEFINED, 0 },
1495 };
1496
1497 struct
1498 {
1499 int element;
1500 int direction;
1501 }
1502 access_direction_list[] =
1503 {
1504 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1505 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1506 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1507 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1508 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1509 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1510 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1511 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1512 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1513 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1514 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1515
1516 { EL_SP_PORT_LEFT, MV_RIGHT },
1517 { EL_SP_PORT_RIGHT, MV_LEFT },
1518 { EL_SP_PORT_UP, MV_DOWN },
1519 { EL_SP_PORT_DOWN, MV_UP },
1520 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1521 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1522 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1524 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1525 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1526 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1527 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1528 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1529 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1530 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1531 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1532 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1533 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1534 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1535
1536 { EL_UNDEFINED, MV_NONE }
1537 };
1538
1539 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1540
1541 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1542 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1543 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1544 IS_JUST_CHANGING(x, y))
1545
1546 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1547
1548 /* static variables for playfield scan mode (scanning forward or backward) */
1549 static int playfield_scan_start_x = 0;
1550 static int playfield_scan_start_y = 0;
1551 static int playfield_scan_delta_x = 1;
1552 static int playfield_scan_delta_y = 1;
1553
1554 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1555 (y) >= 0 && (y) <= lev_fieldy - 1; \
1556 (y) += playfield_scan_delta_y) \
1557 for ((x) = playfield_scan_start_x; \
1558 (x) >= 0 && (x) <= lev_fieldx - 1; \
1559 (x) += playfield_scan_delta_x)
1560
1561 #ifdef DEBUG
DEBUG_SetMaximumDynamite()1562 void DEBUG_SetMaximumDynamite()
1563 {
1564 int i;
1565
1566 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1567 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1568 local_player->inventory_element[local_player->inventory_size++] =
1569 EL_DYNAMITE;
1570 }
1571 #endif
1572
InitPlayfieldScanModeVars()1573 static void InitPlayfieldScanModeVars()
1574 {
1575 if (game.use_reverse_scan_direction)
1576 {
1577 playfield_scan_start_x = lev_fieldx - 1;
1578 playfield_scan_start_y = lev_fieldy - 1;
1579
1580 playfield_scan_delta_x = -1;
1581 playfield_scan_delta_y = -1;
1582 }
1583 else
1584 {
1585 playfield_scan_start_x = 0;
1586 playfield_scan_start_y = 0;
1587
1588 playfield_scan_delta_x = 1;
1589 playfield_scan_delta_y = 1;
1590 }
1591 }
1592
InitPlayfieldScanMode(int mode)1593 static void InitPlayfieldScanMode(int mode)
1594 {
1595 game.use_reverse_scan_direction =
1596 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1597
1598 InitPlayfieldScanModeVars();
1599 }
1600
get_move_delay_from_stepsize(int move_stepsize)1601 static int get_move_delay_from_stepsize(int move_stepsize)
1602 {
1603 move_stepsize =
1604 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1605
1606 /* make sure that stepsize value is always a power of 2 */
1607 move_stepsize = (1 << log_2(move_stepsize));
1608
1609 return TILEX / move_stepsize;
1610 }
1611
SetPlayerMoveSpeed(struct PlayerInfo * player,int move_stepsize,boolean init_game)1612 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1613 boolean init_game)
1614 {
1615 int player_nr = player->index_nr;
1616 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1617 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1618
1619 /* do no immediately change move delay -- the player might just be moving */
1620 player->move_delay_value_next = move_delay;
1621
1622 /* information if player can move must be set separately */
1623 player->cannot_move = cannot_move;
1624
1625 if (init_game)
1626 {
1627 player->move_delay = game.initial_move_delay[player_nr];
1628 player->move_delay_value = game.initial_move_delay_value[player_nr];
1629
1630 player->move_delay_value_next = -1;
1631
1632 player->move_delay_reset_counter = 0;
1633 }
1634 }
1635
GetPlayerConfig()1636 void GetPlayerConfig()
1637 {
1638 GameFrameDelay = setup.game_frame_delay;
1639
1640 if (!audio.sound_available)
1641 setup.sound_simple = FALSE;
1642
1643 if (!audio.loops_available)
1644 setup.sound_loops = FALSE;
1645
1646 if (!audio.music_available)
1647 setup.sound_music = FALSE;
1648
1649 if (!video.fullscreen_available)
1650 setup.fullscreen = FALSE;
1651
1652 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1653
1654 SetAudioMode(setup.sound);
1655 InitJoysticks();
1656 }
1657
GetElementFromGroupElement(int element)1658 int GetElementFromGroupElement(int element)
1659 {
1660 if (IS_GROUP_ELEMENT(element))
1661 {
1662 struct ElementGroupInfo *group = element_info[element].group;
1663 int last_anim_random_frame = gfx.anim_random_frame;
1664 int element_pos;
1665
1666 if (group->choice_mode == ANIM_RANDOM)
1667 gfx.anim_random_frame = RND(group->num_elements_resolved);
1668
1669 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1670 group->choice_mode, 0,
1671 group->choice_pos);
1672
1673 if (group->choice_mode == ANIM_RANDOM)
1674 gfx.anim_random_frame = last_anim_random_frame;
1675
1676 group->choice_pos++;
1677
1678 element = group->element_resolved[element_pos];
1679 }
1680
1681 return element;
1682 }
1683
InitPlayerField(int x,int y,int element,boolean init_game)1684 static void InitPlayerField(int x, int y, int element, boolean init_game)
1685 {
1686 if (element == EL_SP_MURPHY)
1687 {
1688 if (init_game)
1689 {
1690 if (stored_player[0].present)
1691 {
1692 Feld[x][y] = EL_SP_MURPHY_CLONE;
1693
1694 return;
1695 }
1696 else
1697 {
1698 stored_player[0].initial_element = element;
1699 stored_player[0].use_murphy = TRUE;
1700
1701 if (!level.use_artwork_element[0])
1702 stored_player[0].artwork_element = EL_SP_MURPHY;
1703 }
1704
1705 Feld[x][y] = EL_PLAYER_1;
1706 }
1707 }
1708
1709 if (init_game)
1710 {
1711 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1712 int jx = player->jx, jy = player->jy;
1713
1714 player->present = TRUE;
1715
1716 player->block_last_field = (element == EL_SP_MURPHY ?
1717 level.sp_block_last_field :
1718 level.block_last_field);
1719
1720 /* ---------- initialize player's last field block delay --------------- */
1721
1722 /* always start with reliable default value (no adjustment needed) */
1723 player->block_delay_adjustment = 0;
1724
1725 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1726 if (player->block_last_field && element == EL_SP_MURPHY)
1727 player->block_delay_adjustment = 1;
1728
1729 /* special case 2: in game engines before 3.1.1, blocking was different */
1730 if (game.use_block_last_field_bug)
1731 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1732
1733 if (!options.network || player->connected)
1734 {
1735 player->active = TRUE;
1736
1737 /* remove potentially duplicate players */
1738 if (StorePlayer[jx][jy] == Feld[x][y])
1739 StorePlayer[jx][jy] = 0;
1740
1741 StorePlayer[x][y] = Feld[x][y];
1742
1743 if (options.debug)
1744 {
1745 printf("Player %d activated.\n", player->element_nr);
1746 printf("[Local player is %d and currently %s.]\n",
1747 local_player->element_nr,
1748 local_player->active ? "active" : "not active");
1749 }
1750 }
1751
1752 Feld[x][y] = EL_EMPTY;
1753
1754 player->jx = player->last_jx = x;
1755 player->jy = player->last_jy = y;
1756 }
1757
1758 #if USE_PLAYER_REANIMATION
1759 if (!init_game)
1760 {
1761 int player_nr = GET_PLAYER_NR(element);
1762 struct PlayerInfo *player = &stored_player[player_nr];
1763
1764 if (player->active && player->killed)
1765 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1766 }
1767 #endif
1768 }
1769
InitField(int x,int y,boolean init_game)1770 static void InitField(int x, int y, boolean init_game)
1771 {
1772 int element = Feld[x][y];
1773
1774 switch (element)
1775 {
1776 case EL_SP_MURPHY:
1777 case EL_PLAYER_1:
1778 case EL_PLAYER_2:
1779 case EL_PLAYER_3:
1780 case EL_PLAYER_4:
1781 InitPlayerField(x, y, element, init_game);
1782 break;
1783
1784 case EL_SOKOBAN_FIELD_PLAYER:
1785 element = Feld[x][y] = EL_PLAYER_1;
1786 InitField(x, y, init_game);
1787
1788 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1789 InitField(x, y, init_game);
1790 break;
1791
1792 case EL_SOKOBAN_FIELD_EMPTY:
1793 local_player->sokobanfields_still_needed++;
1794 break;
1795
1796 case EL_STONEBLOCK:
1797 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1798 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1799 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1800 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1801 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1802 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1803 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1804 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1805 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1806 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1807 break;
1808
1809 case EL_BUG:
1810 case EL_BUG_RIGHT:
1811 case EL_BUG_UP:
1812 case EL_BUG_LEFT:
1813 case EL_BUG_DOWN:
1814 case EL_SPACESHIP:
1815 case EL_SPACESHIP_RIGHT:
1816 case EL_SPACESHIP_UP:
1817 case EL_SPACESHIP_LEFT:
1818 case EL_SPACESHIP_DOWN:
1819 case EL_BD_BUTTERFLY:
1820 case EL_BD_BUTTERFLY_RIGHT:
1821 case EL_BD_BUTTERFLY_UP:
1822 case EL_BD_BUTTERFLY_LEFT:
1823 case EL_BD_BUTTERFLY_DOWN:
1824 case EL_BD_FIREFLY:
1825 case EL_BD_FIREFLY_RIGHT:
1826 case EL_BD_FIREFLY_UP:
1827 case EL_BD_FIREFLY_LEFT:
1828 case EL_BD_FIREFLY_DOWN:
1829 case EL_PACMAN_RIGHT:
1830 case EL_PACMAN_UP:
1831 case EL_PACMAN_LEFT:
1832 case EL_PACMAN_DOWN:
1833 case EL_YAMYAM:
1834 case EL_YAMYAM_LEFT:
1835 case EL_YAMYAM_RIGHT:
1836 case EL_YAMYAM_UP:
1837 case EL_YAMYAM_DOWN:
1838 case EL_DARK_YAMYAM:
1839 case EL_ROBOT:
1840 case EL_PACMAN:
1841 case EL_SP_SNIKSNAK:
1842 case EL_SP_ELECTRON:
1843 case EL_MOLE:
1844 case EL_MOLE_LEFT:
1845 case EL_MOLE_RIGHT:
1846 case EL_MOLE_UP:
1847 case EL_MOLE_DOWN:
1848 InitMovDir(x, y);
1849 break;
1850
1851 case EL_AMOEBA_FULL:
1852 case EL_BD_AMOEBA:
1853 InitAmoebaNr(x, y);
1854 break;
1855
1856 case EL_AMOEBA_DROP:
1857 if (y == lev_fieldy - 1)
1858 {
1859 Feld[x][y] = EL_AMOEBA_GROWING;
1860 Store[x][y] = EL_AMOEBA_WET;
1861 }
1862 break;
1863
1864 case EL_DYNAMITE_ACTIVE:
1865 case EL_SP_DISK_RED_ACTIVE:
1866 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1870 MovDelay[x][y] = 96;
1871 break;
1872
1873 case EL_EM_DYNAMITE_ACTIVE:
1874 MovDelay[x][y] = 32;
1875 break;
1876
1877 case EL_LAMP:
1878 local_player->lights_still_needed++;
1879 break;
1880
1881 case EL_PENGUIN:
1882 local_player->friends_still_needed++;
1883 break;
1884
1885 case EL_PIG:
1886 case EL_DRAGON:
1887 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1888 break;
1889
1890 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1891 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1892 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1893 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1895 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1896 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1898 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1899 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1901 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1902 if (init_game)
1903 {
1904 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1905 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1907
1908 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1909 {
1910 game.belt_dir[belt_nr] = belt_dir;
1911 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1912 }
1913 else /* more than one switch -- set it like the first switch */
1914 {
1915 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1916 }
1917 }
1918 break;
1919
1920 #if !USE_BOTH_SWITCHGATE_SWITCHES
1921 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1922 if (init_game)
1923 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1924 break;
1925
1926 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1927 if (init_game)
1928 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1929 break;
1930 #endif
1931
1932 case EL_LIGHT_SWITCH_ACTIVE:
1933 if (init_game)
1934 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1935 break;
1936
1937 case EL_INVISIBLE_STEELWALL:
1938 case EL_INVISIBLE_WALL:
1939 case EL_INVISIBLE_SAND:
1940 if (game.light_time_left > 0 ||
1941 game.lenses_time_left > 0)
1942 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1943 break;
1944
1945 case EL_EMC_MAGIC_BALL:
1946 if (game.ball_state)
1947 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1948 break;
1949
1950 case EL_EMC_MAGIC_BALL_SWITCH:
1951 if (game.ball_state)
1952 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1953 break;
1954
1955 case EL_TRIGGER_PLAYER:
1956 case EL_TRIGGER_ELEMENT:
1957 case EL_TRIGGER_CE_VALUE:
1958 case EL_TRIGGER_CE_SCORE:
1959 case EL_SELF:
1960 case EL_ANY_ELEMENT:
1961 case EL_CURRENT_CE_VALUE:
1962 case EL_CURRENT_CE_SCORE:
1963 case EL_PREV_CE_1:
1964 case EL_PREV_CE_2:
1965 case EL_PREV_CE_3:
1966 case EL_PREV_CE_4:
1967 case EL_PREV_CE_5:
1968 case EL_PREV_CE_6:
1969 case EL_PREV_CE_7:
1970 case EL_PREV_CE_8:
1971 case EL_NEXT_CE_1:
1972 case EL_NEXT_CE_2:
1973 case EL_NEXT_CE_3:
1974 case EL_NEXT_CE_4:
1975 case EL_NEXT_CE_5:
1976 case EL_NEXT_CE_6:
1977 case EL_NEXT_CE_7:
1978 case EL_NEXT_CE_8:
1979 /* reference elements should not be used on the playfield */
1980 Feld[x][y] = EL_EMPTY;
1981 break;
1982
1983 default:
1984 if (IS_CUSTOM_ELEMENT(element))
1985 {
1986 if (CAN_MOVE(element))
1987 InitMovDir(x, y);
1988
1989 #if USE_NEW_CUSTOM_VALUE
1990 if (!element_info[element].use_last_ce_value || init_game)
1991 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1992 #endif
1993 }
1994 else if (IS_GROUP_ELEMENT(element))
1995 {
1996 Feld[x][y] = GetElementFromGroupElement(element);
1997
1998 InitField(x, y, init_game);
1999 }
2000
2001 break;
2002 }
2003
2004 if (!init_game)
2005 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2006 }
2007
InitField_WithBug1(int x,int y,boolean init_game)2008 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2009 {
2010 InitField(x, y, init_game);
2011
2012 /* not needed to call InitMovDir() -- already done by InitField()! */
2013 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014 CAN_MOVE(Feld[x][y]))
2015 InitMovDir(x, y);
2016 }
2017
InitField_WithBug2(int x,int y,boolean init_game)2018 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2019 {
2020 int old_element = Feld[x][y];
2021
2022 InitField(x, y, init_game);
2023
2024 /* not needed to call InitMovDir() -- already done by InitField()! */
2025 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2026 CAN_MOVE(old_element) &&
2027 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2028 InitMovDir(x, y);
2029
2030 /* this case is in fact a combination of not less than three bugs:
2031 first, it calls InitMovDir() for elements that can move, although this is
2032 already done by InitField(); then, it checks the element that was at this
2033 field _before_ the call to InitField() (which can change it); lastly, it
2034 was not called for "mole with direction" elements, which were treated as
2035 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2036 */
2037 }
2038
get_key_element_from_nr(int key_nr)2039 static int get_key_element_from_nr(int key_nr)
2040 {
2041 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2042 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2043 EL_EM_KEY_1 : EL_KEY_1);
2044
2045 return key_base_element + key_nr;
2046 }
2047
get_next_dropped_element(struct PlayerInfo * player)2048 static int get_next_dropped_element(struct PlayerInfo *player)
2049 {
2050 return (player->inventory_size > 0 ?
2051 player->inventory_element[player->inventory_size - 1] :
2052 player->inventory_infinite_element != EL_UNDEFINED ?
2053 player->inventory_infinite_element :
2054 player->dynabombs_left > 0 ?
2055 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2056 EL_UNDEFINED);
2057 }
2058
get_inventory_element_from_pos(struct PlayerInfo * player,int pos)2059 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2060 {
2061 /* pos >= 0: get element from bottom of the stack;
2062 pos < 0: get element from top of the stack */
2063
2064 if (pos < 0)
2065 {
2066 int min_inventory_size = -pos;
2067 int inventory_pos = player->inventory_size - min_inventory_size;
2068 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2069
2070 return (player->inventory_size >= min_inventory_size ?
2071 player->inventory_element[inventory_pos] :
2072 player->inventory_infinite_element != EL_UNDEFINED ?
2073 player->inventory_infinite_element :
2074 player->dynabombs_left >= min_dynabombs_left ?
2075 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076 EL_UNDEFINED);
2077 }
2078 else
2079 {
2080 int min_dynabombs_left = pos + 1;
2081 int min_inventory_size = pos + 1 - player->dynabombs_left;
2082 int inventory_pos = pos - player->dynabombs_left;
2083
2084 return (player->inventory_infinite_element != EL_UNDEFINED ?
2085 player->inventory_infinite_element :
2086 player->dynabombs_left >= min_dynabombs_left ?
2087 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 player->inventory_size >= min_inventory_size ?
2089 player->inventory_element[inventory_pos] :
2090 EL_UNDEFINED);
2091 }
2092 }
2093
compareGamePanelOrderInfo(const void * object1,const void * object2)2094 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2095 {
2096 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2097 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2098 int compare_result;
2099
2100 if (gpo1->sort_priority != gpo2->sort_priority)
2101 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2102 else
2103 compare_result = gpo1->nr - gpo2->nr;
2104
2105 return compare_result;
2106 }
2107
InitGameControlValues()2108 void InitGameControlValues()
2109 {
2110 int i;
2111
2112 for (i = 0; game_panel_controls[i].nr != -1; i++)
2113 {
2114 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2115 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2116 struct TextPosInfo *pos = gpc->pos;
2117 int nr = gpc->nr;
2118 int type = gpc->type;
2119
2120 if (nr != i)
2121 {
2122 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2123 Error(ERR_EXIT, "this should not happen -- please debug");
2124 }
2125
2126 /* force update of game controls after initialization */
2127 gpc->value = gpc->last_value = -1;
2128 gpc->frame = gpc->last_frame = -1;
2129 gpc->gfx_frame = -1;
2130
2131 /* determine panel value width for later calculation of alignment */
2132 if (type == TYPE_INTEGER || type == TYPE_STRING)
2133 {
2134 pos->width = pos->size * getFontWidth(pos->font);
2135 pos->height = getFontHeight(pos->font);
2136 }
2137 else if (type == TYPE_ELEMENT)
2138 {
2139 pos->width = pos->size;
2140 pos->height = pos->size;
2141 }
2142
2143 /* fill structure for game panel draw order */
2144 gpo->nr = gpc->nr;
2145 gpo->sort_priority = pos->sort_priority;
2146 }
2147
2148 /* sort game panel controls according to sort_priority and control number */
2149 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2150 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2151 }
2152
UpdatePlayfieldElementCount()2153 void UpdatePlayfieldElementCount()
2154 {
2155 boolean use_element_count = FALSE;
2156 int i, j, x, y;
2157
2158 /* first check if it is needed at all to calculate playfield element count */
2159 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2160 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2161 use_element_count = TRUE;
2162
2163 if (!use_element_count)
2164 return;
2165
2166 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2167 element_info[i].element_count = 0;
2168
2169 SCAN_PLAYFIELD(x, y)
2170 {
2171 element_info[Feld[x][y]].element_count++;
2172 }
2173
2174 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2175 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2176 if (IS_IN_GROUP(j, i))
2177 element_info[EL_GROUP_START + i].element_count +=
2178 element_info[j].element_count;
2179 }
2180
UpdateGameControlValues()2181 void UpdateGameControlValues()
2182 {
2183 int i, k;
2184 int time = (local_player->LevelSolved ?
2185 local_player->LevelSolved_CountingTime :
2186 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187 level.native_em_level->lev->time :
2188 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189 level.native_sp_level->game_sp->time_played :
2190 game.no_time_limit ? TimePlayed : TimeLeft);
2191 int score = (local_player->LevelSolved ?
2192 local_player->LevelSolved_CountingScore :
2193 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->score :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->score :
2197 local_player->score);
2198 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199 level.native_em_level->lev->required :
2200 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201 level.native_sp_level->game_sp->infotrons_still_needed :
2202 local_player->gems_still_needed);
2203 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204 level.native_em_level->lev->required > 0 :
2205 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2206 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2207 local_player->gems_still_needed > 0 ||
2208 local_player->sokobanfields_still_needed > 0 ||
2209 local_player->lights_still_needed > 0);
2210
2211 UpdatePlayfieldElementCount();
2212
2213 /* update game panel control values */
2214
2215 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2217
2218 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219 for (i = 0; i < MAX_NUM_KEYS; i++)
2220 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2223
2224 if (game.centered_player_nr == -1)
2225 {
2226 for (i = 0; i < MAX_PLAYERS; i++)
2227 {
2228 /* only one player in Supaplex game engine */
2229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2230 break;
2231
2232 for (k = 0; k < MAX_NUM_KEYS; k++)
2233 {
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2235 {
2236 if (level.native_em_level->ply[i]->keys & (1 << k))
2237 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 get_key_element_from_nr(k);
2239 }
2240 else if (stored_player[i].key[k])
2241 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 get_key_element_from_nr(k);
2243 }
2244
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2247 level.native_em_level->ply[i]->dynamite;
2248 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2249 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250 level.native_sp_level->game_sp->red_disk_count;
2251 else
2252 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253 stored_player[i].inventory_size;
2254
2255 if (stored_player[i].num_white_keys > 0)
2256 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257 EL_DC_KEY_WHITE;
2258
2259 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260 stored_player[i].num_white_keys;
2261 }
2262 }
2263 else
2264 {
2265 int player_nr = game.centered_player_nr;
2266
2267 for (k = 0; k < MAX_NUM_KEYS; k++)
2268 {
2269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270 {
2271 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273 get_key_element_from_nr(k);
2274 }
2275 else if (stored_player[player_nr].key[k])
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2278 }
2279
2280 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282 level.native_em_level->ply[player_nr]->dynamite;
2283 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 level.native_sp_level->game_sp->red_disk_count;
2286 else
2287 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288 stored_player[player_nr].inventory_size;
2289
2290 if (stored_player[player_nr].num_white_keys > 0)
2291 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2292
2293 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294 stored_player[player_nr].num_white_keys;
2295 }
2296
2297 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2298 {
2299 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300 get_inventory_element_from_pos(local_player, i);
2301 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302 get_inventory_element_from_pos(local_player, -i - 1);
2303 }
2304
2305 game_panel_controls[GAME_PANEL_SCORE].value = score;
2306 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2307
2308 game_panel_controls[GAME_PANEL_TIME].value = time;
2309
2310 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2313
2314 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2315
2316 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2317 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2318 EL_EMPTY);
2319 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2320 local_player->shield_normal_time_left;
2321 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2322 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2323 EL_EMPTY);
2324 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2325 local_player->shield_deadly_time_left;
2326
2327 game_panel_controls[GAME_PANEL_EXIT].value =
2328 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2329
2330 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2331 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2332 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2333 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2334 EL_EMC_MAGIC_BALL_SWITCH);
2335
2336 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2337 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2338 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2339 game.light_time_left;
2340
2341 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2342 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2343 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2344 game.timegate_time_left;
2345
2346 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2347 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2348
2349 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2350 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2351 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2352 game.lenses_time_left;
2353
2354 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2355 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2357 game.magnify_time_left;
2358
2359 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2360 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2361 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2362 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2363 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2364 EL_BALLOON_SWITCH_NONE);
2365
2366 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2367 local_player->dynabomb_count;
2368 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2369 local_player->dynabomb_size;
2370 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2371 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2372
2373 game_panel_controls[GAME_PANEL_PENGUINS].value =
2374 local_player->friends_still_needed;
2375
2376 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2377 local_player->sokobanfields_still_needed;
2378 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2379 local_player->sokobanfields_still_needed;
2380
2381 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2382 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2383
2384 for (i = 0; i < NUM_BELTS; i++)
2385 {
2386 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2387 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2388 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2389 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2390 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2391 }
2392
2393 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2394 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2395 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2396 game.magic_wall_time_left;
2397
2398 #if USE_PLAYER_GRAVITY
2399 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2400 local_player->gravity;
2401 #else
2402 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2403 #endif
2404
2405 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2406 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2407
2408 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2410 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2411 game.panel.element[i].id : EL_UNDEFINED);
2412
2413 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2414 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2415 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2416 element_info[game.panel.element_count[i].id].element_count : 0);
2417
2418 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2420 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2421 element_info[game.panel.ce_score[i].id].collect_score : 0);
2422
2423 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2424 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2425 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2426 element_info[game.panel.ce_score_element[i].id].collect_score :
2427 EL_UNDEFINED);
2428
2429 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2430 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2432
2433 /* update game panel control frames */
2434
2435 for (i = 0; game_panel_controls[i].nr != -1; i++)
2436 {
2437 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2438
2439 if (gpc->type == TYPE_ELEMENT)
2440 {
2441 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2442 {
2443 int last_anim_random_frame = gfx.anim_random_frame;
2444 int element = gpc->value;
2445 int graphic = el2panelimg(element);
2446
2447 if (gpc->value != gpc->last_value)
2448 {
2449 gpc->gfx_frame = 0;
2450 gpc->gfx_random = INIT_GFX_RANDOM();
2451 }
2452 else
2453 {
2454 gpc->gfx_frame++;
2455
2456 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2457 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2458 gpc->gfx_random = INIT_GFX_RANDOM();
2459 }
2460
2461 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2462 gfx.anim_random_frame = gpc->gfx_random;
2463
2464 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2465 gpc->gfx_frame = element_info[element].collect_score;
2466
2467 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2468 gpc->gfx_frame);
2469
2470 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471 gfx.anim_random_frame = last_anim_random_frame;
2472 }
2473 }
2474 }
2475 }
2476
DisplayGameControlValues()2477 void DisplayGameControlValues()
2478 {
2479 boolean redraw_panel = FALSE;
2480 int i;
2481
2482 for (i = 0; game_panel_controls[i].nr != -1; i++)
2483 {
2484 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2485
2486 if (PANEL_DEACTIVATED(gpc->pos))
2487 continue;
2488
2489 if (gpc->value == gpc->last_value &&
2490 gpc->frame == gpc->last_frame)
2491 continue;
2492
2493 redraw_panel = TRUE;
2494 }
2495
2496 if (!redraw_panel)
2497 return;
2498
2499 /* copy default game door content to main double buffer */
2500 #if 1
2501 /* !!! CHECK AGAIN !!! */
2502 SetPanelBackground();
2503 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2504 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2505 #else
2506 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2507 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2508 #endif
2509
2510 /* redraw game control buttons */
2511 #if 1
2512 RedrawGameButtons();
2513 #else
2514 UnmapGameButtons();
2515 MapGameButtons();
2516 #endif
2517
2518 game_status = GAME_MODE_PSEUDO_PANEL;
2519
2520 #if 1
2521 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2522 #else
2523 for (i = 0; game_panel_controls[i].nr != -1; i++)
2524 #endif
2525 {
2526 #if 1
2527 int nr = game_panel_order[i].nr;
2528 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2529 #else
2530 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2531 int nr = gpc->nr;
2532 #endif
2533 struct TextPosInfo *pos = gpc->pos;
2534 int type = gpc->type;
2535 int value = gpc->value;
2536 int frame = gpc->frame;
2537 #if 0
2538 int last_value = gpc->last_value;
2539 int last_frame = gpc->last_frame;
2540 #endif
2541 int size = pos->size;
2542 int font = pos->font;
2543 boolean draw_masked = pos->draw_masked;
2544 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2545
2546 if (PANEL_DEACTIVATED(pos))
2547 continue;
2548
2549 #if 0
2550 if (value == last_value && frame == last_frame)
2551 continue;
2552 #endif
2553
2554 gpc->last_value = value;
2555 gpc->last_frame = frame;
2556
2557 #if 0
2558 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2559 #endif
2560
2561 if (type == TYPE_INTEGER)
2562 {
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2565 {
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568 if (use_dynamic_size) /* use dynamic number of digits */
2569 {
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2575
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2578
2579 #if 0
2580 /* clear background if value just changed its size (dynamic digits) */
2581 if ((last_value < value_change) != (value < value_change))
2582 {
2583 int width1 = size1 * getFontWidth(font1);
2584 int width2 = size2 * getFontWidth(font2);
2585 int max_width = MAX(width1, width2);
2586 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2587
2588 pos->width = max_width;
2589
2590 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591 max_width, max_height);
2592 }
2593 #endif
2594 }
2595 }
2596
2597 #if 1
2598 /* correct text size if "digits" is zero or less */
2599 if (size <= 0)
2600 size = strlen(int2str(value, size));
2601
2602 /* dynamically correct text alignment */
2603 pos->width = size * getFontWidth(font);
2604 #endif
2605
2606 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2607 int2str(value, size), font, mask_mode);
2608 }
2609 else if (type == TYPE_ELEMENT)
2610 {
2611 int element, graphic;
2612 Bitmap *src_bitmap;
2613 int src_x, src_y;
2614 int width, height;
2615 int dst_x = PANEL_XPOS(pos);
2616 int dst_y = PANEL_YPOS(pos);
2617
2618 #if 1
2619 if (value != EL_UNDEFINED && value != EL_EMPTY)
2620 {
2621 element = value;
2622 graphic = el2panelimg(value);
2623
2624 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2625
2626 #if 1
2627 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2628 size = TILESIZE;
2629 #endif
2630
2631 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2632 &src_x, &src_y);
2633
2634 width = graphic_info[graphic].width * size / TILESIZE;
2635 height = graphic_info[graphic].height * size / TILESIZE;
2636
2637 if (draw_masked)
2638 {
2639 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2640 dst_x - src_x, dst_y - src_y);
2641 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2642 dst_x, dst_y);
2643 }
2644 else
2645 {
2646 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2647 dst_x, dst_y);
2648 }
2649 }
2650 #else
2651 if (value == EL_UNDEFINED || value == EL_EMPTY)
2652 {
2653 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2654 graphic = el2panelimg(element);
2655
2656 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2657 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2658 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2659 }
2660 else
2661 {
2662 element = value;
2663 graphic = el2panelimg(value);
2664
2665 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2666 }
2667
2668 width = graphic_info[graphic].width * size / TILESIZE;
2669 height = graphic_info[graphic].height * size / TILESIZE;
2670
2671 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2672 #endif
2673 }
2674 else if (type == TYPE_STRING)
2675 {
2676 boolean active = (value != 0);
2677 char *state_normal = "off";
2678 char *state_active = "on";
2679 char *state = (active ? state_active : state_normal);
2680 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2681 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2682 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2683 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2684
2685 if (nr == GAME_PANEL_GRAVITY_STATE)
2686 {
2687 int font1 = pos->font; /* (used for normal state) */
2688 int font2 = pos->font_alt; /* (used for active state) */
2689 #if 0
2690 int size1 = strlen(state_normal);
2691 int size2 = strlen(state_active);
2692 int width1 = size1 * getFontWidth(font1);
2693 int width2 = size2 * getFontWidth(font2);
2694 int max_width = MAX(width1, width2);
2695 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2696
2697 pos->width = max_width;
2698
2699 /* clear background for values that may have changed its size */
2700 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701 max_width, max_height);
2702 #endif
2703
2704 font = (active ? font2 : font1);
2705 }
2706
2707 if (s != NULL)
2708 {
2709 char *s_cut;
2710
2711 #if 1
2712 if (size <= 0)
2713 {
2714 /* don't truncate output if "chars" is zero or less */
2715 size = strlen(s);
2716
2717 /* dynamically correct text alignment */
2718 pos->width = size * getFontWidth(font);
2719 }
2720 #endif
2721
2722 s_cut = getStringCopyN(s, size);
2723
2724 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2725 s_cut, font, mask_mode);
2726
2727 free(s_cut);
2728 }
2729 }
2730
2731 redraw_mask |= REDRAW_DOOR_1;
2732 }
2733
2734 game_status = GAME_MODE_PLAYING;
2735 }
2736
UpdateAndDisplayGameControlValues()2737 void UpdateAndDisplayGameControlValues()
2738 {
2739 if (tape.warp_forward)
2740 return;
2741
2742 UpdateGameControlValues();
2743 DisplayGameControlValues();
2744 }
2745
DrawGameValue_Emeralds(int value)2746 void DrawGameValue_Emeralds(int value)
2747 {
2748 struct TextPosInfo *pos = &game.panel.gems;
2749 int font_nr = pos->font;
2750 int font_width = getFontWidth(font_nr);
2751 int chars = pos->size;
2752
2753 #if 1
2754 return; /* !!! USE NEW STUFF !!! */
2755 #endif
2756
2757 if (PANEL_DEACTIVATED(pos))
2758 return;
2759
2760 pos->width = chars * font_width;
2761
2762 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2763 }
2764
DrawGameValue_Dynamite(int value)2765 void DrawGameValue_Dynamite(int value)
2766 {
2767 struct TextPosInfo *pos = &game.panel.inventory_count;
2768 int font_nr = pos->font;
2769 int font_width = getFontWidth(font_nr);
2770 int chars = pos->size;
2771
2772 #if 1
2773 return; /* !!! USE NEW STUFF !!! */
2774 #endif
2775
2776 if (PANEL_DEACTIVATED(pos))
2777 return;
2778
2779 pos->width = chars * font_width;
2780
2781 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2782 }
2783
DrawGameValue_Score(int value)2784 void DrawGameValue_Score(int value)
2785 {
2786 struct TextPosInfo *pos = &game.panel.score;
2787 int font_nr = pos->font;
2788 int font_width = getFontWidth(font_nr);
2789 int chars = pos->size;
2790
2791 #if 1
2792 return; /* !!! USE NEW STUFF !!! */
2793 #endif
2794
2795 if (PANEL_DEACTIVATED(pos))
2796 return;
2797
2798 pos->width = chars * font_width;
2799
2800 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 }
2802
DrawGameValue_Time(int value)2803 void DrawGameValue_Time(int value)
2804 {
2805 struct TextPosInfo *pos = &game.panel.time;
2806 static int last_value = -1;
2807 int chars1 = 3;
2808 int chars2 = 4;
2809 int chars = pos->size;
2810 int font1_nr = pos->font;
2811 int font2_nr = pos->font_alt;
2812 int font_nr = font1_nr;
2813 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2814
2815 #if 1
2816 return; /* !!! USE NEW STUFF !!! */
2817 #endif
2818
2819 if (PANEL_DEACTIVATED(pos))
2820 return;
2821
2822 if (use_dynamic_chars) /* use dynamic number of chars */
2823 {
2824 chars = (value < 1000 ? chars1 : chars2);
2825 font_nr = (value < 1000 ? font1_nr : font2_nr);
2826 }
2827
2828 /* clear background if value just changed its size (dynamic chars only) */
2829 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2830 {
2831 int width1 = chars1 * getFontWidth(font1_nr);
2832 int width2 = chars2 * getFontWidth(font2_nr);
2833 int max_width = MAX(width1, width2);
2834 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2835
2836 pos->width = max_width;
2837
2838 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2839 max_width, max_height);
2840 }
2841
2842 pos->width = chars * getFontWidth(font_nr);
2843
2844 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2845
2846 last_value = value;
2847 }
2848
DrawGameValue_Level(int value)2849 void DrawGameValue_Level(int value)
2850 {
2851 struct TextPosInfo *pos = &game.panel.level_number;
2852 int chars1 = 2;
2853 int chars2 = 3;
2854 int chars = pos->size;
2855 int font1_nr = pos->font;
2856 int font2_nr = pos->font_alt;
2857 int font_nr = font1_nr;
2858 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2859
2860 #if 1
2861 return; /* !!! USE NEW STUFF !!! */
2862 #endif
2863
2864 if (PANEL_DEACTIVATED(pos))
2865 return;
2866
2867 if (use_dynamic_chars) /* use dynamic number of chars */
2868 {
2869 chars = (level_nr < 100 ? chars1 : chars2);
2870 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2871 }
2872
2873 pos->width = chars * getFontWidth(font_nr);
2874
2875 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2876 }
2877
DrawGameValue_Keys(int key[MAX_NUM_KEYS])2878 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2879 {
2880 int i;
2881
2882 #if 1
2883 return; /* !!! USE NEW STUFF !!! */
2884 #endif
2885
2886 for (i = 0; i < MAX_NUM_KEYS; i++)
2887 {
2888 struct TextPosInfo *pos = &game.panel.key[i];
2889 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2890 int src_y = DOOR_GFX_PAGEY1 + 123;
2891 int dst_x = PANEL_XPOS(pos);
2892 int dst_y = PANEL_YPOS(pos);
2893
2894 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2895 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2896 EL_KEY_1) + i;
2897 int graphic = el2edimg(element);
2898
2899 if (PANEL_DEACTIVATED(pos))
2900 continue;
2901
2902 #if 0
2903 /* masked blit with tiles from half-size scaled bitmap does not work yet
2904 (no mask bitmap created for these sizes after loading and scaling) --
2905 solution: load without creating mask, scale, then create final mask */
2906
2907 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2909
2910 if (key[i])
2911 {
2912 Bitmap *src_bitmap;
2913 int src_x, src_y;
2914
2915 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2916
2917 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2918 dst_x - src_x, dst_y - src_y);
2919 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2920 dst_x, dst_y);
2921 }
2922 #else
2923 if (key[i])
2924 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2925 else
2926 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2927 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2928 #endif
2929 }
2930 }
2931
DrawAllGameValues(int emeralds,int dynamite,int score,int time,int key_bits)2932 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2933 int key_bits)
2934 {
2935 int key[MAX_NUM_KEYS];
2936 int i;
2937
2938 /* prevent EM engine from updating time/score values parallel to GameWon() */
2939 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2940 local_player->LevelSolved)
2941 return;
2942
2943 for (i = 0; i < MAX_NUM_KEYS; i++)
2944 key[i] = key_bits & (1 << i);
2945
2946 DrawGameValue_Level(level_nr);
2947
2948 DrawGameValue_Emeralds(emeralds);
2949 DrawGameValue_Dynamite(dynamite);
2950 DrawGameValue_Score(score);
2951 DrawGameValue_Time(time);
2952
2953 DrawGameValue_Keys(key);
2954 }
2955
UpdateGameDoorValues()2956 void UpdateGameDoorValues()
2957 {
2958 UpdateGameControlValues();
2959 }
2960
DrawGameDoorValues()2961 void DrawGameDoorValues()
2962 {
2963 DisplayGameControlValues();
2964 }
2965
DrawGameDoorValues_OLD()2966 void DrawGameDoorValues_OLD()
2967 {
2968 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2969 int dynamite_value = 0;
2970 int score_value = (local_player->LevelSolved ? local_player->score_final :
2971 local_player->score);
2972 int gems_value = local_player->gems_still_needed;
2973 int key_bits = 0;
2974 int i, j;
2975
2976 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2977 {
2978 DrawGameDoorValues_EM();
2979
2980 return;
2981 }
2982
2983 if (game.centered_player_nr == -1)
2984 {
2985 for (i = 0; i < MAX_PLAYERS; i++)
2986 {
2987 for (j = 0; j < MAX_NUM_KEYS; j++)
2988 if (stored_player[i].key[j])
2989 key_bits |= (1 << j);
2990
2991 dynamite_value += stored_player[i].inventory_size;
2992 }
2993 }
2994 else
2995 {
2996 int player_nr = game.centered_player_nr;
2997
2998 for (i = 0; i < MAX_NUM_KEYS; i++)
2999 if (stored_player[player_nr].key[i])
3000 key_bits |= (1 << i);
3001
3002 dynamite_value = stored_player[player_nr].inventory_size;
3003 }
3004
3005 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3006 key_bits);
3007 }
3008
3009
3010 /*
3011 =============================================================================
3012 InitGameEngine()
3013 -----------------------------------------------------------------------------
3014 initialize game engine due to level / tape version number
3015 =============================================================================
3016 */
3017
InitGameEngine()3018 static void InitGameEngine()
3019 {
3020 int i, j, k, l, x, y;
3021
3022 /* set game engine from tape file when re-playing, else from level file */
3023 game.engine_version = (tape.playing ? tape.engine_version :
3024 level.game_version);
3025
3026 /* ---------------------------------------------------------------------- */
3027 /* set flags for bugs and changes according to active game engine version */
3028 /* ---------------------------------------------------------------------- */
3029
3030 /*
3031 Summary of bugfix/change:
3032 Fixed handling for custom elements that change when pushed by the player.
3033
3034 Fixed/changed in version:
3035 3.1.0
3036
3037 Description:
3038 Before 3.1.0, custom elements that "change when pushing" changed directly
3039 after the player started pushing them (until then handled in "DigField()").
3040 Since 3.1.0, these custom elements are not changed until the "pushing"
3041 move of the element is finished (now handled in "ContinueMoving()").
3042
3043 Affected levels/tapes:
3044 The first condition is generally needed for all levels/tapes before version
3045 3.1.0, which might use the old behaviour before it was changed; known tapes
3046 that are affected are some tapes from the level set "Walpurgis Gardens" by
3047 Jamie Cullen.
3048 The second condition is an exception from the above case and is needed for
3049 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3050 above (including some development versions of 3.1.0), but before it was
3051 known that this change would break tapes like the above and was fixed in
3052 3.1.1, so that the changed behaviour was active although the engine version
3053 while recording maybe was before 3.1.0. There is at least one tape that is
3054 affected by this exception, which is the tape for the one-level set "Bug
3055 Machine" by Juergen Bonhagen.
3056 */
3057
3058 game.use_change_when_pushing_bug =
3059 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3060 !(tape.playing &&
3061 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3062 tape.game_version < VERSION_IDENT(3,1,1,0)));
3063
3064 /*
3065 Summary of bugfix/change:
3066 Fixed handling for blocking the field the player leaves when moving.
3067
3068 Fixed/changed in version:
3069 3.1.1
3070
3071 Description:
3072 Before 3.1.1, when "block last field when moving" was enabled, the field
3073 the player is leaving when moving was blocked for the time of the move,
3074 and was directly unblocked afterwards. This resulted in the last field
3075 being blocked for exactly one less than the number of frames of one player
3076 move. Additionally, even when blocking was disabled, the last field was
3077 blocked for exactly one frame.
3078 Since 3.1.1, due to changes in player movement handling, the last field
3079 is not blocked at all when blocking is disabled. When blocking is enabled,
3080 the last field is blocked for exactly the number of frames of one player
3081 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3082 last field is blocked for exactly one more than the number of frames of
3083 one player move.
3084
3085 Affected levels/tapes:
3086 (!!! yet to be determined -- probably many !!!)
3087 */
3088
3089 game.use_block_last_field_bug =
3090 (game.engine_version < VERSION_IDENT(3,1,1,0));
3091
3092 /*
3093 Summary of bugfix/change:
3094 Changed behaviour of CE changes with multiple changes per single frame.
3095
3096 Fixed/changed in version:
3097 3.2.0-6
3098
3099 Description:
3100 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3101 This resulted in race conditions where CEs seem to behave strange in some
3102 situations (where triggered CE changes were just skipped because there was
3103 already a CE change on that tile in the playfield in that engine frame).
3104 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3105 (The number of changes per frame must be limited in any case, because else
3106 it is easily possible to define CE changes that would result in an infinite
3107 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3108 should be set large enough so that it would only be reached in cases where
3109 the corresponding CE change conditions run into a loop. Therefore, it seems
3110 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3111 maximal number of change pages for custom elements.)
3112
3113 Affected levels/tapes:
3114 Probably many.
3115 */
3116
3117 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3118 game.max_num_changes_per_frame = 1;
3119 #else
3120 game.max_num_changes_per_frame =
3121 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3122 #endif
3123
3124 /* ---------------------------------------------------------------------- */
3125
3126 /* default scan direction: scan playfield from top/left to bottom/right */
3127 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3128
3129 /* dynamically adjust element properties according to game engine version */
3130 InitElementPropertiesEngine(game.engine_version);
3131
3132 #if 0
3133 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3134 printf(" tape version == %06d [%s] [file: %06d]\n",
3135 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3136 tape.file_version);
3137 printf(" => game.engine_version == %06d\n", game.engine_version);
3138 #endif
3139
3140 /* ---------- initialize player's initial move delay --------------------- */
3141
3142 /* dynamically adjust player properties according to level information */
3143 for (i = 0; i < MAX_PLAYERS; i++)
3144 game.initial_move_delay_value[i] =
3145 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3146
3147 /* dynamically adjust player properties according to game engine version */
3148 for (i = 0; i < MAX_PLAYERS; i++)
3149 game.initial_move_delay[i] =
3150 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3151 game.initial_move_delay_value[i] : 0);
3152
3153 /* ---------- initialize player's initial push delay --------------------- */
3154
3155 /* dynamically adjust player properties according to game engine version */
3156 game.initial_push_delay_value =
3157 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3158
3159 /* ---------- initialize changing elements ------------------------------- */
3160
3161 /* initialize changing elements information */
3162 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3163 {
3164 struct ElementInfo *ei = &element_info[i];
3165
3166 /* this pointer might have been changed in the level editor */
3167 ei->change = &ei->change_page[0];
3168
3169 if (!IS_CUSTOM_ELEMENT(i))
3170 {
3171 ei->change->target_element = EL_EMPTY_SPACE;
3172 ei->change->delay_fixed = 0;
3173 ei->change->delay_random = 0;
3174 ei->change->delay_frames = 1;
3175 }
3176
3177 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3178 {
3179 ei->has_change_event[j] = FALSE;
3180
3181 ei->event_page_nr[j] = 0;
3182 ei->event_page[j] = &ei->change_page[0];
3183 }
3184 }
3185
3186 /* add changing elements from pre-defined list */
3187 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3188 {
3189 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3190 struct ElementInfo *ei = &element_info[ch_delay->element];
3191
3192 ei->change->target_element = ch_delay->target_element;
3193 ei->change->delay_fixed = ch_delay->change_delay;
3194
3195 ei->change->pre_change_function = ch_delay->pre_change_function;
3196 ei->change->change_function = ch_delay->change_function;
3197 ei->change->post_change_function = ch_delay->post_change_function;
3198
3199 ei->change->can_change = TRUE;
3200 ei->change->can_change_or_has_action = TRUE;
3201
3202 ei->has_change_event[CE_DELAY] = TRUE;
3203
3204 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3205 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3206 }
3207
3208 /* ---------- initialize internal run-time variables --------------------- */
3209
3210 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3211 {
3212 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3213
3214 for (j = 0; j < ei->num_change_pages; j++)
3215 {
3216 ei->change_page[j].can_change_or_has_action =
3217 (ei->change_page[j].can_change |
3218 ei->change_page[j].has_action);
3219 }
3220 }
3221
3222 /* add change events from custom element configuration */
3223 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3224 {
3225 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3226
3227 for (j = 0; j < ei->num_change_pages; j++)
3228 {
3229 if (!ei->change_page[j].can_change_or_has_action)
3230 continue;
3231
3232 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3233 {
3234 /* only add event page for the first page found with this event */
3235 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3236 {
3237 ei->has_change_event[k] = TRUE;
3238
3239 ei->event_page_nr[k] = j;
3240 ei->event_page[k] = &ei->change_page[j];
3241 }
3242 }
3243 }
3244 }
3245
3246 #if 1
3247 /* ---------- initialize reference elements in change conditions --------- */
3248
3249 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250 {
3251 int element = EL_CUSTOM_START + i;
3252 struct ElementInfo *ei = &element_info[element];
3253
3254 for (j = 0; j < ei->num_change_pages; j++)
3255 {
3256 int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258 if (trigger_element >= EL_PREV_CE_8 &&
3259 trigger_element <= EL_NEXT_CE_8)
3260 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262 ei->change_page[j].trigger_element = trigger_element;
3263 }
3264 }
3265 #endif
3266
3267 /* ---------- initialize run-time trigger player and element ------------- */
3268
3269 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3270 {
3271 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3272
3273 for (j = 0; j < ei->num_change_pages; j++)
3274 {
3275 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3276 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3277 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3278 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3279 ei->change_page[j].actual_trigger_ce_value = 0;
3280 ei->change_page[j].actual_trigger_ce_score = 0;
3281 }
3282 }
3283
3284 /* ---------- initialize trigger events ---------------------------------- */
3285
3286 /* initialize trigger events information */
3287 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3288 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3289 trigger_events[i][j] = FALSE;
3290
3291 /* add trigger events from element change event properties */
3292 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3293 {
3294 struct ElementInfo *ei = &element_info[i];
3295
3296 for (j = 0; j < ei->num_change_pages; j++)
3297 {
3298 if (!ei->change_page[j].can_change_or_has_action)
3299 continue;
3300
3301 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3302 {
3303 int trigger_element = ei->change_page[j].trigger_element;
3304
3305 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3306 {
3307 if (ei->change_page[j].has_event[k])
3308 {
3309 if (IS_GROUP_ELEMENT(trigger_element))
3310 {
3311 struct ElementGroupInfo *group =
3312 element_info[trigger_element].group;
3313
3314 for (l = 0; l < group->num_elements_resolved; l++)
3315 trigger_events[group->element_resolved[l]][k] = TRUE;
3316 }
3317 else if (trigger_element == EL_ANY_ELEMENT)
3318 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3319 trigger_events[l][k] = TRUE;
3320 else
3321 trigger_events[trigger_element][k] = TRUE;
3322 }
3323 }
3324 }
3325 }
3326 }
3327
3328 /* ---------- initialize push delay -------------------------------------- */
3329
3330 /* initialize push delay values to default */
3331 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3332 {
3333 if (!IS_CUSTOM_ELEMENT(i))
3334 {
3335 /* set default push delay values (corrected since version 3.0.7-1) */
3336 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3337 {
3338 element_info[i].push_delay_fixed = 2;
3339 element_info[i].push_delay_random = 8;
3340 }
3341 else
3342 {
3343 element_info[i].push_delay_fixed = 8;
3344 element_info[i].push_delay_random = 8;
3345 }
3346 }
3347 }
3348
3349 /* set push delay value for certain elements from pre-defined list */
3350 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3351 {
3352 int e = push_delay_list[i].element;
3353
3354 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3355 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3356 }
3357
3358 /* set push delay value for Supaplex elements for newer engine versions */
3359 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3360 {
3361 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362 {
3363 if (IS_SP_ELEMENT(i))
3364 {
3365 /* set SP push delay to just enough to push under a falling zonk */
3366 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3367
3368 element_info[i].push_delay_fixed = delay;
3369 element_info[i].push_delay_random = 0;
3370 }
3371 }
3372 }
3373
3374 /* ---------- initialize move stepsize ----------------------------------- */
3375
3376 /* initialize move stepsize values to default */
3377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378 if (!IS_CUSTOM_ELEMENT(i))
3379 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3380
3381 /* set move stepsize value for certain elements from pre-defined list */
3382 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3383 {
3384 int e = move_stepsize_list[i].element;
3385
3386 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3387 }
3388
3389 /* ---------- initialize collect score ----------------------------------- */
3390
3391 /* initialize collect score values for custom elements from initial value */
3392 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3393 if (IS_CUSTOM_ELEMENT(i))
3394 element_info[i].collect_score = element_info[i].collect_score_initial;
3395
3396 /* ---------- initialize collect count ----------------------------------- */
3397
3398 /* initialize collect count values for non-custom elements */
3399 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400 if (!IS_CUSTOM_ELEMENT(i))
3401 element_info[i].collect_count_initial = 0;
3402
3403 /* add collect count values for all elements from pre-defined list */
3404 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3405 element_info[collect_count_list[i].element].collect_count_initial =
3406 collect_count_list[i].count;
3407
3408 /* ---------- initialize access direction -------------------------------- */
3409
3410 /* initialize access direction values to default (access from every side) */
3411 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412 if (!IS_CUSTOM_ELEMENT(i))
3413 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3414
3415 /* set access direction value for certain elements from pre-defined list */
3416 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3417 element_info[access_direction_list[i].element].access_direction =
3418 access_direction_list[i].direction;
3419
3420 /* ---------- initialize explosion content ------------------------------- */
3421 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3422 {
3423 if (IS_CUSTOM_ELEMENT(i))
3424 continue;
3425
3426 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3427 {
3428 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3429
3430 element_info[i].content.e[x][y] =
3431 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3432 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3433 i == EL_PLAYER_3 ? EL_EMERALD :
3434 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3435 i == EL_MOLE ? EL_EMERALD_RED :
3436 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3437 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3438 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3439 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3440 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3441 i == EL_WALL_EMERALD ? EL_EMERALD :
3442 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3443 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3444 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3445 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3446 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3447 i == EL_WALL_PEARL ? EL_PEARL :
3448 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3449 EL_EMPTY);
3450 }
3451 }
3452
3453 /* ---------- initialize recursion detection ------------------------------ */
3454 recursion_loop_depth = 0;
3455 recursion_loop_detected = FALSE;
3456 recursion_loop_element = EL_UNDEFINED;
3457
3458 /* ---------- initialize graphics engine ---------------------------------- */
3459 game.scroll_delay_value =
3460 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3461 setup.scroll_delay ? setup.scroll_delay_value : 0);
3462 game.scroll_delay_value =
3463 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464 }
3465
get_num_special_action(int element,int action_first,int action_last)3466 int get_num_special_action(int element, int action_first, int action_last)
3467 {
3468 int num_special_action = 0;
3469 int i, j;
3470
3471 for (i = action_first; i <= action_last; i++)
3472 {
3473 boolean found = FALSE;
3474
3475 for (j = 0; j < NUM_DIRECTIONS; j++)
3476 if (el_act_dir2img(element, i, j) !=
3477 el_act_dir2img(element, ACTION_DEFAULT, j))
3478 found = TRUE;
3479
3480 if (found)
3481 num_special_action++;
3482 else
3483 break;
3484 }
3485
3486 return num_special_action;
3487 }
3488
3489
3490 /*
3491 =============================================================================
3492 InitGame()
3493 -----------------------------------------------------------------------------
3494 initialize and start new game
3495 =============================================================================
3496 */
3497
InitGame()3498 void InitGame()
3499 {
3500 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3501 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3502 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3503 #if 0
3504 boolean do_fading = (game_status == GAME_MODE_MAIN);
3505 #endif
3506 #if 1
3507 int initial_move_dir = MV_DOWN;
3508 #else
3509 int initial_move_dir = MV_NONE;
3510 #endif
3511 int i, j, x, y;
3512
3513 game_status = GAME_MODE_PLAYING;
3514
3515 #if 1
3516 /* needed if different viewport properties defined for playing */
3517 ChangeViewportPropertiesIfNeeded();
3518 #endif
3519
3520 #if 1
3521 DrawCompleteVideoDisplay();
3522 #endif
3523
3524 InitGameEngine();
3525 InitGameControlValues();
3526
3527 /* don't play tapes over network */
3528 network_playing = (options.network && !tape.playing);
3529
3530 for (i = 0; i < MAX_PLAYERS; i++)
3531 {
3532 struct PlayerInfo *player = &stored_player[i];
3533
3534 player->index_nr = i;
3535 player->index_bit = (1 << i);
3536 player->element_nr = EL_PLAYER_1 + i;
3537
3538 player->present = FALSE;
3539 player->active = FALSE;
3540 player->mapped = FALSE;
3541
3542 player->killed = FALSE;
3543 player->reanimated = FALSE;
3544
3545 player->action = 0;
3546 player->effective_action = 0;
3547 player->programmed_action = 0;
3548
3549 player->score = 0;
3550 player->score_final = 0;
3551
3552 player->gems_still_needed = level.gems_needed;
3553 player->sokobanfields_still_needed = 0;
3554 player->lights_still_needed = 0;
3555 player->friends_still_needed = 0;
3556
3557 for (j = 0; j < MAX_NUM_KEYS; j++)
3558 player->key[j] = FALSE;
3559
3560 player->num_white_keys = 0;
3561
3562 player->dynabomb_count = 0;
3563 player->dynabomb_size = 1;
3564 player->dynabombs_left = 0;
3565 player->dynabomb_xl = FALSE;
3566
3567 player->MovDir = initial_move_dir;
3568 player->MovPos = 0;
3569 player->GfxPos = 0;
3570 player->GfxDir = initial_move_dir;
3571 player->GfxAction = ACTION_DEFAULT;
3572 player->Frame = 0;
3573 player->StepFrame = 0;
3574
3575 player->initial_element = player->element_nr;
3576 player->artwork_element =
3577 (level.use_artwork_element[i] ? level.artwork_element[i] :
3578 player->element_nr);
3579 player->use_murphy = FALSE;
3580
3581 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3582 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3583
3584 player->gravity = level.initial_player_gravity[i];
3585
3586 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3587
3588 player->actual_frame_counter = 0;
3589
3590 player->step_counter = 0;
3591
3592 player->last_move_dir = initial_move_dir;
3593
3594 player->is_active = FALSE;
3595
3596 player->is_waiting = FALSE;
3597 player->is_moving = FALSE;
3598 player->is_auto_moving = FALSE;
3599 player->is_digging = FALSE;
3600 player->is_snapping = FALSE;
3601 player->is_collecting = FALSE;
3602 player->is_pushing = FALSE;
3603 player->is_switching = FALSE;
3604 player->is_dropping = FALSE;
3605 player->is_dropping_pressed = FALSE;
3606
3607 player->is_bored = FALSE;
3608 player->is_sleeping = FALSE;
3609
3610 player->frame_counter_bored = -1;
3611 player->frame_counter_sleeping = -1;
3612
3613 player->anim_delay_counter = 0;
3614 player->post_delay_counter = 0;
3615
3616 player->dir_waiting = initial_move_dir;
3617 player->action_waiting = ACTION_DEFAULT;
3618 player->last_action_waiting = ACTION_DEFAULT;
3619 player->special_action_bored = ACTION_DEFAULT;
3620 player->special_action_sleeping = ACTION_DEFAULT;
3621
3622 player->switch_x = -1;
3623 player->switch_y = -1;
3624
3625 player->drop_x = -1;
3626 player->drop_y = -1;
3627
3628 player->show_envelope = 0;
3629
3630 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3631
3632 player->push_delay = -1; /* initialized when pushing starts */
3633 player->push_delay_value = game.initial_push_delay_value;
3634
3635 player->drop_delay = 0;
3636 player->drop_pressed_delay = 0;
3637
3638 player->last_jx = -1;
3639 player->last_jy = -1;
3640 player->jx = -1;
3641 player->jy = -1;
3642
3643 player->shield_normal_time_left = 0;
3644 player->shield_deadly_time_left = 0;
3645
3646 player->inventory_infinite_element = EL_UNDEFINED;
3647 player->inventory_size = 0;
3648
3649 if (level.use_initial_inventory[i])
3650 {
3651 for (j = 0; j < level.initial_inventory_size[i]; j++)
3652 {
3653 int element = level.initial_inventory_content[i][j];
3654 int collect_count = element_info[element].collect_count_initial;
3655 int k;
3656
3657 if (!IS_CUSTOM_ELEMENT(element))
3658 collect_count = 1;
3659
3660 if (collect_count == 0)
3661 player->inventory_infinite_element = element;
3662 else
3663 for (k = 0; k < collect_count; k++)
3664 if (player->inventory_size < MAX_INVENTORY_SIZE)
3665 player->inventory_element[player->inventory_size++] = element;
3666 }
3667 }
3668
3669 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3670 SnapField(player, 0, 0);
3671
3672 player->LevelSolved = FALSE;
3673 player->GameOver = FALSE;
3674
3675 player->LevelSolved_GameWon = FALSE;
3676 player->LevelSolved_GameEnd = FALSE;
3677 player->LevelSolved_PanelOff = FALSE;
3678 player->LevelSolved_SaveTape = FALSE;
3679 player->LevelSolved_SaveScore = FALSE;
3680 player->LevelSolved_CountingTime = 0;
3681 player->LevelSolved_CountingScore = 0;
3682
3683 map_player_action[i] = i;
3684 }
3685
3686 network_player_action_received = FALSE;
3687
3688 #if defined(NETWORK_AVALIABLE)
3689 /* initial null action */
3690 if (network_playing)
3691 SendToServer_MovePlayer(MV_NONE);
3692 #endif
3693
3694 ZX = ZY = -1;
3695 ExitX = ExitY = -1;
3696
3697 FrameCounter = 0;
3698 TimeFrames = 0;
3699 TimePlayed = 0;
3700 TimeLeft = level.time;
3701 TapeTime = 0;
3702
3703 ScreenMovDir = MV_NONE;
3704 ScreenMovPos = 0;
3705 ScreenGfxPos = 0;
3706
3707 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3708
3709 AllPlayersGone = FALSE;
3710
3711 game.no_time_limit = (level.time == 0);
3712
3713 game.yamyam_content_nr = 0;
3714 game.robot_wheel_active = FALSE;
3715 game.magic_wall_active = FALSE;
3716 game.magic_wall_time_left = 0;
3717 game.light_time_left = 0;
3718 game.timegate_time_left = 0;
3719 game.switchgate_pos = 0;
3720 game.wind_direction = level.wind_direction_initial;
3721
3722 #if !USE_PLAYER_GRAVITY
3723 game.gravity = FALSE;
3724 game.explosions_delayed = TRUE;
3725 #endif
3726
3727 game.lenses_time_left = 0;
3728 game.magnify_time_left = 0;
3729
3730 game.ball_state = level.ball_state_initial;
3731 game.ball_content_nr = 0;
3732
3733 game.envelope_active = FALSE;
3734
3735 /* set focus to local player for network games, else to all players */
3736 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3737 game.centered_player_nr_next = game.centered_player_nr;
3738 game.set_centered_player = FALSE;
3739
3740 if (network_playing && tape.recording)
3741 {
3742 /* store client dependent player focus when recording network games */
3743 tape.centered_player_nr_next = game.centered_player_nr_next;
3744 tape.set_centered_player = TRUE;
3745 }
3746
3747 for (i = 0; i < NUM_BELTS; i++)
3748 {
3749 game.belt_dir[i] = MV_NONE;
3750 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3751 }
3752
3753 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3754 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3755
3756 SCAN_PLAYFIELD(x, y)
3757 {
3758 Feld[x][y] = level.field[x][y];
3759 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3760 ChangeDelay[x][y] = 0;
3761 ChangePage[x][y] = -1;
3762 #if USE_NEW_CUSTOM_VALUE
3763 CustomValue[x][y] = 0; /* initialized in InitField() */
3764 #endif
3765 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3766 AmoebaNr[x][y] = 0;
3767 WasJustMoving[x][y] = 0;
3768 WasJustFalling[x][y] = 0;
3769 CheckCollision[x][y] = 0;
3770 CheckImpact[x][y] = 0;
3771 Stop[x][y] = FALSE;
3772 Pushed[x][y] = FALSE;
3773
3774 ChangeCount[x][y] = 0;
3775 ChangeEvent[x][y] = -1;
3776
3777 ExplodePhase[x][y] = 0;
3778 ExplodeDelay[x][y] = 0;
3779 ExplodeField[x][y] = EX_TYPE_NONE;
3780
3781 RunnerVisit[x][y] = 0;
3782 PlayerVisit[x][y] = 0;
3783
3784 GfxFrame[x][y] = 0;
3785 GfxRandom[x][y] = INIT_GFX_RANDOM();
3786 GfxElement[x][y] = EL_UNDEFINED;
3787 GfxAction[x][y] = ACTION_DEFAULT;
3788 GfxDir[x][y] = MV_NONE;
3789 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3790 }
3791
3792 SCAN_PLAYFIELD(x, y)
3793 {
3794 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3795 emulate_bd = FALSE;
3796 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3797 emulate_sb = FALSE;
3798 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3799 emulate_sp = FALSE;
3800
3801 InitField(x, y, TRUE);
3802
3803 ResetGfxAnimation(x, y);
3804 }
3805
3806 InitBeltMovement();
3807
3808 for (i = 0; i < MAX_PLAYERS; i++)
3809 {
3810 struct PlayerInfo *player = &stored_player[i];
3811
3812 /* set number of special actions for bored and sleeping animation */
3813 player->num_special_action_bored =
3814 get_num_special_action(player->artwork_element,
3815 ACTION_BORING_1, ACTION_BORING_LAST);
3816 player->num_special_action_sleeping =
3817 get_num_special_action(player->artwork_element,
3818 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3819 }
3820
3821 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3822 emulate_sb ? EMU_SOKOBAN :
3823 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3824
3825 #if USE_NEW_ALL_SLIPPERY
3826 /* initialize type of slippery elements */
3827 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3828 {
3829 if (!IS_CUSTOM_ELEMENT(i))
3830 {
3831 /* default: elements slip down either to the left or right randomly */
3832 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3833
3834 /* SP style elements prefer to slip down on the left side */
3835 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3837
3838 /* BD style elements prefer to slip down on the left side */
3839 if (game.emulation == EMU_BOULDERDASH)
3840 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3841 }
3842 }
3843 #endif
3844
3845 /* initialize explosion and ignition delay */
3846 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3847 {
3848 if (!IS_CUSTOM_ELEMENT(i))
3849 {
3850 int num_phase = 8;
3851 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3852 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3853 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3854 int last_phase = (num_phase + 1) * delay;
3855 int half_phase = (num_phase / 2) * delay;
3856
3857 element_info[i].explosion_delay = last_phase - 1;
3858 element_info[i].ignition_delay = half_phase;
3859
3860 if (i == EL_BLACK_ORB)
3861 element_info[i].ignition_delay = 1;
3862 }
3863
3864 #if 0
3865 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3866 element_info[i].explosion_delay = 1;
3867
3868 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3869 element_info[i].ignition_delay = 1;
3870 #endif
3871 }
3872
3873 /* correct non-moving belts to start moving left */
3874 for (i = 0; i < NUM_BELTS; i++)
3875 if (game.belt_dir[i] == MV_NONE)
3876 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3877
3878 #if USE_NEW_PLAYER_ASSIGNMENTS
3879 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3880 /* choose default local player */
3881 local_player = &stored_player[0];
3882
3883 for (i = 0; i < MAX_PLAYERS; i++)
3884 stored_player[i].connected = FALSE;
3885
3886 local_player->connected = TRUE;
3887 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3888
3889 if (tape.playing)
3890 {
3891 /* try to guess locally connected team mode players (needed for correct
3892 assignment of player figures from level to locally playing players) */
3893
3894 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (tape.player_participates[i])
3896 stored_player[i].connected = TRUE;
3897 }
3898 else if (setup.team_mode && !options.network)
3899 {
3900 /* try to guess locally connected team mode players (needed for correct
3901 assignment of player figures from level to locally playing players) */
3902
3903 for (i = 0; i < MAX_PLAYERS; i++)
3904 if (setup.input[i].use_joystick ||
3905 setup.input[i].key.left != KSYM_UNDEFINED)
3906 stored_player[i].connected = TRUE;
3907 }
3908
3909 #if 0
3910 for (i = 0; i < MAX_PLAYERS; i++)
3911 printf("::: player %d: %s\n", i,
3912 (stored_player[i].connected ? "connected" : "not connected"));
3913
3914 for (i = 0; i < MAX_PLAYERS; i++)
3915 printf("::: player %d: %s\n", i,
3916 (stored_player[i].present ? "present" : "not present"));
3917 #endif
3918
3919 /* check if any connected player was not found in playfield */
3920 for (i = 0; i < MAX_PLAYERS; i++)
3921 {
3922 struct PlayerInfo *player = &stored_player[i];
3923
3924 if (player->connected && !player->present)
3925 {
3926 struct PlayerInfo *field_player = NULL;
3927
3928 #if 0
3929 printf("::: looking for field player for player %d ...\n", i);
3930 #endif
3931
3932 /* assign first free player found that is present in the playfield */
3933
3934 /* first try: look for unmapped playfield player that is not connected */
3935 if (field_player == NULL)
3936 for (j = 0; j < MAX_PLAYERS; j++)
3937 if (stored_player[j].present &&
3938 !stored_player[j].mapped &&
3939 !stored_player[j].connected)
3940 field_player = &stored_player[j];
3941
3942 /* second try: look for *any* unmapped playfield player */
3943 if (field_player == NULL)
3944 for (j = 0; j < MAX_PLAYERS; j++)
3945 if (stored_player[j].present &&
3946 !stored_player[j].mapped)
3947 field_player = &stored_player[j];
3948
3949 if (field_player != NULL)
3950 {
3951 int jx = field_player->jx, jy = field_player->jy;
3952
3953 #if 0
3954 printf("::: found player figure %d\n", field_player->index_nr);
3955 #endif
3956
3957 player->present = FALSE;
3958 player->active = FALSE;
3959
3960 field_player->present = TRUE;
3961 field_player->active = TRUE;
3962
3963 /*
3964 player->initial_element = field_player->initial_element;
3965 player->artwork_element = field_player->artwork_element;
3966
3967 player->block_last_field = field_player->block_last_field;
3968 player->block_delay_adjustment = field_player->block_delay_adjustment;
3969 */
3970
3971 StorePlayer[jx][jy] = field_player->element_nr;
3972
3973 field_player->jx = field_player->last_jx = jx;
3974 field_player->jy = field_player->last_jy = jy;
3975
3976 if (local_player == player)
3977 local_player = field_player;
3978
3979 map_player_action[field_player->index_nr] = i;
3980
3981 field_player->mapped = TRUE;
3982
3983 #if 0
3984 printf("::: map_player_action[%d] == %d\n",
3985 field_player->index_nr, i);
3986 #endif
3987 }
3988 }
3989
3990 if (player->connected && player->present)
3991 player->mapped = TRUE;
3992 }
3993
3994 #else
3995
3996 /* check if any connected player was not found in playfield */
3997 for (i = 0; i < MAX_PLAYERS; i++)
3998 {
3999 struct PlayerInfo *player = &stored_player[i];
4000
4001 if (player->connected && !player->present)
4002 {
4003 for (j = 0; j < MAX_PLAYERS; j++)
4004 {
4005 struct PlayerInfo *field_player = &stored_player[j];
4006 int jx = field_player->jx, jy = field_player->jy;
4007
4008 /* assign first free player found that is present in the playfield */
4009 if (field_player->present && !field_player->connected)
4010 {
4011 player->present = TRUE;
4012 player->active = TRUE;
4013
4014 field_player->present = FALSE;
4015 field_player->active = FALSE;
4016
4017 player->initial_element = field_player->initial_element;
4018 player->artwork_element = field_player->artwork_element;
4019
4020 player->block_last_field = field_player->block_last_field;
4021 player->block_delay_adjustment = field_player->block_delay_adjustment;
4022
4023 StorePlayer[jx][jy] = player->element_nr;
4024
4025 player->jx = player->last_jx = jx;
4026 player->jy = player->last_jy = jy;
4027
4028 break;
4029 }
4030 }
4031 }
4032 }
4033 #endif
4034
4035 #if 0
4036 printf("::: local_player->present == %d\n", local_player->present);
4037 #endif
4038
4039 if (tape.playing)
4040 {
4041 /* when playing a tape, eliminate all players who do not participate */
4042
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044 for (i = 0; i < MAX_PLAYERS; i++)
4045 {
4046 if (stored_player[i].active &&
4047 !tape.player_participates[map_player_action[i]])
4048 {
4049 struct PlayerInfo *player = &stored_player[i];
4050 int jx = player->jx, jy = player->jy;
4051
4052 player->active = FALSE;
4053 StorePlayer[jx][jy] = 0;
4054 Feld[jx][jy] = EL_EMPTY;
4055 }
4056 }
4057 #else
4058 for (i = 0; i < MAX_PLAYERS; i++)
4059 {
4060 if (stored_player[i].active &&
4061 !tape.player_participates[i])
4062 {
4063 struct PlayerInfo *player = &stored_player[i];
4064 int jx = player->jx, jy = player->jy;
4065
4066 player->active = FALSE;
4067 StorePlayer[jx][jy] = 0;
4068 Feld[jx][jy] = EL_EMPTY;
4069 }
4070 }
4071 #endif
4072 }
4073 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4074 {
4075 /* when in single player mode, eliminate all but the first active player */
4076
4077 for (i = 0; i < MAX_PLAYERS; i++)
4078 {
4079 if (stored_player[i].active)
4080 {
4081 for (j = i + 1; j < MAX_PLAYERS; j++)
4082 {
4083 if (stored_player[j].active)
4084 {
4085 struct PlayerInfo *player = &stored_player[j];
4086 int jx = player->jx, jy = player->jy;
4087
4088 player->active = FALSE;
4089 player->present = FALSE;
4090
4091 StorePlayer[jx][jy] = 0;
4092 Feld[jx][jy] = EL_EMPTY;
4093 }
4094 }
4095 }
4096 }
4097 }
4098
4099 /* when recording the game, store which players take part in the game */
4100 if (tape.recording)
4101 {
4102 #if USE_NEW_PLAYER_ASSIGNMENTS
4103 for (i = 0; i < MAX_PLAYERS; i++)
4104 if (stored_player[i].connected)
4105 tape.player_participates[i] = TRUE;
4106 #else
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 if (stored_player[i].active)
4109 tape.player_participates[i] = TRUE;
4110 #endif
4111 }
4112
4113 if (options.debug)
4114 {
4115 for (i = 0; i < MAX_PLAYERS; i++)
4116 {
4117 struct PlayerInfo *player = &stored_player[i];
4118
4119 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4120 i+1,
4121 player->present,
4122 player->connected,
4123 player->active);
4124 if (local_player == player)
4125 printf("Player %d is local player.\n", i+1);
4126 }
4127 }
4128
4129 if (BorderElement == EL_EMPTY)
4130 {
4131 SBX_Left = 0;
4132 SBX_Right = lev_fieldx - SCR_FIELDX;
4133 SBY_Upper = 0;
4134 SBY_Lower = lev_fieldy - SCR_FIELDY;
4135 }
4136 else
4137 {
4138 SBX_Left = -1;
4139 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4140 SBY_Upper = -1;
4141 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4142 }
4143
4144 #if NEW_TILESIZE
4145
4146 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4147 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4148
4149 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4150 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4151
4152 if (EVEN(SCR_FIELDX))
4153 SBX_Left--;
4154 if (EVEN(SCR_FIELDY))
4155 SBY_Upper--;
4156
4157 #else
4158
4159 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4161
4162 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4164 #endif
4165
4166 /* if local player not found, look for custom element that might create
4167 the player (make some assumptions about the right custom element) */
4168 if (!local_player->present)
4169 {
4170 int start_x = 0, start_y = 0;
4171 int found_rating = 0;
4172 int found_element = EL_UNDEFINED;
4173 int player_nr = local_player->index_nr;
4174
4175 SCAN_PLAYFIELD(x, y)
4176 {
4177 int element = Feld[x][y];
4178 int content;
4179 int xx, yy;
4180 boolean is_player;
4181
4182 if (level.use_start_element[player_nr] &&
4183 level.start_element[player_nr] == element &&
4184 found_rating < 4)
4185 {
4186 start_x = x;
4187 start_y = y;
4188
4189 found_rating = 4;
4190 found_element = element;
4191 }
4192
4193 if (!IS_CUSTOM_ELEMENT(element))
4194 continue;
4195
4196 if (CAN_CHANGE(element))
4197 {
4198 for (i = 0; i < element_info[element].num_change_pages; i++)
4199 {
4200 /* check for player created from custom element as single target */
4201 content = element_info[element].change_page[i].target_element;
4202 is_player = ELEM_IS_PLAYER(content);
4203
4204 if (is_player && (found_rating < 3 ||
4205 (found_rating == 3 && element < found_element)))
4206 {
4207 start_x = x;
4208 start_y = y;
4209
4210 found_rating = 3;
4211 found_element = element;
4212 }
4213 }
4214 }
4215
4216 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4217 {
4218 /* check for player created from custom element as explosion content */
4219 content = element_info[element].content.e[xx][yy];
4220 is_player = ELEM_IS_PLAYER(content);
4221
4222 if (is_player && (found_rating < 2 ||
4223 (found_rating == 2 && element < found_element)))
4224 {
4225 start_x = x + xx - 1;
4226 start_y = y + yy - 1;
4227
4228 found_rating = 2;
4229 found_element = element;
4230 }
4231
4232 if (!CAN_CHANGE(element))
4233 continue;
4234
4235 for (i = 0; i < element_info[element].num_change_pages; i++)
4236 {
4237 /* check for player created from custom element as extended target */
4238 content =
4239 element_info[element].change_page[i].target_content.e[xx][yy];
4240
4241 is_player = ELEM_IS_PLAYER(content);
4242
4243 if (is_player && (found_rating < 1 ||
4244 (found_rating == 1 && element < found_element)))
4245 {
4246 start_x = x + xx - 1;
4247 start_y = y + yy - 1;
4248
4249 found_rating = 1;
4250 found_element = element;
4251 }
4252 }
4253 }
4254 }
4255
4256 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4257 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4258 start_x - MIDPOSX);
4259
4260 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4262 start_y - MIDPOSY);
4263 }
4264 else
4265 {
4266 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4267 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268 local_player->jx - MIDPOSX);
4269
4270 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272 local_player->jy - MIDPOSY);
4273 }
4274
4275 #if 0
4276 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4277 #endif
4278
4279 #if 0
4280 /* do not use PLAYING mask for fading out from main screen */
4281 game_status = GAME_MODE_MAIN;
4282 #endif
4283
4284 StopAnimation();
4285
4286 if (!game.restart_level)
4287 CloseDoor(DOOR_CLOSE_1);
4288
4289 #if 1
4290 if (level_editor_test_game)
4291 FadeSkipNextFadeIn();
4292 else
4293 FadeSetEnterScreen();
4294 #else
4295 if (level_editor_test_game)
4296 fading = fading_none;
4297 else
4298 fading = menu.destination;
4299 #endif
4300
4301 #if 1
4302 FadeOut(REDRAW_FIELD);
4303 #else
4304 if (do_fading)
4305 FadeOut(REDRAW_FIELD);
4306 #endif
4307
4308 #if 0
4309 game_status = GAME_MODE_PLAYING;
4310 #endif
4311
4312 /* !!! FIX THIS (START) !!! */
4313 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4314 {
4315 InitGameEngine_EM();
4316
4317 /* blit playfield from scroll buffer to normal back buffer for fading in */
4318 BlitScreenToBitmap_EM(backbuffer);
4319 }
4320 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4321 {
4322 InitGameEngine_SP();
4323
4324 /* blit playfield from scroll buffer to normal back buffer for fading in */
4325 BlitScreenToBitmap_SP(backbuffer);
4326 }
4327 else
4328 {
4329 DrawLevel();
4330 DrawAllPlayers();
4331
4332 /* after drawing the level, correct some elements */
4333 if (game.timegate_time_left == 0)
4334 CloseAllOpenTimegates();
4335
4336 #if NEW_TILESIZE
4337 BlitScreenToBitmap(backbuffer);
4338 #else
4339 /* blit playfield from scroll buffer to normal back buffer for fading in */
4340 if (setup.soft_scrolling)
4341 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4342 #endif
4343
4344 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4345 }
4346 /* !!! FIX THIS (END) !!! */
4347
4348 #if 1
4349 FadeIn(REDRAW_FIELD);
4350 #else
4351 if (do_fading)
4352 FadeIn(REDRAW_FIELD);
4353
4354 BackToFront();
4355 #endif
4356
4357 if (!game.restart_level)
4358 {
4359 /* copy default game door content to main double buffer */
4360 #if 1
4361 #if 1
4362 /* !!! CHECK AGAIN !!! */
4363 SetPanelBackground();
4364 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4365 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4366 #else
4367 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4368
4369 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4370 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4371 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4372 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4373 #endif
4374 #else
4375 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4376 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4377 #endif
4378 }
4379
4380 SetPanelBackground();
4381 SetDrawBackgroundMask(REDRAW_DOOR_1);
4382
4383 #if 1
4384 UpdateAndDisplayGameControlValues();
4385 #else
4386 UpdateGameDoorValues();
4387 DrawGameDoorValues();
4388 #endif
4389
4390 if (!game.restart_level)
4391 {
4392 UnmapGameButtons();
4393 UnmapTapeButtons();
4394 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4395 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4396 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4397 MapGameButtons();
4398 MapTapeButtons();
4399
4400 /* copy actual game door content to door double buffer for OpenDoor() */
4401 BlitBitmap(drawto, bitmap_db_door,
4402 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4403
4404 OpenDoor(DOOR_OPEN_ALL);
4405
4406 PlaySound(SND_GAME_STARTING);
4407
4408 if (setup.sound_music)
4409 PlayLevelMusic();
4410
4411 KeyboardAutoRepeatOffUnlessAutoplay();
4412
4413 if (options.debug)
4414 {
4415 for (i = 0; i < MAX_PLAYERS; i++)
4416 printf("Player %d %sactive.\n",
4417 i + 1, (stored_player[i].active ? "" : "not "));
4418 }
4419 }
4420
4421 #if 1
4422 UnmapAllGadgets();
4423
4424 MapGameButtons();
4425 MapTapeButtons();
4426 #endif
4427
4428 if (!game.restart_level && !tape.playing)
4429 {
4430 LevelStats_incPlayed(level_nr);
4431
4432 SaveLevelSetup_SeriesInfo();
4433
4434 #if 0
4435 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4436 #endif
4437 }
4438
4439 game.restart_level = FALSE;
4440 }
4441
UpdateEngineValues(int actual_scroll_x,int actual_scroll_y)4442 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4443 {
4444 /* this is used for non-R'n'D game engines to update certain engine values */
4445
4446 /* needed to determine if sounds are played within the visible screen area */
4447 scroll_x = actual_scroll_x;
4448 scroll_y = actual_scroll_y;
4449 }
4450
InitMovDir(int x,int y)4451 void InitMovDir(int x, int y)
4452 {
4453 int i, element = Feld[x][y];
4454 static int xy[4][2] =
4455 {
4456 { 0, +1 },
4457 { +1, 0 },
4458 { 0, -1 },
4459 { -1, 0 }
4460 };
4461 static int direction[3][4] =
4462 {
4463 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4464 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4465 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4466 };
4467
4468 switch (element)
4469 {
4470 case EL_BUG_RIGHT:
4471 case EL_BUG_UP:
4472 case EL_BUG_LEFT:
4473 case EL_BUG_DOWN:
4474 Feld[x][y] = EL_BUG;
4475 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4476 break;
4477
4478 case EL_SPACESHIP_RIGHT:
4479 case EL_SPACESHIP_UP:
4480 case EL_SPACESHIP_LEFT:
4481 case EL_SPACESHIP_DOWN:
4482 Feld[x][y] = EL_SPACESHIP;
4483 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4484 break;
4485
4486 case EL_BD_BUTTERFLY_RIGHT:
4487 case EL_BD_BUTTERFLY_UP:
4488 case EL_BD_BUTTERFLY_LEFT:
4489 case EL_BD_BUTTERFLY_DOWN:
4490 Feld[x][y] = EL_BD_BUTTERFLY;
4491 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4492 break;
4493
4494 case EL_BD_FIREFLY_RIGHT:
4495 case EL_BD_FIREFLY_UP:
4496 case EL_BD_FIREFLY_LEFT:
4497 case EL_BD_FIREFLY_DOWN:
4498 Feld[x][y] = EL_BD_FIREFLY;
4499 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4500 break;
4501
4502 case EL_PACMAN_RIGHT:
4503 case EL_PACMAN_UP:
4504 case EL_PACMAN_LEFT:
4505 case EL_PACMAN_DOWN:
4506 Feld[x][y] = EL_PACMAN;
4507 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4508 break;
4509
4510 case EL_YAMYAM_LEFT:
4511 case EL_YAMYAM_RIGHT:
4512 case EL_YAMYAM_UP:
4513 case EL_YAMYAM_DOWN:
4514 Feld[x][y] = EL_YAMYAM;
4515 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4516 break;
4517
4518 case EL_SP_SNIKSNAK:
4519 MovDir[x][y] = MV_UP;
4520 break;
4521
4522 case EL_SP_ELECTRON:
4523 MovDir[x][y] = MV_LEFT;
4524 break;
4525
4526 case EL_MOLE_LEFT:
4527 case EL_MOLE_RIGHT:
4528 case EL_MOLE_UP:
4529 case EL_MOLE_DOWN:
4530 Feld[x][y] = EL_MOLE;
4531 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4532 break;
4533
4534 default:
4535 if (IS_CUSTOM_ELEMENT(element))
4536 {
4537 struct ElementInfo *ei = &element_info[element];
4538 int move_direction_initial = ei->move_direction_initial;
4539 int move_pattern = ei->move_pattern;
4540
4541 if (move_direction_initial == MV_START_PREVIOUS)
4542 {
4543 if (MovDir[x][y] != MV_NONE)
4544 return;
4545
4546 move_direction_initial = MV_START_AUTOMATIC;
4547 }
4548
4549 if (move_direction_initial == MV_START_RANDOM)
4550 MovDir[x][y] = 1 << RND(4);
4551 else if (move_direction_initial & MV_ANY_DIRECTION)
4552 MovDir[x][y] = move_direction_initial;
4553 else if (move_pattern == MV_ALL_DIRECTIONS ||
4554 move_pattern == MV_TURNING_LEFT ||
4555 move_pattern == MV_TURNING_RIGHT ||
4556 move_pattern == MV_TURNING_LEFT_RIGHT ||
4557 move_pattern == MV_TURNING_RIGHT_LEFT ||
4558 move_pattern == MV_TURNING_RANDOM)
4559 MovDir[x][y] = 1 << RND(4);
4560 else if (move_pattern == MV_HORIZONTAL)
4561 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4562 else if (move_pattern == MV_VERTICAL)
4563 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4564 else if (move_pattern & MV_ANY_DIRECTION)
4565 MovDir[x][y] = element_info[element].move_pattern;
4566 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4567 move_pattern == MV_ALONG_RIGHT_SIDE)
4568 {
4569 /* use random direction as default start direction */
4570 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4571 MovDir[x][y] = 1 << RND(4);
4572
4573 for (i = 0; i < NUM_DIRECTIONS; i++)
4574 {
4575 int x1 = x + xy[i][0];
4576 int y1 = y + xy[i][1];
4577
4578 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4579 {
4580 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4581 MovDir[x][y] = direction[0][i];
4582 else
4583 MovDir[x][y] = direction[1][i];
4584
4585 break;
4586 }
4587 }
4588 }
4589 }
4590 else
4591 {
4592 MovDir[x][y] = 1 << RND(4);
4593
4594 if (element != EL_BUG &&
4595 element != EL_SPACESHIP &&
4596 element != EL_BD_BUTTERFLY &&
4597 element != EL_BD_FIREFLY)
4598 break;
4599
4600 for (i = 0; i < NUM_DIRECTIONS; i++)
4601 {
4602 int x1 = x + xy[i][0];
4603 int y1 = y + xy[i][1];
4604
4605 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4606 {
4607 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4608 {
4609 MovDir[x][y] = direction[0][i];
4610 break;
4611 }
4612 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4613 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4614 {
4615 MovDir[x][y] = direction[1][i];
4616 break;
4617 }
4618 }
4619 }
4620 }
4621 break;
4622 }
4623
4624 GfxDir[x][y] = MovDir[x][y];
4625 }
4626
InitAmoebaNr(int x,int y)4627 void InitAmoebaNr(int x, int y)
4628 {
4629 int i;
4630 int group_nr = AmoebeNachbarNr(x, y);
4631
4632 if (group_nr == 0)
4633 {
4634 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4635 {
4636 if (AmoebaCnt[i] == 0)
4637 {
4638 group_nr = i;
4639 break;
4640 }
4641 }
4642 }
4643
4644 AmoebaNr[x][y] = group_nr;
4645 AmoebaCnt[group_nr]++;
4646 AmoebaCnt2[group_nr]++;
4647 }
4648
PlayerWins(struct PlayerInfo * player)4649 static void PlayerWins(struct PlayerInfo *player)
4650 {
4651 player->LevelSolved = TRUE;
4652 player->GameOver = TRUE;
4653
4654 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4655 level.native_em_level->lev->score : player->score);
4656
4657 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4658 TimeLeft);
4659 player->LevelSolved_CountingScore = player->score_final;
4660 }
4661
GameWon()4662 void GameWon()
4663 {
4664 static int time, time_final;
4665 static int score, score_final;
4666 static int game_over_delay_1 = 0;
4667 static int game_over_delay_2 = 0;
4668 int game_over_delay_value_1 = 50;
4669 int game_over_delay_value_2 = 50;
4670
4671 if (!local_player->LevelSolved_GameWon)
4672 {
4673 int i;
4674
4675 /* do not start end game actions before the player stops moving (to exit) */
4676 if (local_player->MovPos)
4677 return;
4678
4679 local_player->LevelSolved_GameWon = TRUE;
4680 local_player->LevelSolved_SaveTape = tape.recording;
4681 local_player->LevelSolved_SaveScore = !tape.playing;
4682
4683 if (!tape.playing)
4684 {
4685 LevelStats_incSolved(level_nr);
4686
4687 SaveLevelSetup_SeriesInfo();
4688
4689 #if 0
4690 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4691 #endif
4692 }
4693
4694 if (tape.auto_play) /* tape might already be stopped here */
4695 tape.auto_play_level_solved = TRUE;
4696
4697 #if 1
4698 TapeStop();
4699 #endif
4700
4701 game_over_delay_1 = game_over_delay_value_1;
4702 game_over_delay_2 = game_over_delay_value_2;
4703
4704 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4705 score = score_final = local_player->score_final;
4706
4707 if (TimeLeft > 0)
4708 {
4709 time_final = 0;
4710 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4711 }
4712 else if (game.no_time_limit && TimePlayed < 999)
4713 {
4714 time_final = 999;
4715 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4716 }
4717
4718 local_player->score_final = score_final;
4719
4720 if (level_editor_test_game)
4721 {
4722 time = time_final;
4723 score = score_final;
4724
4725 #if 1
4726 local_player->LevelSolved_CountingTime = time;
4727 local_player->LevelSolved_CountingScore = score;
4728
4729 game_panel_controls[GAME_PANEL_TIME].value = time;
4730 game_panel_controls[GAME_PANEL_SCORE].value = score;
4731
4732 DisplayGameControlValues();
4733 #else
4734 DrawGameValue_Time(time);
4735 DrawGameValue_Score(score);
4736 #endif
4737 }
4738
4739 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4740 {
4741 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4742 {
4743 /* close exit door after last player */
4744 if ((AllPlayersGone &&
4745 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4746 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4747 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4748 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4749 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4750 {
4751 int element = Feld[ExitX][ExitY];
4752
4753 #if 0
4754 if (element == EL_EM_EXIT_OPEN ||
4755 element == EL_EM_STEEL_EXIT_OPEN)
4756 {
4757 Bang(ExitX, ExitY);
4758 }
4759 else
4760 #endif
4761 {
4762 Feld[ExitX][ExitY] =
4763 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4764 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4765 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4766 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4767 EL_EM_STEEL_EXIT_CLOSING);
4768
4769 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4770 }
4771 }
4772
4773 /* player disappears */
4774 DrawLevelField(ExitX, ExitY);
4775 }
4776
4777 for (i = 0; i < MAX_PLAYERS; i++)
4778 {
4779 struct PlayerInfo *player = &stored_player[i];
4780
4781 if (player->present)
4782 {
4783 RemovePlayer(player);
4784
4785 /* player disappears */
4786 DrawLevelField(player->jx, player->jy);
4787 }
4788 }
4789 }
4790
4791 PlaySound(SND_GAME_WINNING);
4792 }
4793
4794 if (game_over_delay_1 > 0)
4795 {
4796 game_over_delay_1--;
4797
4798 return;
4799 }
4800
4801 if (time != time_final)
4802 {
4803 int time_to_go = ABS(time_final - time);
4804 int time_count_dir = (time < time_final ? +1 : -1);
4805 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4806
4807 time += time_count_steps * time_count_dir;
4808 score += time_count_steps * level.score[SC_TIME_BONUS];
4809
4810 #if 1
4811 local_player->LevelSolved_CountingTime = time;
4812 local_player->LevelSolved_CountingScore = score;
4813
4814 game_panel_controls[GAME_PANEL_TIME].value = time;
4815 game_panel_controls[GAME_PANEL_SCORE].value = score;
4816
4817 DisplayGameControlValues();
4818 #else
4819 DrawGameValue_Time(time);
4820 DrawGameValue_Score(score);
4821 #endif
4822
4823 if (time == time_final)
4824 StopSound(SND_GAME_LEVELTIME_BONUS);
4825 else if (setup.sound_loops)
4826 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4827 else
4828 PlaySound(SND_GAME_LEVELTIME_BONUS);
4829
4830 return;
4831 }
4832
4833 local_player->LevelSolved_PanelOff = TRUE;
4834
4835 if (game_over_delay_2 > 0)
4836 {
4837 game_over_delay_2--;
4838
4839 return;
4840 }
4841
4842 #if 1
4843 GameEnd();
4844 #endif
4845 }
4846
GameEnd()4847 void GameEnd()
4848 {
4849 int hi_pos;
4850 boolean raise_level = FALSE;
4851
4852 local_player->LevelSolved_GameEnd = TRUE;
4853
4854 CloseDoor(DOOR_CLOSE_1);
4855
4856 if (local_player->LevelSolved_SaveTape)
4857 {
4858 #if 0
4859 TapeStop();
4860 #endif
4861
4862 #if 1
4863 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4864 #else
4865 SaveTape(tape.level_nr); /* ask to save tape */
4866 #endif
4867 }
4868
4869 if (level_editor_test_game)
4870 {
4871 game_status = GAME_MODE_MAIN;
4872
4873 #if 1
4874 DrawAndFadeInMainMenu(REDRAW_FIELD);
4875 #else
4876 DrawMainMenu();
4877 #endif
4878
4879 return;
4880 }
4881
4882 if (!local_player->LevelSolved_SaveScore)
4883 {
4884 #if 1
4885 FadeOut(REDRAW_FIELD);
4886 #endif
4887
4888 game_status = GAME_MODE_MAIN;
4889
4890 DrawAndFadeInMainMenu(REDRAW_FIELD);
4891
4892 return;
4893 }
4894
4895 if (level_nr == leveldir_current->handicap_level)
4896 {
4897 leveldir_current->handicap_level++;
4898
4899 SaveLevelSetup_SeriesInfo();
4900 }
4901
4902 if (level_nr < leveldir_current->last_level)
4903 raise_level = TRUE; /* advance to next level */
4904
4905 if ((hi_pos = NewHiScore()) >= 0)
4906 {
4907 game_status = GAME_MODE_SCORES;
4908
4909 DrawHallOfFame(hi_pos);
4910
4911 if (raise_level)
4912 {
4913 level_nr++;
4914 TapeErase();
4915 }
4916 }
4917 else
4918 {
4919 #if 1
4920 FadeOut(REDRAW_FIELD);
4921 #endif
4922
4923 game_status = GAME_MODE_MAIN;
4924
4925 if (raise_level)
4926 {
4927 level_nr++;
4928 TapeErase();
4929 }
4930
4931 DrawAndFadeInMainMenu(REDRAW_FIELD);
4932 }
4933 }
4934
NewHiScore()4935 int NewHiScore()
4936 {
4937 int k, l;
4938 int position = -1;
4939
4940 LoadScore(level_nr);
4941
4942 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4943 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4944 return -1;
4945
4946 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4947 {
4948 if (local_player->score_final > highscore[k].Score)
4949 {
4950 /* player has made it to the hall of fame */
4951
4952 if (k < MAX_SCORE_ENTRIES - 1)
4953 {
4954 int m = MAX_SCORE_ENTRIES - 1;
4955
4956 #ifdef ONE_PER_NAME
4957 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4958 if (strEqual(setup.player_name, highscore[l].Name))
4959 m = l;
4960 if (m == k) /* player's new highscore overwrites his old one */
4961 goto put_into_list;
4962 #endif
4963
4964 for (l = m; l > k; l--)
4965 {
4966 strcpy(highscore[l].Name, highscore[l - 1].Name);
4967 highscore[l].Score = highscore[l - 1].Score;
4968 }
4969 }
4970
4971 #ifdef ONE_PER_NAME
4972 put_into_list:
4973 #endif
4974 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4975 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4976 highscore[k].Score = local_player->score_final;
4977 position = k;
4978 break;
4979 }
4980
4981 #ifdef ONE_PER_NAME
4982 else if (!strncmp(setup.player_name, highscore[k].Name,
4983 MAX_PLAYER_NAME_LEN))
4984 break; /* player already there with a higher score */
4985 #endif
4986
4987 }
4988
4989 if (position >= 0)
4990 SaveScore(level_nr);
4991
4992 return position;
4993 }
4994
getElementMoveStepsizeExt(int x,int y,int direction)4995 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4996 {
4997 int element = Feld[x][y];
4998 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4999 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5000 int horiz_move = (dx != 0);
5001 int sign = (horiz_move ? dx : dy);
5002 int step = sign * element_info[element].move_stepsize;
5003
5004 /* special values for move stepsize for spring and things on conveyor belt */
5005 if (horiz_move)
5006 {
5007 if (CAN_FALL(element) &&
5008 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5009 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5010 else if (element == EL_SPRING)
5011 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5012 }
5013
5014 return step;
5015 }
5016
getElementMoveStepsize(int x,int y)5017 inline static int getElementMoveStepsize(int x, int y)
5018 {
5019 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5020 }
5021
InitPlayerGfxAnimation(struct PlayerInfo * player,int action,int dir)5022 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5023 {
5024 if (player->GfxAction != action || player->GfxDir != dir)
5025 {
5026 #if 0
5027 printf("Player frame reset! (%d => %d, %d => %d)\n",
5028 player->GfxAction, action, player->GfxDir, dir);
5029 #endif
5030
5031 player->GfxAction = action;
5032 player->GfxDir = dir;
5033 player->Frame = 0;
5034 player->StepFrame = 0;
5035 }
5036 }
5037
5038 #if USE_GFX_RESET_GFX_ANIMATION
ResetGfxFrame(int x,int y,boolean redraw)5039 static void ResetGfxFrame(int x, int y, boolean redraw)
5040 {
5041 int element = Feld[x][y];
5042 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5043 int last_gfx_frame = GfxFrame[x][y];
5044
5045 if (graphic_info[graphic].anim_global_sync)
5046 GfxFrame[x][y] = FrameCounter;
5047 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048 GfxFrame[x][y] = CustomValue[x][y];
5049 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050 GfxFrame[x][y] = element_info[element].collect_score;
5051 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052 GfxFrame[x][y] = ChangeDelay[x][y];
5053
5054 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5055 DrawLevelGraphicAnimation(x, y, graphic);
5056 }
5057 #endif
5058
ResetGfxAnimation(int x,int y)5059 static void ResetGfxAnimation(int x, int y)
5060 {
5061 GfxAction[x][y] = ACTION_DEFAULT;
5062 GfxDir[x][y] = MovDir[x][y];
5063 GfxFrame[x][y] = 0;
5064
5065 #if USE_GFX_RESET_GFX_ANIMATION
5066 ResetGfxFrame(x, y, FALSE);
5067 #endif
5068 }
5069
ResetRandomAnimationValue(int x,int y)5070 static void ResetRandomAnimationValue(int x, int y)
5071 {
5072 GfxRandom[x][y] = INIT_GFX_RANDOM();
5073 }
5074
InitMovingField(int x,int y,int direction)5075 void InitMovingField(int x, int y, int direction)
5076 {
5077 int element = Feld[x][y];
5078 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5080 int newx = x + dx;
5081 int newy = y + dy;
5082 boolean is_moving_before, is_moving_after;
5083 #if 0
5084 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5085 #endif
5086
5087 /* check if element was/is moving or being moved before/after mode change */
5088 #if 1
5089 #if 1
5090 is_moving_before = (WasJustMoving[x][y] != 0);
5091 #else
5092 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5093 is_moving_before = WasJustMoving[x][y];
5094 #endif
5095 #else
5096 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5097 #endif
5098 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5099
5100 /* reset animation only for moving elements which change direction of moving
5101 or which just started or stopped moving
5102 (else CEs with property "can move" / "not moving" are reset each frame) */
5103 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5104 #if 1
5105 if (is_moving_before != is_moving_after ||
5106 direction != MovDir[x][y])
5107 ResetGfxAnimation(x, y);
5108 #else
5109 if ((is_moving_before || is_moving_after) && !continues_moving)
5110 ResetGfxAnimation(x, y);
5111 #endif
5112 #else
5113 if (!continues_moving)
5114 ResetGfxAnimation(x, y);
5115 #endif
5116
5117 MovDir[x][y] = direction;
5118 GfxDir[x][y] = direction;
5119
5120 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5121 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5122 direction == MV_DOWN && CAN_FALL(element) ?
5123 ACTION_FALLING : ACTION_MOVING);
5124 #else
5125 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5126 ACTION_FALLING : ACTION_MOVING);
5127 #endif
5128
5129 /* this is needed for CEs with property "can move" / "not moving" */
5130
5131 if (is_moving_after)
5132 {
5133 if (Feld[newx][newy] == EL_EMPTY)
5134 Feld[newx][newy] = EL_BLOCKED;
5135
5136 MovDir[newx][newy] = MovDir[x][y];
5137
5138 #if USE_NEW_CUSTOM_VALUE
5139 CustomValue[newx][newy] = CustomValue[x][y];
5140 #endif
5141
5142 GfxFrame[newx][newy] = GfxFrame[x][y];
5143 GfxRandom[newx][newy] = GfxRandom[x][y];
5144 GfxAction[newx][newy] = GfxAction[x][y];
5145 GfxDir[newx][newy] = GfxDir[x][y];
5146 }
5147 }
5148
Moving2Blocked(int x,int y,int * goes_to_x,int * goes_to_y)5149 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5150 {
5151 int direction = MovDir[x][y];
5152 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5153 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5154
5155 *goes_to_x = newx;
5156 *goes_to_y = newy;
5157 }
5158
Blocked2Moving(int x,int y,int * comes_from_x,int * comes_from_y)5159 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5160 {
5161 int oldx = x, oldy = y;
5162 int direction = MovDir[x][y];
5163
5164 if (direction == MV_LEFT)
5165 oldx++;
5166 else if (direction == MV_RIGHT)
5167 oldx--;
5168 else if (direction == MV_UP)
5169 oldy++;
5170 else if (direction == MV_DOWN)
5171 oldy--;
5172
5173 *comes_from_x = oldx;
5174 *comes_from_y = oldy;
5175 }
5176
MovingOrBlocked2Element(int x,int y)5177 int MovingOrBlocked2Element(int x, int y)
5178 {
5179 int element = Feld[x][y];
5180
5181 if (element == EL_BLOCKED)
5182 {
5183 int oldx, oldy;
5184
5185 Blocked2Moving(x, y, &oldx, &oldy);
5186 return Feld[oldx][oldy];
5187 }
5188 else
5189 return element;
5190 }
5191
MovingOrBlocked2ElementIfNotLeaving(int x,int y)5192 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5193 {
5194 /* like MovingOrBlocked2Element(), but if element is moving
5195 and (x,y) is the field the moving element is just leaving,
5196 return EL_BLOCKED instead of the element value */
5197 int element = Feld[x][y];
5198
5199 if (IS_MOVING(x, y))
5200 {
5201 if (element == EL_BLOCKED)
5202 {
5203 int oldx, oldy;
5204
5205 Blocked2Moving(x, y, &oldx, &oldy);
5206 return Feld[oldx][oldy];
5207 }
5208 else
5209 return EL_BLOCKED;
5210 }
5211 else
5212 return element;
5213 }
5214
RemoveField(int x,int y)5215 static void RemoveField(int x, int y)
5216 {
5217 Feld[x][y] = EL_EMPTY;
5218
5219 MovPos[x][y] = 0;
5220 MovDir[x][y] = 0;
5221 MovDelay[x][y] = 0;
5222
5223 #if USE_NEW_CUSTOM_VALUE
5224 CustomValue[x][y] = 0;
5225 #endif
5226
5227 AmoebaNr[x][y] = 0;
5228 ChangeDelay[x][y] = 0;
5229 ChangePage[x][y] = -1;
5230 Pushed[x][y] = FALSE;
5231
5232 #if 0
5233 ExplodeField[x][y] = EX_TYPE_NONE;
5234 #endif
5235
5236 GfxElement[x][y] = EL_UNDEFINED;
5237 GfxAction[x][y] = ACTION_DEFAULT;
5238 GfxDir[x][y] = MV_NONE;
5239 #if 0
5240 /* !!! this would prevent the removed tile from being redrawn !!! */
5241 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5242 #endif
5243 }
5244
RemoveMovingField(int x,int y)5245 void RemoveMovingField(int x, int y)
5246 {
5247 int oldx = x, oldy = y, newx = x, newy = y;
5248 int element = Feld[x][y];
5249 int next_element = EL_UNDEFINED;
5250
5251 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5252 return;
5253
5254 if (IS_MOVING(x, y))
5255 {
5256 Moving2Blocked(x, y, &newx, &newy);
5257
5258 if (Feld[newx][newy] != EL_BLOCKED)
5259 {
5260 /* element is moving, but target field is not free (blocked), but
5261 already occupied by something different (example: acid pool);
5262 in this case, only remove the moving field, but not the target */
5263
5264 RemoveField(oldx, oldy);
5265
5266 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5267
5268 TEST_DrawLevelField(oldx, oldy);
5269
5270 return;
5271 }
5272 }
5273 else if (element == EL_BLOCKED)
5274 {
5275 Blocked2Moving(x, y, &oldx, &oldy);
5276 if (!IS_MOVING(oldx, oldy))
5277 return;
5278 }
5279
5280 if (element == EL_BLOCKED &&
5281 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5282 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5283 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5284 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5285 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5286 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5287 next_element = get_next_element(Feld[oldx][oldy]);
5288
5289 RemoveField(oldx, oldy);
5290 RemoveField(newx, newy);
5291
5292 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5293
5294 if (next_element != EL_UNDEFINED)
5295 Feld[oldx][oldy] = next_element;
5296
5297 TEST_DrawLevelField(oldx, oldy);
5298 TEST_DrawLevelField(newx, newy);
5299 }
5300
DrawDynamite(int x,int y)5301 void DrawDynamite(int x, int y)
5302 {
5303 int sx = SCREENX(x), sy = SCREENY(y);
5304 int graphic = el2img(Feld[x][y]);
5305 int frame;
5306
5307 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5308 return;
5309
5310 if (IS_WALKABLE_INSIDE(Back[x][y]))
5311 return;
5312
5313 if (Back[x][y])
5314 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5315 else if (Store[x][y])
5316 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5317
5318 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5319
5320 if (Back[x][y] || Store[x][y])
5321 DrawGraphicThruMask(sx, sy, graphic, frame);
5322 else
5323 DrawGraphic(sx, sy, graphic, frame);
5324 }
5325
CheckDynamite(int x,int y)5326 void CheckDynamite(int x, int y)
5327 {
5328 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5329 {
5330 MovDelay[x][y]--;
5331
5332 if (MovDelay[x][y] != 0)
5333 {
5334 DrawDynamite(x, y);
5335 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5336
5337 return;
5338 }
5339 }
5340
5341 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5342
5343 Bang(x, y);
5344 }
5345
setMinimalPlayerBoundaries(int * sx1,int * sy1,int * sx2,int * sy2)5346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5347 {
5348 boolean num_checked_players = 0;
5349 int i;
5350
5351 for (i = 0; i < MAX_PLAYERS; i++)
5352 {
5353 if (stored_player[i].active)
5354 {
5355 int sx = stored_player[i].jx;
5356 int sy = stored_player[i].jy;
5357
5358 if (num_checked_players == 0)
5359 {
5360 *sx1 = *sx2 = sx;
5361 *sy1 = *sy2 = sy;
5362 }
5363 else
5364 {
5365 *sx1 = MIN(*sx1, sx);
5366 *sy1 = MIN(*sy1, sy);
5367 *sx2 = MAX(*sx2, sx);
5368 *sy2 = MAX(*sy2, sy);
5369 }
5370
5371 num_checked_players++;
5372 }
5373 }
5374 }
5375
checkIfAllPlayersFitToScreen_RND()5376 static boolean checkIfAllPlayersFitToScreen_RND()
5377 {
5378 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5379
5380 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5381
5382 return (sx2 - sx1 < SCR_FIELDX &&
5383 sy2 - sy1 < SCR_FIELDY);
5384 }
5385
setScreenCenteredToAllPlayers(int * sx,int * sy)5386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5387 {
5388 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5389
5390 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5391
5392 *sx = (sx1 + sx2) / 2;
5393 *sy = (sy1 + sy2) / 2;
5394 }
5395
DrawRelocateScreen(int old_x,int old_y,int x,int y,int move_dir,boolean center_screen,boolean quick_relocation)5396 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5397 boolean center_screen, boolean quick_relocation)
5398 {
5399 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5400 boolean no_delay = (tape.warp_forward);
5401 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5402 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5403
5404 if (quick_relocation)
5405 {
5406 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5407 {
5408 if (!level.shifted_relocation || center_screen)
5409 {
5410 /* quick relocation (without scrolling), with centering of screen */
5411
5412 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5413 x > SBX_Right + MIDPOSX ? SBX_Right :
5414 x - MIDPOSX);
5415
5416 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5417 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5418 y - MIDPOSY);
5419 }
5420 else
5421 {
5422 /* quick relocation (without scrolling), but do not center screen */
5423
5424 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5425 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5426 old_x - MIDPOSX);
5427
5428 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5429 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5430 old_y - MIDPOSY);
5431
5432 int offset_x = x + (scroll_x - center_scroll_x);
5433 int offset_y = y + (scroll_y - center_scroll_y);
5434
5435 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5436 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5437 offset_x - MIDPOSX);
5438
5439 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441 offset_y - MIDPOSY);
5442 }
5443 }
5444 else
5445 {
5446 #if 1
5447 if (!level.shifted_relocation || center_screen)
5448 {
5449 /* quick relocation (without scrolling), with centering of screen */
5450
5451 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5452 x > SBX_Right + MIDPOSX ? SBX_Right :
5453 x - MIDPOSX);
5454
5455 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5456 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5457 y - MIDPOSY);
5458 }
5459 else
5460 {
5461 /* quick relocation (without scrolling), but do not center screen */
5462
5463 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5464 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5465 old_x - MIDPOSX);
5466
5467 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5468 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5469 old_y - MIDPOSY);
5470
5471 int offset_x = x + (scroll_x - center_scroll_x);
5472 int offset_y = y + (scroll_y - center_scroll_y);
5473
5474 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5475 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5476 offset_x - MIDPOSX);
5477
5478 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5479 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5480 offset_y - MIDPOSY);
5481 }
5482 #else
5483 /* quick relocation (without scrolling), inside visible screen area */
5484
5485 int offset = game.scroll_delay_value;
5486
5487 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5488 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5489 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5490
5491 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5492 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5493 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5494
5495 /* don't scroll over playfield boundaries */
5496 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5497 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5498
5499 /* don't scroll over playfield boundaries */
5500 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5501 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5502 #endif
5503 }
5504
5505 RedrawPlayfield(TRUE, 0,0,0,0);
5506 }
5507 else
5508 {
5509 #if 1
5510 int scroll_xx, scroll_yy;
5511
5512 if (!level.shifted_relocation || center_screen)
5513 {
5514 /* visible relocation (with scrolling), with centering of screen */
5515
5516 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5517 x > SBX_Right + MIDPOSX ? SBX_Right :
5518 x - MIDPOSX);
5519
5520 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5521 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5522 y - MIDPOSY);
5523 }
5524 else
5525 {
5526 /* visible relocation (with scrolling), but do not center screen */
5527
5528 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5529 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5530 old_x - MIDPOSX);
5531
5532 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5533 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5534 old_y - MIDPOSY);
5535
5536 int offset_x = x + (scroll_x - center_scroll_x);
5537 int offset_y = y + (scroll_y - center_scroll_y);
5538
5539 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5540 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5541 offset_x - MIDPOSX);
5542
5543 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5544 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5545 offset_y - MIDPOSY);
5546 }
5547
5548 #else
5549
5550 /* visible relocation (with scrolling), with centering of screen */
5551
5552 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5553 x > SBX_Right + MIDPOSX ? SBX_Right :
5554 x - MIDPOSX);
5555
5556 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5557 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5558 y - MIDPOSY);
5559 #endif
5560
5561 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5562
5563 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5564 {
5565 int dx = 0, dy = 0;
5566 int fx = FX, fy = FY;
5567
5568 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5569 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5570
5571 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5572 break;
5573
5574 scroll_x -= dx;
5575 scroll_y -= dy;
5576
5577 fx += dx * TILEX / 2;
5578 fy += dy * TILEY / 2;
5579
5580 ScrollLevel(dx, dy);
5581 DrawAllPlayers();
5582
5583 /* scroll in two steps of half tile size to make things smoother */
5584 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5585 FlushDisplay();
5586 Delay(wait_delay_value);
5587
5588 /* scroll second step to align at full tile size */
5589 BackToFront();
5590 Delay(wait_delay_value);
5591 }
5592
5593 DrawAllPlayers();
5594 BackToFront();
5595 Delay(wait_delay_value);
5596 }
5597 }
5598
RelocatePlayer(int jx,int jy,int el_player_raw)5599 void RelocatePlayer(int jx, int jy, int el_player_raw)
5600 {
5601 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5602 int player_nr = GET_PLAYER_NR(el_player);
5603 struct PlayerInfo *player = &stored_player[player_nr];
5604 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5605 boolean no_delay = (tape.warp_forward);
5606 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5607 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5608 int old_jx = player->jx;
5609 int old_jy = player->jy;
5610 int old_element = Feld[old_jx][old_jy];
5611 int element = Feld[jx][jy];
5612 boolean player_relocated = (old_jx != jx || old_jy != jy);
5613
5614 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5615 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5616 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5617 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5618 int leave_side_horiz = move_dir_horiz;
5619 int leave_side_vert = move_dir_vert;
5620 int enter_side = enter_side_horiz | enter_side_vert;
5621 int leave_side = leave_side_horiz | leave_side_vert;
5622
5623 if (player->GameOver) /* do not reanimate dead player */
5624 return;
5625
5626 if (!player_relocated) /* no need to relocate the player */
5627 return;
5628
5629 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5630 {
5631 RemoveField(jx, jy); /* temporarily remove newly placed player */
5632 DrawLevelField(jx, jy);
5633 }
5634
5635 if (player->present)
5636 {
5637 while (player->MovPos)
5638 {
5639 ScrollPlayer(player, SCROLL_GO_ON);
5640 ScrollScreen(NULL, SCROLL_GO_ON);
5641
5642 AdvanceFrameAndPlayerCounters(player->index_nr);
5643
5644 DrawPlayer(player);
5645
5646 BackToFront();
5647 Delay(wait_delay_value);
5648 }
5649
5650 DrawPlayer(player); /* needed here only to cleanup last field */
5651 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5652
5653 player->is_moving = FALSE;
5654 }
5655
5656 if (IS_CUSTOM_ELEMENT(old_element))
5657 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5658 CE_LEFT_BY_PLAYER,
5659 player->index_bit, leave_side);
5660
5661 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5662 CE_PLAYER_LEAVES_X,
5663 player->index_bit, leave_side);
5664
5665 Feld[jx][jy] = el_player;
5666 InitPlayerField(jx, jy, el_player, TRUE);
5667
5668 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5669 possible that the relocation target field did not contain a player element,
5670 but a walkable element, to which the new player was relocated -- in this
5671 case, restore that (already initialized!) element on the player field */
5672 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5673 {
5674 Feld[jx][jy] = element; /* restore previously existing element */
5675 #if 0
5676 /* !!! do not initialize already initialized element a second time !!! */
5677 /* (this causes at least problems with "element creation" CE trigger for
5678 already existing elements, and existing Sokoban fields counted twice) */
5679 InitField(jx, jy, FALSE);
5680 #endif
5681 }
5682
5683 /* only visually relocate centered player */
5684 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5685 FALSE, level.instant_relocation);
5686
5687 TestIfPlayerTouchesBadThing(jx, jy);
5688 TestIfPlayerTouchesCustomElement(jx, jy);
5689
5690 if (IS_CUSTOM_ELEMENT(element))
5691 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5692 player->index_bit, enter_side);
5693
5694 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5695 player->index_bit, enter_side);
5696
5697 #if 1
5698 if (player->is_switching)
5699 {
5700 /* ensure that relocation while still switching an element does not cause
5701 a new element to be treated as also switched directly after relocation
5702 (this is important for teleporter switches that teleport the player to
5703 a place where another teleporter switch is in the same direction, which
5704 would then incorrectly be treated as immediately switched before the
5705 direction key that caused the switch was released) */
5706
5707 player->switch_x += jx - old_jx;
5708 player->switch_y += jy - old_jy;
5709 }
5710 #endif
5711 }
5712
Explode(int ex,int ey,int phase,int mode)5713 void Explode(int ex, int ey, int phase, int mode)
5714 {
5715 int x, y;
5716 int last_phase;
5717 int border_element;
5718
5719 /* !!! eliminate this variable !!! */
5720 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5721
5722 if (game.explosions_delayed)
5723 {
5724 ExplodeField[ex][ey] = mode;
5725 return;
5726 }
5727
5728 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5729 {
5730 int center_element = Feld[ex][ey];
5731 int artwork_element, explosion_element; /* set these values later */
5732
5733 #if 0
5734 /* --- This is only really needed (and now handled) in "Impact()". --- */
5735 /* do not explode moving elements that left the explode field in time */
5736 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5737 center_element == EL_EMPTY &&
5738 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5739 return;
5740 #endif
5741
5742 #if 0
5743 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5744 if (mode == EX_TYPE_NORMAL ||
5745 mode == EX_TYPE_CENTER ||
5746 mode == EX_TYPE_CROSS)
5747 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5748 #endif
5749
5750 /* remove things displayed in background while burning dynamite */
5751 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5752 Back[ex][ey] = 0;
5753
5754 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5755 {
5756 /* put moving element to center field (and let it explode there) */
5757 center_element = MovingOrBlocked2Element(ex, ey);
5758 RemoveMovingField(ex, ey);
5759 Feld[ex][ey] = center_element;
5760 }
5761
5762 /* now "center_element" is finally determined -- set related values now */
5763 artwork_element = center_element; /* for custom player artwork */
5764 explosion_element = center_element; /* for custom player artwork */
5765
5766 if (IS_PLAYER(ex, ey))
5767 {
5768 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5769
5770 artwork_element = stored_player[player_nr].artwork_element;
5771
5772 if (level.use_explosion_element[player_nr])
5773 {
5774 explosion_element = level.explosion_element[player_nr];
5775 artwork_element = explosion_element;
5776 }
5777 }
5778
5779 #if 1
5780 if (mode == EX_TYPE_NORMAL ||
5781 mode == EX_TYPE_CENTER ||
5782 mode == EX_TYPE_CROSS)
5783 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5784 #endif
5785
5786 last_phase = element_info[explosion_element].explosion_delay + 1;
5787
5788 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5789 {
5790 int xx = x - ex + 1;
5791 int yy = y - ey + 1;
5792 int element;
5793
5794 if (!IN_LEV_FIELD(x, y) ||
5795 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5796 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5797 continue;
5798
5799 element = Feld[x][y];
5800
5801 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5802 {
5803 element = MovingOrBlocked2Element(x, y);
5804
5805 if (!IS_EXPLOSION_PROOF(element))
5806 RemoveMovingField(x, y);
5807 }
5808
5809 /* indestructible elements can only explode in center (but not flames) */
5810 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5811 mode == EX_TYPE_BORDER)) ||
5812 element == EL_FLAMES)
5813 continue;
5814
5815 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5816 behaviour, for example when touching a yamyam that explodes to rocks
5817 with active deadly shield, a rock is created under the player !!! */
5818 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5819 #if 0
5820 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5821 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5822 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5823 #else
5824 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5825 #endif
5826 {
5827 if (IS_ACTIVE_BOMB(element))
5828 {
5829 /* re-activate things under the bomb like gate or penguin */
5830 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5831 Back[x][y] = 0;
5832 }
5833
5834 continue;
5835 }
5836
5837 /* save walkable background elements while explosion on same tile */
5838 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5839 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5840 Back[x][y] = element;
5841
5842 /* ignite explodable elements reached by other explosion */
5843 if (element == EL_EXPLOSION)
5844 element = Store2[x][y];
5845
5846 if (AmoebaNr[x][y] &&
5847 (element == EL_AMOEBA_FULL ||
5848 element == EL_BD_AMOEBA ||
5849 element == EL_AMOEBA_GROWING))
5850 {
5851 AmoebaCnt[AmoebaNr[x][y]]--;
5852 AmoebaCnt2[AmoebaNr[x][y]]--;
5853 }
5854
5855 RemoveField(x, y);
5856
5857 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5858 {
5859 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5860
5861 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5862
5863 if (PLAYERINFO(ex, ey)->use_murphy)
5864 Store[x][y] = EL_EMPTY;
5865 }
5866
5867 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5868 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5869 else if (ELEM_IS_PLAYER(center_element))
5870 Store[x][y] = EL_EMPTY;
5871 else if (center_element == EL_YAMYAM)
5872 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5873 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5874 Store[x][y] = element_info[center_element].content.e[xx][yy];
5875 #if 1
5876 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5877 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5878 otherwise) -- FIX THIS !!! */
5879 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5880 Store[x][y] = element_info[element].content.e[1][1];
5881 #else
5882 else if (!CAN_EXPLODE(element))
5883 Store[x][y] = element_info[element].content.e[1][1];
5884 #endif
5885 else
5886 Store[x][y] = EL_EMPTY;
5887
5888 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5889 center_element == EL_AMOEBA_TO_DIAMOND)
5890 Store2[x][y] = element;
5891
5892 Feld[x][y] = EL_EXPLOSION;
5893 GfxElement[x][y] = artwork_element;
5894
5895 ExplodePhase[x][y] = 1;
5896 ExplodeDelay[x][y] = last_phase;
5897
5898 Stop[x][y] = TRUE;
5899 }
5900
5901 if (center_element == EL_YAMYAM)
5902 game.yamyam_content_nr =
5903 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5904
5905 return;
5906 }
5907
5908 if (Stop[ex][ey])
5909 return;
5910
5911 x = ex;
5912 y = ey;
5913
5914 if (phase == 1)
5915 GfxFrame[x][y] = 0; /* restart explosion animation */
5916
5917 last_phase = ExplodeDelay[x][y];
5918
5919 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5920
5921 #ifdef DEBUG
5922
5923 /* activate this even in non-DEBUG version until cause for crash in
5924 getGraphicAnimationFrame() (see below) is found and eliminated */
5925
5926 #endif
5927 #if 1
5928
5929 #if 1
5930 /* this can happen if the player leaves an explosion just in time */
5931 if (GfxElement[x][y] == EL_UNDEFINED)
5932 GfxElement[x][y] = EL_EMPTY;
5933 #else
5934 if (GfxElement[x][y] == EL_UNDEFINED)
5935 {
5936 printf("\n\n");
5937 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5938 printf("Explode(): This should never happen!\n");
5939 printf("\n\n");
5940
5941 GfxElement[x][y] = EL_EMPTY;
5942 }
5943 #endif
5944
5945 #endif
5946
5947 border_element = Store2[x][y];
5948 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5949 border_element = StorePlayer[x][y];
5950
5951 if (phase == element_info[border_element].ignition_delay ||
5952 phase == last_phase)
5953 {
5954 boolean border_explosion = FALSE;
5955
5956 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5957 !PLAYER_EXPLOSION_PROTECTED(x, y))
5958 {
5959 KillPlayerUnlessExplosionProtected(x, y);
5960 border_explosion = TRUE;
5961 }
5962 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5963 {
5964 Feld[x][y] = Store2[x][y];
5965 Store2[x][y] = 0;
5966 Bang(x, y);
5967 border_explosion = TRUE;
5968 }
5969 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5970 {
5971 AmoebeUmwandeln(x, y);
5972 Store2[x][y] = 0;
5973 border_explosion = TRUE;
5974 }
5975
5976 /* if an element just explodes due to another explosion (chain-reaction),
5977 do not immediately end the new explosion when it was the last frame of
5978 the explosion (as it would be done in the following "if"-statement!) */
5979 if (border_explosion && phase == last_phase)
5980 return;
5981 }
5982
5983 if (phase == last_phase)
5984 {
5985 int element;
5986
5987 element = Feld[x][y] = Store[x][y];
5988 Store[x][y] = Store2[x][y] = 0;
5989 GfxElement[x][y] = EL_UNDEFINED;
5990
5991 /* player can escape from explosions and might therefore be still alive */
5992 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5993 element <= EL_PLAYER_IS_EXPLODING_4)
5994 {
5995 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5996 int explosion_element = EL_PLAYER_1 + player_nr;
5997 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5998 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5999
6000 if (level.use_explosion_element[player_nr])
6001 explosion_element = level.explosion_element[player_nr];
6002
6003 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6004 element_info[explosion_element].content.e[xx][yy]);
6005 }
6006
6007 /* restore probably existing indestructible background element */
6008 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6009 element = Feld[x][y] = Back[x][y];
6010 Back[x][y] = 0;
6011
6012 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6013 GfxDir[x][y] = MV_NONE;
6014 ChangeDelay[x][y] = 0;
6015 ChangePage[x][y] = -1;
6016
6017 #if USE_NEW_CUSTOM_VALUE
6018 CustomValue[x][y] = 0;
6019 #endif
6020
6021 InitField_WithBug2(x, y, FALSE);
6022
6023 TEST_DrawLevelField(x, y);
6024
6025 TestIfElementTouchesCustomElement(x, y);
6026
6027 if (GFX_CRUMBLED(element))
6028 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6029
6030 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6031 StorePlayer[x][y] = 0;
6032
6033 if (ELEM_IS_PLAYER(element))
6034 RelocatePlayer(x, y, element);
6035 }
6036 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6037 {
6038 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6039 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6040
6041 if (phase == delay)
6042 TEST_DrawLevelFieldCrumbled(x, y);
6043
6044 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6045 {
6046 DrawLevelElement(x, y, Back[x][y]);
6047 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6048 }
6049 else if (IS_WALKABLE_UNDER(Back[x][y]))
6050 {
6051 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6052 DrawLevelElementThruMask(x, y, Back[x][y]);
6053 }
6054 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6055 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6056 }
6057 }
6058
DynaExplode(int ex,int ey)6059 void DynaExplode(int ex, int ey)
6060 {
6061 int i, j;
6062 int dynabomb_element = Feld[ex][ey];
6063 int dynabomb_size = 1;
6064 boolean dynabomb_xl = FALSE;
6065 struct PlayerInfo *player;
6066 static int xy[4][2] =
6067 {
6068 { 0, -1 },
6069 { -1, 0 },
6070 { +1, 0 },
6071 { 0, +1 }
6072 };
6073
6074 if (IS_ACTIVE_BOMB(dynabomb_element))
6075 {
6076 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6077 dynabomb_size = player->dynabomb_size;
6078 dynabomb_xl = player->dynabomb_xl;
6079 player->dynabombs_left++;
6080 }
6081
6082 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6083
6084 for (i = 0; i < NUM_DIRECTIONS; i++)
6085 {
6086 for (j = 1; j <= dynabomb_size; j++)
6087 {
6088 int x = ex + j * xy[i][0];
6089 int y = ey + j * xy[i][1];
6090 int element;
6091
6092 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6093 break;
6094
6095 element = Feld[x][y];
6096
6097 /* do not restart explosions of fields with active bombs */
6098 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6099 continue;
6100
6101 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6102
6103 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6104 !IS_DIGGABLE(element) && !dynabomb_xl)
6105 break;
6106 }
6107 }
6108 }
6109
Bang(int x,int y)6110 void Bang(int x, int y)
6111 {
6112 int element = MovingOrBlocked2Element(x, y);
6113 int explosion_type = EX_TYPE_NORMAL;
6114
6115 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6116 {
6117 struct PlayerInfo *player = PLAYERINFO(x, y);
6118
6119 #if USE_FIX_CE_ACTION_WITH_PLAYER
6120 element = Feld[x][y] = player->initial_element;
6121 #else
6122 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6123 player->element_nr);
6124 #endif
6125
6126 if (level.use_explosion_element[player->index_nr])
6127 {
6128 int explosion_element = level.explosion_element[player->index_nr];
6129
6130 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6131 explosion_type = EX_TYPE_CROSS;
6132 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6133 explosion_type = EX_TYPE_CENTER;
6134 }
6135 }
6136
6137 switch (element)
6138 {
6139 case EL_BUG:
6140 case EL_SPACESHIP:
6141 case EL_BD_BUTTERFLY:
6142 case EL_BD_FIREFLY:
6143 case EL_YAMYAM:
6144 case EL_DARK_YAMYAM:
6145 case EL_ROBOT:
6146 case EL_PACMAN:
6147 case EL_MOLE:
6148 RaiseScoreElement(element);
6149 break;
6150
6151 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6152 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6153 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6154 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6155 case EL_DYNABOMB_INCREASE_NUMBER:
6156 case EL_DYNABOMB_INCREASE_SIZE:
6157 case EL_DYNABOMB_INCREASE_POWER:
6158 explosion_type = EX_TYPE_DYNA;
6159 break;
6160
6161 case EL_DC_LANDMINE:
6162 #if 0
6163 case EL_EM_EXIT_OPEN:
6164 case EL_EM_STEEL_EXIT_OPEN:
6165 #endif
6166 explosion_type = EX_TYPE_CENTER;
6167 break;
6168
6169 case EL_PENGUIN:
6170 case EL_LAMP:
6171 case EL_LAMP_ACTIVE:
6172 case EL_AMOEBA_TO_DIAMOND:
6173 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6174 explosion_type = EX_TYPE_CENTER;
6175 break;
6176
6177 default:
6178 if (element_info[element].explosion_type == EXPLODES_CROSS)
6179 explosion_type = EX_TYPE_CROSS;
6180 else if (element_info[element].explosion_type == EXPLODES_1X1)
6181 explosion_type = EX_TYPE_CENTER;
6182 break;
6183 }
6184
6185 if (explosion_type == EX_TYPE_DYNA)
6186 DynaExplode(x, y);
6187 else
6188 Explode(x, y, EX_PHASE_START, explosion_type);
6189
6190 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6191 }
6192
SplashAcid(int x,int y)6193 void SplashAcid(int x, int y)
6194 {
6195 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6196 (!IN_LEV_FIELD(x - 1, y - 2) ||
6197 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6198 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6199
6200 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6201 (!IN_LEV_FIELD(x + 1, y - 2) ||
6202 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6203 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6204
6205 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6206 }
6207
InitBeltMovement()6208 static void InitBeltMovement()
6209 {
6210 static int belt_base_element[4] =
6211 {
6212 EL_CONVEYOR_BELT_1_LEFT,
6213 EL_CONVEYOR_BELT_2_LEFT,
6214 EL_CONVEYOR_BELT_3_LEFT,
6215 EL_CONVEYOR_BELT_4_LEFT
6216 };
6217 static int belt_base_active_element[4] =
6218 {
6219 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6220 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6221 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6222 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6223 };
6224
6225 int x, y, i, j;
6226
6227 /* set frame order for belt animation graphic according to belt direction */
6228 for (i = 0; i < NUM_BELTS; i++)
6229 {
6230 int belt_nr = i;
6231
6232 for (j = 0; j < NUM_BELT_PARTS; j++)
6233 {
6234 int element = belt_base_active_element[belt_nr] + j;
6235 int graphic_1 = el2img(element);
6236 int graphic_2 = el2panelimg(element);
6237
6238 if (game.belt_dir[i] == MV_LEFT)
6239 {
6240 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6241 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6242 }
6243 else
6244 {
6245 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6246 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6247 }
6248 }
6249 }
6250
6251 SCAN_PLAYFIELD(x, y)
6252 {
6253 int element = Feld[x][y];
6254
6255 for (i = 0; i < NUM_BELTS; i++)
6256 {
6257 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6258 {
6259 int e_belt_nr = getBeltNrFromBeltElement(element);
6260 int belt_nr = i;
6261
6262 if (e_belt_nr == belt_nr)
6263 {
6264 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6265
6266 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6267 }
6268 }
6269 }
6270 }
6271 }
6272
ToggleBeltSwitch(int x,int y)6273 static void ToggleBeltSwitch(int x, int y)
6274 {
6275 static int belt_base_element[4] =
6276 {
6277 EL_CONVEYOR_BELT_1_LEFT,
6278 EL_CONVEYOR_BELT_2_LEFT,
6279 EL_CONVEYOR_BELT_3_LEFT,
6280 EL_CONVEYOR_BELT_4_LEFT
6281 };
6282 static int belt_base_active_element[4] =
6283 {
6284 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6285 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6286 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6287 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6288 };
6289 static int belt_base_switch_element[4] =
6290 {
6291 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6292 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6293 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6294 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6295 };
6296 static int belt_move_dir[4] =
6297 {
6298 MV_LEFT,
6299 MV_NONE,
6300 MV_RIGHT,
6301 MV_NONE,
6302 };
6303
6304 int element = Feld[x][y];
6305 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6306 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6307 int belt_dir = belt_move_dir[belt_dir_nr];
6308 int xx, yy, i;
6309
6310 if (!IS_BELT_SWITCH(element))
6311 return;
6312
6313 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6314 game.belt_dir[belt_nr] = belt_dir;
6315
6316 if (belt_dir_nr == 3)
6317 belt_dir_nr = 1;
6318
6319 /* set frame order for belt animation graphic according to belt direction */
6320 for (i = 0; i < NUM_BELT_PARTS; i++)
6321 {
6322 int element = belt_base_active_element[belt_nr] + i;
6323 int graphic_1 = el2img(element);
6324 int graphic_2 = el2panelimg(element);
6325
6326 if (belt_dir == MV_LEFT)
6327 {
6328 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6329 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6330 }
6331 else
6332 {
6333 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6334 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6335 }
6336 }
6337
6338 SCAN_PLAYFIELD(xx, yy)
6339 {
6340 int element = Feld[xx][yy];
6341
6342 if (IS_BELT_SWITCH(element))
6343 {
6344 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6345
6346 if (e_belt_nr == belt_nr)
6347 {
6348 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6349 TEST_DrawLevelField(xx, yy);
6350 }
6351 }
6352 else if (IS_BELT(element) && belt_dir != MV_NONE)
6353 {
6354 int e_belt_nr = getBeltNrFromBeltElement(element);
6355
6356 if (e_belt_nr == belt_nr)
6357 {
6358 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6359
6360 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6361 TEST_DrawLevelField(xx, yy);
6362 }
6363 }
6364 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6365 {
6366 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6367
6368 if (e_belt_nr == belt_nr)
6369 {
6370 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6371
6372 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6373 TEST_DrawLevelField(xx, yy);
6374 }
6375 }
6376 }
6377 }
6378
ToggleSwitchgateSwitch(int x,int y)6379 static void ToggleSwitchgateSwitch(int x, int y)
6380 {
6381 int xx, yy;
6382
6383 game.switchgate_pos = !game.switchgate_pos;
6384
6385 SCAN_PLAYFIELD(xx, yy)
6386 {
6387 int element = Feld[xx][yy];
6388
6389 #if !USE_BOTH_SWITCHGATE_SWITCHES
6390 if (element == EL_SWITCHGATE_SWITCH_UP ||
6391 element == EL_SWITCHGATE_SWITCH_DOWN)
6392 {
6393 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6394 TEST_DrawLevelField(xx, yy);
6395 }
6396 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6397 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6398 {
6399 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6400 TEST_DrawLevelField(xx, yy);
6401 }
6402 #else
6403 if (element == EL_SWITCHGATE_SWITCH_UP)
6404 {
6405 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6406 TEST_DrawLevelField(xx, yy);
6407 }
6408 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6409 {
6410 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6411 TEST_DrawLevelField(xx, yy);
6412 }
6413 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6414 {
6415 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6416 TEST_DrawLevelField(xx, yy);
6417 }
6418 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6419 {
6420 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6421 TEST_DrawLevelField(xx, yy);
6422 }
6423 #endif
6424 else if (element == EL_SWITCHGATE_OPEN ||
6425 element == EL_SWITCHGATE_OPENING)
6426 {
6427 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6428
6429 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6430 }
6431 else if (element == EL_SWITCHGATE_CLOSED ||
6432 element == EL_SWITCHGATE_CLOSING)
6433 {
6434 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6435
6436 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6437 }
6438 }
6439 }
6440
getInvisibleActiveFromInvisibleElement(int element)6441 static int getInvisibleActiveFromInvisibleElement(int element)
6442 {
6443 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6444 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6445 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6446 element);
6447 }
6448
getInvisibleFromInvisibleActiveElement(int element)6449 static int getInvisibleFromInvisibleActiveElement(int element)
6450 {
6451 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6452 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6453 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6454 element);
6455 }
6456
RedrawAllLightSwitchesAndInvisibleElements()6457 static void RedrawAllLightSwitchesAndInvisibleElements()
6458 {
6459 int x, y;
6460
6461 SCAN_PLAYFIELD(x, y)
6462 {
6463 int element = Feld[x][y];
6464
6465 if (element == EL_LIGHT_SWITCH &&
6466 game.light_time_left > 0)
6467 {
6468 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6469 TEST_DrawLevelField(x, y);
6470 }
6471 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6472 game.light_time_left == 0)
6473 {
6474 Feld[x][y] = EL_LIGHT_SWITCH;
6475 TEST_DrawLevelField(x, y);
6476 }
6477 else if (element == EL_EMC_DRIPPER &&
6478 game.light_time_left > 0)
6479 {
6480 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6481 TEST_DrawLevelField(x, y);
6482 }
6483 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6484 game.light_time_left == 0)
6485 {
6486 Feld[x][y] = EL_EMC_DRIPPER;
6487 TEST_DrawLevelField(x, y);
6488 }
6489 else if (element == EL_INVISIBLE_STEELWALL ||
6490 element == EL_INVISIBLE_WALL ||
6491 element == EL_INVISIBLE_SAND)
6492 {
6493 if (game.light_time_left > 0)
6494 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6495
6496 TEST_DrawLevelField(x, y);
6497
6498 /* uncrumble neighbour fields, if needed */
6499 if (element == EL_INVISIBLE_SAND)
6500 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6501 }
6502 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6503 element == EL_INVISIBLE_WALL_ACTIVE ||
6504 element == EL_INVISIBLE_SAND_ACTIVE)
6505 {
6506 if (game.light_time_left == 0)
6507 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6508
6509 TEST_DrawLevelField(x, y);
6510
6511 /* re-crumble neighbour fields, if needed */
6512 if (element == EL_INVISIBLE_SAND)
6513 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6514 }
6515 }
6516 }
6517
RedrawAllInvisibleElementsForLenses()6518 static void RedrawAllInvisibleElementsForLenses()
6519 {
6520 int x, y;
6521
6522 SCAN_PLAYFIELD(x, y)
6523 {
6524 int element = Feld[x][y];
6525
6526 if (element == EL_EMC_DRIPPER &&
6527 game.lenses_time_left > 0)
6528 {
6529 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6530 TEST_DrawLevelField(x, y);
6531 }
6532 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6533 game.lenses_time_left == 0)
6534 {
6535 Feld[x][y] = EL_EMC_DRIPPER;
6536 TEST_DrawLevelField(x, y);
6537 }
6538 else if (element == EL_INVISIBLE_STEELWALL ||
6539 element == EL_INVISIBLE_WALL ||
6540 element == EL_INVISIBLE_SAND)
6541 {
6542 if (game.lenses_time_left > 0)
6543 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6544
6545 TEST_DrawLevelField(x, y);
6546
6547 /* uncrumble neighbour fields, if needed */
6548 if (element == EL_INVISIBLE_SAND)
6549 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550 }
6551 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6552 element == EL_INVISIBLE_WALL_ACTIVE ||
6553 element == EL_INVISIBLE_SAND_ACTIVE)
6554 {
6555 if (game.lenses_time_left == 0)
6556 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6557
6558 TEST_DrawLevelField(x, y);
6559
6560 /* re-crumble neighbour fields, if needed */
6561 if (element == EL_INVISIBLE_SAND)
6562 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6563 }
6564 }
6565 }
6566
RedrawAllInvisibleElementsForMagnifier()6567 static void RedrawAllInvisibleElementsForMagnifier()
6568 {
6569 int x, y;
6570
6571 SCAN_PLAYFIELD(x, y)
6572 {
6573 int element = Feld[x][y];
6574
6575 if (element == EL_EMC_FAKE_GRASS &&
6576 game.magnify_time_left > 0)
6577 {
6578 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6579 TEST_DrawLevelField(x, y);
6580 }
6581 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6582 game.magnify_time_left == 0)
6583 {
6584 Feld[x][y] = EL_EMC_FAKE_GRASS;
6585 TEST_DrawLevelField(x, y);
6586 }
6587 else if (IS_GATE_GRAY(element) &&
6588 game.magnify_time_left > 0)
6589 {
6590 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6591 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6592 IS_EM_GATE_GRAY(element) ?
6593 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6594 IS_EMC_GATE_GRAY(element) ?
6595 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6596 IS_DC_GATE_GRAY(element) ?
6597 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6598 element);
6599 TEST_DrawLevelField(x, y);
6600 }
6601 else if (IS_GATE_GRAY_ACTIVE(element) &&
6602 game.magnify_time_left == 0)
6603 {
6604 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6605 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6606 IS_EM_GATE_GRAY_ACTIVE(element) ?
6607 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6608 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6609 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6610 IS_DC_GATE_GRAY_ACTIVE(element) ?
6611 EL_DC_GATE_WHITE_GRAY :
6612 element);
6613 TEST_DrawLevelField(x, y);
6614 }
6615 }
6616 }
6617
ToggleLightSwitch(int x,int y)6618 static void ToggleLightSwitch(int x, int y)
6619 {
6620 int element = Feld[x][y];
6621
6622 game.light_time_left =
6623 (element == EL_LIGHT_SWITCH ?
6624 level.time_light * FRAMES_PER_SECOND : 0);
6625
6626 RedrawAllLightSwitchesAndInvisibleElements();
6627 }
6628
ActivateTimegateSwitch(int x,int y)6629 static void ActivateTimegateSwitch(int x, int y)
6630 {
6631 int xx, yy;
6632
6633 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6634
6635 SCAN_PLAYFIELD(xx, yy)
6636 {
6637 int element = Feld[xx][yy];
6638
6639 if (element == EL_TIMEGATE_CLOSED ||
6640 element == EL_TIMEGATE_CLOSING)
6641 {
6642 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6643 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6644 }
6645
6646 /*
6647 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6648 {
6649 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6650 TEST_DrawLevelField(xx, yy);
6651 }
6652 */
6653
6654 }
6655
6656 #if 1
6657 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6658 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6659 #else
6660 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6661 #endif
6662 }
6663
Impact(int x,int y)6664 void Impact(int x, int y)
6665 {
6666 boolean last_line = (y == lev_fieldy - 1);
6667 boolean object_hit = FALSE;
6668 boolean impact = (last_line || object_hit);
6669 int element = Feld[x][y];
6670 int smashed = EL_STEELWALL;
6671
6672 if (!last_line) /* check if element below was hit */
6673 {
6674 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6675 return;
6676
6677 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6678 MovDir[x][y + 1] != MV_DOWN ||
6679 MovPos[x][y + 1] <= TILEY / 2));
6680
6681 /* do not smash moving elements that left the smashed field in time */
6682 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6683 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6684 object_hit = FALSE;
6685
6686 #if USE_QUICKSAND_IMPACT_BUGFIX
6687 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6688 {
6689 RemoveMovingField(x, y + 1);
6690 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6691 Feld[x][y + 2] = EL_ROCK;
6692 TEST_DrawLevelField(x, y + 2);
6693
6694 object_hit = TRUE;
6695 }
6696
6697 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6698 {
6699 RemoveMovingField(x, y + 1);
6700 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6701 Feld[x][y + 2] = EL_ROCK;
6702 TEST_DrawLevelField(x, y + 2);
6703
6704 object_hit = TRUE;
6705 }
6706 #endif
6707
6708 if (object_hit)
6709 smashed = MovingOrBlocked2Element(x, y + 1);
6710
6711 impact = (last_line || object_hit);
6712 }
6713
6714 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6715 {
6716 SplashAcid(x, y + 1);
6717 return;
6718 }
6719
6720 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6721 /* only reset graphic animation if graphic really changes after impact */
6722 if (impact &&
6723 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6724 {
6725 ResetGfxAnimation(x, y);
6726 TEST_DrawLevelField(x, y);
6727 }
6728
6729 if (impact && CAN_EXPLODE_IMPACT(element))
6730 {
6731 Bang(x, y);
6732 return;
6733 }
6734 else if (impact && element == EL_PEARL &&
6735 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6736 {
6737 ResetGfxAnimation(x, y);
6738
6739 Feld[x][y] = EL_PEARL_BREAKING;
6740 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6741 return;
6742 }
6743 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6744 {
6745 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6746
6747 return;
6748 }
6749
6750 if (impact && element == EL_AMOEBA_DROP)
6751 {
6752 if (object_hit && IS_PLAYER(x, y + 1))
6753 KillPlayerUnlessEnemyProtected(x, y + 1);
6754 else if (object_hit && smashed == EL_PENGUIN)
6755 Bang(x, y + 1);
6756 else
6757 {
6758 Feld[x][y] = EL_AMOEBA_GROWING;
6759 Store[x][y] = EL_AMOEBA_WET;
6760
6761 ResetRandomAnimationValue(x, y);
6762 }
6763 return;
6764 }
6765
6766 if (object_hit) /* check which object was hit */
6767 {
6768 if ((CAN_PASS_MAGIC_WALL(element) &&
6769 (smashed == EL_MAGIC_WALL ||
6770 smashed == EL_BD_MAGIC_WALL)) ||
6771 (CAN_PASS_DC_MAGIC_WALL(element) &&
6772 smashed == EL_DC_MAGIC_WALL))
6773 {
6774 int xx, yy;
6775 int activated_magic_wall =
6776 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6777 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6778 EL_DC_MAGIC_WALL_ACTIVE);
6779
6780 /* activate magic wall / mill */
6781 SCAN_PLAYFIELD(xx, yy)
6782 {
6783 if (Feld[xx][yy] == smashed)
6784 Feld[xx][yy] = activated_magic_wall;
6785 }
6786
6787 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6788 game.magic_wall_active = TRUE;
6789
6790 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6791 SND_MAGIC_WALL_ACTIVATING :
6792 smashed == EL_BD_MAGIC_WALL ?
6793 SND_BD_MAGIC_WALL_ACTIVATING :
6794 SND_DC_MAGIC_WALL_ACTIVATING));
6795 }
6796
6797 if (IS_PLAYER(x, y + 1))
6798 {
6799 if (CAN_SMASH_PLAYER(element))
6800 {
6801 KillPlayerUnlessEnemyProtected(x, y + 1);
6802 return;
6803 }
6804 }
6805 else if (smashed == EL_PENGUIN)
6806 {
6807 if (CAN_SMASH_PLAYER(element))
6808 {
6809 Bang(x, y + 1);
6810 return;
6811 }
6812 }
6813 else if (element == EL_BD_DIAMOND)
6814 {
6815 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6816 {
6817 Bang(x, y + 1);
6818 return;
6819 }
6820 }
6821 else if (((element == EL_SP_INFOTRON ||
6822 element == EL_SP_ZONK) &&
6823 (smashed == EL_SP_SNIKSNAK ||
6824 smashed == EL_SP_ELECTRON ||
6825 smashed == EL_SP_DISK_ORANGE)) ||
6826 (element == EL_SP_INFOTRON &&
6827 smashed == EL_SP_DISK_YELLOW))
6828 {
6829 Bang(x, y + 1);
6830 return;
6831 }
6832 else if (CAN_SMASH_EVERYTHING(element))
6833 {
6834 if (IS_CLASSIC_ENEMY(smashed) ||
6835 CAN_EXPLODE_SMASHED(smashed))
6836 {
6837 Bang(x, y + 1);
6838 return;
6839 }
6840 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6841 {
6842 if (smashed == EL_LAMP ||
6843 smashed == EL_LAMP_ACTIVE)
6844 {
6845 Bang(x, y + 1);
6846 return;
6847 }
6848 else if (smashed == EL_NUT)
6849 {
6850 Feld[x][y + 1] = EL_NUT_BREAKING;
6851 PlayLevelSound(x, y, SND_NUT_BREAKING);
6852 RaiseScoreElement(EL_NUT);
6853 return;
6854 }
6855 else if (smashed == EL_PEARL)
6856 {
6857 ResetGfxAnimation(x, y);
6858
6859 Feld[x][y + 1] = EL_PEARL_BREAKING;
6860 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6861 return;
6862 }
6863 else if (smashed == EL_DIAMOND)
6864 {
6865 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6866 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6867 return;
6868 }
6869 else if (IS_BELT_SWITCH(smashed))
6870 {
6871 ToggleBeltSwitch(x, y + 1);
6872 }
6873 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6874 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6875 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6876 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6877 {
6878 ToggleSwitchgateSwitch(x, y + 1);
6879 }
6880 else if (smashed == EL_LIGHT_SWITCH ||
6881 smashed == EL_LIGHT_SWITCH_ACTIVE)
6882 {
6883 ToggleLightSwitch(x, y + 1);
6884 }
6885 else
6886 {
6887 #if 0
6888 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6889 #endif
6890
6891 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6892
6893 CheckElementChangeBySide(x, y + 1, smashed, element,
6894 CE_SWITCHED, CH_SIDE_TOP);
6895 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6896 CH_SIDE_TOP);
6897 }
6898 }
6899 else
6900 {
6901 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6902 }
6903 }
6904 }
6905
6906 /* play sound of magic wall / mill */
6907 if (!last_line &&
6908 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6911 {
6912 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6918
6919 return;
6920 }
6921
6922 /* play sound of object that hits the ground */
6923 if (last_line || object_hit)
6924 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6925 }
6926
TurnRoundExt(int x,int y)6927 inline static void TurnRoundExt(int x, int y)
6928 {
6929 static struct
6930 {
6931 int dx, dy;
6932 } move_xy[] =
6933 {
6934 { 0, 0 },
6935 { -1, 0 },
6936 { +1, 0 },
6937 { 0, 0 },
6938 { 0, -1 },
6939 { 0, 0 }, { 0, 0 }, { 0, 0 },
6940 { 0, +1 }
6941 };
6942 static struct
6943 {
6944 int left, right, back;
6945 } turn[] =
6946 {
6947 { 0, 0, 0 },
6948 { MV_DOWN, MV_UP, MV_RIGHT },
6949 { MV_UP, MV_DOWN, MV_LEFT },
6950 { 0, 0, 0 },
6951 { MV_LEFT, MV_RIGHT, MV_DOWN },
6952 { 0, 0, 0 },
6953 { 0, 0, 0 },
6954 { 0, 0, 0 },
6955 { MV_RIGHT, MV_LEFT, MV_UP }
6956 };
6957
6958 int element = Feld[x][y];
6959 int move_pattern = element_info[element].move_pattern;
6960
6961 int old_move_dir = MovDir[x][y];
6962 int left_dir = turn[old_move_dir].left;
6963 int right_dir = turn[old_move_dir].right;
6964 int back_dir = turn[old_move_dir].back;
6965
6966 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6967 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6968 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6969 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6970
6971 int left_x = x + left_dx, left_y = y + left_dy;
6972 int right_x = x + right_dx, right_y = y + right_dy;
6973 int move_x = x + move_dx, move_y = y + move_dy;
6974
6975 int xx, yy;
6976
6977 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6978 {
6979 TestIfBadThingTouchesOtherBadThing(x, y);
6980
6981 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982 MovDir[x][y] = right_dir;
6983 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984 MovDir[x][y] = left_dir;
6985
6986 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6987 MovDelay[x][y] = 9;
6988 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6989 MovDelay[x][y] = 1;
6990 }
6991 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6992 {
6993 TestIfBadThingTouchesOtherBadThing(x, y);
6994
6995 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996 MovDir[x][y] = left_dir;
6997 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998 MovDir[x][y] = right_dir;
6999
7000 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7001 MovDelay[x][y] = 9;
7002 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7003 MovDelay[x][y] = 1;
7004 }
7005 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7006 {
7007 TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010 MovDir[x][y] = left_dir;
7011 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012 MovDir[x][y] = right_dir;
7013
7014 if (MovDir[x][y] != old_move_dir)
7015 MovDelay[x][y] = 9;
7016 }
7017 else if (element == EL_YAMYAM)
7018 {
7019 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7021
7022 if (can_turn_left && can_turn_right)
7023 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024 else if (can_turn_left)
7025 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026 else if (can_turn_right)
7027 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7028 else
7029 MovDir[x][y] = back_dir;
7030
7031 MovDelay[x][y] = 16 + 16 * RND(3);
7032 }
7033 else if (element == EL_DARK_YAMYAM)
7034 {
7035 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7036 left_x, left_y);
7037 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7038 right_x, right_y);
7039
7040 if (can_turn_left && can_turn_right)
7041 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042 else if (can_turn_left)
7043 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044 else if (can_turn_right)
7045 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7046 else
7047 MovDir[x][y] = back_dir;
7048
7049 MovDelay[x][y] = 16 + 16 * RND(3);
7050 }
7051 else if (element == EL_PACMAN)
7052 {
7053 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7055
7056 if (can_turn_left && can_turn_right)
7057 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058 else if (can_turn_left)
7059 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060 else if (can_turn_right)
7061 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7062 else
7063 MovDir[x][y] = back_dir;
7064
7065 MovDelay[x][y] = 6 + RND(40);
7066 }
7067 else if (element == EL_PIG)
7068 {
7069 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072 boolean should_turn_left, should_turn_right, should_move_on;
7073 int rnd_value = 24;
7074 int rnd = RND(rnd_value);
7075
7076 should_turn_left = (can_turn_left &&
7077 (!can_move_on ||
7078 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079 y + back_dy + left_dy)));
7080 should_turn_right = (can_turn_right &&
7081 (!can_move_on ||
7082 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083 y + back_dy + right_dy)));
7084 should_move_on = (can_move_on &&
7085 (!can_turn_left ||
7086 !can_turn_right ||
7087 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088 y + move_dy + left_dy) ||
7089 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090 y + move_dy + right_dy)));
7091
7092 if (should_turn_left || should_turn_right || should_move_on)
7093 {
7094 if (should_turn_left && should_turn_right && should_move_on)
7095 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7096 rnd < 2 * rnd_value / 3 ? right_dir :
7097 old_move_dir);
7098 else if (should_turn_left && should_turn_right)
7099 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100 else if (should_turn_left && should_move_on)
7101 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102 else if (should_turn_right && should_move_on)
7103 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104 else if (should_turn_left)
7105 MovDir[x][y] = left_dir;
7106 else if (should_turn_right)
7107 MovDir[x][y] = right_dir;
7108 else if (should_move_on)
7109 MovDir[x][y] = old_move_dir;
7110 }
7111 else if (can_move_on && rnd > rnd_value / 8)
7112 MovDir[x][y] = old_move_dir;
7113 else if (can_turn_left && can_turn_right)
7114 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115 else if (can_turn_left && rnd > rnd_value / 8)
7116 MovDir[x][y] = left_dir;
7117 else if (can_turn_right && rnd > rnd_value/8)
7118 MovDir[x][y] = right_dir;
7119 else
7120 MovDir[x][y] = back_dir;
7121
7122 xx = x + move_xy[MovDir[x][y]].dx;
7123 yy = y + move_xy[MovDir[x][y]].dy;
7124
7125 if (!IN_LEV_FIELD(xx, yy) ||
7126 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7127 MovDir[x][y] = old_move_dir;
7128
7129 MovDelay[x][y] = 0;
7130 }
7131 else if (element == EL_DRAGON)
7132 {
7133 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7136 int rnd_value = 24;
7137 int rnd = RND(rnd_value);
7138
7139 if (can_move_on && rnd > rnd_value / 8)
7140 MovDir[x][y] = old_move_dir;
7141 else if (can_turn_left && can_turn_right)
7142 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143 else if (can_turn_left && rnd > rnd_value / 8)
7144 MovDir[x][y] = left_dir;
7145 else if (can_turn_right && rnd > rnd_value / 8)
7146 MovDir[x][y] = right_dir;
7147 else
7148 MovDir[x][y] = back_dir;
7149
7150 xx = x + move_xy[MovDir[x][y]].dx;
7151 yy = y + move_xy[MovDir[x][y]].dy;
7152
7153 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154 MovDir[x][y] = old_move_dir;
7155
7156 MovDelay[x][y] = 0;
7157 }
7158 else if (element == EL_MOLE)
7159 {
7160 boolean can_move_on =
7161 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162 IS_AMOEBOID(Feld[move_x][move_y]) ||
7163 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7164 if (!can_move_on)
7165 {
7166 boolean can_turn_left =
7167 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168 IS_AMOEBOID(Feld[left_x][left_y])));
7169
7170 boolean can_turn_right =
7171 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172 IS_AMOEBOID(Feld[right_x][right_y])));
7173
7174 if (can_turn_left && can_turn_right)
7175 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176 else if (can_turn_left)
7177 MovDir[x][y] = left_dir;
7178 else
7179 MovDir[x][y] = right_dir;
7180 }
7181
7182 if (MovDir[x][y] != old_move_dir)
7183 MovDelay[x][y] = 9;
7184 }
7185 else if (element == EL_BALLOON)
7186 {
7187 MovDir[x][y] = game.wind_direction;
7188 MovDelay[x][y] = 0;
7189 }
7190 else if (element == EL_SPRING)
7191 {
7192 #if USE_NEW_SPRING_BUMPER
7193 if (MovDir[x][y] & MV_HORIZONTAL)
7194 {
7195 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7196 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7197 {
7198 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7199 ResetGfxAnimation(move_x, move_y);
7200 TEST_DrawLevelField(move_x, move_y);
7201
7202 MovDir[x][y] = back_dir;
7203 }
7204 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7205 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7206 MovDir[x][y] = MV_NONE;
7207 }
7208 #else
7209 if (MovDir[x][y] & MV_HORIZONTAL &&
7210 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7211 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7212 MovDir[x][y] = MV_NONE;
7213 #endif
7214
7215 MovDelay[x][y] = 0;
7216 }
7217 else if (element == EL_ROBOT ||
7218 element == EL_SATELLITE ||
7219 element == EL_PENGUIN ||
7220 element == EL_EMC_ANDROID)
7221 {
7222 int attr_x = -1, attr_y = -1;
7223
7224 if (AllPlayersGone)
7225 {
7226 attr_x = ExitX;
7227 attr_y = ExitY;
7228 }
7229 else
7230 {
7231 int i;
7232
7233 for (i = 0; i < MAX_PLAYERS; i++)
7234 {
7235 struct PlayerInfo *player = &stored_player[i];
7236 int jx = player->jx, jy = player->jy;
7237
7238 if (!player->active)
7239 continue;
7240
7241 if (attr_x == -1 ||
7242 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7243 {
7244 attr_x = jx;
7245 attr_y = jy;
7246 }
7247 }
7248 }
7249
7250 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7251 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7252 game.engine_version < VERSION_IDENT(3,1,0,0)))
7253 {
7254 attr_x = ZX;
7255 attr_y = ZY;
7256 }
7257
7258 if (element == EL_PENGUIN)
7259 {
7260 int i;
7261 static int xy[4][2] =
7262 {
7263 { 0, -1 },
7264 { -1, 0 },
7265 { +1, 0 },
7266 { 0, +1 }
7267 };
7268
7269 for (i = 0; i < NUM_DIRECTIONS; i++)
7270 {
7271 int ex = x + xy[i][0];
7272 int ey = y + xy[i][1];
7273
7274 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7275 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7276 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7277 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7278 {
7279 attr_x = ex;
7280 attr_y = ey;
7281 break;
7282 }
7283 }
7284 }
7285
7286 MovDir[x][y] = MV_NONE;
7287 if (attr_x < x)
7288 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7289 else if (attr_x > x)
7290 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7291 if (attr_y < y)
7292 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7293 else if (attr_y > y)
7294 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7295
7296 if (element == EL_ROBOT)
7297 {
7298 int newx, newy;
7299
7300 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7302 Moving2Blocked(x, y, &newx, &newy);
7303
7304 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7305 MovDelay[x][y] = 8 + 8 * !RND(3);
7306 else
7307 MovDelay[x][y] = 16;
7308 }
7309 else if (element == EL_PENGUIN)
7310 {
7311 int newx, newy;
7312
7313 MovDelay[x][y] = 1;
7314
7315 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7316 {
7317 boolean first_horiz = RND(2);
7318 int new_move_dir = MovDir[x][y];
7319
7320 MovDir[x][y] =
7321 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322 Moving2Blocked(x, y, &newx, &newy);
7323
7324 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7325 return;
7326
7327 MovDir[x][y] =
7328 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329 Moving2Blocked(x, y, &newx, &newy);
7330
7331 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7332 return;
7333
7334 MovDir[x][y] = old_move_dir;
7335 return;
7336 }
7337 }
7338 else if (element == EL_SATELLITE)
7339 {
7340 int newx, newy;
7341
7342 MovDelay[x][y] = 1;
7343
7344 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7345 {
7346 boolean first_horiz = RND(2);
7347 int new_move_dir = MovDir[x][y];
7348
7349 MovDir[x][y] =
7350 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7351 Moving2Blocked(x, y, &newx, &newy);
7352
7353 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7354 return;
7355
7356 MovDir[x][y] =
7357 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358 Moving2Blocked(x, y, &newx, &newy);
7359
7360 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7361 return;
7362
7363 MovDir[x][y] = old_move_dir;
7364 return;
7365 }
7366 }
7367 else if (element == EL_EMC_ANDROID)
7368 {
7369 static int check_pos[16] =
7370 {
7371 -1, /* 0 => (invalid) */
7372 7, /* 1 => MV_LEFT */
7373 3, /* 2 => MV_RIGHT */
7374 -1, /* 3 => (invalid) */
7375 1, /* 4 => MV_UP */
7376 0, /* 5 => MV_LEFT | MV_UP */
7377 2, /* 6 => MV_RIGHT | MV_UP */
7378 -1, /* 7 => (invalid) */
7379 5, /* 8 => MV_DOWN */
7380 6, /* 9 => MV_LEFT | MV_DOWN */
7381 4, /* 10 => MV_RIGHT | MV_DOWN */
7382 -1, /* 11 => (invalid) */
7383 -1, /* 12 => (invalid) */
7384 -1, /* 13 => (invalid) */
7385 -1, /* 14 => (invalid) */
7386 -1, /* 15 => (invalid) */
7387 };
7388 static struct
7389 {
7390 int dx, dy;
7391 int dir;
7392 } check_xy[8] =
7393 {
7394 { -1, -1, MV_LEFT | MV_UP },
7395 { 0, -1, MV_UP },
7396 { +1, -1, MV_RIGHT | MV_UP },
7397 { +1, 0, MV_RIGHT },
7398 { +1, +1, MV_RIGHT | MV_DOWN },
7399 { 0, +1, MV_DOWN },
7400 { -1, +1, MV_LEFT | MV_DOWN },
7401 { -1, 0, MV_LEFT },
7402 };
7403 int start_pos, check_order;
7404 boolean can_clone = FALSE;
7405 int i;
7406
7407 /* check if there is any free field around current position */
7408 for (i = 0; i < 8; i++)
7409 {
7410 int newx = x + check_xy[i].dx;
7411 int newy = y + check_xy[i].dy;
7412
7413 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7414 {
7415 can_clone = TRUE;
7416
7417 break;
7418 }
7419 }
7420
7421 if (can_clone) /* randomly find an element to clone */
7422 {
7423 can_clone = FALSE;
7424
7425 start_pos = check_pos[RND(8)];
7426 check_order = (RND(2) ? -1 : +1);
7427
7428 for (i = 0; i < 8; i++)
7429 {
7430 int pos_raw = start_pos + i * check_order;
7431 int pos = (pos_raw + 8) % 8;
7432 int newx = x + check_xy[pos].dx;
7433 int newy = y + check_xy[pos].dy;
7434
7435 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7436 {
7437 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7438 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7439
7440 Store[x][y] = Feld[newx][newy];
7441
7442 can_clone = TRUE;
7443
7444 break;
7445 }
7446 }
7447 }
7448
7449 if (can_clone) /* randomly find a direction to move */
7450 {
7451 can_clone = FALSE;
7452
7453 start_pos = check_pos[RND(8)];
7454 check_order = (RND(2) ? -1 : +1);
7455
7456 for (i = 0; i < 8; i++)
7457 {
7458 int pos_raw = start_pos + i * check_order;
7459 int pos = (pos_raw + 8) % 8;
7460 int newx = x + check_xy[pos].dx;
7461 int newy = y + check_xy[pos].dy;
7462 int new_move_dir = check_xy[pos].dir;
7463
7464 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7465 {
7466 MovDir[x][y] = new_move_dir;
7467 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7468
7469 can_clone = TRUE;
7470
7471 break;
7472 }
7473 }
7474 }
7475
7476 if (can_clone) /* cloning and moving successful */
7477 return;
7478
7479 /* cannot clone -- try to move towards player */
7480
7481 start_pos = check_pos[MovDir[x][y] & 0x0f];
7482 check_order = (RND(2) ? -1 : +1);
7483
7484 for (i = 0; i < 3; i++)
7485 {
7486 /* first check start_pos, then previous/next or (next/previous) pos */
7487 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7488 int pos = (pos_raw + 8) % 8;
7489 int newx = x + check_xy[pos].dx;
7490 int newy = y + check_xy[pos].dy;
7491 int new_move_dir = check_xy[pos].dir;
7492
7493 if (IS_PLAYER(newx, newy))
7494 break;
7495
7496 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7497 {
7498 MovDir[x][y] = new_move_dir;
7499 MovDelay[x][y] = level.android_move_time * 8 + 1;
7500
7501 break;
7502 }
7503 }
7504 }
7505 }
7506 else if (move_pattern == MV_TURNING_LEFT ||
7507 move_pattern == MV_TURNING_RIGHT ||
7508 move_pattern == MV_TURNING_LEFT_RIGHT ||
7509 move_pattern == MV_TURNING_RIGHT_LEFT ||
7510 move_pattern == MV_TURNING_RANDOM ||
7511 move_pattern == MV_ALL_DIRECTIONS)
7512 {
7513 boolean can_turn_left =
7514 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7515 boolean can_turn_right =
7516 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7517
7518 if (element_info[element].move_stepsize == 0) /* "not moving" */
7519 return;
7520
7521 if (move_pattern == MV_TURNING_LEFT)
7522 MovDir[x][y] = left_dir;
7523 else if (move_pattern == MV_TURNING_RIGHT)
7524 MovDir[x][y] = right_dir;
7525 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7526 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7527 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7528 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7529 else if (move_pattern == MV_TURNING_RANDOM)
7530 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7531 can_turn_right && !can_turn_left ? right_dir :
7532 RND(2) ? left_dir : right_dir);
7533 else if (can_turn_left && can_turn_right)
7534 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7535 else if (can_turn_left)
7536 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7537 else if (can_turn_right)
7538 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7539 else
7540 MovDir[x][y] = back_dir;
7541
7542 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7543 }
7544 else if (move_pattern == MV_HORIZONTAL ||
7545 move_pattern == MV_VERTICAL)
7546 {
7547 if (move_pattern & old_move_dir)
7548 MovDir[x][y] = back_dir;
7549 else if (move_pattern == MV_HORIZONTAL)
7550 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7551 else if (move_pattern == MV_VERTICAL)
7552 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7553
7554 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7555 }
7556 else if (move_pattern & MV_ANY_DIRECTION)
7557 {
7558 MovDir[x][y] = move_pattern;
7559 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560 }
7561 else if (move_pattern & MV_WIND_DIRECTION)
7562 {
7563 MovDir[x][y] = game.wind_direction;
7564 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7565 }
7566 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7567 {
7568 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7569 MovDir[x][y] = left_dir;
7570 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7571 MovDir[x][y] = right_dir;
7572
7573 if (MovDir[x][y] != old_move_dir)
7574 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7575 }
7576 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7577 {
7578 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7579 MovDir[x][y] = right_dir;
7580 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7581 MovDir[x][y] = left_dir;
7582
7583 if (MovDir[x][y] != old_move_dir)
7584 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7585 }
7586 else if (move_pattern == MV_TOWARDS_PLAYER ||
7587 move_pattern == MV_AWAY_FROM_PLAYER)
7588 {
7589 int attr_x = -1, attr_y = -1;
7590 int newx, newy;
7591 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7592
7593 if (AllPlayersGone)
7594 {
7595 attr_x = ExitX;
7596 attr_y = ExitY;
7597 }
7598 else
7599 {
7600 int i;
7601
7602 for (i = 0; i < MAX_PLAYERS; i++)
7603 {
7604 struct PlayerInfo *player = &stored_player[i];
7605 int jx = player->jx, jy = player->jy;
7606
7607 if (!player->active)
7608 continue;
7609
7610 if (attr_x == -1 ||
7611 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7612 {
7613 attr_x = jx;
7614 attr_y = jy;
7615 }
7616 }
7617 }
7618
7619 MovDir[x][y] = MV_NONE;
7620 if (attr_x < x)
7621 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7622 else if (attr_x > x)
7623 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7624 if (attr_y < y)
7625 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7626 else if (attr_y > y)
7627 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7628
7629 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7630
7631 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7632 {
7633 boolean first_horiz = RND(2);
7634 int new_move_dir = MovDir[x][y];
7635
7636 if (element_info[element].move_stepsize == 0) /* "not moving" */
7637 {
7638 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7639 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7640
7641 return;
7642 }
7643
7644 MovDir[x][y] =
7645 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7646 Moving2Blocked(x, y, &newx, &newy);
7647
7648 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7649 return;
7650
7651 MovDir[x][y] =
7652 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7653 Moving2Blocked(x, y, &newx, &newy);
7654
7655 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7656 return;
7657
7658 MovDir[x][y] = old_move_dir;
7659 }
7660 }
7661 else if (move_pattern == MV_WHEN_PUSHED ||
7662 move_pattern == MV_WHEN_DROPPED)
7663 {
7664 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7665 MovDir[x][y] = MV_NONE;
7666
7667 MovDelay[x][y] = 0;
7668 }
7669 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7670 {
7671 static int test_xy[7][2] =
7672 {
7673 { 0, -1 },
7674 { -1, 0 },
7675 { +1, 0 },
7676 { 0, +1 },
7677 { 0, -1 },
7678 { -1, 0 },
7679 { +1, 0 },
7680 };
7681 static int test_dir[7] =
7682 {
7683 MV_UP,
7684 MV_LEFT,
7685 MV_RIGHT,
7686 MV_DOWN,
7687 MV_UP,
7688 MV_LEFT,
7689 MV_RIGHT,
7690 };
7691 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7692 int move_preference = -1000000; /* start with very low preference */
7693 int new_move_dir = MV_NONE;
7694 int start_test = RND(4);
7695 int i;
7696
7697 for (i = 0; i < NUM_DIRECTIONS; i++)
7698 {
7699 int move_dir = test_dir[start_test + i];
7700 int move_dir_preference;
7701
7702 xx = x + test_xy[start_test + i][0];
7703 yy = y + test_xy[start_test + i][1];
7704
7705 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7707 {
7708 new_move_dir = move_dir;
7709
7710 break;
7711 }
7712
7713 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7714 continue;
7715
7716 move_dir_preference = -1 * RunnerVisit[xx][yy];
7717 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718 move_dir_preference = PlayerVisit[xx][yy];
7719
7720 if (move_dir_preference > move_preference)
7721 {
7722 /* prefer field that has not been visited for the longest time */
7723 move_preference = move_dir_preference;
7724 new_move_dir = move_dir;
7725 }
7726 else if (move_dir_preference == move_preference &&
7727 move_dir == old_move_dir)
7728 {
7729 /* prefer last direction when all directions are preferred equally */
7730 move_preference = move_dir_preference;
7731 new_move_dir = move_dir;
7732 }
7733 }
7734
7735 MovDir[x][y] = new_move_dir;
7736 if (old_move_dir != new_move_dir)
7737 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738 }
7739 }
7740
TurnRound(int x,int y)7741 static void TurnRound(int x, int y)
7742 {
7743 int direction = MovDir[x][y];
7744
7745 TurnRoundExt(x, y);
7746
7747 GfxDir[x][y] = MovDir[x][y];
7748
7749 if (direction != MovDir[x][y])
7750 GfxFrame[x][y] = 0;
7751
7752 if (MovDelay[x][y])
7753 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7754
7755 ResetGfxFrame(x, y, FALSE);
7756 }
7757
JustBeingPushed(int x,int y)7758 static boolean JustBeingPushed(int x, int y)
7759 {
7760 int i;
7761
7762 for (i = 0; i < MAX_PLAYERS; i++)
7763 {
7764 struct PlayerInfo *player = &stored_player[i];
7765
7766 if (player->active && player->is_pushing && player->MovPos)
7767 {
7768 int next_jx = player->jx + (player->jx - player->last_jx);
7769 int next_jy = player->jy + (player->jy - player->last_jy);
7770
7771 if (x == next_jx && y == next_jy)
7772 return TRUE;
7773 }
7774 }
7775
7776 return FALSE;
7777 }
7778
StartMoving(int x,int y)7779 void StartMoving(int x, int y)
7780 {
7781 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7782 int element = Feld[x][y];
7783
7784 if (Stop[x][y])
7785 return;
7786
7787 if (MovDelay[x][y] == 0)
7788 GfxAction[x][y] = ACTION_DEFAULT;
7789
7790 if (CAN_FALL(element) && y < lev_fieldy - 1)
7791 {
7792 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7793 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794 if (JustBeingPushed(x, y))
7795 return;
7796
7797 if (element == EL_QUICKSAND_FULL)
7798 {
7799 if (IS_FREE(x, y + 1))
7800 {
7801 InitMovingField(x, y, MV_DOWN);
7802 started_moving = TRUE;
7803
7804 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807 Store[x][y] = EL_ROCK;
7808 #else
7809 Store[x][y] = EL_ROCK;
7810 #endif
7811
7812 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7813 }
7814 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7815 {
7816 if (!MovDelay[x][y])
7817 {
7818 MovDelay[x][y] = TILEY + 1;
7819
7820 ResetGfxAnimation(x, y);
7821 ResetGfxAnimation(x, y + 1);
7822 }
7823
7824 if (MovDelay[x][y])
7825 {
7826 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7828
7829 MovDelay[x][y]--;
7830 if (MovDelay[x][y])
7831 return;
7832 }
7833
7834 Feld[x][y] = EL_QUICKSAND_EMPTY;
7835 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7836 Store[x][y + 1] = Store[x][y];
7837 Store[x][y] = 0;
7838
7839 PlayLevelSoundAction(x, y, ACTION_FILLING);
7840 }
7841 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7842 {
7843 if (!MovDelay[x][y])
7844 {
7845 MovDelay[x][y] = TILEY + 1;
7846
7847 ResetGfxAnimation(x, y);
7848 ResetGfxAnimation(x, y + 1);
7849 }
7850
7851 if (MovDelay[x][y])
7852 {
7853 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7855
7856 MovDelay[x][y]--;
7857 if (MovDelay[x][y])
7858 return;
7859 }
7860
7861 Feld[x][y] = EL_QUICKSAND_EMPTY;
7862 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863 Store[x][y + 1] = Store[x][y];
7864 Store[x][y] = 0;
7865
7866 PlayLevelSoundAction(x, y, ACTION_FILLING);
7867 }
7868 }
7869 else if (element == EL_QUICKSAND_FAST_FULL)
7870 {
7871 if (IS_FREE(x, y + 1))
7872 {
7873 InitMovingField(x, y, MV_DOWN);
7874 started_moving = TRUE;
7875
7876 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879 Store[x][y] = EL_ROCK;
7880 #else
7881 Store[x][y] = EL_ROCK;
7882 #endif
7883
7884 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7885 }
7886 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887 {
7888 if (!MovDelay[x][y])
7889 {
7890 MovDelay[x][y] = TILEY + 1;
7891
7892 ResetGfxAnimation(x, y);
7893 ResetGfxAnimation(x, y + 1);
7894 }
7895
7896 if (MovDelay[x][y])
7897 {
7898 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7900
7901 MovDelay[x][y]--;
7902 if (MovDelay[x][y])
7903 return;
7904 }
7905
7906 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908 Store[x][y + 1] = Store[x][y];
7909 Store[x][y] = 0;
7910
7911 PlayLevelSoundAction(x, y, ACTION_FILLING);
7912 }
7913 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7914 {
7915 if (!MovDelay[x][y])
7916 {
7917 MovDelay[x][y] = TILEY + 1;
7918
7919 ResetGfxAnimation(x, y);
7920 ResetGfxAnimation(x, y + 1);
7921 }
7922
7923 if (MovDelay[x][y])
7924 {
7925 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7927
7928 MovDelay[x][y]--;
7929 if (MovDelay[x][y])
7930 return;
7931 }
7932
7933 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7935 Store[x][y + 1] = Store[x][y];
7936 Store[x][y] = 0;
7937
7938 PlayLevelSoundAction(x, y, ACTION_FILLING);
7939 }
7940 }
7941 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7943 {
7944 InitMovingField(x, y, MV_DOWN);
7945 started_moving = TRUE;
7946
7947 Feld[x][y] = EL_QUICKSAND_FILLING;
7948 Store[x][y] = element;
7949
7950 PlayLevelSoundAction(x, y, ACTION_FILLING);
7951 }
7952 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7954 {
7955 InitMovingField(x, y, MV_DOWN);
7956 started_moving = TRUE;
7957
7958 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7959 Store[x][y] = element;
7960
7961 PlayLevelSoundAction(x, y, ACTION_FILLING);
7962 }
7963 else if (element == EL_MAGIC_WALL_FULL)
7964 {
7965 if (IS_FREE(x, y + 1))
7966 {
7967 InitMovingField(x, y, MV_DOWN);
7968 started_moving = TRUE;
7969
7970 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7971 Store[x][y] = EL_CHANGED(Store[x][y]);
7972 }
7973 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7974 {
7975 if (!MovDelay[x][y])
7976 MovDelay[x][y] = TILEY / 4 + 1;
7977
7978 if (MovDelay[x][y])
7979 {
7980 MovDelay[x][y]--;
7981 if (MovDelay[x][y])
7982 return;
7983 }
7984
7985 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7986 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7987 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7988 Store[x][y] = 0;
7989 }
7990 }
7991 else if (element == EL_BD_MAGIC_WALL_FULL)
7992 {
7993 if (IS_FREE(x, y + 1))
7994 {
7995 InitMovingField(x, y, MV_DOWN);
7996 started_moving = TRUE;
7997
7998 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8000 }
8001 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8002 {
8003 if (!MovDelay[x][y])
8004 MovDelay[x][y] = TILEY / 4 + 1;
8005
8006 if (MovDelay[x][y])
8007 {
8008 MovDelay[x][y]--;
8009 if (MovDelay[x][y])
8010 return;
8011 }
8012
8013 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8016 Store[x][y] = 0;
8017 }
8018 }
8019 else if (element == EL_DC_MAGIC_WALL_FULL)
8020 {
8021 if (IS_FREE(x, y + 1))
8022 {
8023 InitMovingField(x, y, MV_DOWN);
8024 started_moving = TRUE;
8025
8026 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8028 }
8029 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8030 {
8031 if (!MovDelay[x][y])
8032 MovDelay[x][y] = TILEY / 4 + 1;
8033
8034 if (MovDelay[x][y])
8035 {
8036 MovDelay[x][y]--;
8037 if (MovDelay[x][y])
8038 return;
8039 }
8040
8041 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8044 Store[x][y] = 0;
8045 }
8046 }
8047 else if ((CAN_PASS_MAGIC_WALL(element) &&
8048 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050 (CAN_PASS_DC_MAGIC_WALL(element) &&
8051 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8052
8053 {
8054 InitMovingField(x, y, MV_DOWN);
8055 started_moving = TRUE;
8056
8057 Feld[x][y] =
8058 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060 EL_DC_MAGIC_WALL_FILLING);
8061 Store[x][y] = element;
8062 }
8063 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8064 {
8065 SplashAcid(x, y + 1);
8066
8067 InitMovingField(x, y, MV_DOWN);
8068 started_moving = TRUE;
8069
8070 Store[x][y] = EL_ACID;
8071 }
8072 else if (
8073 #if USE_FIX_IMPACT_COLLISION
8074 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8075 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8076 #else
8077 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8078 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8079 #endif
8080 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8081 CAN_FALL(element) && WasJustFalling[x][y] &&
8082 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8083
8084 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8085 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8086 (Feld[x][y + 1] == EL_BLOCKED)))
8087 {
8088 /* this is needed for a special case not covered by calling "Impact()"
8089 from "ContinueMoving()": if an element moves to a tile directly below
8090 another element which was just falling on that tile (which was empty
8091 in the previous frame), the falling element above would just stop
8092 instead of smashing the element below (in previous version, the above
8093 element was just checked for "moving" instead of "falling", resulting
8094 in incorrect smashes caused by horizontal movement of the above
8095 element; also, the case of the player being the element to smash was
8096 simply not covered here... :-/ ) */
8097
8098 CheckCollision[x][y] = 0;
8099 CheckImpact[x][y] = 0;
8100
8101 Impact(x, y);
8102 }
8103 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8104 {
8105 if (MovDir[x][y] == MV_NONE)
8106 {
8107 InitMovingField(x, y, MV_DOWN);
8108 started_moving = TRUE;
8109 }
8110 }
8111 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8112 {
8113 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8114 MovDir[x][y] = MV_DOWN;
8115
8116 InitMovingField(x, y, MV_DOWN);
8117 started_moving = TRUE;
8118 }
8119 else if (element == EL_AMOEBA_DROP)
8120 {
8121 Feld[x][y] = EL_AMOEBA_GROWING;
8122 Store[x][y] = EL_AMOEBA_WET;
8123 }
8124 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8125 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8126 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8127 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8128 {
8129 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8130 (IS_FREE(x - 1, y + 1) ||
8131 Feld[x - 1][y + 1] == EL_ACID));
8132 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8133 (IS_FREE(x + 1, y + 1) ||
8134 Feld[x + 1][y + 1] == EL_ACID));
8135 boolean can_fall_any = (can_fall_left || can_fall_right);
8136 boolean can_fall_both = (can_fall_left && can_fall_right);
8137 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8138
8139 #if USE_NEW_ALL_SLIPPERY
8140 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8141 {
8142 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143 can_fall_right = FALSE;
8144 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145 can_fall_left = FALSE;
8146 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147 can_fall_right = FALSE;
8148 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149 can_fall_left = FALSE;
8150
8151 can_fall_any = (can_fall_left || can_fall_right);
8152 can_fall_both = FALSE;
8153 }
8154 #else
8155 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8156 {
8157 if (slippery_type == SLIPPERY_ONLY_LEFT)
8158 can_fall_right = FALSE;
8159 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8160 can_fall_left = FALSE;
8161 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162 can_fall_right = FALSE;
8163 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164 can_fall_left = FALSE;
8165
8166 can_fall_any = (can_fall_left || can_fall_right);
8167 can_fall_both = (can_fall_left && can_fall_right);
8168 }
8169 #endif
8170
8171 #if USE_NEW_ALL_SLIPPERY
8172 #else
8173 #if USE_NEW_SP_SLIPPERY
8174 /* !!! better use the same properties as for custom elements here !!! */
8175 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8176 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8177 {
8178 can_fall_right = FALSE; /* slip down on left side */
8179 can_fall_both = FALSE;
8180 }
8181 #endif
8182 #endif
8183
8184 #if USE_NEW_ALL_SLIPPERY
8185 if (can_fall_both)
8186 {
8187 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8188 can_fall_right = FALSE; /* slip down on left side */
8189 else
8190 can_fall_left = !(can_fall_right = RND(2));
8191
8192 can_fall_both = FALSE;
8193 }
8194 #else
8195 if (can_fall_both)
8196 {
8197 if (game.emulation == EMU_BOULDERDASH ||
8198 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8199 can_fall_right = FALSE; /* slip down on left side */
8200 else
8201 can_fall_left = !(can_fall_right = RND(2));
8202
8203 can_fall_both = FALSE;
8204 }
8205 #endif
8206
8207 if (can_fall_any)
8208 {
8209 /* if not determined otherwise, prefer left side for slipping down */
8210 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8211 started_moving = TRUE;
8212 }
8213 }
8214 #if 0
8215 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8216 #else
8217 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8218 #endif
8219 {
8220 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8221 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8222 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8223 int belt_dir = game.belt_dir[belt_nr];
8224
8225 if ((belt_dir == MV_LEFT && left_is_free) ||
8226 (belt_dir == MV_RIGHT && right_is_free))
8227 {
8228 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8229
8230 InitMovingField(x, y, belt_dir);
8231 started_moving = TRUE;
8232
8233 Pushed[x][y] = TRUE;
8234 Pushed[nextx][y] = TRUE;
8235
8236 GfxAction[x][y] = ACTION_DEFAULT;
8237 }
8238 else
8239 {
8240 MovDir[x][y] = 0; /* if element was moving, stop it */
8241 }
8242 }
8243 }
8244
8245 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8246 #if 0
8247 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8248 #else
8249 if (CAN_MOVE(element) && !started_moving)
8250 #endif
8251 {
8252 int move_pattern = element_info[element].move_pattern;
8253 int newx, newy;
8254
8255 #if 0
8256 #if DEBUG
8257 if (MovDir[x][y] == MV_NONE)
8258 {
8259 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8260 x, y, element, element_info[element].token_name);
8261 printf("StartMoving(): This should never happen!\n");
8262 }
8263 #endif
8264 #endif
8265
8266 Moving2Blocked(x, y, &newx, &newy);
8267
8268 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8269 return;
8270
8271 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8272 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8273 {
8274 WasJustMoving[x][y] = 0;
8275 CheckCollision[x][y] = 0;
8276
8277 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8278
8279 if (Feld[x][y] != element) /* element has changed */
8280 return;
8281 }
8282
8283 if (!MovDelay[x][y]) /* start new movement phase */
8284 {
8285 /* all objects that can change their move direction after each step
8286 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8287
8288 if (element != EL_YAMYAM &&
8289 element != EL_DARK_YAMYAM &&
8290 element != EL_PACMAN &&
8291 !(move_pattern & MV_ANY_DIRECTION) &&
8292 move_pattern != MV_TURNING_LEFT &&
8293 move_pattern != MV_TURNING_RIGHT &&
8294 move_pattern != MV_TURNING_LEFT_RIGHT &&
8295 move_pattern != MV_TURNING_RIGHT_LEFT &&
8296 move_pattern != MV_TURNING_RANDOM)
8297 {
8298 TurnRound(x, y);
8299
8300 if (MovDelay[x][y] && (element == EL_BUG ||
8301 element == EL_SPACESHIP ||
8302 element == EL_SP_SNIKSNAK ||
8303 element == EL_SP_ELECTRON ||
8304 element == EL_MOLE))
8305 TEST_DrawLevelField(x, y);
8306 }
8307 }
8308
8309 if (MovDelay[x][y]) /* wait some time before next movement */
8310 {
8311 MovDelay[x][y]--;
8312
8313 if (element == EL_ROBOT ||
8314 element == EL_YAMYAM ||
8315 element == EL_DARK_YAMYAM)
8316 {
8317 DrawLevelElementAnimationIfNeeded(x, y, element);
8318 PlayLevelSoundAction(x, y, ACTION_WAITING);
8319 }
8320 else if (element == EL_SP_ELECTRON)
8321 DrawLevelElementAnimationIfNeeded(x, y, element);
8322 else if (element == EL_DRAGON)
8323 {
8324 int i;
8325 int dir = MovDir[x][y];
8326 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8327 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8328 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8329 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8330 dir == MV_UP ? IMG_FLAMES_1_UP :
8331 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8332 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8333
8334 GfxAction[x][y] = ACTION_ATTACKING;
8335
8336 if (IS_PLAYER(x, y))
8337 DrawPlayerField(x, y);
8338 else
8339 TEST_DrawLevelField(x, y);
8340
8341 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8342
8343 for (i = 1; i <= 3; i++)
8344 {
8345 int xx = x + i * dx;
8346 int yy = y + i * dy;
8347 int sx = SCREENX(xx);
8348 int sy = SCREENY(yy);
8349 int flame_graphic = graphic + (i - 1);
8350
8351 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8352 break;
8353
8354 if (MovDelay[x][y])
8355 {
8356 int flamed = MovingOrBlocked2Element(xx, yy);
8357
8358 /* !!! */
8359 #if 0
8360 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8361 Bang(xx, yy);
8362 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8363 RemoveMovingField(xx, yy);
8364 else
8365 RemoveField(xx, yy);
8366 #else
8367 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8368 Bang(xx, yy);
8369 else
8370 RemoveMovingField(xx, yy);
8371 #endif
8372
8373 ChangeDelay[xx][yy] = 0;
8374
8375 Feld[xx][yy] = EL_FLAMES;
8376
8377 if (IN_SCR_FIELD(sx, sy))
8378 {
8379 TEST_DrawLevelFieldCrumbled(xx, yy);
8380 DrawGraphic(sx, sy, flame_graphic, frame);
8381 }
8382 }
8383 else
8384 {
8385 if (Feld[xx][yy] == EL_FLAMES)
8386 Feld[xx][yy] = EL_EMPTY;
8387 TEST_DrawLevelField(xx, yy);
8388 }
8389 }
8390 }
8391
8392 if (MovDelay[x][y]) /* element still has to wait some time */
8393 {
8394 PlayLevelSoundAction(x, y, ACTION_WAITING);
8395
8396 return;
8397 }
8398 }
8399
8400 /* now make next step */
8401
8402 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8403
8404 if (DONT_COLLIDE_WITH(element) &&
8405 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8406 !PLAYER_ENEMY_PROTECTED(newx, newy))
8407 {
8408 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8409
8410 return;
8411 }
8412
8413 else if (CAN_MOVE_INTO_ACID(element) &&
8414 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8415 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8416 (MovDir[x][y] == MV_DOWN ||
8417 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8418 {
8419 SplashAcid(newx, newy);
8420 Store[x][y] = EL_ACID;
8421 }
8422 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8423 {
8424 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8425 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8426 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8427 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8428 {
8429 RemoveField(x, y);
8430 TEST_DrawLevelField(x, y);
8431
8432 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8433 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8434 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8435
8436 local_player->friends_still_needed--;
8437 if (!local_player->friends_still_needed &&
8438 !local_player->GameOver && AllPlayersGone)
8439 PlayerWins(local_player);
8440
8441 return;
8442 }
8443 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8444 {
8445 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8446 TEST_DrawLevelField(newx, newy);
8447 else
8448 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8449 }
8450 else if (!IS_FREE(newx, newy))
8451 {
8452 GfxAction[x][y] = ACTION_WAITING;
8453
8454 if (IS_PLAYER(x, y))
8455 DrawPlayerField(x, y);
8456 else
8457 TEST_DrawLevelField(x, y);
8458
8459 return;
8460 }
8461 }
8462 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8463 {
8464 if (IS_FOOD_PIG(Feld[newx][newy]))
8465 {
8466 if (IS_MOVING(newx, newy))
8467 RemoveMovingField(newx, newy);
8468 else
8469 {
8470 Feld[newx][newy] = EL_EMPTY;
8471 TEST_DrawLevelField(newx, newy);
8472 }
8473
8474 PlayLevelSound(x, y, SND_PIG_DIGGING);
8475 }
8476 else if (!IS_FREE(newx, newy))
8477 {
8478 if (IS_PLAYER(x, y))
8479 DrawPlayerField(x, y);
8480 else
8481 TEST_DrawLevelField(x, y);
8482
8483 return;
8484 }
8485 }
8486 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8487 {
8488 if (Store[x][y] != EL_EMPTY)
8489 {
8490 boolean can_clone = FALSE;
8491 int xx, yy;
8492
8493 /* check if element to clone is still there */
8494 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8495 {
8496 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8497 {
8498 can_clone = TRUE;
8499
8500 break;
8501 }
8502 }
8503
8504 /* cannot clone or target field not free anymore -- do not clone */
8505 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8506 Store[x][y] = EL_EMPTY;
8507 }
8508
8509 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8510 {
8511 if (IS_MV_DIAGONAL(MovDir[x][y]))
8512 {
8513 int diagonal_move_dir = MovDir[x][y];
8514 int stored = Store[x][y];
8515 int change_delay = 8;
8516 int graphic;
8517
8518 /* android is moving diagonally */
8519
8520 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8521
8522 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8523 GfxElement[x][y] = EL_EMC_ANDROID;
8524 GfxAction[x][y] = ACTION_SHRINKING;
8525 GfxDir[x][y] = diagonal_move_dir;
8526 ChangeDelay[x][y] = change_delay;
8527
8528 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8529 GfxDir[x][y]);
8530
8531 DrawLevelGraphicAnimation(x, y, graphic);
8532 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8533
8534 if (Feld[newx][newy] == EL_ACID)
8535 {
8536 SplashAcid(newx, newy);
8537
8538 return;
8539 }
8540
8541 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8542
8543 Store[newx][newy] = EL_EMC_ANDROID;
8544 GfxElement[newx][newy] = EL_EMC_ANDROID;
8545 GfxAction[newx][newy] = ACTION_GROWING;
8546 GfxDir[newx][newy] = diagonal_move_dir;
8547 ChangeDelay[newx][newy] = change_delay;
8548
8549 graphic = el_act_dir2img(GfxElement[newx][newy],
8550 GfxAction[newx][newy], GfxDir[newx][newy]);
8551
8552 DrawLevelGraphicAnimation(newx, newy, graphic);
8553 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8554
8555 return;
8556 }
8557 else
8558 {
8559 Feld[newx][newy] = EL_EMPTY;
8560 TEST_DrawLevelField(newx, newy);
8561
8562 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8563 }
8564 }
8565 else if (!IS_FREE(newx, newy))
8566 {
8567 #if 0
8568 if (IS_PLAYER(x, y))
8569 DrawPlayerField(x, y);
8570 else
8571 TEST_DrawLevelField(x, y);
8572 #endif
8573
8574 return;
8575 }
8576 }
8577 else if (IS_CUSTOM_ELEMENT(element) &&
8578 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8579 {
8580 #if 1
8581 if (!DigFieldByCE(newx, newy, element))
8582 return;
8583 #else
8584 int new_element = Feld[newx][newy];
8585
8586 if (!IS_FREE(newx, newy))
8587 {
8588 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8589 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8590 ACTION_BREAKING);
8591
8592 /* no element can dig solid indestructible elements */
8593 if (IS_INDESTRUCTIBLE(new_element) &&
8594 !IS_DIGGABLE(new_element) &&
8595 !IS_COLLECTIBLE(new_element))
8596 return;
8597
8598 if (AmoebaNr[newx][newy] &&
8599 (new_element == EL_AMOEBA_FULL ||
8600 new_element == EL_BD_AMOEBA ||
8601 new_element == EL_AMOEBA_GROWING))
8602 {
8603 AmoebaCnt[AmoebaNr[newx][newy]]--;
8604 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8605 }
8606
8607 if (IS_MOVING(newx, newy))
8608 RemoveMovingField(newx, newy);
8609 else
8610 {
8611 RemoveField(newx, newy);
8612 TEST_DrawLevelField(newx, newy);
8613 }
8614
8615 /* if digged element was about to explode, prevent the explosion */
8616 ExplodeField[newx][newy] = EX_TYPE_NONE;
8617
8618 PlayLevelSoundAction(x, y, action);
8619 }
8620
8621 Store[newx][newy] = EL_EMPTY;
8622
8623 #if 1
8624 /* this makes it possible to leave the removed element again */
8625 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8626 Store[newx][newy] = new_element;
8627 #else
8628 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8629 {
8630 int move_leave_element = element_info[element].move_leave_element;
8631
8632 /* this makes it possible to leave the removed element again */
8633 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8634 new_element : move_leave_element);
8635 }
8636 #endif
8637
8638 #endif
8639
8640 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8641 {
8642 RunnerVisit[x][y] = FrameCounter;
8643 PlayerVisit[x][y] /= 8; /* expire player visit path */
8644 }
8645 }
8646 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8647 {
8648 if (!IS_FREE(newx, newy))
8649 {
8650 if (IS_PLAYER(x, y))
8651 DrawPlayerField(x, y);
8652 else
8653 TEST_DrawLevelField(x, y);
8654
8655 return;
8656 }
8657 else
8658 {
8659 boolean wanna_flame = !RND(10);
8660 int dx = newx - x, dy = newy - y;
8661 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8662 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8663 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8664 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8665 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8666 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8667
8668 if ((wanna_flame ||
8669 IS_CLASSIC_ENEMY(element1) ||
8670 IS_CLASSIC_ENEMY(element2)) &&
8671 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8672 element1 != EL_FLAMES && element2 != EL_FLAMES)
8673 {
8674 ResetGfxAnimation(x, y);
8675 GfxAction[x][y] = ACTION_ATTACKING;
8676
8677 if (IS_PLAYER(x, y))
8678 DrawPlayerField(x, y);
8679 else
8680 TEST_DrawLevelField(x, y);
8681
8682 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8683
8684 MovDelay[x][y] = 50;
8685
8686 /* !!! */
8687 #if 0
8688 RemoveField(newx, newy);
8689 #endif
8690 Feld[newx][newy] = EL_FLAMES;
8691 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8692 {
8693 #if 0
8694 RemoveField(newx1, newy1);
8695 #endif
8696 Feld[newx1][newy1] = EL_FLAMES;
8697 }
8698 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8699 {
8700 #if 0
8701 RemoveField(newx2, newy2);
8702 #endif
8703 Feld[newx2][newy2] = EL_FLAMES;
8704 }
8705
8706 return;
8707 }
8708 }
8709 }
8710 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8711 Feld[newx][newy] == EL_DIAMOND)
8712 {
8713 if (IS_MOVING(newx, newy))
8714 RemoveMovingField(newx, newy);
8715 else
8716 {
8717 Feld[newx][newy] = EL_EMPTY;
8718 TEST_DrawLevelField(newx, newy);
8719 }
8720
8721 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8722 }
8723 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8724 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8725 {
8726 if (AmoebaNr[newx][newy])
8727 {
8728 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8729 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8730 Feld[newx][newy] == EL_BD_AMOEBA)
8731 AmoebaCnt[AmoebaNr[newx][newy]]--;
8732 }
8733
8734 #if 0
8735 /* !!! test !!! */
8736 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8737 {
8738 RemoveMovingField(newx, newy);
8739 }
8740 #else
8741 if (IS_MOVING(newx, newy))
8742 {
8743 RemoveMovingField(newx, newy);
8744 }
8745 #endif
8746 else
8747 {
8748 Feld[newx][newy] = EL_EMPTY;
8749 TEST_DrawLevelField(newx, newy);
8750 }
8751
8752 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8753 }
8754 else if ((element == EL_PACMAN || element == EL_MOLE)
8755 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8756 {
8757 if (AmoebaNr[newx][newy])
8758 {
8759 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8760 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8761 Feld[newx][newy] == EL_BD_AMOEBA)
8762 AmoebaCnt[AmoebaNr[newx][newy]]--;
8763 }
8764
8765 if (element == EL_MOLE)
8766 {
8767 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8768 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8769
8770 ResetGfxAnimation(x, y);
8771 GfxAction[x][y] = ACTION_DIGGING;
8772 TEST_DrawLevelField(x, y);
8773
8774 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8775
8776 return; /* wait for shrinking amoeba */
8777 }
8778 else /* element == EL_PACMAN */
8779 {
8780 Feld[newx][newy] = EL_EMPTY;
8781 TEST_DrawLevelField(newx, newy);
8782 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8783 }
8784 }
8785 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8786 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8787 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8788 {
8789 /* wait for shrinking amoeba to completely disappear */
8790 return;
8791 }
8792 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8793 {
8794 /* object was running against a wall */
8795
8796 TurnRound(x, y);
8797
8798 #if 0
8799 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8800 if (move_pattern & MV_ANY_DIRECTION &&
8801 move_pattern == MovDir[x][y])
8802 {
8803 int blocking_element =
8804 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8805
8806 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8807 MovDir[x][y]);
8808
8809 element = Feld[x][y]; /* element might have changed */
8810 }
8811 #endif
8812
8813 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8814 DrawLevelElementAnimation(x, y, element);
8815
8816 if (DONT_TOUCH(element))
8817 TestIfBadThingTouchesPlayer(x, y);
8818
8819 return;
8820 }
8821
8822 InitMovingField(x, y, MovDir[x][y]);
8823
8824 PlayLevelSoundAction(x, y, ACTION_MOVING);
8825 }
8826
8827 if (MovDir[x][y])
8828 ContinueMoving(x, y);
8829 }
8830
ContinueMoving(int x,int y)8831 void ContinueMoving(int x, int y)
8832 {
8833 int element = Feld[x][y];
8834 struct ElementInfo *ei = &element_info[element];
8835 int direction = MovDir[x][y];
8836 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8837 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8838 int newx = x + dx, newy = y + dy;
8839 int stored = Store[x][y];
8840 int stored_new = Store[newx][newy];
8841 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8842 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8843 boolean last_line = (newy == lev_fieldy - 1);
8844
8845 MovPos[x][y] += getElementMoveStepsize(x, y);
8846
8847 if (pushed_by_player) /* special case: moving object pushed by player */
8848 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8849
8850 if (ABS(MovPos[x][y]) < TILEX)
8851 {
8852 #if 0
8853 int ee = Feld[x][y];
8854 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8855 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8856
8857 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8858 x, y, ABS(MovPos[x][y]),
8859 ee, gg, ff,
8860 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8861 #endif
8862
8863 TEST_DrawLevelField(x, y);
8864
8865 return; /* element is still moving */
8866 }
8867
8868 /* element reached destination field */
8869
8870 Feld[x][y] = EL_EMPTY;
8871 Feld[newx][newy] = element;
8872 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8873
8874 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8875 {
8876 element = Feld[newx][newy] = EL_ACID;
8877 }
8878 else if (element == EL_MOLE)
8879 {
8880 Feld[x][y] = EL_SAND;
8881
8882 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8883 }
8884 else if (element == EL_QUICKSAND_FILLING)
8885 {
8886 element = Feld[newx][newy] = get_next_element(element);
8887 Store[newx][newy] = Store[x][y];
8888 }
8889 else if (element == EL_QUICKSAND_EMPTYING)
8890 {
8891 Feld[x][y] = get_next_element(element);
8892 element = Feld[newx][newy] = Store[x][y];
8893 }
8894 else if (element == EL_QUICKSAND_FAST_FILLING)
8895 {
8896 element = Feld[newx][newy] = get_next_element(element);
8897 Store[newx][newy] = Store[x][y];
8898 }
8899 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8900 {
8901 Feld[x][y] = get_next_element(element);
8902 element = Feld[newx][newy] = Store[x][y];
8903 }
8904 else if (element == EL_MAGIC_WALL_FILLING)
8905 {
8906 element = Feld[newx][newy] = get_next_element(element);
8907 if (!game.magic_wall_active)
8908 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8909 Store[newx][newy] = Store[x][y];
8910 }
8911 else if (element == EL_MAGIC_WALL_EMPTYING)
8912 {
8913 Feld[x][y] = get_next_element(element);
8914 if (!game.magic_wall_active)
8915 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8916 element = Feld[newx][newy] = Store[x][y];
8917
8918 #if USE_NEW_CUSTOM_VALUE
8919 InitField(newx, newy, FALSE);
8920 #endif
8921 }
8922 else if (element == EL_BD_MAGIC_WALL_FILLING)
8923 {
8924 element = Feld[newx][newy] = get_next_element(element);
8925 if (!game.magic_wall_active)
8926 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8927 Store[newx][newy] = Store[x][y];
8928 }
8929 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8930 {
8931 Feld[x][y] = get_next_element(element);
8932 if (!game.magic_wall_active)
8933 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8934 element = Feld[newx][newy] = Store[x][y];
8935
8936 #if USE_NEW_CUSTOM_VALUE
8937 InitField(newx, newy, FALSE);
8938 #endif
8939 }
8940 else if (element == EL_DC_MAGIC_WALL_FILLING)
8941 {
8942 element = Feld[newx][newy] = get_next_element(element);
8943 if (!game.magic_wall_active)
8944 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8945 Store[newx][newy] = Store[x][y];
8946 }
8947 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8948 {
8949 Feld[x][y] = get_next_element(element);
8950 if (!game.magic_wall_active)
8951 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8952 element = Feld[newx][newy] = Store[x][y];
8953
8954 #if USE_NEW_CUSTOM_VALUE
8955 InitField(newx, newy, FALSE);
8956 #endif
8957 }
8958 else if (element == EL_AMOEBA_DROPPING)
8959 {
8960 Feld[x][y] = get_next_element(element);
8961 element = Feld[newx][newy] = Store[x][y];
8962 }
8963 else if (element == EL_SOKOBAN_OBJECT)
8964 {
8965 if (Back[x][y])
8966 Feld[x][y] = Back[x][y];
8967
8968 if (Back[newx][newy])
8969 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8970
8971 Back[x][y] = Back[newx][newy] = 0;
8972 }
8973
8974 Store[x][y] = EL_EMPTY;
8975 MovPos[x][y] = 0;
8976 MovDir[x][y] = 0;
8977 MovDelay[x][y] = 0;
8978
8979 MovDelay[newx][newy] = 0;
8980
8981 if (CAN_CHANGE_OR_HAS_ACTION(element))
8982 {
8983 /* copy element change control values to new field */
8984 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8985 ChangePage[newx][newy] = ChangePage[x][y];
8986 ChangeCount[newx][newy] = ChangeCount[x][y];
8987 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8988 }
8989
8990 #if USE_NEW_CUSTOM_VALUE
8991 CustomValue[newx][newy] = CustomValue[x][y];
8992 #endif
8993
8994 ChangeDelay[x][y] = 0;
8995 ChangePage[x][y] = -1;
8996 ChangeCount[x][y] = 0;
8997 ChangeEvent[x][y] = -1;
8998
8999 #if USE_NEW_CUSTOM_VALUE
9000 CustomValue[x][y] = 0;
9001 #endif
9002
9003 /* copy animation control values to new field */
9004 GfxFrame[newx][newy] = GfxFrame[x][y];
9005 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9006 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9007 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9008
9009 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9010
9011 /* some elements can leave other elements behind after moving */
9012 #if 1
9013 if (ei->move_leave_element != EL_EMPTY &&
9014 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9015 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9016 #else
9017 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9018 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9019 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9020 #endif
9021 {
9022 int move_leave_element = ei->move_leave_element;
9023
9024 #if 1
9025 #if 1
9026 /* this makes it possible to leave the removed element again */
9027 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9028 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9029 #else
9030 /* this makes it possible to leave the removed element again */
9031 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9032 move_leave_element = stored;
9033 #endif
9034 #else
9035 /* this makes it possible to leave the removed element again */
9036 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9037 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9038 move_leave_element = stored;
9039 #endif
9040
9041 Feld[x][y] = move_leave_element;
9042
9043 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9044 MovDir[x][y] = direction;
9045
9046 InitField(x, y, FALSE);
9047
9048 if (GFX_CRUMBLED(Feld[x][y]))
9049 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9050
9051 if (ELEM_IS_PLAYER(move_leave_element))
9052 RelocatePlayer(x, y, move_leave_element);
9053 }
9054
9055 /* do this after checking for left-behind element */
9056 ResetGfxAnimation(x, y); /* reset animation values for old field */
9057
9058 if (!CAN_MOVE(element) ||
9059 (CAN_FALL(element) && direction == MV_DOWN &&
9060 (element == EL_SPRING ||
9061 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9062 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9063 GfxDir[x][y] = MovDir[newx][newy] = 0;
9064
9065 TEST_DrawLevelField(x, y);
9066 TEST_DrawLevelField(newx, newy);
9067
9068 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9069
9070 /* prevent pushed element from moving on in pushed direction */
9071 if (pushed_by_player && CAN_MOVE(element) &&
9072 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9073 !(element_info[element].move_pattern & direction))
9074 TurnRound(newx, newy);
9075
9076 /* prevent elements on conveyor belt from moving on in last direction */
9077 if (pushed_by_conveyor && CAN_FALL(element) &&
9078 direction & MV_HORIZONTAL)
9079 MovDir[newx][newy] = 0;
9080
9081 if (!pushed_by_player)
9082 {
9083 int nextx = newx + dx, nexty = newy + dy;
9084 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9085
9086 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9087
9088 if (CAN_FALL(element) && direction == MV_DOWN)
9089 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9090
9091 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9092 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9093
9094 #if USE_FIX_IMPACT_COLLISION
9095 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9096 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9097 #endif
9098 }
9099
9100 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9101 {
9102 TestIfBadThingTouchesPlayer(newx, newy);
9103 TestIfBadThingTouchesFriend(newx, newy);
9104
9105 if (!IS_CUSTOM_ELEMENT(element))
9106 TestIfBadThingTouchesOtherBadThing(newx, newy);
9107 }
9108 else if (element == EL_PENGUIN)
9109 TestIfFriendTouchesBadThing(newx, newy);
9110
9111 if (DONT_GET_HIT_BY(element))
9112 {
9113 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9114 }
9115
9116 /* give the player one last chance (one more frame) to move away */
9117 if (CAN_FALL(element) && direction == MV_DOWN &&
9118 (last_line || (!IS_FREE(x, newy + 1) &&
9119 (!IS_PLAYER(x, newy + 1) ||
9120 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9121 Impact(x, newy);
9122
9123 if (pushed_by_player && !game.use_change_when_pushing_bug)
9124 {
9125 int push_side = MV_DIR_OPPOSITE(direction);
9126 struct PlayerInfo *player = PLAYERINFO(x, y);
9127
9128 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9129 player->index_bit, push_side);
9130 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9131 player->index_bit, push_side);
9132 }
9133
9134 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9135 MovDelay[newx][newy] = 1;
9136
9137 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9138
9139 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9140
9141 #if 0
9142 if (ChangePage[newx][newy] != -1) /* delayed change */
9143 {
9144 int page = ChangePage[newx][newy];
9145 struct ElementChangeInfo *change = &ei->change_page[page];
9146
9147 ChangePage[newx][newy] = -1;
9148
9149 if (change->can_change)
9150 {
9151 if (ChangeElement(newx, newy, element, page))
9152 {
9153 if (change->post_change_function)
9154 change->post_change_function(newx, newy);
9155 }
9156 }
9157
9158 if (change->has_action)
9159 ExecuteCustomElementAction(newx, newy, element, page);
9160 }
9161 #endif
9162
9163 TestIfElementHitsCustomElement(newx, newy, direction);
9164 TestIfPlayerTouchesCustomElement(newx, newy);
9165 TestIfElementTouchesCustomElement(newx, newy);
9166
9167 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9168 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9169 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9170 MV_DIR_OPPOSITE(direction));
9171 }
9172
AmoebeNachbarNr(int ax,int ay)9173 int AmoebeNachbarNr(int ax, int ay)
9174 {
9175 int i;
9176 int element = Feld[ax][ay];
9177 int group_nr = 0;
9178 static int xy[4][2] =
9179 {
9180 { 0, -1 },
9181 { -1, 0 },
9182 { +1, 0 },
9183 { 0, +1 }
9184 };
9185
9186 for (i = 0; i < NUM_DIRECTIONS; i++)
9187 {
9188 int x = ax + xy[i][0];
9189 int y = ay + xy[i][1];
9190
9191 if (!IN_LEV_FIELD(x, y))
9192 continue;
9193
9194 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9195 group_nr = AmoebaNr[x][y];
9196 }
9197
9198 return group_nr;
9199 }
9200
AmoebenVereinigen(int ax,int ay)9201 void AmoebenVereinigen(int ax, int ay)
9202 {
9203 int i, x, y, xx, yy;
9204 int new_group_nr = AmoebaNr[ax][ay];
9205 static int xy[4][2] =
9206 {
9207 { 0, -1 },
9208 { -1, 0 },
9209 { +1, 0 },
9210 { 0, +1 }
9211 };
9212
9213 if (new_group_nr == 0)
9214 return;
9215
9216 for (i = 0; i < NUM_DIRECTIONS; i++)
9217 {
9218 x = ax + xy[i][0];
9219 y = ay + xy[i][1];
9220
9221 if (!IN_LEV_FIELD(x, y))
9222 continue;
9223
9224 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9225 Feld[x][y] == EL_BD_AMOEBA ||
9226 Feld[x][y] == EL_AMOEBA_DEAD) &&
9227 AmoebaNr[x][y] != new_group_nr)
9228 {
9229 int old_group_nr = AmoebaNr[x][y];
9230
9231 if (old_group_nr == 0)
9232 return;
9233
9234 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9235 AmoebaCnt[old_group_nr] = 0;
9236 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9237 AmoebaCnt2[old_group_nr] = 0;
9238
9239 SCAN_PLAYFIELD(xx, yy)
9240 {
9241 if (AmoebaNr[xx][yy] == old_group_nr)
9242 AmoebaNr[xx][yy] = new_group_nr;
9243 }
9244 }
9245 }
9246 }
9247
AmoebeUmwandeln(int ax,int ay)9248 void AmoebeUmwandeln(int ax, int ay)
9249 {
9250 int i, x, y;
9251
9252 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9253 {
9254 int group_nr = AmoebaNr[ax][ay];
9255
9256 #ifdef DEBUG
9257 if (group_nr == 0)
9258 {
9259 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9260 printf("AmoebeUmwandeln(): This should never happen!\n");
9261 return;
9262 }
9263 #endif
9264
9265 SCAN_PLAYFIELD(x, y)
9266 {
9267 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9268 {
9269 AmoebaNr[x][y] = 0;
9270 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9271 }
9272 }
9273
9274 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9275 SND_AMOEBA_TURNING_TO_GEM :
9276 SND_AMOEBA_TURNING_TO_ROCK));
9277 Bang(ax, ay);
9278 }
9279 else
9280 {
9281 static int xy[4][2] =
9282 {
9283 { 0, -1 },
9284 { -1, 0 },
9285 { +1, 0 },
9286 { 0, +1 }
9287 };
9288
9289 for (i = 0; i < NUM_DIRECTIONS; i++)
9290 {
9291 x = ax + xy[i][0];
9292 y = ay + xy[i][1];
9293
9294 if (!IN_LEV_FIELD(x, y))
9295 continue;
9296
9297 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9298 {
9299 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9300 SND_AMOEBA_TURNING_TO_GEM :
9301 SND_AMOEBA_TURNING_TO_ROCK));
9302 Bang(x, y);
9303 }
9304 }
9305 }
9306 }
9307
AmoebeUmwandelnBD(int ax,int ay,int new_element)9308 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9309 {
9310 int x, y;
9311 int group_nr = AmoebaNr[ax][ay];
9312 boolean done = FALSE;
9313
9314 #ifdef DEBUG
9315 if (group_nr == 0)
9316 {
9317 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9318 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9319 return;
9320 }
9321 #endif
9322
9323 SCAN_PLAYFIELD(x, y)
9324 {
9325 if (AmoebaNr[x][y] == group_nr &&
9326 (Feld[x][y] == EL_AMOEBA_DEAD ||
9327 Feld[x][y] == EL_BD_AMOEBA ||
9328 Feld[x][y] == EL_AMOEBA_GROWING))
9329 {
9330 AmoebaNr[x][y] = 0;
9331 Feld[x][y] = new_element;
9332 InitField(x, y, FALSE);
9333 TEST_DrawLevelField(x, y);
9334 done = TRUE;
9335 }
9336 }
9337
9338 if (done)
9339 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9340 SND_BD_AMOEBA_TURNING_TO_ROCK :
9341 SND_BD_AMOEBA_TURNING_TO_GEM));
9342 }
9343
AmoebeWaechst(int x,int y)9344 void AmoebeWaechst(int x, int y)
9345 {
9346 static unsigned int sound_delay = 0;
9347 static unsigned int sound_delay_value = 0;
9348
9349 if (!MovDelay[x][y]) /* start new growing cycle */
9350 {
9351 MovDelay[x][y] = 7;
9352
9353 if (DelayReached(&sound_delay, sound_delay_value))
9354 {
9355 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9356 sound_delay_value = 30;
9357 }
9358 }
9359
9360 if (MovDelay[x][y]) /* wait some time before growing bigger */
9361 {
9362 MovDelay[x][y]--;
9363 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9364 {
9365 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9366 6 - MovDelay[x][y]);
9367
9368 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9369 }
9370
9371 if (!MovDelay[x][y])
9372 {
9373 Feld[x][y] = Store[x][y];
9374 Store[x][y] = 0;
9375 TEST_DrawLevelField(x, y);
9376 }
9377 }
9378 }
9379
AmoebaDisappearing(int x,int y)9380 void AmoebaDisappearing(int x, int y)
9381 {
9382 static unsigned int sound_delay = 0;
9383 static unsigned int sound_delay_value = 0;
9384
9385 if (!MovDelay[x][y]) /* start new shrinking cycle */
9386 {
9387 MovDelay[x][y] = 7;
9388
9389 if (DelayReached(&sound_delay, sound_delay_value))
9390 sound_delay_value = 30;
9391 }
9392
9393 if (MovDelay[x][y]) /* wait some time before shrinking */
9394 {
9395 MovDelay[x][y]--;
9396 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9397 {
9398 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9399 6 - MovDelay[x][y]);
9400
9401 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9402 }
9403
9404 if (!MovDelay[x][y])
9405 {
9406 Feld[x][y] = EL_EMPTY;
9407 TEST_DrawLevelField(x, y);
9408
9409 /* don't let mole enter this field in this cycle;
9410 (give priority to objects falling to this field from above) */
9411 Stop[x][y] = TRUE;
9412 }
9413 }
9414 }
9415
AmoebeAbleger(int ax,int ay)9416 void AmoebeAbleger(int ax, int ay)
9417 {
9418 int i;
9419 int element = Feld[ax][ay];
9420 int graphic = el2img(element);
9421 int newax = ax, neway = ay;
9422 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9423 static int xy[4][2] =
9424 {
9425 { 0, -1 },
9426 { -1, 0 },
9427 { +1, 0 },
9428 { 0, +1 }
9429 };
9430
9431 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9432 {
9433 Feld[ax][ay] = EL_AMOEBA_DEAD;
9434 TEST_DrawLevelField(ax, ay);
9435 return;
9436 }
9437
9438 if (IS_ANIMATED(graphic))
9439 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9440
9441 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9442 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9443
9444 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9445 {
9446 MovDelay[ax][ay]--;
9447 if (MovDelay[ax][ay])
9448 return;
9449 }
9450
9451 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9452 {
9453 int start = RND(4);
9454 int x = ax + xy[start][0];
9455 int y = ay + xy[start][1];
9456
9457 if (!IN_LEV_FIELD(x, y))
9458 return;
9459
9460 if (IS_FREE(x, y) ||
9461 CAN_GROW_INTO(Feld[x][y]) ||
9462 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9463 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9464 {
9465 newax = x;
9466 neway = y;
9467 }
9468
9469 if (newax == ax && neway == ay)
9470 return;
9471 }
9472 else /* normal or "filled" (BD style) amoeba */
9473 {
9474 int start = RND(4);
9475 boolean waiting_for_player = FALSE;
9476
9477 for (i = 0; i < NUM_DIRECTIONS; i++)
9478 {
9479 int j = (start + i) % 4;
9480 int x = ax + xy[j][0];
9481 int y = ay + xy[j][1];
9482
9483 if (!IN_LEV_FIELD(x, y))
9484 continue;
9485
9486 if (IS_FREE(x, y) ||
9487 CAN_GROW_INTO(Feld[x][y]) ||
9488 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9489 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9490 {
9491 newax = x;
9492 neway = y;
9493 break;
9494 }
9495 else if (IS_PLAYER(x, y))
9496 waiting_for_player = TRUE;
9497 }
9498
9499 if (newax == ax && neway == ay) /* amoeba cannot grow */
9500 {
9501 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9502 {
9503 Feld[ax][ay] = EL_AMOEBA_DEAD;
9504 TEST_DrawLevelField(ax, ay);
9505 AmoebaCnt[AmoebaNr[ax][ay]]--;
9506
9507 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9508 {
9509 if (element == EL_AMOEBA_FULL)
9510 AmoebeUmwandeln(ax, ay);
9511 else if (element == EL_BD_AMOEBA)
9512 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9513 }
9514 }
9515 return;
9516 }
9517 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9518 {
9519 /* amoeba gets larger by growing in some direction */
9520
9521 int new_group_nr = AmoebaNr[ax][ay];
9522
9523 #ifdef DEBUG
9524 if (new_group_nr == 0)
9525 {
9526 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9527 printf("AmoebeAbleger(): This should never happen!\n");
9528 return;
9529 }
9530 #endif
9531
9532 AmoebaNr[newax][neway] = new_group_nr;
9533 AmoebaCnt[new_group_nr]++;
9534 AmoebaCnt2[new_group_nr]++;
9535
9536 /* if amoeba touches other amoeba(s) after growing, unify them */
9537 AmoebenVereinigen(newax, neway);
9538
9539 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9540 {
9541 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9542 return;
9543 }
9544 }
9545 }
9546
9547 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9548 (neway == lev_fieldy - 1 && newax != ax))
9549 {
9550 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9551 Store[newax][neway] = element;
9552 }
9553 else if (neway == ay || element == EL_EMC_DRIPPER)
9554 {
9555 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9556
9557 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9558 }
9559 else
9560 {
9561 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9562 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9563 Store[ax][ay] = EL_AMOEBA_DROP;
9564 ContinueMoving(ax, ay);
9565 return;
9566 }
9567
9568 TEST_DrawLevelField(newax, neway);
9569 }
9570
Life(int ax,int ay)9571 void Life(int ax, int ay)
9572 {
9573 int x1, y1, x2, y2;
9574 int life_time = 40;
9575 int element = Feld[ax][ay];
9576 int graphic = el2img(element);
9577 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9578 level.biomaze);
9579 boolean changed = FALSE;
9580
9581 if (IS_ANIMATED(graphic))
9582 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9583
9584 if (Stop[ax][ay])
9585 return;
9586
9587 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9588 MovDelay[ax][ay] = life_time;
9589
9590 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9591 {
9592 MovDelay[ax][ay]--;
9593 if (MovDelay[ax][ay])
9594 return;
9595 }
9596
9597 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9598 {
9599 int xx = ax+x1, yy = ay+y1;
9600 int nachbarn = 0;
9601
9602 if (!IN_LEV_FIELD(xx, yy))
9603 continue;
9604
9605 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9606 {
9607 int x = xx+x2, y = yy+y2;
9608
9609 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9610 continue;
9611
9612 if (((Feld[x][y] == element ||
9613 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9614 !Stop[x][y]) ||
9615 (IS_FREE(x, y) && Stop[x][y]))
9616 nachbarn++;
9617 }
9618
9619 if (xx == ax && yy == ay) /* field in the middle */
9620 {
9621 if (nachbarn < life_parameter[0] ||
9622 nachbarn > life_parameter[1])
9623 {
9624 Feld[xx][yy] = EL_EMPTY;
9625 if (!Stop[xx][yy])
9626 TEST_DrawLevelField(xx, yy);
9627 Stop[xx][yy] = TRUE;
9628 changed = TRUE;
9629 }
9630 }
9631 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9632 { /* free border field */
9633 if (nachbarn >= life_parameter[2] &&
9634 nachbarn <= life_parameter[3])
9635 {
9636 Feld[xx][yy] = element;
9637 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9638 if (!Stop[xx][yy])
9639 TEST_DrawLevelField(xx, yy);
9640 Stop[xx][yy] = TRUE;
9641 changed = TRUE;
9642 }
9643 }
9644 }
9645
9646 if (changed)
9647 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9648 SND_GAME_OF_LIFE_GROWING);
9649 }
9650
InitRobotWheel(int x,int y)9651 static void InitRobotWheel(int x, int y)
9652 {
9653 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9654 }
9655
RunRobotWheel(int x,int y)9656 static void RunRobotWheel(int x, int y)
9657 {
9658 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9659 }
9660
StopRobotWheel(int x,int y)9661 static void StopRobotWheel(int x, int y)
9662 {
9663 if (ZX == x && ZY == y)
9664 {
9665 ZX = ZY = -1;
9666
9667 game.robot_wheel_active = FALSE;
9668 }
9669 }
9670
InitTimegateWheel(int x,int y)9671 static void InitTimegateWheel(int x, int y)
9672 {
9673 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9674 }
9675
RunTimegateWheel(int x,int y)9676 static void RunTimegateWheel(int x, int y)
9677 {
9678 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9679 }
9680
InitMagicBallDelay(int x,int y)9681 static void InitMagicBallDelay(int x, int y)
9682 {
9683 #if 1
9684 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9685 #else
9686 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9687 #endif
9688 }
9689
ActivateMagicBall(int bx,int by)9690 static void ActivateMagicBall(int bx, int by)
9691 {
9692 int x, y;
9693
9694 if (level.ball_random)
9695 {
9696 int pos_border = RND(8); /* select one of the eight border elements */
9697 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9698 int xx = pos_content % 3;
9699 int yy = pos_content / 3;
9700
9701 x = bx - 1 + xx;
9702 y = by - 1 + yy;
9703
9704 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9705 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9706 }
9707 else
9708 {
9709 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9710 {
9711 int xx = x - bx + 1;
9712 int yy = y - by + 1;
9713
9714 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9715 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9716 }
9717 }
9718
9719 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9720 }
9721
CheckExit(int x,int y)9722 void CheckExit(int x, int y)
9723 {
9724 if (local_player->gems_still_needed > 0 ||
9725 local_player->sokobanfields_still_needed > 0 ||
9726 local_player->lights_still_needed > 0)
9727 {
9728 int element = Feld[x][y];
9729 int graphic = el2img(element);
9730
9731 if (IS_ANIMATED(graphic))
9732 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9733
9734 return;
9735 }
9736
9737 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9738 return;
9739
9740 Feld[x][y] = EL_EXIT_OPENING;
9741
9742 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9743 }
9744
CheckExitEM(int x,int y)9745 void CheckExitEM(int x, int y)
9746 {
9747 if (local_player->gems_still_needed > 0 ||
9748 local_player->sokobanfields_still_needed > 0 ||
9749 local_player->lights_still_needed > 0)
9750 {
9751 int element = Feld[x][y];
9752 int graphic = el2img(element);
9753
9754 if (IS_ANIMATED(graphic))
9755 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9756
9757 return;
9758 }
9759
9760 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9761 return;
9762
9763 Feld[x][y] = EL_EM_EXIT_OPENING;
9764
9765 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9766 }
9767
CheckExitSteel(int x,int y)9768 void CheckExitSteel(int x, int y)
9769 {
9770 if (local_player->gems_still_needed > 0 ||
9771 local_player->sokobanfields_still_needed > 0 ||
9772 local_player->lights_still_needed > 0)
9773 {
9774 int element = Feld[x][y];
9775 int graphic = el2img(element);
9776
9777 if (IS_ANIMATED(graphic))
9778 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9779
9780 return;
9781 }
9782
9783 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9784 return;
9785
9786 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9787
9788 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9789 }
9790
CheckExitSteelEM(int x,int y)9791 void CheckExitSteelEM(int x, int y)
9792 {
9793 if (local_player->gems_still_needed > 0 ||
9794 local_player->sokobanfields_still_needed > 0 ||
9795 local_player->lights_still_needed > 0)
9796 {
9797 int element = Feld[x][y];
9798 int graphic = el2img(element);
9799
9800 if (IS_ANIMATED(graphic))
9801 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9802
9803 return;
9804 }
9805
9806 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9807 return;
9808
9809 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9810
9811 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9812 }
9813
CheckExitSP(int x,int y)9814 void CheckExitSP(int x, int y)
9815 {
9816 if (local_player->gems_still_needed > 0)
9817 {
9818 int element = Feld[x][y];
9819 int graphic = el2img(element);
9820
9821 if (IS_ANIMATED(graphic))
9822 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9823
9824 return;
9825 }
9826
9827 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9828 return;
9829
9830 Feld[x][y] = EL_SP_EXIT_OPENING;
9831
9832 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9833 }
9834
CloseAllOpenTimegates()9835 static void CloseAllOpenTimegates()
9836 {
9837 int x, y;
9838
9839 SCAN_PLAYFIELD(x, y)
9840 {
9841 int element = Feld[x][y];
9842
9843 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9844 {
9845 Feld[x][y] = EL_TIMEGATE_CLOSING;
9846
9847 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9848 }
9849 }
9850 }
9851
DrawTwinkleOnField(int x,int y)9852 void DrawTwinkleOnField(int x, int y)
9853 {
9854 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9855 return;
9856
9857 if (Feld[x][y] == EL_BD_DIAMOND)
9858 return;
9859
9860 if (MovDelay[x][y] == 0) /* next animation frame */
9861 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9862
9863 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9864 {
9865 MovDelay[x][y]--;
9866
9867 DrawLevelElementAnimation(x, y, Feld[x][y]);
9868
9869 if (MovDelay[x][y] != 0)
9870 {
9871 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9872 10 - MovDelay[x][y]);
9873
9874 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9875 }
9876 }
9877 }
9878
MauerWaechst(int x,int y)9879 void MauerWaechst(int x, int y)
9880 {
9881 int delay = 6;
9882
9883 if (!MovDelay[x][y]) /* next animation frame */
9884 MovDelay[x][y] = 3 * delay;
9885
9886 if (MovDelay[x][y]) /* wait some time before next frame */
9887 {
9888 MovDelay[x][y]--;
9889
9890 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9891 {
9892 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9893 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9894
9895 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9896 }
9897
9898 if (!MovDelay[x][y])
9899 {
9900 if (MovDir[x][y] == MV_LEFT)
9901 {
9902 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9903 TEST_DrawLevelField(x - 1, y);
9904 }
9905 else if (MovDir[x][y] == MV_RIGHT)
9906 {
9907 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9908 TEST_DrawLevelField(x + 1, y);
9909 }
9910 else if (MovDir[x][y] == MV_UP)
9911 {
9912 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9913 TEST_DrawLevelField(x, y - 1);
9914 }
9915 else
9916 {
9917 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9918 TEST_DrawLevelField(x, y + 1);
9919 }
9920
9921 Feld[x][y] = Store[x][y];
9922 Store[x][y] = 0;
9923 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9924 TEST_DrawLevelField(x, y);
9925 }
9926 }
9927 }
9928
MauerAbleger(int ax,int ay)9929 void MauerAbleger(int ax, int ay)
9930 {
9931 int element = Feld[ax][ay];
9932 int graphic = el2img(element);
9933 boolean oben_frei = FALSE, unten_frei = FALSE;
9934 boolean links_frei = FALSE, rechts_frei = FALSE;
9935 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9936 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9937 boolean new_wall = FALSE;
9938
9939 if (IS_ANIMATED(graphic))
9940 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9941
9942 if (!MovDelay[ax][ay]) /* start building new wall */
9943 MovDelay[ax][ay] = 6;
9944
9945 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9946 {
9947 MovDelay[ax][ay]--;
9948 if (MovDelay[ax][ay])
9949 return;
9950 }
9951
9952 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9953 oben_frei = TRUE;
9954 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9955 unten_frei = TRUE;
9956 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9957 links_frei = TRUE;
9958 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9959 rechts_frei = TRUE;
9960
9961 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9962 element == EL_EXPANDABLE_WALL_ANY)
9963 {
9964 if (oben_frei)
9965 {
9966 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9967 Store[ax][ay-1] = element;
9968 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9969 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9970 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9971 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9972 new_wall = TRUE;
9973 }
9974 if (unten_frei)
9975 {
9976 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9977 Store[ax][ay+1] = element;
9978 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9979 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9980 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9981 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9982 new_wall = TRUE;
9983 }
9984 }
9985
9986 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9987 element == EL_EXPANDABLE_WALL_ANY ||
9988 element == EL_EXPANDABLE_WALL ||
9989 element == EL_BD_EXPANDABLE_WALL)
9990 {
9991 if (links_frei)
9992 {
9993 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9994 Store[ax-1][ay] = element;
9995 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9996 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9997 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9998 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9999 new_wall = TRUE;
10000 }
10001
10002 if (rechts_frei)
10003 {
10004 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10005 Store[ax+1][ay] = element;
10006 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10007 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10008 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10009 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10010 new_wall = TRUE;
10011 }
10012 }
10013
10014 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10015 TEST_DrawLevelField(ax, ay);
10016
10017 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10018 oben_massiv = TRUE;
10019 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10020 unten_massiv = TRUE;
10021 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10022 links_massiv = TRUE;
10023 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10024 rechts_massiv = TRUE;
10025
10026 if (((oben_massiv && unten_massiv) ||
10027 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10028 element == EL_EXPANDABLE_WALL) &&
10029 ((links_massiv && rechts_massiv) ||
10030 element == EL_EXPANDABLE_WALL_VERTICAL))
10031 Feld[ax][ay] = EL_WALL;
10032
10033 if (new_wall)
10034 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10035 }
10036
MauerAblegerStahl(int ax,int ay)10037 void MauerAblegerStahl(int ax, int ay)
10038 {
10039 int element = Feld[ax][ay];
10040 int graphic = el2img(element);
10041 boolean oben_frei = FALSE, unten_frei = FALSE;
10042 boolean links_frei = FALSE, rechts_frei = FALSE;
10043 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10044 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10045 boolean new_wall = FALSE;
10046
10047 if (IS_ANIMATED(graphic))
10048 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10049
10050 if (!MovDelay[ax][ay]) /* start building new wall */
10051 MovDelay[ax][ay] = 6;
10052
10053 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10054 {
10055 MovDelay[ax][ay]--;
10056 if (MovDelay[ax][ay])
10057 return;
10058 }
10059
10060 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10061 oben_frei = TRUE;
10062 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10063 unten_frei = TRUE;
10064 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10065 links_frei = TRUE;
10066 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10067 rechts_frei = TRUE;
10068
10069 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10070 element == EL_EXPANDABLE_STEELWALL_ANY)
10071 {
10072 if (oben_frei)
10073 {
10074 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10075 Store[ax][ay-1] = element;
10076 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10077 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10078 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10079 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10080 new_wall = TRUE;
10081 }
10082 if (unten_frei)
10083 {
10084 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10085 Store[ax][ay+1] = element;
10086 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10087 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10088 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10089 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10090 new_wall = TRUE;
10091 }
10092 }
10093
10094 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10095 element == EL_EXPANDABLE_STEELWALL_ANY)
10096 {
10097 if (links_frei)
10098 {
10099 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10100 Store[ax-1][ay] = element;
10101 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10102 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10103 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10104 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10105 new_wall = TRUE;
10106 }
10107
10108 if (rechts_frei)
10109 {
10110 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10111 Store[ax+1][ay] = element;
10112 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10113 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10114 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10115 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10116 new_wall = TRUE;
10117 }
10118 }
10119
10120 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10121 oben_massiv = TRUE;
10122 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10123 unten_massiv = TRUE;
10124 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10125 links_massiv = TRUE;
10126 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10127 rechts_massiv = TRUE;
10128
10129 if (((oben_massiv && unten_massiv) ||
10130 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10131 ((links_massiv && rechts_massiv) ||
10132 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10133 Feld[ax][ay] = EL_STEELWALL;
10134
10135 if (new_wall)
10136 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10137 }
10138
CheckForDragon(int x,int y)10139 void CheckForDragon(int x, int y)
10140 {
10141 int i, j;
10142 boolean dragon_found = FALSE;
10143 static int xy[4][2] =
10144 {
10145 { 0, -1 },
10146 { -1, 0 },
10147 { +1, 0 },
10148 { 0, +1 }
10149 };
10150
10151 for (i = 0; i < NUM_DIRECTIONS; i++)
10152 {
10153 for (j = 0; j < 4; j++)
10154 {
10155 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10156
10157 if (IN_LEV_FIELD(xx, yy) &&
10158 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10159 {
10160 if (Feld[xx][yy] == EL_DRAGON)
10161 dragon_found = TRUE;
10162 }
10163 else
10164 break;
10165 }
10166 }
10167
10168 if (!dragon_found)
10169 {
10170 for (i = 0; i < NUM_DIRECTIONS; i++)
10171 {
10172 for (j = 0; j < 3; j++)
10173 {
10174 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10175
10176 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10177 {
10178 Feld[xx][yy] = EL_EMPTY;
10179 TEST_DrawLevelField(xx, yy);
10180 }
10181 else
10182 break;
10183 }
10184 }
10185 }
10186 }
10187
InitBuggyBase(int x,int y)10188 static void InitBuggyBase(int x, int y)
10189 {
10190 int element = Feld[x][y];
10191 int activating_delay = FRAMES_PER_SECOND / 4;
10192
10193 ChangeDelay[x][y] =
10194 (element == EL_SP_BUGGY_BASE ?
10195 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10196 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10197 activating_delay :
10198 element == EL_SP_BUGGY_BASE_ACTIVE ?
10199 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10200 }
10201
WarnBuggyBase(int x,int y)10202 static void WarnBuggyBase(int x, int y)
10203 {
10204 int i;
10205 static int xy[4][2] =
10206 {
10207 { 0, -1 },
10208 { -1, 0 },
10209 { +1, 0 },
10210 { 0, +1 }
10211 };
10212
10213 for (i = 0; i < NUM_DIRECTIONS; i++)
10214 {
10215 int xx = x + xy[i][0];
10216 int yy = y + xy[i][1];
10217
10218 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10219 {
10220 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10221
10222 break;
10223 }
10224 }
10225 }
10226
InitTrap(int x,int y)10227 static void InitTrap(int x, int y)
10228 {
10229 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10230 }
10231
ActivateTrap(int x,int y)10232 static void ActivateTrap(int x, int y)
10233 {
10234 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10235 }
10236
ChangeActiveTrap(int x,int y)10237 static void ChangeActiveTrap(int x, int y)
10238 {
10239 int graphic = IMG_TRAP_ACTIVE;
10240
10241 /* if new animation frame was drawn, correct crumbled sand border */
10242 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10243 TEST_DrawLevelFieldCrumbled(x, y);
10244 }
10245
getSpecialActionElement(int element,int number,int base_element)10246 static int getSpecialActionElement(int element, int number, int base_element)
10247 {
10248 return (element != EL_EMPTY ? element :
10249 number != -1 ? base_element + number - 1 :
10250 EL_EMPTY);
10251 }
10252
getModifiedActionNumber(int value_old,int operator,int operand,int value_min,int value_max)10253 static int getModifiedActionNumber(int value_old, int operator, int operand,
10254 int value_min, int value_max)
10255 {
10256 int value_new = (operator == CA_MODE_SET ? operand :
10257 operator == CA_MODE_ADD ? value_old + operand :
10258 operator == CA_MODE_SUBTRACT ? value_old - operand :
10259 operator == CA_MODE_MULTIPLY ? value_old * operand :
10260 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10261 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10262 value_old);
10263
10264 return (value_new < value_min ? value_min :
10265 value_new > value_max ? value_max :
10266 value_new);
10267 }
10268
ExecuteCustomElementAction(int x,int y,int element,int page)10269 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10270 {
10271 struct ElementInfo *ei = &element_info[element];
10272 struct ElementChangeInfo *change = &ei->change_page[page];
10273 int target_element = change->target_element;
10274 int action_type = change->action_type;
10275 int action_mode = change->action_mode;
10276 int action_arg = change->action_arg;
10277 int action_element = change->action_element;
10278 int i;
10279
10280 if (!change->has_action)
10281 return;
10282
10283 /* ---------- determine action paramater values -------------------------- */
10284
10285 int level_time_value =
10286 (level.time > 0 ? TimeLeft :
10287 TimePlayed);
10288
10289 int action_arg_element_raw =
10290 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10291 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10292 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10293 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10294 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10295 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10296 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10297 EL_EMPTY);
10298 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10299
10300 #if 0
10301 if (action_arg_element_raw == EL_GROUP_START)
10302 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10303 #endif
10304
10305 int action_arg_direction =
10306 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10307 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10308 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10309 change->actual_trigger_side :
10310 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10311 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10312 MV_NONE);
10313
10314 int action_arg_number_min =
10315 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10316 CA_ARG_MIN);
10317
10318 int action_arg_number_max =
10319 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10320 action_type == CA_SET_LEVEL_GEMS ? 999 :
10321 action_type == CA_SET_LEVEL_TIME ? 9999 :
10322 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10323 action_type == CA_SET_CE_VALUE ? 9999 :
10324 action_type == CA_SET_CE_SCORE ? 9999 :
10325 CA_ARG_MAX);
10326
10327 int action_arg_number_reset =
10328 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10329 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10330 action_type == CA_SET_LEVEL_TIME ? level.time :
10331 action_type == CA_SET_LEVEL_SCORE ? 0 :
10332 #if USE_NEW_CUSTOM_VALUE
10333 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10334 #else
10335 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10336 #endif
10337 action_type == CA_SET_CE_SCORE ? 0 :
10338 0);
10339
10340 int action_arg_number =
10341 (action_arg <= CA_ARG_MAX ? action_arg :
10342 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10343 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10344 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10345 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10346 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10347 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10348 #if USE_NEW_CUSTOM_VALUE
10349 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10350 #else
10351 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10352 #endif
10353 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10354 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10355 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10356 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10357 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10358 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10359 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10360 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10361 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10362 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10363 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10364 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10365 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10366 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10367 -1);
10368
10369 int action_arg_number_old =
10370 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10371 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10372 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10373 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10374 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10375 0);
10376
10377 int action_arg_number_new =
10378 getModifiedActionNumber(action_arg_number_old,
10379 action_mode, action_arg_number,
10380 action_arg_number_min, action_arg_number_max);
10381
10382 #if 1
10383 int trigger_player_bits =
10384 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10385 change->actual_trigger_player_bits : change->trigger_player);
10386 #else
10387 int trigger_player_bits =
10388 (change->actual_trigger_player >= EL_PLAYER_1 &&
10389 change->actual_trigger_player <= EL_PLAYER_4 ?
10390 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10391 PLAYER_BITS_ANY);
10392 #endif
10393
10394 int action_arg_player_bits =
10395 (action_arg >= CA_ARG_PLAYER_1 &&
10396 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10397 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10398 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10399 PLAYER_BITS_ANY);
10400
10401 /* ---------- execute action -------------------------------------------- */
10402
10403 switch (action_type)
10404 {
10405 case CA_NO_ACTION:
10406 {
10407 return;
10408 }
10409
10410 /* ---------- level actions ------------------------------------------- */
10411
10412 case CA_RESTART_LEVEL:
10413 {
10414 game.restart_level = TRUE;
10415
10416 break;
10417 }
10418
10419 case CA_SHOW_ENVELOPE:
10420 {
10421 int element = getSpecialActionElement(action_arg_element,
10422 action_arg_number, EL_ENVELOPE_1);
10423
10424 if (IS_ENVELOPE(element))
10425 local_player->show_envelope = element;
10426
10427 break;
10428 }
10429
10430 case CA_SET_LEVEL_TIME:
10431 {
10432 if (level.time > 0) /* only modify limited time value */
10433 {
10434 TimeLeft = action_arg_number_new;
10435
10436 #if 1
10437 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10438
10439 DisplayGameControlValues();
10440 #else
10441 DrawGameValue_Time(TimeLeft);
10442 #endif
10443
10444 if (!TimeLeft && setup.time_limit)
10445 for (i = 0; i < MAX_PLAYERS; i++)
10446 KillPlayer(&stored_player[i]);
10447 }
10448
10449 break;
10450 }
10451
10452 case CA_SET_LEVEL_SCORE:
10453 {
10454 local_player->score = action_arg_number_new;
10455
10456 #if 1
10457 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10458
10459 DisplayGameControlValues();
10460 #else
10461 DrawGameValue_Score(local_player->score);
10462 #endif
10463
10464 break;
10465 }
10466
10467 case CA_SET_LEVEL_GEMS:
10468 {
10469 local_player->gems_still_needed = action_arg_number_new;
10470
10471 #if 1
10472 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10473
10474 DisplayGameControlValues();
10475 #else
10476 DrawGameValue_Emeralds(local_player->gems_still_needed);
10477 #endif
10478
10479 break;
10480 }
10481
10482 #if !USE_PLAYER_GRAVITY
10483 case CA_SET_LEVEL_GRAVITY:
10484 {
10485 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10486 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10487 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10488 game.gravity);
10489 break;
10490 }
10491 #endif
10492
10493 case CA_SET_LEVEL_WIND:
10494 {
10495 game.wind_direction = action_arg_direction;
10496
10497 break;
10498 }
10499
10500 case CA_SET_LEVEL_RANDOM_SEED:
10501 {
10502 #if 1
10503 /* ensure that setting a new random seed while playing is predictable */
10504 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10505 #else
10506 InitRND(action_arg_number_new);
10507 #endif
10508
10509 #if 0
10510 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10511 #endif
10512
10513 #if 0
10514 {
10515 int i;
10516
10517 printf("::: ");
10518 for (i = 0; i < 9; i++)
10519 printf("%d, ", RND(2));
10520 printf("\n");
10521 }
10522 #endif
10523
10524 break;
10525 }
10526
10527 /* ---------- player actions ------------------------------------------ */
10528
10529 case CA_MOVE_PLAYER:
10530 {
10531 /* automatically move to the next field in specified direction */
10532 for (i = 0; i < MAX_PLAYERS; i++)
10533 if (trigger_player_bits & (1 << i))
10534 stored_player[i].programmed_action = action_arg_direction;
10535
10536 break;
10537 }
10538
10539 case CA_EXIT_PLAYER:
10540 {
10541 for (i = 0; i < MAX_PLAYERS; i++)
10542 if (action_arg_player_bits & (1 << i))
10543 PlayerWins(&stored_player[i]);
10544
10545 break;
10546 }
10547
10548 case CA_KILL_PLAYER:
10549 {
10550 for (i = 0; i < MAX_PLAYERS; i++)
10551 if (action_arg_player_bits & (1 << i))
10552 KillPlayer(&stored_player[i]);
10553
10554 break;
10555 }
10556
10557 case CA_SET_PLAYER_KEYS:
10558 {
10559 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10560 int element = getSpecialActionElement(action_arg_element,
10561 action_arg_number, EL_KEY_1);
10562
10563 if (IS_KEY(element))
10564 {
10565 for (i = 0; i < MAX_PLAYERS; i++)
10566 {
10567 if (trigger_player_bits & (1 << i))
10568 {
10569 stored_player[i].key[KEY_NR(element)] = key_state;
10570
10571 DrawGameDoorValues();
10572 }
10573 }
10574 }
10575
10576 break;
10577 }
10578
10579 case CA_SET_PLAYER_SPEED:
10580 {
10581 #if 0
10582 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10583 #endif
10584
10585 for (i = 0; i < MAX_PLAYERS; i++)
10586 {
10587 if (trigger_player_bits & (1 << i))
10588 {
10589 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10590
10591 if (action_arg == CA_ARG_SPEED_FASTER &&
10592 stored_player[i].cannot_move)
10593 {
10594 action_arg_number = STEPSIZE_VERY_SLOW;
10595 }
10596 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10597 action_arg == CA_ARG_SPEED_FASTER)
10598 {
10599 action_arg_number = 2;
10600 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10601 CA_MODE_MULTIPLY);
10602 }
10603 else if (action_arg == CA_ARG_NUMBER_RESET)
10604 {
10605 action_arg_number = level.initial_player_stepsize[i];
10606 }
10607
10608 move_stepsize =
10609 getModifiedActionNumber(move_stepsize,
10610 action_mode,
10611 action_arg_number,
10612 action_arg_number_min,
10613 action_arg_number_max);
10614
10615 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10616 }
10617 }
10618
10619 break;
10620 }
10621
10622 case CA_SET_PLAYER_SHIELD:
10623 {
10624 for (i = 0; i < MAX_PLAYERS; i++)
10625 {
10626 if (trigger_player_bits & (1 << i))
10627 {
10628 if (action_arg == CA_ARG_SHIELD_OFF)
10629 {
10630 stored_player[i].shield_normal_time_left = 0;
10631 stored_player[i].shield_deadly_time_left = 0;
10632 }
10633 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10634 {
10635 stored_player[i].shield_normal_time_left = 999999;
10636 }
10637 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10638 {
10639 stored_player[i].shield_normal_time_left = 999999;
10640 stored_player[i].shield_deadly_time_left = 999999;
10641 }
10642 }
10643 }
10644
10645 break;
10646 }
10647
10648 #if USE_PLAYER_GRAVITY
10649 case CA_SET_PLAYER_GRAVITY:
10650 {
10651 for (i = 0; i < MAX_PLAYERS; i++)
10652 {
10653 if (trigger_player_bits & (1 << i))
10654 {
10655 stored_player[i].gravity =
10656 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10657 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10658 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10659 stored_player[i].gravity);
10660 }
10661 }
10662
10663 break;
10664 }
10665 #endif
10666
10667 case CA_SET_PLAYER_ARTWORK:
10668 {
10669 for (i = 0; i < MAX_PLAYERS; i++)
10670 {
10671 if (trigger_player_bits & (1 << i))
10672 {
10673 int artwork_element = action_arg_element;
10674
10675 if (action_arg == CA_ARG_ELEMENT_RESET)
10676 artwork_element =
10677 (level.use_artwork_element[i] ? level.artwork_element[i] :
10678 stored_player[i].element_nr);
10679
10680 #if USE_GFX_RESET_PLAYER_ARTWORK
10681 if (stored_player[i].artwork_element != artwork_element)
10682 stored_player[i].Frame = 0;
10683 #endif
10684
10685 stored_player[i].artwork_element = artwork_element;
10686
10687 SetPlayerWaiting(&stored_player[i], FALSE);
10688
10689 /* set number of special actions for bored and sleeping animation */
10690 stored_player[i].num_special_action_bored =
10691 get_num_special_action(artwork_element,
10692 ACTION_BORING_1, ACTION_BORING_LAST);
10693 stored_player[i].num_special_action_sleeping =
10694 get_num_special_action(artwork_element,
10695 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10696 }
10697 }
10698
10699 break;
10700 }
10701
10702 case CA_SET_PLAYER_INVENTORY:
10703 {
10704 for (i = 0; i < MAX_PLAYERS; i++)
10705 {
10706 struct PlayerInfo *player = &stored_player[i];
10707 int j, k;
10708
10709 if (trigger_player_bits & (1 << i))
10710 {
10711 int inventory_element = action_arg_element;
10712
10713 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10714 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10715 action_arg == CA_ARG_ELEMENT_ACTION)
10716 {
10717 int element = inventory_element;
10718 int collect_count = element_info[element].collect_count_initial;
10719
10720 if (!IS_CUSTOM_ELEMENT(element))
10721 collect_count = 1;
10722
10723 if (collect_count == 0)
10724 player->inventory_infinite_element = element;
10725 else
10726 for (k = 0; k < collect_count; k++)
10727 if (player->inventory_size < MAX_INVENTORY_SIZE)
10728 player->inventory_element[player->inventory_size++] =
10729 element;
10730 }
10731 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10732 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10733 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10734 {
10735 if (player->inventory_infinite_element != EL_UNDEFINED &&
10736 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10737 action_arg_element_raw))
10738 player->inventory_infinite_element = EL_UNDEFINED;
10739
10740 for (k = 0, j = 0; j < player->inventory_size; j++)
10741 {
10742 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10743 action_arg_element_raw))
10744 player->inventory_element[k++] = player->inventory_element[j];
10745 }
10746
10747 player->inventory_size = k;
10748 }
10749 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10750 {
10751 if (player->inventory_size > 0)
10752 {
10753 for (j = 0; j < player->inventory_size - 1; j++)
10754 player->inventory_element[j] = player->inventory_element[j + 1];
10755
10756 player->inventory_size--;
10757 }
10758 }
10759 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10760 {
10761 if (player->inventory_size > 0)
10762 player->inventory_size--;
10763 }
10764 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10765 {
10766 player->inventory_infinite_element = EL_UNDEFINED;
10767 player->inventory_size = 0;
10768 }
10769 else if (action_arg == CA_ARG_INVENTORY_RESET)
10770 {
10771 player->inventory_infinite_element = EL_UNDEFINED;
10772 player->inventory_size = 0;
10773
10774 if (level.use_initial_inventory[i])
10775 {
10776 for (j = 0; j < level.initial_inventory_size[i]; j++)
10777 {
10778 int element = level.initial_inventory_content[i][j];
10779 int collect_count = element_info[element].collect_count_initial;
10780
10781 if (!IS_CUSTOM_ELEMENT(element))
10782 collect_count = 1;
10783
10784 if (collect_count == 0)
10785 player->inventory_infinite_element = element;
10786 else
10787 for (k = 0; k < collect_count; k++)
10788 if (player->inventory_size < MAX_INVENTORY_SIZE)
10789 player->inventory_element[player->inventory_size++] =
10790 element;
10791 }
10792 }
10793 }
10794 }
10795 }
10796
10797 break;
10798 }
10799
10800 /* ---------- CE actions ---------------------------------------------- */
10801
10802 case CA_SET_CE_VALUE:
10803 {
10804 #if USE_NEW_CUSTOM_VALUE
10805 int last_ce_value = CustomValue[x][y];
10806
10807 CustomValue[x][y] = action_arg_number_new;
10808
10809 if (CustomValue[x][y] != last_ce_value)
10810 {
10811 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10812 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10813
10814 if (CustomValue[x][y] == 0)
10815 {
10816 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10817 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10818 }
10819 }
10820 #endif
10821
10822 break;
10823 }
10824
10825 case CA_SET_CE_SCORE:
10826 {
10827 #if USE_NEW_CUSTOM_VALUE
10828 int last_ce_score = ei->collect_score;
10829
10830 ei->collect_score = action_arg_number_new;
10831
10832 if (ei->collect_score != last_ce_score)
10833 {
10834 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10835 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10836
10837 if (ei->collect_score == 0)
10838 {
10839 int xx, yy;
10840
10841 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10842 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10843
10844 /*
10845 This is a very special case that seems to be a mixture between
10846 CheckElementChange() and CheckTriggeredElementChange(): while
10847 the first one only affects single elements that are triggered
10848 directly, the second one affects multiple elements in the playfield
10849 that are triggered indirectly by another element. This is a third
10850 case: Changing the CE score always affects multiple identical CEs,
10851 so every affected CE must be checked, not only the single CE for
10852 which the CE score was changed in the first place (as every instance
10853 of that CE shares the same CE score, and therefore also can change)!
10854 */
10855 SCAN_PLAYFIELD(xx, yy)
10856 {
10857 if (Feld[xx][yy] == element)
10858 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10859 CE_SCORE_GETS_ZERO);
10860 }
10861 }
10862 }
10863 #endif
10864
10865 break;
10866 }
10867
10868 case CA_SET_CE_ARTWORK:
10869 {
10870 int artwork_element = action_arg_element;
10871 boolean reset_frame = FALSE;
10872 int xx, yy;
10873
10874 if (action_arg == CA_ARG_ELEMENT_RESET)
10875 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10876 element);
10877
10878 if (ei->gfx_element != artwork_element)
10879 reset_frame = TRUE;
10880
10881 ei->gfx_element = artwork_element;
10882
10883 SCAN_PLAYFIELD(xx, yy)
10884 {
10885 if (Feld[xx][yy] == element)
10886 {
10887 if (reset_frame)
10888 {
10889 ResetGfxAnimation(xx, yy);
10890 ResetRandomAnimationValue(xx, yy);
10891 }
10892
10893 TEST_DrawLevelField(xx, yy);
10894 }
10895 }
10896
10897 break;
10898 }
10899
10900 /* ---------- engine actions ------------------------------------------ */
10901
10902 case CA_SET_ENGINE_SCAN_MODE:
10903 {
10904 InitPlayfieldScanMode(action_arg);
10905
10906 break;
10907 }
10908
10909 default:
10910 break;
10911 }
10912 }
10913
CreateFieldExt(int x,int y,int element,boolean is_change)10914 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10915 {
10916 int old_element = Feld[x][y];
10917 int new_element = GetElementFromGroupElement(element);
10918 int previous_move_direction = MovDir[x][y];
10919 #if USE_NEW_CUSTOM_VALUE
10920 int last_ce_value = CustomValue[x][y];
10921 #endif
10922 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10923 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10924 boolean add_player_onto_element = (new_element_is_player &&
10925 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10926 /* this breaks SnakeBite when a snake is
10927 halfway through a door that closes */
10928 /* NOW FIXED AT LEVEL INIT IN files.c */
10929 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10930 #endif
10931 IS_WALKABLE(old_element));
10932
10933 #if 0
10934 /* check if element under the player changes from accessible to unaccessible
10935 (needed for special case of dropping element which then changes) */
10936 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10937 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10938 {
10939 Bang(x, y);
10940
10941 return;
10942 }
10943 #endif
10944
10945 if (!add_player_onto_element)
10946 {
10947 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10948 RemoveMovingField(x, y);
10949 else
10950 RemoveField(x, y);
10951
10952 Feld[x][y] = new_element;
10953
10954 #if !USE_GFX_RESET_GFX_ANIMATION
10955 ResetGfxAnimation(x, y);
10956 ResetRandomAnimationValue(x, y);
10957 #endif
10958
10959 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10960 MovDir[x][y] = previous_move_direction;
10961
10962 #if USE_NEW_CUSTOM_VALUE
10963 if (element_info[new_element].use_last_ce_value)
10964 CustomValue[x][y] = last_ce_value;
10965 #endif
10966
10967 InitField_WithBug1(x, y, FALSE);
10968
10969 new_element = Feld[x][y]; /* element may have changed */
10970
10971 #if USE_GFX_RESET_GFX_ANIMATION
10972 ResetGfxAnimation(x, y);
10973 ResetRandomAnimationValue(x, y);
10974 #endif
10975
10976 TEST_DrawLevelField(x, y);
10977
10978 if (GFX_CRUMBLED(new_element))
10979 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10980 }
10981
10982 #if 1
10983 /* check if element under the player changes from accessible to unaccessible
10984 (needed for special case of dropping element which then changes) */
10985 /* (must be checked after creating new element for walkable group elements) */
10986 #if USE_FIX_KILLED_BY_NON_WALKABLE
10987 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10988 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10989 {
10990 Bang(x, y);
10991
10992 return;
10993 }
10994 #else
10995 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10996 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10997 {
10998 Bang(x, y);
10999
11000 return;
11001 }
11002 #endif
11003 #endif
11004
11005 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11006 if (new_element_is_player)
11007 RelocatePlayer(x, y, new_element);
11008
11009 if (is_change)
11010 ChangeCount[x][y]++; /* count number of changes in the same frame */
11011
11012 TestIfBadThingTouchesPlayer(x, y);
11013 TestIfPlayerTouchesCustomElement(x, y);
11014 TestIfElementTouchesCustomElement(x, y);
11015 }
11016
CreateField(int x,int y,int element)11017 static void CreateField(int x, int y, int element)
11018 {
11019 CreateFieldExt(x, y, element, FALSE);
11020 }
11021
CreateElementFromChange(int x,int y,int element)11022 static void CreateElementFromChange(int x, int y, int element)
11023 {
11024 element = GET_VALID_RUNTIME_ELEMENT(element);
11025
11026 #if USE_STOP_CHANGED_ELEMENTS
11027 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11028 {
11029 int old_element = Feld[x][y];
11030
11031 /* prevent changed element from moving in same engine frame
11032 unless both old and new element can either fall or move */
11033 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11034 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11035 Stop[x][y] = TRUE;
11036 }
11037 #endif
11038
11039 CreateFieldExt(x, y, element, TRUE);
11040 }
11041
ChangeElement(int x,int y,int element,int page)11042 static boolean ChangeElement(int x, int y, int element, int page)
11043 {
11044 struct ElementInfo *ei = &element_info[element];
11045 struct ElementChangeInfo *change = &ei->change_page[page];
11046 int ce_value = CustomValue[x][y];
11047 int ce_score = ei->collect_score;
11048 int target_element;
11049 int old_element = Feld[x][y];
11050
11051 /* always use default change event to prevent running into a loop */
11052 if (ChangeEvent[x][y] == -1)
11053 ChangeEvent[x][y] = CE_DELAY;
11054
11055 if (ChangeEvent[x][y] == CE_DELAY)
11056 {
11057 /* reset actual trigger element, trigger player and action element */
11058 change->actual_trigger_element = EL_EMPTY;
11059 change->actual_trigger_player = EL_EMPTY;
11060 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11061 change->actual_trigger_side = CH_SIDE_NONE;
11062 change->actual_trigger_ce_value = 0;
11063 change->actual_trigger_ce_score = 0;
11064 }
11065
11066 /* do not change elements more than a specified maximum number of changes */
11067 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11068 return FALSE;
11069
11070 ChangeCount[x][y]++; /* count number of changes in the same frame */
11071
11072 if (change->explode)
11073 {
11074 Bang(x, y);
11075
11076 return TRUE;
11077 }
11078
11079 if (change->use_target_content)
11080 {
11081 boolean complete_replace = TRUE;
11082 boolean can_replace[3][3];
11083 int xx, yy;
11084
11085 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11086 {
11087 boolean is_empty;
11088 boolean is_walkable;
11089 boolean is_diggable;
11090 boolean is_collectible;
11091 boolean is_removable;
11092 boolean is_destructible;
11093 int ex = x + xx - 1;
11094 int ey = y + yy - 1;
11095 int content_element = change->target_content.e[xx][yy];
11096 int e;
11097
11098 can_replace[xx][yy] = TRUE;
11099
11100 if (ex == x && ey == y) /* do not check changing element itself */
11101 continue;
11102
11103 if (content_element == EL_EMPTY_SPACE)
11104 {
11105 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11106
11107 continue;
11108 }
11109
11110 if (!IN_LEV_FIELD(ex, ey))
11111 {
11112 can_replace[xx][yy] = FALSE;
11113 complete_replace = FALSE;
11114
11115 continue;
11116 }
11117
11118 e = Feld[ex][ey];
11119
11120 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11121 e = MovingOrBlocked2Element(ex, ey);
11122
11123 is_empty = (IS_FREE(ex, ey) ||
11124 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11125
11126 is_walkable = (is_empty || IS_WALKABLE(e));
11127 is_diggable = (is_empty || IS_DIGGABLE(e));
11128 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11129 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11130 is_removable = (is_diggable || is_collectible);
11131
11132 can_replace[xx][yy] =
11133 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11134 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11135 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11136 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11137 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11138 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11139 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11140
11141 if (!can_replace[xx][yy])
11142 complete_replace = FALSE;
11143 }
11144
11145 if (!change->only_if_complete || complete_replace)
11146 {
11147 boolean something_has_changed = FALSE;
11148
11149 if (change->only_if_complete && change->use_random_replace &&
11150 RND(100) < change->random_percentage)
11151 return FALSE;
11152
11153 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11154 {
11155 int ex = x + xx - 1;
11156 int ey = y + yy - 1;
11157 int content_element;
11158
11159 if (can_replace[xx][yy] && (!change->use_random_replace ||
11160 RND(100) < change->random_percentage))
11161 {
11162 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11163 RemoveMovingField(ex, ey);
11164
11165 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11166
11167 content_element = change->target_content.e[xx][yy];
11168 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11169 ce_value, ce_score);
11170
11171 CreateElementFromChange(ex, ey, target_element);
11172
11173 something_has_changed = TRUE;
11174
11175 /* for symmetry reasons, freeze newly created border elements */
11176 if (ex != x || ey != y)
11177 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11178 }
11179 }
11180
11181 if (something_has_changed)
11182 {
11183 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11184 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11185 }
11186 }
11187 }
11188 else
11189 {
11190 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11191 ce_value, ce_score);
11192
11193 if (element == EL_DIAGONAL_GROWING ||
11194 element == EL_DIAGONAL_SHRINKING)
11195 {
11196 target_element = Store[x][y];
11197
11198 Store[x][y] = EL_EMPTY;
11199 }
11200
11201 CreateElementFromChange(x, y, target_element);
11202
11203 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11204 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11205 }
11206
11207 /* this uses direct change before indirect change */
11208 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11209
11210 return TRUE;
11211 }
11212
11213 #if USE_NEW_DELAYED_ACTION
11214
HandleElementChange(int x,int y,int page)11215 static void HandleElementChange(int x, int y, int page)
11216 {
11217 int element = MovingOrBlocked2Element(x, y);
11218 struct ElementInfo *ei = &element_info[element];
11219 struct ElementChangeInfo *change = &ei->change_page[page];
11220 boolean handle_action_before_change = FALSE;
11221
11222 #ifdef DEBUG
11223 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11224 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11225 {
11226 printf("\n\n");
11227 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11228 x, y, element, element_info[element].token_name);
11229 printf("HandleElementChange(): This should never happen!\n");
11230 printf("\n\n");
11231 }
11232 #endif
11233
11234 /* this can happen with classic bombs on walkable, changing elements */
11235 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11236 {
11237 #if 0
11238 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11239 ChangeDelay[x][y] = 0;
11240 #endif
11241
11242 return;
11243 }
11244
11245 if (ChangeDelay[x][y] == 0) /* initialize element change */
11246 {
11247 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11248
11249 if (change->can_change)
11250 {
11251 #if 1
11252 /* !!! not clear why graphic animation should be reset at all here !!! */
11253 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11254 #if USE_GFX_RESET_WHEN_NOT_MOVING
11255 /* when a custom element is about to change (for example by change delay),
11256 do not reset graphic animation when the custom element is moving */
11257 if (!IS_MOVING(x, y))
11258 #endif
11259 {
11260 ResetGfxAnimation(x, y);
11261 ResetRandomAnimationValue(x, y);
11262 }
11263 #endif
11264
11265 if (change->pre_change_function)
11266 change->pre_change_function(x, y);
11267 }
11268 }
11269
11270 ChangeDelay[x][y]--;
11271
11272 if (ChangeDelay[x][y] != 0) /* continue element change */
11273 {
11274 if (change->can_change)
11275 {
11276 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11277
11278 if (IS_ANIMATED(graphic))
11279 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11280
11281 if (change->change_function)
11282 change->change_function(x, y);
11283 }
11284 }
11285 else /* finish element change */
11286 {
11287 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11288 {
11289 page = ChangePage[x][y];
11290 ChangePage[x][y] = -1;
11291
11292 change = &ei->change_page[page];
11293 }
11294
11295 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11296 {
11297 ChangeDelay[x][y] = 1; /* try change after next move step */
11298 ChangePage[x][y] = page; /* remember page to use for change */
11299
11300 return;
11301 }
11302
11303 #if 1
11304 /* special case: set new level random seed before changing element */
11305 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11306 handle_action_before_change = TRUE;
11307
11308 if (change->has_action && handle_action_before_change)
11309 ExecuteCustomElementAction(x, y, element, page);
11310 #endif
11311
11312 if (change->can_change)
11313 {
11314 if (ChangeElement(x, y, element, page))
11315 {
11316 if (change->post_change_function)
11317 change->post_change_function(x, y);
11318 }
11319 }
11320
11321 if (change->has_action && !handle_action_before_change)
11322 ExecuteCustomElementAction(x, y, element, page);
11323 }
11324 }
11325
11326 #else
11327
HandleElementChange(int x,int y,int page)11328 static void HandleElementChange(int x, int y, int page)
11329 {
11330 int element = MovingOrBlocked2Element(x, y);
11331 struct ElementInfo *ei = &element_info[element];
11332 struct ElementChangeInfo *change = &ei->change_page[page];
11333
11334 #ifdef DEBUG
11335 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11336 {
11337 printf("\n\n");
11338 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11339 x, y, element, element_info[element].token_name);
11340 printf("HandleElementChange(): This should never happen!\n");
11341 printf("\n\n");
11342 }
11343 #endif
11344
11345 /* this can happen with classic bombs on walkable, changing elements */
11346 if (!CAN_CHANGE(element))
11347 {
11348 #if 0
11349 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11350 ChangeDelay[x][y] = 0;
11351 #endif
11352
11353 return;
11354 }
11355
11356 if (ChangeDelay[x][y] == 0) /* initialize element change */
11357 {
11358 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11359
11360 ResetGfxAnimation(x, y);
11361 ResetRandomAnimationValue(x, y);
11362
11363 if (change->pre_change_function)
11364 change->pre_change_function(x, y);
11365 }
11366
11367 ChangeDelay[x][y]--;
11368
11369 if (ChangeDelay[x][y] != 0) /* continue element change */
11370 {
11371 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11372
11373 if (IS_ANIMATED(graphic))
11374 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11375
11376 if (change->change_function)
11377 change->change_function(x, y);
11378 }
11379 else /* finish element change */
11380 {
11381 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11382 {
11383 page = ChangePage[x][y];
11384 ChangePage[x][y] = -1;
11385
11386 change = &ei->change_page[page];
11387 }
11388
11389 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11390 {
11391 ChangeDelay[x][y] = 1; /* try change after next move step */
11392 ChangePage[x][y] = page; /* remember page to use for change */
11393
11394 return;
11395 }
11396
11397 if (ChangeElement(x, y, element, page))
11398 {
11399 if (change->post_change_function)
11400 change->post_change_function(x, y);
11401 }
11402 }
11403 }
11404
11405 #endif
11406
CheckTriggeredElementChangeExt(int trigger_x,int trigger_y,int trigger_element,int trigger_event,int trigger_player,int trigger_side,int trigger_page)11407 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11408 int trigger_element,
11409 int trigger_event,
11410 int trigger_player,
11411 int trigger_side,
11412 int trigger_page)
11413 {
11414 boolean change_done_any = FALSE;
11415 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11416 int i;
11417
11418 if (!(trigger_events[trigger_element][trigger_event]))
11419 return FALSE;
11420
11421 #if 0
11422 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11423 trigger_event, recursion_loop_depth, recursion_loop_detected,
11424 recursion_loop_element, EL_NAME(recursion_loop_element));
11425 #endif
11426
11427 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11428
11429 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11430 {
11431 int element = EL_CUSTOM_START + i;
11432 boolean change_done = FALSE;
11433 int p;
11434
11435 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11436 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11437 continue;
11438
11439 for (p = 0; p < element_info[element].num_change_pages; p++)
11440 {
11441 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11442
11443 if (change->can_change_or_has_action &&
11444 change->has_event[trigger_event] &&
11445 change->trigger_side & trigger_side &&
11446 change->trigger_player & trigger_player &&
11447 change->trigger_page & trigger_page_bits &&
11448 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11449 {
11450 change->actual_trigger_element = trigger_element;
11451 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11452 change->actual_trigger_player_bits = trigger_player;
11453 change->actual_trigger_side = trigger_side;
11454 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11455 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11456
11457 #if 0
11458 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11459 element, EL_NAME(element), p);
11460 #endif
11461
11462 if ((change->can_change && !change_done) || change->has_action)
11463 {
11464 int x, y;
11465
11466 SCAN_PLAYFIELD(x, y)
11467 {
11468 if (Feld[x][y] == element)
11469 {
11470 if (change->can_change && !change_done)
11471 {
11472 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11473 /* if element already changed in this frame, not only prevent
11474 another element change (checked in ChangeElement()), but
11475 also prevent additional element actions for this element */
11476
11477 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11478 !level.use_action_after_change_bug)
11479 continue;
11480 #endif
11481
11482 #if 0
11483 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11484 element, EL_NAME(element), p);
11485 #endif
11486
11487 ChangeDelay[x][y] = 1;
11488 ChangeEvent[x][y] = trigger_event;
11489
11490 HandleElementChange(x, y, p);
11491 }
11492 #if USE_NEW_DELAYED_ACTION
11493 else if (change->has_action)
11494 {
11495 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11496 /* if element already changed in this frame, not only prevent
11497 another element change (checked in ChangeElement()), but
11498 also prevent additional element actions for this element */
11499
11500 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11501 !level.use_action_after_change_bug)
11502 continue;
11503 #endif
11504
11505
11506 #if 0
11507 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11508 element, EL_NAME(element), p);
11509 #endif
11510
11511 ExecuteCustomElementAction(x, y, element, p);
11512 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11513 }
11514 #else
11515 if (change->has_action)
11516 {
11517 ExecuteCustomElementAction(x, y, element, p);
11518 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11519 }
11520 #endif
11521 }
11522 }
11523
11524 if (change->can_change)
11525 {
11526 change_done = TRUE;
11527 change_done_any = TRUE;
11528
11529 #if 0
11530 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11531 element, EL_NAME(element), p);
11532 #endif
11533
11534 }
11535 }
11536 }
11537 }
11538 }
11539
11540 RECURSION_LOOP_DETECTION_END();
11541
11542 return change_done_any;
11543 }
11544
CheckElementChangeExt(int x,int y,int element,int trigger_element,int trigger_event,int trigger_player,int trigger_side)11545 static boolean CheckElementChangeExt(int x, int y,
11546 int element,
11547 int trigger_element,
11548 int trigger_event,
11549 int trigger_player,
11550 int trigger_side)
11551 {
11552 boolean change_done = FALSE;
11553 int p;
11554
11555 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11556 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11557 return FALSE;
11558
11559 if (Feld[x][y] == EL_BLOCKED)
11560 {
11561 Blocked2Moving(x, y, &x, &y);
11562 element = Feld[x][y];
11563 }
11564
11565 #if 0
11566 /* check if element has already changed */
11567 if (Feld[x][y] != element)
11568 return FALSE;
11569 #else
11570 /* check if element has already changed or is about to change after moving */
11571 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11572 Feld[x][y] != element) ||
11573
11574 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11575 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11576 ChangePage[x][y] != -1)))
11577 return FALSE;
11578 #endif
11579
11580 #if 0
11581 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11582 trigger_event, recursion_loop_depth, recursion_loop_detected,
11583 recursion_loop_element, EL_NAME(recursion_loop_element));
11584 #endif
11585
11586 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11587
11588 #if 0
11589 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11590 #endif
11591
11592 for (p = 0; p < element_info[element].num_change_pages; p++)
11593 {
11594 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11595
11596 /* check trigger element for all events where the element that is checked
11597 for changing interacts with a directly adjacent element -- this is
11598 different to element changes that affect other elements to change on the
11599 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11600 boolean check_trigger_element =
11601 (trigger_event == CE_TOUCHING_X ||
11602 trigger_event == CE_HITTING_X ||
11603 trigger_event == CE_HIT_BY_X ||
11604 #if 1
11605 /* this one was forgotten until 3.2.3 */
11606 trigger_event == CE_DIGGING_X);
11607 #endif
11608
11609 if (change->can_change_or_has_action &&
11610 change->has_event[trigger_event] &&
11611 change->trigger_side & trigger_side &&
11612 change->trigger_player & trigger_player &&
11613 (!check_trigger_element ||
11614 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11615 {
11616 change->actual_trigger_element = trigger_element;
11617 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11618 change->actual_trigger_player_bits = trigger_player;
11619 change->actual_trigger_side = trigger_side;
11620 change->actual_trigger_ce_value = CustomValue[x][y];
11621 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11622
11623 /* special case: trigger element not at (x,y) position for some events */
11624 if (check_trigger_element)
11625 {
11626 static struct
11627 {
11628 int dx, dy;
11629 } move_xy[] =
11630 {
11631 { 0, 0 },
11632 { -1, 0 },
11633 { +1, 0 },
11634 { 0, 0 },
11635 { 0, -1 },
11636 { 0, 0 }, { 0, 0 }, { 0, 0 },
11637 { 0, +1 }
11638 };
11639
11640 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11641 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11642
11643 change->actual_trigger_ce_value = CustomValue[xx][yy];
11644 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11645 }
11646
11647 if (change->can_change && !change_done)
11648 {
11649 ChangeDelay[x][y] = 1;
11650 ChangeEvent[x][y] = trigger_event;
11651
11652 HandleElementChange(x, y, p);
11653
11654 change_done = TRUE;
11655 }
11656 #if USE_NEW_DELAYED_ACTION
11657 else if (change->has_action)
11658 {
11659 ExecuteCustomElementAction(x, y, element, p);
11660 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11661 }
11662 #else
11663 if (change->has_action)
11664 {
11665 ExecuteCustomElementAction(x, y, element, p);
11666 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11667 }
11668 #endif
11669 }
11670 }
11671
11672 RECURSION_LOOP_DETECTION_END();
11673
11674 return change_done;
11675 }
11676
PlayPlayerSound(struct PlayerInfo * player)11677 static void PlayPlayerSound(struct PlayerInfo *player)
11678 {
11679 int jx = player->jx, jy = player->jy;
11680 int sound_element = player->artwork_element;
11681 int last_action = player->last_action_waiting;
11682 int action = player->action_waiting;
11683
11684 if (player->is_waiting)
11685 {
11686 if (action != last_action)
11687 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11688 else
11689 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11690 }
11691 else
11692 {
11693 if (action != last_action)
11694 StopSound(element_info[sound_element].sound[last_action]);
11695
11696 if (last_action == ACTION_SLEEPING)
11697 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11698 }
11699 }
11700
PlayAllPlayersSound()11701 static void PlayAllPlayersSound()
11702 {
11703 int i;
11704
11705 for (i = 0; i < MAX_PLAYERS; i++)
11706 if (stored_player[i].active)
11707 PlayPlayerSound(&stored_player[i]);
11708 }
11709
SetPlayerWaiting(struct PlayerInfo * player,boolean is_waiting)11710 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11711 {
11712 boolean last_waiting = player->is_waiting;
11713 int move_dir = player->MovDir;
11714
11715 player->dir_waiting = move_dir;
11716 player->last_action_waiting = player->action_waiting;
11717
11718 if (is_waiting)
11719 {
11720 if (!last_waiting) /* not waiting -> waiting */
11721 {
11722 player->is_waiting = TRUE;
11723
11724 player->frame_counter_bored =
11725 FrameCounter +
11726 game.player_boring_delay_fixed +
11727 GetSimpleRandom(game.player_boring_delay_random);
11728 player->frame_counter_sleeping =
11729 FrameCounter +
11730 game.player_sleeping_delay_fixed +
11731 GetSimpleRandom(game.player_sleeping_delay_random);
11732
11733 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11734 }
11735
11736 if (game.player_sleeping_delay_fixed +
11737 game.player_sleeping_delay_random > 0 &&
11738 player->anim_delay_counter == 0 &&
11739 player->post_delay_counter == 0 &&
11740 FrameCounter >= player->frame_counter_sleeping)
11741 player->is_sleeping = TRUE;
11742 else if (game.player_boring_delay_fixed +
11743 game.player_boring_delay_random > 0 &&
11744 FrameCounter >= player->frame_counter_bored)
11745 player->is_bored = TRUE;
11746
11747 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11748 player->is_bored ? ACTION_BORING :
11749 ACTION_WAITING);
11750
11751 if (player->is_sleeping && player->use_murphy)
11752 {
11753 /* special case for sleeping Murphy when leaning against non-free tile */
11754
11755 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11756 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11757 !IS_MOVING(player->jx - 1, player->jy)))
11758 move_dir = MV_LEFT;
11759 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11760 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11761 !IS_MOVING(player->jx + 1, player->jy)))
11762 move_dir = MV_RIGHT;
11763 else
11764 player->is_sleeping = FALSE;
11765
11766 player->dir_waiting = move_dir;
11767 }
11768
11769 if (player->is_sleeping)
11770 {
11771 if (player->num_special_action_sleeping > 0)
11772 {
11773 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11774 {
11775 int last_special_action = player->special_action_sleeping;
11776 int num_special_action = player->num_special_action_sleeping;
11777 int special_action =
11778 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11779 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11780 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11781 last_special_action + 1 : ACTION_SLEEPING);
11782 int special_graphic =
11783 el_act_dir2img(player->artwork_element, special_action, move_dir);
11784
11785 player->anim_delay_counter =
11786 graphic_info[special_graphic].anim_delay_fixed +
11787 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11788 player->post_delay_counter =
11789 graphic_info[special_graphic].post_delay_fixed +
11790 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11791
11792 player->special_action_sleeping = special_action;
11793 }
11794
11795 if (player->anim_delay_counter > 0)
11796 {
11797 player->action_waiting = player->special_action_sleeping;
11798 player->anim_delay_counter--;
11799 }
11800 else if (player->post_delay_counter > 0)
11801 {
11802 player->post_delay_counter--;
11803 }
11804 }
11805 }
11806 else if (player->is_bored)
11807 {
11808 if (player->num_special_action_bored > 0)
11809 {
11810 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11811 {
11812 int special_action =
11813 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11814 int special_graphic =
11815 el_act_dir2img(player->artwork_element, special_action, move_dir);
11816
11817 player->anim_delay_counter =
11818 graphic_info[special_graphic].anim_delay_fixed +
11819 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11820 player->post_delay_counter =
11821 graphic_info[special_graphic].post_delay_fixed +
11822 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11823
11824 player->special_action_bored = special_action;
11825 }
11826
11827 if (player->anim_delay_counter > 0)
11828 {
11829 player->action_waiting = player->special_action_bored;
11830 player->anim_delay_counter--;
11831 }
11832 else if (player->post_delay_counter > 0)
11833 {
11834 player->post_delay_counter--;
11835 }
11836 }
11837 }
11838 }
11839 else if (last_waiting) /* waiting -> not waiting */
11840 {
11841 player->is_waiting = FALSE;
11842 player->is_bored = FALSE;
11843 player->is_sleeping = FALSE;
11844
11845 player->frame_counter_bored = -1;
11846 player->frame_counter_sleeping = -1;
11847
11848 player->anim_delay_counter = 0;
11849 player->post_delay_counter = 0;
11850
11851 player->dir_waiting = player->MovDir;
11852 player->action_waiting = ACTION_DEFAULT;
11853
11854 player->special_action_bored = ACTION_DEFAULT;
11855 player->special_action_sleeping = ACTION_DEFAULT;
11856 }
11857 }
11858
CheckSingleStepMode(struct PlayerInfo * player)11859 static void CheckSingleStepMode(struct PlayerInfo *player)
11860 {
11861 if (tape.single_step && tape.recording && !tape.pausing)
11862 {
11863 /* as it is called "single step mode", just return to pause mode when the
11864 player stopped moving after one tile (or never starts moving at all) */
11865 if (!player->is_moving && !player->is_pushing)
11866 {
11867 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11868 SnapField(player, 0, 0); /* stop snapping */
11869 }
11870 }
11871 }
11872
PlayerActions(struct PlayerInfo * player,byte player_action)11873 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11874 {
11875 int left = player_action & JOY_LEFT;
11876 int right = player_action & JOY_RIGHT;
11877 int up = player_action & JOY_UP;
11878 int down = player_action & JOY_DOWN;
11879 int button1 = player_action & JOY_BUTTON_1;
11880 int button2 = player_action & JOY_BUTTON_2;
11881 int dx = (left ? -1 : right ? 1 : 0);
11882 int dy = (up ? -1 : down ? 1 : 0);
11883
11884 if (!player->active || tape.pausing)
11885 return 0;
11886
11887 if (player_action)
11888 {
11889 if (button1)
11890 SnapField(player, dx, dy);
11891 else
11892 {
11893 if (button2)
11894 DropElement(player);
11895
11896 MovePlayer(player, dx, dy);
11897 }
11898
11899 CheckSingleStepMode(player);
11900
11901 SetPlayerWaiting(player, FALSE);
11902
11903 return player_action;
11904 }
11905 else
11906 {
11907 /* no actions for this player (no input at player's configured device) */
11908
11909 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11910 SnapField(player, 0, 0);
11911 CheckGravityMovementWhenNotMoving(player);
11912
11913 if (player->MovPos == 0)
11914 SetPlayerWaiting(player, TRUE);
11915
11916 if (player->MovPos == 0) /* needed for tape.playing */
11917 player->is_moving = FALSE;
11918
11919 player->is_dropping = FALSE;
11920 player->is_dropping_pressed = FALSE;
11921 player->drop_pressed_delay = 0;
11922
11923 CheckSingleStepMode(player);
11924
11925 return 0;
11926 }
11927 }
11928
CheckLevelTime()11929 static void CheckLevelTime()
11930 {
11931 int i;
11932
11933 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11934 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11935 {
11936 if (level.native_em_level->lev->home == 0) /* all players at home */
11937 {
11938 PlayerWins(local_player);
11939
11940 AllPlayersGone = TRUE;
11941
11942 level.native_em_level->lev->home = -1;
11943 }
11944
11945 if (level.native_em_level->ply[0]->alive == 0 &&
11946 level.native_em_level->ply[1]->alive == 0 &&
11947 level.native_em_level->ply[2]->alive == 0 &&
11948 level.native_em_level->ply[3]->alive == 0) /* all dead */
11949 AllPlayersGone = TRUE;
11950 }
11951 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11952 {
11953 if (game_sp.LevelSolved &&
11954 !game_sp.GameOver) /* game won */
11955 {
11956 PlayerWins(local_player);
11957
11958 game_sp.GameOver = TRUE;
11959
11960 AllPlayersGone = TRUE;
11961 }
11962
11963 if (game_sp.GameOver) /* game lost */
11964 AllPlayersGone = TRUE;
11965 }
11966
11967 if (TimeFrames >= FRAMES_PER_SECOND)
11968 {
11969 TimeFrames = 0;
11970 TapeTime++;
11971
11972 for (i = 0; i < MAX_PLAYERS; i++)
11973 {
11974 struct PlayerInfo *player = &stored_player[i];
11975
11976 if (SHIELD_ON(player))
11977 {
11978 player->shield_normal_time_left--;
11979
11980 if (player->shield_deadly_time_left > 0)
11981 player->shield_deadly_time_left--;
11982 }
11983 }
11984
11985 if (!local_player->LevelSolved && !level.use_step_counter)
11986 {
11987 TimePlayed++;
11988
11989 if (TimeLeft > 0)
11990 {
11991 TimeLeft--;
11992
11993 if (TimeLeft <= 10 && setup.time_limit)
11994 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11995
11996 #if 1
11997 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11998 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11999
12000 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12001
12002 /* (already called by UpdateAndDisplayGameControlValues() below) */
12003 // DisplayGameControlValues();
12004 #else
12005 DrawGameValue_Time(TimeLeft);
12006 #endif
12007
12008 if (!TimeLeft && setup.time_limit)
12009 {
12010 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12011 level.native_em_level->lev->killed_out_of_time = TRUE;
12012 else
12013 for (i = 0; i < MAX_PLAYERS; i++)
12014 KillPlayer(&stored_player[i]);
12015 }
12016 }
12017 #if 1
12018 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12019 {
12020 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12021
12022 /* (already called by UpdateAndDisplayGameControlValues() below) */
12023 // DisplayGameControlValues();
12024 }
12025 #else
12026 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12027 DrawGameValue_Time(TimePlayed);
12028 #endif
12029
12030 level.native_em_level->lev->time =
12031 (game.no_time_limit ? TimePlayed : TimeLeft);
12032 }
12033
12034 if (tape.recording || tape.playing)
12035 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12036 }
12037
12038 #if 1
12039 UpdateAndDisplayGameControlValues();
12040 #else
12041 UpdateGameDoorValues();
12042 DrawGameDoorValues();
12043 #endif
12044 }
12045
AdvanceFrameAndPlayerCounters(int player_nr)12046 void AdvanceFrameAndPlayerCounters(int player_nr)
12047 {
12048 int i;
12049
12050 /* advance frame counters (global frame counter and time frame counter) */
12051 FrameCounter++;
12052 TimeFrames++;
12053
12054 /* advance player counters (counters for move delay, move animation etc.) */
12055 for (i = 0; i < MAX_PLAYERS; i++)
12056 {
12057 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12058 int move_delay_value = stored_player[i].move_delay_value;
12059 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12060
12061 if (!advance_player_counters) /* not all players may be affected */
12062 continue;
12063
12064 #if USE_NEW_PLAYER_ANIM
12065 if (move_frames == 0) /* less than one move per game frame */
12066 {
12067 int stepsize = TILEX / move_delay_value;
12068 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12069 int count = (stored_player[i].is_moving ?
12070 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12071
12072 if (count % delay == 0)
12073 move_frames = 1;
12074 }
12075 #endif
12076
12077 stored_player[i].Frame += move_frames;
12078
12079 if (stored_player[i].MovPos != 0)
12080 stored_player[i].StepFrame += move_frames;
12081
12082 if (stored_player[i].move_delay > 0)
12083 stored_player[i].move_delay--;
12084
12085 /* due to bugs in previous versions, counter must count up, not down */
12086 if (stored_player[i].push_delay != -1)
12087 stored_player[i].push_delay++;
12088
12089 if (stored_player[i].drop_delay > 0)
12090 stored_player[i].drop_delay--;
12091
12092 if (stored_player[i].is_dropping_pressed)
12093 stored_player[i].drop_pressed_delay++;
12094 }
12095 }
12096
StartGameActions(boolean init_network_game,boolean record_tape,int random_seed)12097 void StartGameActions(boolean init_network_game, boolean record_tape,
12098 int random_seed)
12099 {
12100 unsigned int new_random_seed = InitRND(random_seed);
12101
12102 if (record_tape)
12103 TapeStartRecording(new_random_seed);
12104
12105 #if defined(NETWORK_AVALIABLE)
12106 if (init_network_game)
12107 {
12108 SendToServer_StartPlaying();
12109
12110 return;
12111 }
12112 #endif
12113
12114 InitGame();
12115 }
12116
GameActions()12117 void GameActions()
12118 {
12119 static unsigned int game_frame_delay = 0;
12120 unsigned int game_frame_delay_value;
12121 byte *recorded_player_action;
12122 byte summarized_player_action = 0;
12123 byte tape_action[MAX_PLAYERS];
12124 int i;
12125
12126 /* detect endless loops, caused by custom element programming */
12127 if (recursion_loop_detected && recursion_loop_depth == 0)
12128 {
12129 char *message = getStringCat3("Internal Error ! Element ",
12130 EL_NAME(recursion_loop_element),
12131 " caused endless loop ! Quit the game ?");
12132
12133 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12134 EL_NAME(recursion_loop_element));
12135
12136 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12137
12138 recursion_loop_detected = FALSE; /* if game should be continued */
12139
12140 free(message);
12141
12142 return;
12143 }
12144
12145 if (game.restart_level)
12146 StartGameActions(options.network, setup.autorecord, level.random_seed);
12147
12148 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12149 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150 {
12151 if (level.native_em_level->lev->home == 0) /* all players at home */
12152 {
12153 PlayerWins(local_player);
12154
12155 AllPlayersGone = TRUE;
12156
12157 level.native_em_level->lev->home = -1;
12158 }
12159
12160 if (level.native_em_level->ply[0]->alive == 0 &&
12161 level.native_em_level->ply[1]->alive == 0 &&
12162 level.native_em_level->ply[2]->alive == 0 &&
12163 level.native_em_level->ply[3]->alive == 0) /* all dead */
12164 AllPlayersGone = TRUE;
12165 }
12166 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12167 {
12168 if (game_sp.LevelSolved &&
12169 !game_sp.GameOver) /* game won */
12170 {
12171 PlayerWins(local_player);
12172
12173 game_sp.GameOver = TRUE;
12174
12175 AllPlayersGone = TRUE;
12176 }
12177
12178 if (game_sp.GameOver) /* game lost */
12179 AllPlayersGone = TRUE;
12180 }
12181
12182 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12183 GameWon();
12184
12185 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12186 TapeStop();
12187
12188 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12189 return;
12190
12191 game_frame_delay_value =
12192 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12193
12194 if (tape.playing && tape.warp_forward && !tape.pausing)
12195 game_frame_delay_value = 0;
12196
12197 /* ---------- main game synchronization point ---------- */
12198
12199 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12200
12201 if (network_playing && !network_player_action_received)
12202 {
12203 /* try to get network player actions in time */
12204
12205 #if defined(NETWORK_AVALIABLE)
12206 /* last chance to get network player actions without main loop delay */
12207 HandleNetworking();
12208 #endif
12209
12210 /* game was quit by network peer */
12211 if (game_status != GAME_MODE_PLAYING)
12212 return;
12213
12214 if (!network_player_action_received)
12215 return; /* failed to get network player actions in time */
12216
12217 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12218 }
12219
12220 if (tape.pausing)
12221 return;
12222
12223 /* at this point we know that we really continue executing the game */
12224
12225 network_player_action_received = FALSE;
12226
12227 /* when playing tape, read previously recorded player input from tape data */
12228 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12229
12230 #if 1
12231 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12232 if (tape.pausing)
12233 return;
12234 #endif
12235
12236 if (tape.set_centered_player)
12237 {
12238 game.centered_player_nr_next = tape.centered_player_nr_next;
12239 game.set_centered_player = TRUE;
12240 }
12241
12242 for (i = 0; i < MAX_PLAYERS; i++)
12243 {
12244 summarized_player_action |= stored_player[i].action;
12245
12246 if (!network_playing)
12247 stored_player[i].effective_action = stored_player[i].action;
12248 }
12249
12250 #if defined(NETWORK_AVALIABLE)
12251 if (network_playing)
12252 SendToServer_MovePlayer(summarized_player_action);
12253 #endif
12254
12255 if (!options.network && !setup.team_mode)
12256 local_player->effective_action = summarized_player_action;
12257
12258 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12259 {
12260 for (i = 0; i < MAX_PLAYERS; i++)
12261 stored_player[i].effective_action =
12262 (i == game.centered_player_nr ? summarized_player_action : 0);
12263 }
12264
12265 if (recorded_player_action != NULL)
12266 for (i = 0; i < MAX_PLAYERS; i++)
12267 stored_player[i].effective_action = recorded_player_action[i];
12268
12269 for (i = 0; i < MAX_PLAYERS; i++)
12270 {
12271 tape_action[i] = stored_player[i].effective_action;
12272
12273 /* (this can only happen in the R'n'D game engine) */
12274 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12275 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12276 }
12277
12278 /* only record actions from input devices, but not programmed actions */
12279 if (tape.recording)
12280 TapeRecordAction(tape_action);
12281
12282 #if USE_NEW_PLAYER_ASSIGNMENTS
12283 {
12284 byte mapped_action[MAX_PLAYERS];
12285
12286 for (i = 0; i < MAX_PLAYERS; i++)
12287 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12288
12289 for (i = 0; i < MAX_PLAYERS; i++)
12290 stored_player[i].effective_action = mapped_action[i];
12291 }
12292 #endif
12293
12294 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12295 {
12296 GameActions_EM_Main();
12297 }
12298 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12299 {
12300 GameActions_SP_Main();
12301 }
12302 else
12303 {
12304 GameActions_RND();
12305 }
12306 }
12307
GameActions_EM_Main()12308 void GameActions_EM_Main()
12309 {
12310 byte effective_action[MAX_PLAYERS];
12311 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12312 int i;
12313
12314 for (i = 0; i < MAX_PLAYERS; i++)
12315 effective_action[i] = stored_player[i].effective_action;
12316
12317 GameActions_EM(effective_action, warp_mode);
12318
12319 CheckLevelTime();
12320
12321 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12322 }
12323
GameActions_SP_Main()12324 void GameActions_SP_Main()
12325 {
12326 byte effective_action[MAX_PLAYERS];
12327 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12328 int i;
12329
12330 for (i = 0; i < MAX_PLAYERS; i++)
12331 effective_action[i] = stored_player[i].effective_action;
12332
12333 GameActions_SP(effective_action, warp_mode);
12334
12335 CheckLevelTime();
12336
12337 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12338 }
12339
GameActions_RND()12340 void GameActions_RND()
12341 {
12342 int magic_wall_x = 0, magic_wall_y = 0;
12343 int i, x, y, element, graphic;
12344
12345 InitPlayfieldScanModeVars();
12346
12347 #if USE_ONE_MORE_CHANGE_PER_FRAME
12348 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12349 {
12350 SCAN_PLAYFIELD(x, y)
12351 {
12352 ChangeCount[x][y] = 0;
12353 ChangeEvent[x][y] = -1;
12354 }
12355 }
12356 #endif
12357
12358 if (game.set_centered_player)
12359 {
12360 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12361
12362 /* switching to "all players" only possible if all players fit to screen */
12363 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12364 {
12365 game.centered_player_nr_next = game.centered_player_nr;
12366 game.set_centered_player = FALSE;
12367 }
12368
12369 /* do not switch focus to non-existing (or non-active) player */
12370 if (game.centered_player_nr_next >= 0 &&
12371 !stored_player[game.centered_player_nr_next].active)
12372 {
12373 game.centered_player_nr_next = game.centered_player_nr;
12374 game.set_centered_player = FALSE;
12375 }
12376 }
12377
12378 if (game.set_centered_player &&
12379 ScreenMovPos == 0) /* screen currently aligned at tile position */
12380 {
12381 int sx, sy;
12382
12383 if (game.centered_player_nr_next == -1)
12384 {
12385 setScreenCenteredToAllPlayers(&sx, &sy);
12386 }
12387 else
12388 {
12389 sx = stored_player[game.centered_player_nr_next].jx;
12390 sy = stored_player[game.centered_player_nr_next].jy;
12391 }
12392
12393 game.centered_player_nr = game.centered_player_nr_next;
12394 game.set_centered_player = FALSE;
12395
12396 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12397 DrawGameDoorValues();
12398 }
12399
12400 for (i = 0; i < MAX_PLAYERS; i++)
12401 {
12402 int actual_player_action = stored_player[i].effective_action;
12403
12404 #if 1
12405 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12406 - rnd_equinox_tetrachloride 048
12407 - rnd_equinox_tetrachloride_ii 096
12408 - rnd_emanuel_schmieg 002
12409 - doctor_sloan_ww 001, 020
12410 */
12411 if (stored_player[i].MovPos == 0)
12412 CheckGravityMovement(&stored_player[i]);
12413 #endif
12414
12415 /* overwrite programmed action with tape action */
12416 if (stored_player[i].programmed_action)
12417 actual_player_action = stored_player[i].programmed_action;
12418
12419 PlayerActions(&stored_player[i], actual_player_action);
12420
12421 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12422 }
12423
12424 ScrollScreen(NULL, SCROLL_GO_ON);
12425
12426 /* for backwards compatibility, the following code emulates a fixed bug that
12427 occured when pushing elements (causing elements that just made their last
12428 pushing step to already (if possible) make their first falling step in the
12429 same game frame, which is bad); this code is also needed to use the famous
12430 "spring push bug" which is used in older levels and might be wanted to be
12431 used also in newer levels, but in this case the buggy pushing code is only
12432 affecting the "spring" element and no other elements */
12433
12434 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12435 {
12436 for (i = 0; i < MAX_PLAYERS; i++)
12437 {
12438 struct PlayerInfo *player = &stored_player[i];
12439 int x = player->jx;
12440 int y = player->jy;
12441
12442 if (player->active && player->is_pushing && player->is_moving &&
12443 IS_MOVING(x, y) &&
12444 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12445 Feld[x][y] == EL_SPRING))
12446 {
12447 ContinueMoving(x, y);
12448
12449 /* continue moving after pushing (this is actually a bug) */
12450 if (!IS_MOVING(x, y))
12451 Stop[x][y] = FALSE;
12452 }
12453 }
12454 }
12455
12456 #if 0
12457 debug_print_timestamp(0, "start main loop profiling");
12458 #endif
12459
12460 SCAN_PLAYFIELD(x, y)
12461 {
12462 ChangeCount[x][y] = 0;
12463 ChangeEvent[x][y] = -1;
12464
12465 /* this must be handled before main playfield loop */
12466 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12467 {
12468 MovDelay[x][y]--;
12469 if (MovDelay[x][y] <= 0)
12470 RemoveField(x, y);
12471 }
12472
12473 #if USE_NEW_SNAP_DELAY
12474 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12475 {
12476 MovDelay[x][y]--;
12477 if (MovDelay[x][y] <= 0)
12478 {
12479 RemoveField(x, y);
12480 TEST_DrawLevelField(x, y);
12481
12482 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12483 }
12484 }
12485 #endif
12486
12487 #if DEBUG
12488 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12489 {
12490 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12491 printf("GameActions(): This should never happen!\n");
12492
12493 ChangePage[x][y] = -1;
12494 }
12495 #endif
12496
12497 Stop[x][y] = FALSE;
12498 if (WasJustMoving[x][y] > 0)
12499 WasJustMoving[x][y]--;
12500 if (WasJustFalling[x][y] > 0)
12501 WasJustFalling[x][y]--;
12502 if (CheckCollision[x][y] > 0)
12503 CheckCollision[x][y]--;
12504 if (CheckImpact[x][y] > 0)
12505 CheckImpact[x][y]--;
12506
12507 GfxFrame[x][y]++;
12508
12509 /* reset finished pushing action (not done in ContinueMoving() to allow
12510 continuous pushing animation for elements with zero push delay) */
12511 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12512 {
12513 ResetGfxAnimation(x, y);
12514 TEST_DrawLevelField(x, y);
12515 }
12516
12517 #if DEBUG
12518 if (IS_BLOCKED(x, y))
12519 {
12520 int oldx, oldy;
12521
12522 Blocked2Moving(x, y, &oldx, &oldy);
12523 if (!IS_MOVING(oldx, oldy))
12524 {
12525 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12526 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12527 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12528 printf("GameActions(): This should never happen!\n");
12529 }
12530 }
12531 #endif
12532 }
12533
12534 #if 0
12535 debug_print_timestamp(0, "- time for pre-main loop:");
12536 #endif
12537
12538 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12539 SCAN_PLAYFIELD(x, y)
12540 {
12541 element = Feld[x][y];
12542 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12543
12544 #if 1
12545 {
12546 #if 1
12547 int element2 = element;
12548 int graphic2 = graphic;
12549 #else
12550 int element2 = Feld[x][y];
12551 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12552 #endif
12553 int last_gfx_frame = GfxFrame[x][y];
12554
12555 if (graphic_info[graphic2].anim_global_sync)
12556 GfxFrame[x][y] = FrameCounter;
12557 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12558 GfxFrame[x][y] = CustomValue[x][y];
12559 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12560 GfxFrame[x][y] = element_info[element2].collect_score;
12561 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12562 GfxFrame[x][y] = ChangeDelay[x][y];
12563
12564 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12565 DrawLevelGraphicAnimation(x, y, graphic2);
12566 }
12567 #else
12568 ResetGfxFrame(x, y, TRUE);
12569 #endif
12570
12571 #if 1
12572 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12573 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12574 ResetRandomAnimationValue(x, y);
12575 #endif
12576
12577 #if 1
12578 SetRandomAnimationValue(x, y);
12579 #endif
12580
12581 #if 1
12582 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12583 #endif
12584 }
12585 #endif // -------------------- !!! TEST ONLY !!! --------------------
12586
12587 #if 0
12588 debug_print_timestamp(0, "- time for TEST loop: -->");
12589 #endif
12590
12591 SCAN_PLAYFIELD(x, y)
12592 {
12593 element = Feld[x][y];
12594 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12595
12596 ResetGfxFrame(x, y, TRUE);
12597
12598 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12599 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12600 ResetRandomAnimationValue(x, y);
12601
12602 SetRandomAnimationValue(x, y);
12603
12604 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12605
12606 if (IS_INACTIVE(element))
12607 {
12608 if (IS_ANIMATED(graphic))
12609 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12610
12611 continue;
12612 }
12613
12614 /* this may take place after moving, so 'element' may have changed */
12615 if (IS_CHANGING(x, y) &&
12616 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12617 {
12618 int page = element_info[element].event_page_nr[CE_DELAY];
12619
12620 #if 1
12621 HandleElementChange(x, y, page);
12622 #else
12623 if (CAN_CHANGE(element))
12624 HandleElementChange(x, y, page);
12625
12626 if (HAS_ACTION(element))
12627 ExecuteCustomElementAction(x, y, element, page);
12628 #endif
12629
12630 element = Feld[x][y];
12631 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12632 }
12633
12634 #if 0 // ---------------------------------------------------------------------
12635
12636 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12637 {
12638 StartMoving(x, y);
12639
12640 element = Feld[x][y];
12641 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12642
12643 if (IS_ANIMATED(graphic) &&
12644 !IS_MOVING(x, y) &&
12645 !Stop[x][y])
12646 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12647
12648 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12649 TEST_DrawTwinkleOnField(x, y);
12650 }
12651 else if (IS_MOVING(x, y))
12652 ContinueMoving(x, y);
12653 else
12654 {
12655 switch (element)
12656 {
12657 case EL_ACID:
12658 case EL_EXIT_OPEN:
12659 case EL_EM_EXIT_OPEN:
12660 case EL_SP_EXIT_OPEN:
12661 case EL_STEEL_EXIT_OPEN:
12662 case EL_EM_STEEL_EXIT_OPEN:
12663 case EL_SP_TERMINAL:
12664 case EL_SP_TERMINAL_ACTIVE:
12665 case EL_EXTRA_TIME:
12666 case EL_SHIELD_NORMAL:
12667 case EL_SHIELD_DEADLY:
12668 if (IS_ANIMATED(graphic))
12669 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12670 break;
12671
12672 case EL_DYNAMITE_ACTIVE:
12673 case EL_EM_DYNAMITE_ACTIVE:
12674 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12675 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12676 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12677 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12678 case EL_SP_DISK_RED_ACTIVE:
12679 CheckDynamite(x, y);
12680 break;
12681
12682 case EL_AMOEBA_GROWING:
12683 AmoebeWaechst(x, y);
12684 break;
12685
12686 case EL_AMOEBA_SHRINKING:
12687 AmoebaDisappearing(x, y);
12688 break;
12689
12690 #if !USE_NEW_AMOEBA_CODE
12691 case EL_AMOEBA_WET:
12692 case EL_AMOEBA_DRY:
12693 case EL_AMOEBA_FULL:
12694 case EL_BD_AMOEBA:
12695 case EL_EMC_DRIPPER:
12696 AmoebeAbleger(x, y);
12697 break;
12698 #endif
12699
12700 case EL_GAME_OF_LIFE:
12701 case EL_BIOMAZE:
12702 Life(x, y);
12703 break;
12704
12705 case EL_EXIT_CLOSED:
12706 CheckExit(x, y);
12707 break;
12708
12709 case EL_EM_EXIT_CLOSED:
12710 CheckExitEM(x, y);
12711 break;
12712
12713 case EL_STEEL_EXIT_CLOSED:
12714 CheckExitSteel(x, y);
12715 break;
12716
12717 case EL_EM_STEEL_EXIT_CLOSED:
12718 CheckExitSteelEM(x, y);
12719 break;
12720
12721 case EL_SP_EXIT_CLOSED:
12722 CheckExitSP(x, y);
12723 break;
12724
12725 case EL_EXPANDABLE_WALL_GROWING:
12726 case EL_EXPANDABLE_STEELWALL_GROWING:
12727 MauerWaechst(x, y);
12728 break;
12729
12730 case EL_EXPANDABLE_WALL:
12731 case EL_EXPANDABLE_WALL_HORIZONTAL:
12732 case EL_EXPANDABLE_WALL_VERTICAL:
12733 case EL_EXPANDABLE_WALL_ANY:
12734 case EL_BD_EXPANDABLE_WALL:
12735 MauerAbleger(x, y);
12736 break;
12737
12738 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12739 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12740 case EL_EXPANDABLE_STEELWALL_ANY:
12741 MauerAblegerStahl(x, y);
12742 break;
12743
12744 case EL_FLAMES:
12745 CheckForDragon(x, y);
12746 break;
12747
12748 case EL_EXPLOSION:
12749 break;
12750
12751 case EL_ELEMENT_SNAPPING:
12752 case EL_DIAGONAL_SHRINKING:
12753 case EL_DIAGONAL_GROWING:
12754 {
12755 graphic =
12756 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12757
12758 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12759 break;
12760 }
12761
12762 default:
12763 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12764 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12765 break;
12766 }
12767 }
12768
12769 #else // ---------------------------------------------------------------------
12770
12771 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12772 {
12773 StartMoving(x, y);
12774
12775 element = Feld[x][y];
12776 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12777
12778 if (IS_ANIMATED(graphic) &&
12779 !IS_MOVING(x, y) &&
12780 !Stop[x][y])
12781 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12782
12783 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12784 TEST_DrawTwinkleOnField(x, y);
12785 }
12786 else if ((element == EL_ACID ||
12787 element == EL_EXIT_OPEN ||
12788 element == EL_EM_EXIT_OPEN ||
12789 element == EL_SP_EXIT_OPEN ||
12790 element == EL_STEEL_EXIT_OPEN ||
12791 element == EL_EM_STEEL_EXIT_OPEN ||
12792 element == EL_SP_TERMINAL ||
12793 element == EL_SP_TERMINAL_ACTIVE ||
12794 element == EL_EXTRA_TIME ||
12795 element == EL_SHIELD_NORMAL ||
12796 element == EL_SHIELD_DEADLY) &&
12797 IS_ANIMATED(graphic))
12798 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12799 else if (IS_MOVING(x, y))
12800 ContinueMoving(x, y);
12801 else if (IS_ACTIVE_BOMB(element))
12802 CheckDynamite(x, y);
12803 else if (element == EL_AMOEBA_GROWING)
12804 AmoebeWaechst(x, y);
12805 else if (element == EL_AMOEBA_SHRINKING)
12806 AmoebaDisappearing(x, y);
12807
12808 #if !USE_NEW_AMOEBA_CODE
12809 else if (IS_AMOEBALIVE(element))
12810 AmoebeAbleger(x, y);
12811 #endif
12812
12813 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12814 Life(x, y);
12815 else if (element == EL_EXIT_CLOSED)
12816 CheckExit(x, y);
12817 else if (element == EL_EM_EXIT_CLOSED)
12818 CheckExitEM(x, y);
12819 else if (element == EL_STEEL_EXIT_CLOSED)
12820 CheckExitSteel(x, y);
12821 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12822 CheckExitSteelEM(x, y);
12823 else if (element == EL_SP_EXIT_CLOSED)
12824 CheckExitSP(x, y);
12825 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12826 element == EL_EXPANDABLE_STEELWALL_GROWING)
12827 MauerWaechst(x, y);
12828 else if (element == EL_EXPANDABLE_WALL ||
12829 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12830 element == EL_EXPANDABLE_WALL_VERTICAL ||
12831 element == EL_EXPANDABLE_WALL_ANY ||
12832 element == EL_BD_EXPANDABLE_WALL)
12833 MauerAbleger(x, y);
12834 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12835 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12836 element == EL_EXPANDABLE_STEELWALL_ANY)
12837 MauerAblegerStahl(x, y);
12838 else if (element == EL_FLAMES)
12839 CheckForDragon(x, y);
12840 else if (element == EL_EXPLOSION)
12841 ; /* drawing of correct explosion animation is handled separately */
12842 else if (element == EL_ELEMENT_SNAPPING ||
12843 element == EL_DIAGONAL_SHRINKING ||
12844 element == EL_DIAGONAL_GROWING)
12845 {
12846 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12847
12848 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12849 }
12850 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12852
12853 #endif // ---------------------------------------------------------------------
12854
12855 if (IS_BELT_ACTIVE(element))
12856 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12857
12858 if (game.magic_wall_active)
12859 {
12860 int jx = local_player->jx, jy = local_player->jy;
12861
12862 /* play the element sound at the position nearest to the player */
12863 if ((element == EL_MAGIC_WALL_FULL ||
12864 element == EL_MAGIC_WALL_ACTIVE ||
12865 element == EL_MAGIC_WALL_EMPTYING ||
12866 element == EL_BD_MAGIC_WALL_FULL ||
12867 element == EL_BD_MAGIC_WALL_ACTIVE ||
12868 element == EL_BD_MAGIC_WALL_EMPTYING ||
12869 element == EL_DC_MAGIC_WALL_FULL ||
12870 element == EL_DC_MAGIC_WALL_ACTIVE ||
12871 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12872 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12873 {
12874 magic_wall_x = x;
12875 magic_wall_y = y;
12876 }
12877 }
12878 }
12879
12880 #if 0
12881 debug_print_timestamp(0, "- time for MAIN loop: -->");
12882 #endif
12883
12884 #if USE_NEW_AMOEBA_CODE
12885 /* new experimental amoeba growth stuff */
12886 if (!(FrameCounter % 8))
12887 {
12888 static unsigned int random = 1684108901;
12889
12890 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12891 {
12892 x = RND(lev_fieldx);
12893 y = RND(lev_fieldy);
12894 element = Feld[x][y];
12895
12896 if (!IS_PLAYER(x,y) &&
12897 (element == EL_EMPTY ||
12898 CAN_GROW_INTO(element) ||
12899 element == EL_QUICKSAND_EMPTY ||
12900 element == EL_QUICKSAND_FAST_EMPTY ||
12901 element == EL_ACID_SPLASH_LEFT ||
12902 element == EL_ACID_SPLASH_RIGHT))
12903 {
12904 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12905 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12906 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12907 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12908 Feld[x][y] = EL_AMOEBA_DROP;
12909 }
12910
12911 random = random * 129 + 1;
12912 }
12913 }
12914 #endif
12915
12916 #if 0
12917 if (game.explosions_delayed)
12918 #endif
12919 {
12920 game.explosions_delayed = FALSE;
12921
12922 SCAN_PLAYFIELD(x, y)
12923 {
12924 element = Feld[x][y];
12925
12926 if (ExplodeField[x][y])
12927 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12928 else if (element == EL_EXPLOSION)
12929 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12930
12931 ExplodeField[x][y] = EX_TYPE_NONE;
12932 }
12933
12934 game.explosions_delayed = TRUE;
12935 }
12936
12937 if (game.magic_wall_active)
12938 {
12939 if (!(game.magic_wall_time_left % 4))
12940 {
12941 int element = Feld[magic_wall_x][magic_wall_y];
12942
12943 if (element == EL_BD_MAGIC_WALL_FULL ||
12944 element == EL_BD_MAGIC_WALL_ACTIVE ||
12945 element == EL_BD_MAGIC_WALL_EMPTYING)
12946 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12947 else if (element == EL_DC_MAGIC_WALL_FULL ||
12948 element == EL_DC_MAGIC_WALL_ACTIVE ||
12949 element == EL_DC_MAGIC_WALL_EMPTYING)
12950 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12951 else
12952 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12953 }
12954
12955 if (game.magic_wall_time_left > 0)
12956 {
12957 game.magic_wall_time_left--;
12958
12959 if (!game.magic_wall_time_left)
12960 {
12961 SCAN_PLAYFIELD(x, y)
12962 {
12963 element = Feld[x][y];
12964
12965 if (element == EL_MAGIC_WALL_ACTIVE ||
12966 element == EL_MAGIC_WALL_FULL)
12967 {
12968 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12969 TEST_DrawLevelField(x, y);
12970 }
12971 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12972 element == EL_BD_MAGIC_WALL_FULL)
12973 {
12974 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12975 TEST_DrawLevelField(x, y);
12976 }
12977 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12978 element == EL_DC_MAGIC_WALL_FULL)
12979 {
12980 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12981 TEST_DrawLevelField(x, y);
12982 }
12983 }
12984
12985 game.magic_wall_active = FALSE;
12986 }
12987 }
12988 }
12989
12990 if (game.light_time_left > 0)
12991 {
12992 game.light_time_left--;
12993
12994 if (game.light_time_left == 0)
12995 RedrawAllLightSwitchesAndInvisibleElements();
12996 }
12997
12998 if (game.timegate_time_left > 0)
12999 {
13000 game.timegate_time_left--;
13001
13002 if (game.timegate_time_left == 0)
13003 CloseAllOpenTimegates();
13004 }
13005
13006 if (game.lenses_time_left > 0)
13007 {
13008 game.lenses_time_left--;
13009
13010 if (game.lenses_time_left == 0)
13011 RedrawAllInvisibleElementsForLenses();
13012 }
13013
13014 if (game.magnify_time_left > 0)
13015 {
13016 game.magnify_time_left--;
13017
13018 if (game.magnify_time_left == 0)
13019 RedrawAllInvisibleElementsForMagnifier();
13020 }
13021
13022 for (i = 0; i < MAX_PLAYERS; i++)
13023 {
13024 struct PlayerInfo *player = &stored_player[i];
13025
13026 if (SHIELD_ON(player))
13027 {
13028 if (player->shield_deadly_time_left)
13029 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13030 else if (player->shield_normal_time_left)
13031 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13032 }
13033 }
13034
13035 #if USE_DELAYED_GFX_REDRAW
13036 SCAN_PLAYFIELD(x, y)
13037 {
13038 #if 1
13039 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13040 #else
13041 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13042 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13043 #endif
13044 {
13045 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13046 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13047
13048 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13049 DrawLevelField(x, y);
13050
13051 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13052 DrawLevelFieldCrumbled(x, y);
13053
13054 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13055 DrawLevelFieldCrumbledNeighbours(x, y);
13056
13057 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13058 DrawTwinkleOnField(x, y);
13059 }
13060
13061 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13062 }
13063 #endif
13064
13065 CheckLevelTime();
13066
13067 DrawAllPlayers();
13068 PlayAllPlayersSound();
13069
13070 if (options.debug) /* calculate frames per second */
13071 {
13072 static unsigned int fps_counter = 0;
13073 static int fps_frames = 0;
13074 unsigned int fps_delay_ms = Counter() - fps_counter;
13075
13076 fps_frames++;
13077
13078 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13079 {
13080 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13081
13082 fps_frames = 0;
13083 fps_counter = Counter();
13084 }
13085
13086 redraw_mask |= REDRAW_FPS;
13087 }
13088
13089 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13090
13091 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13092 {
13093 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13094
13095 local_player->show_envelope = 0;
13096 }
13097
13098 #if 0
13099 debug_print_timestamp(0, "stop main loop profiling ");
13100 printf("----------------------------------------------------------\n");
13101 #endif
13102
13103 /* use random number generator in every frame to make it less predictable */
13104 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13105 RND(1);
13106 }
13107
AllPlayersInSight(struct PlayerInfo * player,int x,int y)13108 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13109 {
13110 int min_x = x, min_y = y, max_x = x, max_y = y;
13111 int i;
13112
13113 for (i = 0; i < MAX_PLAYERS; i++)
13114 {
13115 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13116
13117 if (!stored_player[i].active || &stored_player[i] == player)
13118 continue;
13119
13120 min_x = MIN(min_x, jx);
13121 min_y = MIN(min_y, jy);
13122 max_x = MAX(max_x, jx);
13123 max_y = MAX(max_y, jy);
13124 }
13125
13126 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13127 }
13128
AllPlayersInVisibleScreen()13129 static boolean AllPlayersInVisibleScreen()
13130 {
13131 int i;
13132
13133 for (i = 0; i < MAX_PLAYERS; i++)
13134 {
13135 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13136
13137 if (!stored_player[i].active)
13138 continue;
13139
13140 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13141 return FALSE;
13142 }
13143
13144 return TRUE;
13145 }
13146
ScrollLevel(int dx,int dy)13147 void ScrollLevel(int dx, int dy)
13148 {
13149 #if 0
13150 /* (directly solved in BlitBitmap() now) */
13151 static Bitmap *bitmap_db_field2 = NULL;
13152 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13153 int x, y;
13154 #else
13155 int x, y;
13156 #endif
13157
13158 #if 0
13159 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13160 /* only horizontal XOR vertical scroll direction allowed */
13161 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13162 return;
13163 #endif
13164
13165 #if 0
13166 /* (directly solved in BlitBitmap() now) */
13167 if (bitmap_db_field2 == NULL)
13168 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13169
13170 /* needed when blitting directly to same bitmap -- should not be needed with
13171 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13172 BlitBitmap(drawto_field, bitmap_db_field2,
13173 FX + TILEX * (dx == -1) - softscroll_offset,
13174 FY + TILEY * (dy == -1) - softscroll_offset,
13175 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13176 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13177 FX + TILEX * (dx == 1) - softscroll_offset,
13178 FY + TILEY * (dy == 1) - softscroll_offset);
13179 BlitBitmap(bitmap_db_field2, drawto_field,
13180 FX + TILEX * (dx == 1) - softscroll_offset,
13181 FY + TILEY * (dy == 1) - softscroll_offset,
13182 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13183 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13184 FX + TILEX * (dx == 1) - softscroll_offset,
13185 FY + TILEY * (dy == 1) - softscroll_offset);
13186
13187 #else
13188
13189 #if 0
13190 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13191 int xsize = (BX2 - BX1 + 1);
13192 int ysize = (BY2 - BY1 + 1);
13193 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13194 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13195 int step = (start < end ? +1 : -1);
13196
13197 for (i = start; i != end; i += step)
13198 {
13199 BlitBitmap(drawto_field, drawto_field,
13200 FX + TILEX * (dx != 0 ? i + step : 0),
13201 FY + TILEY * (dy != 0 ? i + step : 0),
13202 TILEX * (dx != 0 ? 1 : xsize),
13203 TILEY * (dy != 0 ? 1 : ysize),
13204 FX + TILEX * (dx != 0 ? i : 0),
13205 FY + TILEY * (dy != 0 ? i : 0));
13206 }
13207
13208 #else
13209
13210 #if NEW_TILESIZE
13211 #if NEW_SCROLL
13212 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13213 #else
13214 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13215 #endif
13216 #else
13217 #if NEW_SCROLL
13218 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13219 #else
13220 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13221 #endif
13222 #endif
13223
13224 #if NEW_TILESIZE
13225 BlitBitmap(drawto_field, drawto_field,
13226 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13227 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13228 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13229 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13230 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13231 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13232 #else
13233 BlitBitmap(drawto_field, drawto_field,
13234 FX + TILEX * (dx == -1) - softscroll_offset,
13235 FY + TILEY * (dy == -1) - softscroll_offset,
13236 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13237 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13238 FX + TILEX * (dx == 1) - softscroll_offset,
13239 FY + TILEY * (dy == 1) - softscroll_offset);
13240 #endif
13241
13242 #endif
13243 #endif
13244
13245 if (dx != 0)
13246 {
13247 x = (dx == 1 ? BX1 : BX2);
13248 for (y = BY1; y <= BY2; y++)
13249 DrawScreenField(x, y);
13250 }
13251
13252 if (dy != 0)
13253 {
13254 y = (dy == 1 ? BY1 : BY2);
13255 for (x = BX1; x <= BX2; x++)
13256 DrawScreenField(x, y);
13257 }
13258
13259 redraw_mask |= REDRAW_FIELD;
13260 }
13261
canFallDown(struct PlayerInfo * player)13262 static boolean canFallDown(struct PlayerInfo *player)
13263 {
13264 int jx = player->jx, jy = player->jy;
13265
13266 return (IN_LEV_FIELD(jx, jy + 1) &&
13267 (IS_FREE(jx, jy + 1) ||
13268 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13269 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13270 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13271 }
13272
canPassField(int x,int y,int move_dir)13273 static boolean canPassField(int x, int y, int move_dir)
13274 {
13275 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13276 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13277 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13278 int nextx = x + dx;
13279 int nexty = y + dy;
13280 int element = Feld[x][y];
13281
13282 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13283 !CAN_MOVE(element) &&
13284 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13285 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13286 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13287 }
13288
canMoveToValidFieldWithGravity(int x,int y,int move_dir)13289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13290 {
13291 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13292 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13293 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13294 int newx = x + dx;
13295 int newy = y + dy;
13296
13297 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13298 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13299 (IS_DIGGABLE(Feld[newx][newy]) ||
13300 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13301 canPassField(newx, newy, move_dir)));
13302 }
13303
CheckGravityMovement(struct PlayerInfo * player)13304 static void CheckGravityMovement(struct PlayerInfo *player)
13305 {
13306 #if USE_PLAYER_GRAVITY
13307 if (player->gravity && !player->programmed_action)
13308 #else
13309 if (game.gravity && !player->programmed_action)
13310 #endif
13311 {
13312 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13313 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13314 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13315 int jx = player->jx, jy = player->jy;
13316 boolean player_is_moving_to_valid_field =
13317 (!player_is_snapping &&
13318 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13319 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13320 boolean player_can_fall_down = canFallDown(player);
13321
13322 if (player_can_fall_down &&
13323 !player_is_moving_to_valid_field)
13324 player->programmed_action = MV_DOWN;
13325 }
13326 }
13327
CheckGravityMovementWhenNotMoving(struct PlayerInfo * player)13328 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13329 {
13330 return CheckGravityMovement(player);
13331
13332 #if USE_PLAYER_GRAVITY
13333 if (player->gravity && !player->programmed_action)
13334 #else
13335 if (game.gravity && !player->programmed_action)
13336 #endif
13337 {
13338 int jx = player->jx, jy = player->jy;
13339 boolean field_under_player_is_free =
13340 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13341 boolean player_is_standing_on_valid_field =
13342 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13343 (IS_WALKABLE(Feld[jx][jy]) &&
13344 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13345
13346 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13347 player->programmed_action = MV_DOWN;
13348 }
13349 }
13350
13351 /*
13352 MovePlayerOneStep()
13353 -----------------------------------------------------------------------------
13354 dx, dy: direction (non-diagonal) to try to move the player to
13355 real_dx, real_dy: direction as read from input device (can be diagonal)
13356 */
13357
MovePlayerOneStep(struct PlayerInfo * player,int dx,int dy,int real_dx,int real_dy)13358 boolean MovePlayerOneStep(struct PlayerInfo *player,
13359 int dx, int dy, int real_dx, int real_dy)
13360 {
13361 int jx = player->jx, jy = player->jy;
13362 int new_jx = jx + dx, new_jy = jy + dy;
13363 #if !USE_FIXED_DONT_RUN_INTO
13364 int element;
13365 #endif
13366 int can_move;
13367 boolean player_can_move = !player->cannot_move;
13368
13369 if (!player->active || (!dx && !dy))
13370 return MP_NO_ACTION;
13371
13372 player->MovDir = (dx < 0 ? MV_LEFT :
13373 dx > 0 ? MV_RIGHT :
13374 dy < 0 ? MV_UP :
13375 dy > 0 ? MV_DOWN : MV_NONE);
13376
13377 if (!IN_LEV_FIELD(new_jx, new_jy))
13378 return MP_NO_ACTION;
13379
13380 if (!player_can_move)
13381 {
13382 if (player->MovPos == 0)
13383 {
13384 player->is_moving = FALSE;
13385 player->is_digging = FALSE;
13386 player->is_collecting = FALSE;
13387 player->is_snapping = FALSE;
13388 player->is_pushing = FALSE;
13389 }
13390 }
13391
13392 #if 1
13393 if (!options.network && game.centered_player_nr == -1 &&
13394 !AllPlayersInSight(player, new_jx, new_jy))
13395 return MP_NO_ACTION;
13396 #else
13397 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13398 return MP_NO_ACTION;
13399 #endif
13400
13401 #if !USE_FIXED_DONT_RUN_INTO
13402 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13403
13404 /* (moved to DigField()) */
13405 if (player_can_move && DONT_RUN_INTO(element))
13406 {
13407 if (element == EL_ACID && dx == 0 && dy == 1)
13408 {
13409 SplashAcid(new_jx, new_jy);
13410 Feld[jx][jy] = EL_PLAYER_1;
13411 InitMovingField(jx, jy, MV_DOWN);
13412 Store[jx][jy] = EL_ACID;
13413 ContinueMoving(jx, jy);
13414 BuryPlayer(player);
13415 }
13416 else
13417 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13418
13419 return MP_MOVING;
13420 }
13421 #endif
13422
13423 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13424 if (can_move != MP_MOVING)
13425 return can_move;
13426
13427 /* check if DigField() has caused relocation of the player */
13428 if (player->jx != jx || player->jy != jy)
13429 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13430
13431 StorePlayer[jx][jy] = 0;
13432 player->last_jx = jx;
13433 player->last_jy = jy;
13434 player->jx = new_jx;
13435 player->jy = new_jy;
13436 StorePlayer[new_jx][new_jy] = player->element_nr;
13437
13438 if (player->move_delay_value_next != -1)
13439 {
13440 player->move_delay_value = player->move_delay_value_next;
13441 player->move_delay_value_next = -1;
13442 }
13443
13444 player->MovPos =
13445 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13446
13447 player->step_counter++;
13448
13449 PlayerVisit[jx][jy] = FrameCounter;
13450
13451 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13452 player->is_moving = TRUE;
13453 #endif
13454
13455 #if 1
13456 /* should better be called in MovePlayer(), but this breaks some tapes */
13457 ScrollPlayer(player, SCROLL_INIT);
13458 #endif
13459
13460 return MP_MOVING;
13461 }
13462
MovePlayer(struct PlayerInfo * player,int dx,int dy)13463 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13464 {
13465 int jx = player->jx, jy = player->jy;
13466 int old_jx = jx, old_jy = jy;
13467 int moved = MP_NO_ACTION;
13468
13469 if (!player->active)
13470 return FALSE;
13471
13472 if (!dx && !dy)
13473 {
13474 if (player->MovPos == 0)
13475 {
13476 player->is_moving = FALSE;
13477 player->is_digging = FALSE;
13478 player->is_collecting = FALSE;
13479 player->is_snapping = FALSE;
13480 player->is_pushing = FALSE;
13481 }
13482
13483 return FALSE;
13484 }
13485
13486 if (player->move_delay > 0)
13487 return FALSE;
13488
13489 player->move_delay = -1; /* set to "uninitialized" value */
13490
13491 /* store if player is automatically moved to next field */
13492 player->is_auto_moving = (player->programmed_action != MV_NONE);
13493
13494 /* remove the last programmed player action */
13495 player->programmed_action = 0;
13496
13497 if (player->MovPos)
13498 {
13499 /* should only happen if pre-1.2 tape recordings are played */
13500 /* this is only for backward compatibility */
13501
13502 int original_move_delay_value = player->move_delay_value;
13503
13504 #if DEBUG
13505 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13506 tape.counter);
13507 #endif
13508
13509 /* scroll remaining steps with finest movement resolution */
13510 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13511
13512 while (player->MovPos)
13513 {
13514 ScrollPlayer(player, SCROLL_GO_ON);
13515 ScrollScreen(NULL, SCROLL_GO_ON);
13516
13517 AdvanceFrameAndPlayerCounters(player->index_nr);
13518
13519 DrawAllPlayers();
13520 BackToFront();
13521 }
13522
13523 player->move_delay_value = original_move_delay_value;
13524 }
13525
13526 player->is_active = FALSE;
13527
13528 if (player->last_move_dir & MV_HORIZONTAL)
13529 {
13530 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13531 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13532 }
13533 else
13534 {
13535 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13536 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13537 }
13538
13539 #if USE_FIXED_BORDER_RUNNING_GFX
13540 if (!moved && !player->is_active)
13541 {
13542 player->is_moving = FALSE;
13543 player->is_digging = FALSE;
13544 player->is_collecting = FALSE;
13545 player->is_snapping = FALSE;
13546 player->is_pushing = FALSE;
13547 }
13548 #endif
13549
13550 jx = player->jx;
13551 jy = player->jy;
13552
13553 #if 1
13554 if (moved & MP_MOVING && !ScreenMovPos &&
13555 (player->index_nr == game.centered_player_nr ||
13556 game.centered_player_nr == -1))
13557 #else
13558 if (moved & MP_MOVING && !ScreenMovPos &&
13559 (player == local_player || !options.network))
13560 #endif
13561 {
13562 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13563 int offset = game.scroll_delay_value;
13564
13565 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13566 {
13567 /* actual player has left the screen -- scroll in that direction */
13568 if (jx != old_jx) /* player has moved horizontally */
13569 scroll_x += (jx - old_jx);
13570 else /* player has moved vertically */
13571 scroll_y += (jy - old_jy);
13572 }
13573 else
13574 {
13575 if (jx != old_jx) /* player has moved horizontally */
13576 {
13577 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13578 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13579 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13580
13581 /* don't scroll over playfield boundaries */
13582 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13583 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13584
13585 /* don't scroll more than one field at a time */
13586 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13587
13588 /* don't scroll against the player's moving direction */
13589 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13590 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13591 scroll_x = old_scroll_x;
13592 }
13593 else /* player has moved vertically */
13594 {
13595 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13596 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13597 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13598
13599 /* don't scroll over playfield boundaries */
13600 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13601 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13602
13603 /* don't scroll more than one field at a time */
13604 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13605
13606 /* don't scroll against the player's moving direction */
13607 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13608 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13609 scroll_y = old_scroll_y;
13610 }
13611 }
13612
13613 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13614 {
13615 #if 1
13616 if (!options.network && game.centered_player_nr == -1 &&
13617 !AllPlayersInVisibleScreen())
13618 {
13619 scroll_x = old_scroll_x;
13620 scroll_y = old_scroll_y;
13621 }
13622 else
13623 #else
13624 if (!options.network && !AllPlayersInVisibleScreen())
13625 {
13626 scroll_x = old_scroll_x;
13627 scroll_y = old_scroll_y;
13628 }
13629 else
13630 #endif
13631 {
13632 ScrollScreen(player, SCROLL_INIT);
13633 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13634 }
13635 }
13636 }
13637
13638 player->StepFrame = 0;
13639
13640 if (moved & MP_MOVING)
13641 {
13642 if (old_jx != jx && old_jy == jy)
13643 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13644 else if (old_jx == jx && old_jy != jy)
13645 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13646
13647 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13648
13649 player->last_move_dir = player->MovDir;
13650 player->is_moving = TRUE;
13651 player->is_snapping = FALSE;
13652 player->is_switching = FALSE;
13653 player->is_dropping = FALSE;
13654 player->is_dropping_pressed = FALSE;
13655 player->drop_pressed_delay = 0;
13656
13657 #if 0
13658 /* should better be called here than above, but this breaks some tapes */
13659 ScrollPlayer(player, SCROLL_INIT);
13660 #endif
13661 }
13662 else
13663 {
13664 CheckGravityMovementWhenNotMoving(player);
13665
13666 player->is_moving = FALSE;
13667
13668 /* at this point, the player is allowed to move, but cannot move right now
13669 (e.g. because of something blocking the way) -- ensure that the player
13670 is also allowed to move in the next frame (in old versions before 3.1.1,
13671 the player was forced to wait again for eight frames before next try) */
13672
13673 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13674 player->move_delay = 0; /* allow direct movement in the next frame */
13675 }
13676
13677 if (player->move_delay == -1) /* not yet initialized by DigField() */
13678 player->move_delay = player->move_delay_value;
13679
13680 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13681 {
13682 TestIfPlayerTouchesBadThing(jx, jy);
13683 TestIfPlayerTouchesCustomElement(jx, jy);
13684 }
13685
13686 if (!player->active)
13687 RemovePlayer(player);
13688
13689 return moved;
13690 }
13691
ScrollPlayer(struct PlayerInfo * player,int mode)13692 void ScrollPlayer(struct PlayerInfo *player, int mode)
13693 {
13694 int jx = player->jx, jy = player->jy;
13695 int last_jx = player->last_jx, last_jy = player->last_jy;
13696 int move_stepsize = TILEX / player->move_delay_value;
13697
13698 #if USE_NEW_PLAYER_SPEED
13699 if (!player->active)
13700 return;
13701
13702 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13703 return;
13704 #else
13705 if (!player->active || player->MovPos == 0)
13706 return;
13707 #endif
13708
13709 if (mode == SCROLL_INIT)
13710 {
13711 player->actual_frame_counter = FrameCounter;
13712 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13713
13714 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13715 Feld[last_jx][last_jy] == EL_EMPTY)
13716 {
13717 int last_field_block_delay = 0; /* start with no blocking at all */
13718 int block_delay_adjustment = player->block_delay_adjustment;
13719
13720 /* if player blocks last field, add delay for exactly one move */
13721 if (player->block_last_field)
13722 {
13723 last_field_block_delay += player->move_delay_value;
13724
13725 /* when blocking enabled, prevent moving up despite gravity */
13726 #if USE_PLAYER_GRAVITY
13727 if (player->gravity && player->MovDir == MV_UP)
13728 block_delay_adjustment = -1;
13729 #else
13730 if (game.gravity && player->MovDir == MV_UP)
13731 block_delay_adjustment = -1;
13732 #endif
13733 }
13734
13735 /* add block delay adjustment (also possible when not blocking) */
13736 last_field_block_delay += block_delay_adjustment;
13737
13738 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13739 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13740 }
13741
13742 #if USE_NEW_PLAYER_SPEED
13743 if (player->MovPos != 0) /* player has not yet reached destination */
13744 return;
13745 #else
13746 return;
13747 #endif
13748 }
13749 else if (!FrameReached(&player->actual_frame_counter, 1))
13750 return;
13751
13752 #if USE_NEW_PLAYER_SPEED
13753 if (player->MovPos != 0)
13754 {
13755 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13756 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13757
13758 /* before DrawPlayer() to draw correct player graphic for this case */
13759 if (player->MovPos == 0)
13760 CheckGravityMovement(player);
13761 }
13762 #else
13763 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13764 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13765
13766 /* before DrawPlayer() to draw correct player graphic for this case */
13767 if (player->MovPos == 0)
13768 CheckGravityMovement(player);
13769 #endif
13770
13771 if (player->MovPos == 0) /* player reached destination field */
13772 {
13773 if (player->move_delay_reset_counter > 0)
13774 {
13775 player->move_delay_reset_counter--;
13776
13777 if (player->move_delay_reset_counter == 0)
13778 {
13779 /* continue with normal speed after quickly moving through gate */
13780 HALVE_PLAYER_SPEED(player);
13781
13782 /* be able to make the next move without delay */
13783 player->move_delay = 0;
13784 }
13785 }
13786
13787 player->last_jx = jx;
13788 player->last_jy = jy;
13789
13790 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13791 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13792 #if 1
13793 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13794 #endif
13795 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13796 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13797 #if 1
13798 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13799 #endif
13800 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13801 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13802 {
13803 DrawPlayer(player); /* needed here only to cleanup last field */
13804 RemovePlayer(player);
13805
13806 if (local_player->friends_still_needed == 0 ||
13807 IS_SP_ELEMENT(Feld[jx][jy]))
13808 PlayerWins(player);
13809 }
13810
13811 /* this breaks one level: "machine", level 000 */
13812 {
13813 int move_direction = player->MovDir;
13814 int enter_side = MV_DIR_OPPOSITE(move_direction);
13815 int leave_side = move_direction;
13816 int old_jx = last_jx;
13817 int old_jy = last_jy;
13818 int old_element = Feld[old_jx][old_jy];
13819 int new_element = Feld[jx][jy];
13820
13821 if (IS_CUSTOM_ELEMENT(old_element))
13822 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13823 CE_LEFT_BY_PLAYER,
13824 player->index_bit, leave_side);
13825
13826 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13827 CE_PLAYER_LEAVES_X,
13828 player->index_bit, leave_side);
13829
13830 if (IS_CUSTOM_ELEMENT(new_element))
13831 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13832 player->index_bit, enter_side);
13833
13834 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13835 CE_PLAYER_ENTERS_X,
13836 player->index_bit, enter_side);
13837
13838 #if USE_FIX_CE_ACTION_WITH_PLAYER
13839 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13840 CE_MOVE_OF_X, move_direction);
13841 #else
13842 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13843 CE_MOVE_OF_X, move_direction);
13844 #endif
13845 }
13846
13847 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13848 {
13849 TestIfPlayerTouchesBadThing(jx, jy);
13850 TestIfPlayerTouchesCustomElement(jx, jy);
13851
13852 /* needed because pushed element has not yet reached its destination,
13853 so it would trigger a change event at its previous field location */
13854 if (!player->is_pushing)
13855 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13856
13857 if (!player->active)
13858 RemovePlayer(player);
13859 }
13860
13861 if (!local_player->LevelSolved && level.use_step_counter)
13862 {
13863 int i;
13864
13865 TimePlayed++;
13866
13867 if (TimeLeft > 0)
13868 {
13869 TimeLeft--;
13870
13871 if (TimeLeft <= 10 && setup.time_limit)
13872 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13873
13874 #if 1
13875 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13876
13877 DisplayGameControlValues();
13878 #else
13879 DrawGameValue_Time(TimeLeft);
13880 #endif
13881
13882 if (!TimeLeft && setup.time_limit)
13883 for (i = 0; i < MAX_PLAYERS; i++)
13884 KillPlayer(&stored_player[i]);
13885 }
13886 #if 1
13887 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13888 {
13889 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13890
13891 DisplayGameControlValues();
13892 }
13893 #else
13894 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13895 DrawGameValue_Time(TimePlayed);
13896 #endif
13897 }
13898
13899 if (tape.single_step && tape.recording && !tape.pausing &&
13900 !player->programmed_action)
13901 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13902 }
13903 }
13904
ScrollScreen(struct PlayerInfo * player,int mode)13905 void ScrollScreen(struct PlayerInfo *player, int mode)
13906 {
13907 static unsigned int screen_frame_counter = 0;
13908
13909 if (mode == SCROLL_INIT)
13910 {
13911 /* set scrolling step size according to actual player's moving speed */
13912 ScrollStepSize = TILEX / player->move_delay_value;
13913
13914 screen_frame_counter = FrameCounter;
13915 ScreenMovDir = player->MovDir;
13916 ScreenMovPos = player->MovPos;
13917 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13918 return;
13919 }
13920 else if (!FrameReached(&screen_frame_counter, 1))
13921 return;
13922
13923 if (ScreenMovPos)
13924 {
13925 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13926 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13927 redraw_mask |= REDRAW_FIELD;
13928 }
13929 else
13930 ScreenMovDir = MV_NONE;
13931 }
13932
TestIfPlayerTouchesCustomElement(int x,int y)13933 void TestIfPlayerTouchesCustomElement(int x, int y)
13934 {
13935 static int xy[4][2] =
13936 {
13937 { 0, -1 },
13938 { -1, 0 },
13939 { +1, 0 },
13940 { 0, +1 }
13941 };
13942 static int trigger_sides[4][2] =
13943 {
13944 /* center side border side */
13945 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13946 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13947 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13948 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13949 };
13950 static int touch_dir[4] =
13951 {
13952 MV_LEFT | MV_RIGHT,
13953 MV_UP | MV_DOWN,
13954 MV_UP | MV_DOWN,
13955 MV_LEFT | MV_RIGHT
13956 };
13957 int center_element = Feld[x][y]; /* should always be non-moving! */
13958 int i;
13959
13960 for (i = 0; i < NUM_DIRECTIONS; i++)
13961 {
13962 int xx = x + xy[i][0];
13963 int yy = y + xy[i][1];
13964 int center_side = trigger_sides[i][0];
13965 int border_side = trigger_sides[i][1];
13966 int border_element;
13967
13968 if (!IN_LEV_FIELD(xx, yy))
13969 continue;
13970
13971 if (IS_PLAYER(x, y)) /* player found at center element */
13972 {
13973 struct PlayerInfo *player = PLAYERINFO(x, y);
13974
13975 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13976 border_element = Feld[xx][yy]; /* may be moving! */
13977 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13978 border_element = Feld[xx][yy];
13979 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13980 border_element = MovingOrBlocked2Element(xx, yy);
13981 else
13982 continue; /* center and border element do not touch */
13983
13984 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13985 player->index_bit, border_side);
13986 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13987 CE_PLAYER_TOUCHES_X,
13988 player->index_bit, border_side);
13989
13990 #if USE_FIX_CE_ACTION_WITH_PLAYER
13991 {
13992 /* use player element that is initially defined in the level playfield,
13993 not the player element that corresponds to the runtime player number
13994 (example: a level that contains EL_PLAYER_3 as the only player would
13995 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13996 int player_element = PLAYERINFO(x, y)->initial_element;
13997
13998 CheckElementChangeBySide(xx, yy, border_element, player_element,
13999 CE_TOUCHING_X, border_side);
14000 }
14001 #endif
14002 }
14003 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14004 {
14005 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14006
14007 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14008 {
14009 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14010 continue; /* center and border element do not touch */
14011 }
14012
14013 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14014 player->index_bit, center_side);
14015 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14016 CE_PLAYER_TOUCHES_X,
14017 player->index_bit, center_side);
14018
14019 #if USE_FIX_CE_ACTION_WITH_PLAYER
14020 {
14021 /* use player element that is initially defined in the level playfield,
14022 not the player element that corresponds to the runtime player number
14023 (example: a level that contains EL_PLAYER_3 as the only player would
14024 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14025 int player_element = PLAYERINFO(xx, yy)->initial_element;
14026
14027 CheckElementChangeBySide(x, y, center_element, player_element,
14028 CE_TOUCHING_X, center_side);
14029 }
14030 #endif
14031
14032 break;
14033 }
14034 }
14035 }
14036
14037 #if USE_ELEMENT_TOUCHING_BUGFIX
14038
TestIfElementTouchesCustomElement(int x,int y)14039 void TestIfElementTouchesCustomElement(int x, int y)
14040 {
14041 static int xy[4][2] =
14042 {
14043 { 0, -1 },
14044 { -1, 0 },
14045 { +1, 0 },
14046 { 0, +1 }
14047 };
14048 static int trigger_sides[4][2] =
14049 {
14050 /* center side border side */
14051 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14052 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14053 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14054 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14055 };
14056 static int touch_dir[4] =
14057 {
14058 MV_LEFT | MV_RIGHT,
14059 MV_UP | MV_DOWN,
14060 MV_UP | MV_DOWN,
14061 MV_LEFT | MV_RIGHT
14062 };
14063 boolean change_center_element = FALSE;
14064 int center_element = Feld[x][y]; /* should always be non-moving! */
14065 int border_element_old[NUM_DIRECTIONS];
14066 int i;
14067
14068 for (i = 0; i < NUM_DIRECTIONS; i++)
14069 {
14070 int xx = x + xy[i][0];
14071 int yy = y + xy[i][1];
14072 int border_element;
14073
14074 border_element_old[i] = -1;
14075
14076 if (!IN_LEV_FIELD(xx, yy))
14077 continue;
14078
14079 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14080 border_element = Feld[xx][yy]; /* may be moving! */
14081 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14082 border_element = Feld[xx][yy];
14083 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14084 border_element = MovingOrBlocked2Element(xx, yy);
14085 else
14086 continue; /* center and border element do not touch */
14087
14088 border_element_old[i] = border_element;
14089 }
14090
14091 for (i = 0; i < NUM_DIRECTIONS; i++)
14092 {
14093 int xx = x + xy[i][0];
14094 int yy = y + xy[i][1];
14095 int center_side = trigger_sides[i][0];
14096 int border_element = border_element_old[i];
14097
14098 if (border_element == -1)
14099 continue;
14100
14101 /* check for change of border element */
14102 CheckElementChangeBySide(xx, yy, border_element, center_element,
14103 CE_TOUCHING_X, center_side);
14104
14105 /* (center element cannot be player, so we dont have to check this here) */
14106 }
14107
14108 for (i = 0; i < NUM_DIRECTIONS; i++)
14109 {
14110 int xx = x + xy[i][0];
14111 int yy = y + xy[i][1];
14112 int border_side = trigger_sides[i][1];
14113 int border_element = border_element_old[i];
14114
14115 if (border_element == -1)
14116 continue;
14117
14118 /* check for change of center element (but change it only once) */
14119 if (!change_center_element)
14120 change_center_element =
14121 CheckElementChangeBySide(x, y, center_element, border_element,
14122 CE_TOUCHING_X, border_side);
14123
14124 #if USE_FIX_CE_ACTION_WITH_PLAYER
14125 if (IS_PLAYER(xx, yy))
14126 {
14127 /* use player element that is initially defined in the level playfield,
14128 not the player element that corresponds to the runtime player number
14129 (example: a level that contains EL_PLAYER_3 as the only player would
14130 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14131 int player_element = PLAYERINFO(xx, yy)->initial_element;
14132
14133 CheckElementChangeBySide(x, y, center_element, player_element,
14134 CE_TOUCHING_X, border_side);
14135 }
14136 #endif
14137 }
14138 }
14139
14140 #else
14141
TestIfElementTouchesCustomElement_OLD(int x,int y)14142 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14143 {
14144 static int xy[4][2] =
14145 {
14146 { 0, -1 },
14147 { -1, 0 },
14148 { +1, 0 },
14149 { 0, +1 }
14150 };
14151 static int trigger_sides[4][2] =
14152 {
14153 /* center side border side */
14154 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14155 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14156 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14157 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14158 };
14159 static int touch_dir[4] =
14160 {
14161 MV_LEFT | MV_RIGHT,
14162 MV_UP | MV_DOWN,
14163 MV_UP | MV_DOWN,
14164 MV_LEFT | MV_RIGHT
14165 };
14166 boolean change_center_element = FALSE;
14167 int center_element = Feld[x][y]; /* should always be non-moving! */
14168 int i;
14169
14170 for (i = 0; i < NUM_DIRECTIONS; i++)
14171 {
14172 int xx = x + xy[i][0];
14173 int yy = y + xy[i][1];
14174 int center_side = trigger_sides[i][0];
14175 int border_side = trigger_sides[i][1];
14176 int border_element;
14177
14178 if (!IN_LEV_FIELD(xx, yy))
14179 continue;
14180
14181 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14182 border_element = Feld[xx][yy]; /* may be moving! */
14183 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14184 border_element = Feld[xx][yy];
14185 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14186 border_element = MovingOrBlocked2Element(xx, yy);
14187 else
14188 continue; /* center and border element do not touch */
14189
14190 /* check for change of center element (but change it only once) */
14191 if (!change_center_element)
14192 change_center_element =
14193 CheckElementChangeBySide(x, y, center_element, border_element,
14194 CE_TOUCHING_X, border_side);
14195
14196 /* check for change of border element */
14197 CheckElementChangeBySide(xx, yy, border_element, center_element,
14198 CE_TOUCHING_X, center_side);
14199 }
14200 }
14201
14202 #endif
14203
TestIfElementHitsCustomElement(int x,int y,int direction)14204 void TestIfElementHitsCustomElement(int x, int y, int direction)
14205 {
14206 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14207 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14208 int hitx = x + dx, hity = y + dy;
14209 int hitting_element = Feld[x][y];
14210 int touched_element;
14211
14212 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14213 return;
14214
14215 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14216 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14217
14218 if (IN_LEV_FIELD(hitx, hity))
14219 {
14220 int opposite_direction = MV_DIR_OPPOSITE(direction);
14221 int hitting_side = direction;
14222 int touched_side = opposite_direction;
14223 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14224 MovDir[hitx][hity] != direction ||
14225 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14226
14227 object_hit = TRUE;
14228
14229 if (object_hit)
14230 {
14231 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14232 CE_HITTING_X, touched_side);
14233
14234 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14235 CE_HIT_BY_X, hitting_side);
14236
14237 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14238 CE_HIT_BY_SOMETHING, opposite_direction);
14239
14240 #if USE_FIX_CE_ACTION_WITH_PLAYER
14241 if (IS_PLAYER(hitx, hity))
14242 {
14243 /* use player element that is initially defined in the level playfield,
14244 not the player element that corresponds to the runtime player number
14245 (example: a level that contains EL_PLAYER_3 as the only player would
14246 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14247 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14248
14249 CheckElementChangeBySide(x, y, hitting_element, player_element,
14250 CE_HITTING_X, touched_side);
14251 }
14252 #endif
14253 }
14254 }
14255
14256 /* "hitting something" is also true when hitting the playfield border */
14257 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14258 CE_HITTING_SOMETHING, direction);
14259 }
14260
14261 #if 0
14262 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14263 {
14264 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14265 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14266 int hitx = x + dx, hity = y + dy;
14267 int hitting_element = Feld[x][y];
14268 int touched_element;
14269 #if 0
14270 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14271 !IS_FREE(hitx, hity) &&
14272 (!IS_MOVING(hitx, hity) ||
14273 MovDir[hitx][hity] != direction ||
14274 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14275 #endif
14276
14277 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14278 return;
14279
14280 #if 0
14281 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14282 return;
14283 #endif
14284
14285 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14286 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14287
14288 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14289 EP_CAN_SMASH_EVERYTHING, direction);
14290
14291 if (IN_LEV_FIELD(hitx, hity))
14292 {
14293 int opposite_direction = MV_DIR_OPPOSITE(direction);
14294 int hitting_side = direction;
14295 int touched_side = opposite_direction;
14296 #if 0
14297 int touched_element = MovingOrBlocked2Element(hitx, hity);
14298 #endif
14299 #if 1
14300 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14301 MovDir[hitx][hity] != direction ||
14302 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14303
14304 object_hit = TRUE;
14305 #endif
14306
14307 if (object_hit)
14308 {
14309 int i;
14310
14311 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14312 CE_SMASHED_BY_SOMETHING, opposite_direction);
14313
14314 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14315 CE_OTHER_IS_SMASHING, touched_side);
14316
14317 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14318 CE_OTHER_GETS_SMASHED, hitting_side);
14319 }
14320 }
14321 }
14322 #endif
14323
TestIfGoodThingHitsBadThing(int good_x,int good_y,int good_move_dir)14324 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14325 {
14326 int i, kill_x = -1, kill_y = -1;
14327
14328 int bad_element = -1;
14329 static int test_xy[4][2] =
14330 {
14331 { 0, -1 },
14332 { -1, 0 },
14333 { +1, 0 },
14334 { 0, +1 }
14335 };
14336 static int test_dir[4] =
14337 {
14338 MV_UP,
14339 MV_LEFT,
14340 MV_RIGHT,
14341 MV_DOWN
14342 };
14343
14344 for (i = 0; i < NUM_DIRECTIONS; i++)
14345 {
14346 int test_x, test_y, test_move_dir, test_element;
14347
14348 test_x = good_x + test_xy[i][0];
14349 test_y = good_y + test_xy[i][1];
14350
14351 if (!IN_LEV_FIELD(test_x, test_y))
14352 continue;
14353
14354 test_move_dir =
14355 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14356
14357 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14358
14359 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14360 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14361 */
14362 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14363 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14364 {
14365 kill_x = test_x;
14366 kill_y = test_y;
14367 bad_element = test_element;
14368
14369 break;
14370 }
14371 }
14372
14373 if (kill_x != -1 || kill_y != -1)
14374 {
14375 if (IS_PLAYER(good_x, good_y))
14376 {
14377 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14378
14379 if (player->shield_deadly_time_left > 0 &&
14380 !IS_INDESTRUCTIBLE(bad_element))
14381 Bang(kill_x, kill_y);
14382 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14383 KillPlayer(player);
14384 }
14385 else
14386 Bang(good_x, good_y);
14387 }
14388 }
14389
TestIfBadThingHitsGoodThing(int bad_x,int bad_y,int bad_move_dir)14390 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14391 {
14392 int i, kill_x = -1, kill_y = -1;
14393 int bad_element = Feld[bad_x][bad_y];
14394 static int test_xy[4][2] =
14395 {
14396 { 0, -1 },
14397 { -1, 0 },
14398 { +1, 0 },
14399 { 0, +1 }
14400 };
14401 static int touch_dir[4] =
14402 {
14403 MV_LEFT | MV_RIGHT,
14404 MV_UP | MV_DOWN,
14405 MV_UP | MV_DOWN,
14406 MV_LEFT | MV_RIGHT
14407 };
14408 static int test_dir[4] =
14409 {
14410 MV_UP,
14411 MV_LEFT,
14412 MV_RIGHT,
14413 MV_DOWN
14414 };
14415
14416 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14417 return;
14418
14419 for (i = 0; i < NUM_DIRECTIONS; i++)
14420 {
14421 int test_x, test_y, test_move_dir, test_element;
14422
14423 test_x = bad_x + test_xy[i][0];
14424 test_y = bad_y + test_xy[i][1];
14425
14426 if (!IN_LEV_FIELD(test_x, test_y))
14427 continue;
14428
14429 test_move_dir =
14430 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14431
14432 test_element = Feld[test_x][test_y];
14433
14434 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14435 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14436 */
14437 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14438 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14439 {
14440 /* good thing is player or penguin that does not move away */
14441 if (IS_PLAYER(test_x, test_y))
14442 {
14443 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14444
14445 if (bad_element == EL_ROBOT && player->is_moving)
14446 continue; /* robot does not kill player if he is moving */
14447
14448 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14449 {
14450 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14451 continue; /* center and border element do not touch */
14452 }
14453
14454 kill_x = test_x;
14455 kill_y = test_y;
14456
14457 break;
14458 }
14459 else if (test_element == EL_PENGUIN)
14460 {
14461 kill_x = test_x;
14462 kill_y = test_y;
14463
14464 break;
14465 }
14466 }
14467 }
14468
14469 if (kill_x != -1 || kill_y != -1)
14470 {
14471 if (IS_PLAYER(kill_x, kill_y))
14472 {
14473 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14474
14475 if (player->shield_deadly_time_left > 0 &&
14476 !IS_INDESTRUCTIBLE(bad_element))
14477 Bang(bad_x, bad_y);
14478 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14479 KillPlayer(player);
14480 }
14481 else
14482 Bang(kill_x, kill_y);
14483 }
14484 }
14485
TestIfGoodThingGetsHitByBadThing(int bad_x,int bad_y,int bad_move_dir)14486 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14487 {
14488 int bad_element = Feld[bad_x][bad_y];
14489 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14490 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14491 int test_x = bad_x + dx, test_y = bad_y + dy;
14492 int test_move_dir, test_element;
14493 int kill_x = -1, kill_y = -1;
14494
14495 if (!IN_LEV_FIELD(test_x, test_y))
14496 return;
14497
14498 test_move_dir =
14499 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14500
14501 test_element = Feld[test_x][test_y];
14502
14503 if (test_move_dir != bad_move_dir)
14504 {
14505 /* good thing can be player or penguin that does not move away */
14506 if (IS_PLAYER(test_x, test_y))
14507 {
14508 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14509
14510 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14511 player as being hit when he is moving towards the bad thing, because
14512 the "get hit by" condition would be lost after the player stops) */
14513 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14514 return; /* player moves away from bad thing */
14515
14516 kill_x = test_x;
14517 kill_y = test_y;
14518 }
14519 else if (test_element == EL_PENGUIN)
14520 {
14521 kill_x = test_x;
14522 kill_y = test_y;
14523 }
14524 }
14525
14526 if (kill_x != -1 || kill_y != -1)
14527 {
14528 if (IS_PLAYER(kill_x, kill_y))
14529 {
14530 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14531
14532 if (player->shield_deadly_time_left > 0 &&
14533 !IS_INDESTRUCTIBLE(bad_element))
14534 Bang(bad_x, bad_y);
14535 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14536 KillPlayer(player);
14537 }
14538 else
14539 Bang(kill_x, kill_y);
14540 }
14541 }
14542
TestIfPlayerTouchesBadThing(int x,int y)14543 void TestIfPlayerTouchesBadThing(int x, int y)
14544 {
14545 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14546 }
14547
TestIfPlayerRunsIntoBadThing(int x,int y,int move_dir)14548 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14549 {
14550 TestIfGoodThingHitsBadThing(x, y, move_dir);
14551 }
14552
TestIfBadThingTouchesPlayer(int x,int y)14553 void TestIfBadThingTouchesPlayer(int x, int y)
14554 {
14555 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14556 }
14557
TestIfBadThingRunsIntoPlayer(int x,int y,int move_dir)14558 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14559 {
14560 TestIfBadThingHitsGoodThing(x, y, move_dir);
14561 }
14562
TestIfFriendTouchesBadThing(int x,int y)14563 void TestIfFriendTouchesBadThing(int x, int y)
14564 {
14565 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14566 }
14567
TestIfBadThingTouchesFriend(int x,int y)14568 void TestIfBadThingTouchesFriend(int x, int y)
14569 {
14570 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14571 }
14572
TestIfBadThingTouchesOtherBadThing(int bad_x,int bad_y)14573 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14574 {
14575 int i, kill_x = bad_x, kill_y = bad_y;
14576 static int xy[4][2] =
14577 {
14578 { 0, -1 },
14579 { -1, 0 },
14580 { +1, 0 },
14581 { 0, +1 }
14582 };
14583
14584 for (i = 0; i < NUM_DIRECTIONS; i++)
14585 {
14586 int x, y, element;
14587
14588 x = bad_x + xy[i][0];
14589 y = bad_y + xy[i][1];
14590 if (!IN_LEV_FIELD(x, y))
14591 continue;
14592
14593 element = Feld[x][y];
14594 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14595 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14596 {
14597 kill_x = x;
14598 kill_y = y;
14599 break;
14600 }
14601 }
14602
14603 if (kill_x != bad_x || kill_y != bad_y)
14604 Bang(bad_x, bad_y);
14605 }
14606
KillPlayer(struct PlayerInfo * player)14607 void KillPlayer(struct PlayerInfo *player)
14608 {
14609 int jx = player->jx, jy = player->jy;
14610
14611 if (!player->active)
14612 return;
14613
14614 #if 0
14615 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14616 player->killed, player->active, player->reanimated);
14617 #endif
14618
14619 /* the following code was introduced to prevent an infinite loop when calling
14620 -> Bang()
14621 -> CheckTriggeredElementChangeExt()
14622 -> ExecuteCustomElementAction()
14623 -> KillPlayer()
14624 -> (infinitely repeating the above sequence of function calls)
14625 which occurs when killing the player while having a CE with the setting
14626 "kill player X when explosion of <player X>"; the solution using a new
14627 field "player->killed" was chosen for backwards compatibility, although
14628 clever use of the fields "player->active" etc. would probably also work */
14629 #if 1
14630 if (player->killed)
14631 return;
14632 #endif
14633
14634 player->killed = TRUE;
14635
14636 /* remove accessible field at the player's position */
14637 Feld[jx][jy] = EL_EMPTY;
14638
14639 /* deactivate shield (else Bang()/Explode() would not work right) */
14640 player->shield_normal_time_left = 0;
14641 player->shield_deadly_time_left = 0;
14642
14643 #if 0
14644 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14645 player->killed, player->active, player->reanimated);
14646 #endif
14647
14648 Bang(jx, jy);
14649
14650 #if 0
14651 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14652 player->killed, player->active, player->reanimated);
14653 #endif
14654
14655 #if USE_PLAYER_REANIMATION
14656 #if 1
14657 if (player->reanimated) /* killed player may have been reanimated */
14658 player->killed = player->reanimated = FALSE;
14659 else
14660 BuryPlayer(player);
14661 #else
14662 if (player->killed) /* player may have been reanimated */
14663 BuryPlayer(player);
14664 #endif
14665 #else
14666 BuryPlayer(player);
14667 #endif
14668 }
14669
KillPlayerUnlessEnemyProtected(int x,int y)14670 static void KillPlayerUnlessEnemyProtected(int x, int y)
14671 {
14672 if (!PLAYER_ENEMY_PROTECTED(x, y))
14673 KillPlayer(PLAYERINFO(x, y));
14674 }
14675
KillPlayerUnlessExplosionProtected(int x,int y)14676 static void KillPlayerUnlessExplosionProtected(int x, int y)
14677 {
14678 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14679 KillPlayer(PLAYERINFO(x, y));
14680 }
14681
BuryPlayer(struct PlayerInfo * player)14682 void BuryPlayer(struct PlayerInfo *player)
14683 {
14684 int jx = player->jx, jy = player->jy;
14685
14686 if (!player->active)
14687 return;
14688
14689 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14690 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14691
14692 player->GameOver = TRUE;
14693 RemovePlayer(player);
14694 }
14695
RemovePlayer(struct PlayerInfo * player)14696 void RemovePlayer(struct PlayerInfo *player)
14697 {
14698 int jx = player->jx, jy = player->jy;
14699 int i, found = FALSE;
14700
14701 player->present = FALSE;
14702 player->active = FALSE;
14703
14704 if (!ExplodeField[jx][jy])
14705 StorePlayer[jx][jy] = 0;
14706
14707 if (player->is_moving)
14708 TEST_DrawLevelField(player->last_jx, player->last_jy);
14709
14710 for (i = 0; i < MAX_PLAYERS; i++)
14711 if (stored_player[i].active)
14712 found = TRUE;
14713
14714 if (!found)
14715 AllPlayersGone = TRUE;
14716
14717 ExitX = ZX = jx;
14718 ExitY = ZY = jy;
14719 }
14720
14721 #if USE_NEW_SNAP_DELAY
setFieldForSnapping(int x,int y,int element,int direction)14722 static void setFieldForSnapping(int x, int y, int element, int direction)
14723 {
14724 struct ElementInfo *ei = &element_info[element];
14725 int direction_bit = MV_DIR_TO_BIT(direction);
14726 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14727 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14728 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14729
14730 Feld[x][y] = EL_ELEMENT_SNAPPING;
14731 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14732
14733 ResetGfxAnimation(x, y);
14734
14735 GfxElement[x][y] = element;
14736 GfxAction[x][y] = action;
14737 GfxDir[x][y] = direction;
14738 GfxFrame[x][y] = -1;
14739 }
14740 #endif
14741
14742 /*
14743 =============================================================================
14744 checkDiagonalPushing()
14745 -----------------------------------------------------------------------------
14746 check if diagonal input device direction results in pushing of object
14747 (by checking if the alternative direction is walkable, diggable, ...)
14748 =============================================================================
14749 */
14750
checkDiagonalPushing(struct PlayerInfo * player,int x,int y,int real_dx,int real_dy)14751 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14752 int x, int y, int real_dx, int real_dy)
14753 {
14754 int jx, jy, dx, dy, xx, yy;
14755
14756 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14757 return TRUE;
14758
14759 /* diagonal direction: check alternative direction */
14760 jx = player->jx;
14761 jy = player->jy;
14762 dx = x - jx;
14763 dy = y - jy;
14764 xx = jx + (dx == 0 ? real_dx : 0);
14765 yy = jy + (dy == 0 ? real_dy : 0);
14766
14767 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14768 }
14769
14770 /*
14771 =============================================================================
14772 DigField()
14773 -----------------------------------------------------------------------------
14774 x, y: field next to player (non-diagonal) to try to dig to
14775 real_dx, real_dy: direction as read from input device (can be diagonal)
14776 =============================================================================
14777 */
14778
DigField(struct PlayerInfo * player,int oldx,int oldy,int x,int y,int real_dx,int real_dy,int mode)14779 static int DigField(struct PlayerInfo *player,
14780 int oldx, int oldy, int x, int y,
14781 int real_dx, int real_dy, int mode)
14782 {
14783 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14784 boolean player_was_pushing = player->is_pushing;
14785 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14786 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14787 int jx = oldx, jy = oldy;
14788 int dx = x - jx, dy = y - jy;
14789 int nextx = x + dx, nexty = y + dy;
14790 int move_direction = (dx == -1 ? MV_LEFT :
14791 dx == +1 ? MV_RIGHT :
14792 dy == -1 ? MV_UP :
14793 dy == +1 ? MV_DOWN : MV_NONE);
14794 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14795 int dig_side = MV_DIR_OPPOSITE(move_direction);
14796 int old_element = Feld[jx][jy];
14797 #if USE_FIXED_DONT_RUN_INTO
14798 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14799 #else
14800 int element;
14801 #endif
14802 int collect_count;
14803
14804 if (is_player) /* function can also be called by EL_PENGUIN */
14805 {
14806 if (player->MovPos == 0)
14807 {
14808 player->is_digging = FALSE;
14809 player->is_collecting = FALSE;
14810 }
14811
14812 if (player->MovPos == 0) /* last pushing move finished */
14813 player->is_pushing = FALSE;
14814
14815 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14816 {
14817 player->is_switching = FALSE;
14818 player->push_delay = -1;
14819
14820 return MP_NO_ACTION;
14821 }
14822 }
14823
14824 #if !USE_FIXED_DONT_RUN_INTO
14825 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14826 return MP_NO_ACTION;
14827 #endif
14828
14829 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14830 old_element = Back[jx][jy];
14831
14832 /* in case of element dropped at player position, check background */
14833 else if (Back[jx][jy] != EL_EMPTY &&
14834 game.engine_version >= VERSION_IDENT(2,2,0,0))
14835 old_element = Back[jx][jy];
14836
14837 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14838 return MP_NO_ACTION; /* field has no opening in this direction */
14839
14840 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14841 return MP_NO_ACTION; /* field has no opening in this direction */
14842
14843 #if USE_FIXED_DONT_RUN_INTO
14844 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14845 {
14846 SplashAcid(x, y);
14847
14848 Feld[jx][jy] = player->artwork_element;
14849 InitMovingField(jx, jy, MV_DOWN);
14850 Store[jx][jy] = EL_ACID;
14851 ContinueMoving(jx, jy);
14852 BuryPlayer(player);
14853
14854 return MP_DONT_RUN_INTO;
14855 }
14856 #endif
14857
14858 #if USE_FIXED_DONT_RUN_INTO
14859 if (player_can_move && DONT_RUN_INTO(element))
14860 {
14861 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14862
14863 return MP_DONT_RUN_INTO;
14864 }
14865 #endif
14866
14867 #if USE_FIXED_DONT_RUN_INTO
14868 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14869 return MP_NO_ACTION;
14870 #endif
14871
14872 #if !USE_FIXED_DONT_RUN_INTO
14873 element = Feld[x][y];
14874 #endif
14875
14876 collect_count = element_info[element].collect_count_initial;
14877
14878 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14879 return MP_NO_ACTION;
14880
14881 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14882 player_can_move = player_can_move_or_snap;
14883
14884 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14885 game.engine_version >= VERSION_IDENT(2,2,0,0))
14886 {
14887 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14888 player->index_bit, dig_side);
14889 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14890 player->index_bit, dig_side);
14891
14892 if (element == EL_DC_LANDMINE)
14893 Bang(x, y);
14894
14895 if (Feld[x][y] != element) /* field changed by snapping */
14896 return MP_ACTION;
14897
14898 return MP_NO_ACTION;
14899 }
14900
14901 #if USE_PLAYER_GRAVITY
14902 if (player->gravity && is_player && !player->is_auto_moving &&
14903 canFallDown(player) && move_direction != MV_DOWN &&
14904 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14905 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14906 #else
14907 if (game.gravity && is_player && !player->is_auto_moving &&
14908 canFallDown(player) && move_direction != MV_DOWN &&
14909 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14910 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14911 #endif
14912
14913 if (player_can_move &&
14914 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14915 {
14916 int sound_element = SND_ELEMENT(element);
14917 int sound_action = ACTION_WALKING;
14918
14919 if (IS_RND_GATE(element))
14920 {
14921 if (!player->key[RND_GATE_NR(element)])
14922 return MP_NO_ACTION;
14923 }
14924 else if (IS_RND_GATE_GRAY(element))
14925 {
14926 if (!player->key[RND_GATE_GRAY_NR(element)])
14927 return MP_NO_ACTION;
14928 }
14929 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14930 {
14931 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14932 return MP_NO_ACTION;
14933 }
14934 else if (element == EL_EXIT_OPEN ||
14935 element == EL_EM_EXIT_OPEN ||
14936 #if 1
14937 element == EL_EM_EXIT_OPENING ||
14938 #endif
14939 element == EL_STEEL_EXIT_OPEN ||
14940 element == EL_EM_STEEL_EXIT_OPEN ||
14941 #if 1
14942 element == EL_EM_STEEL_EXIT_OPENING ||
14943 #endif
14944 element == EL_SP_EXIT_OPEN ||
14945 element == EL_SP_EXIT_OPENING)
14946 {
14947 sound_action = ACTION_PASSING; /* player is passing exit */
14948 }
14949 else if (element == EL_EMPTY)
14950 {
14951 sound_action = ACTION_MOVING; /* nothing to walk on */
14952 }
14953
14954 /* play sound from background or player, whatever is available */
14955 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14956 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14957 else
14958 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14959 }
14960 else if (player_can_move &&
14961 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14962 {
14963 if (!ACCESS_FROM(element, opposite_direction))
14964 return MP_NO_ACTION; /* field not accessible from this direction */
14965
14966 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14967 return MP_NO_ACTION;
14968
14969 if (IS_EM_GATE(element))
14970 {
14971 if (!player->key[EM_GATE_NR(element)])
14972 return MP_NO_ACTION;
14973 }
14974 else if (IS_EM_GATE_GRAY(element))
14975 {
14976 if (!player->key[EM_GATE_GRAY_NR(element)])
14977 return MP_NO_ACTION;
14978 }
14979 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14980 {
14981 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14982 return MP_NO_ACTION;
14983 }
14984 else if (IS_EMC_GATE(element))
14985 {
14986 if (!player->key[EMC_GATE_NR(element)])
14987 return MP_NO_ACTION;
14988 }
14989 else if (IS_EMC_GATE_GRAY(element))
14990 {
14991 if (!player->key[EMC_GATE_GRAY_NR(element)])
14992 return MP_NO_ACTION;
14993 }
14994 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14995 {
14996 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14997 return MP_NO_ACTION;
14998 }
14999 else if (element == EL_DC_GATE_WHITE ||
15000 element == EL_DC_GATE_WHITE_GRAY ||
15001 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15002 {
15003 if (player->num_white_keys == 0)
15004 return MP_NO_ACTION;
15005
15006 player->num_white_keys--;
15007 }
15008 else if (IS_SP_PORT(element))
15009 {
15010 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15011 element == EL_SP_GRAVITY_PORT_RIGHT ||
15012 element == EL_SP_GRAVITY_PORT_UP ||
15013 element == EL_SP_GRAVITY_PORT_DOWN)
15014 #if USE_PLAYER_GRAVITY
15015 player->gravity = !player->gravity;
15016 #else
15017 game.gravity = !game.gravity;
15018 #endif
15019 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15020 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15021 element == EL_SP_GRAVITY_ON_PORT_UP ||
15022 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15023 #if USE_PLAYER_GRAVITY
15024 player->gravity = TRUE;
15025 #else
15026 game.gravity = TRUE;
15027 #endif
15028 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15029 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15030 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15031 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15032 #if USE_PLAYER_GRAVITY
15033 player->gravity = FALSE;
15034 #else
15035 game.gravity = FALSE;
15036 #endif
15037 }
15038
15039 /* automatically move to the next field with double speed */
15040 player->programmed_action = move_direction;
15041
15042 if (player->move_delay_reset_counter == 0)
15043 {
15044 player->move_delay_reset_counter = 2; /* two double speed steps */
15045
15046 DOUBLE_PLAYER_SPEED(player);
15047 }
15048
15049 PlayLevelSoundAction(x, y, ACTION_PASSING);
15050 }
15051 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15052 {
15053 RemoveField(x, y);
15054
15055 if (mode != DF_SNAP)
15056 {
15057 GfxElement[x][y] = GFX_ELEMENT(element);
15058 player->is_digging = TRUE;
15059 }
15060
15061 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15062
15063 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15064 player->index_bit, dig_side);
15065
15066 if (mode == DF_SNAP)
15067 {
15068 #if USE_NEW_SNAP_DELAY
15069 if (level.block_snap_field)
15070 setFieldForSnapping(x, y, element, move_direction);
15071 else
15072 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15073 #else
15074 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15075 #endif
15076
15077 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15078 player->index_bit, dig_side);
15079 }
15080 }
15081 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15082 {
15083 RemoveField(x, y);
15084
15085 if (is_player && mode != DF_SNAP)
15086 {
15087 GfxElement[x][y] = element;
15088 player->is_collecting = TRUE;
15089 }
15090
15091 if (element == EL_SPEED_PILL)
15092 {
15093 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15094 }
15095 else if (element == EL_EXTRA_TIME && level.time > 0)
15096 {
15097 TimeLeft += level.extra_time;
15098
15099 #if 1
15100 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15101
15102 DisplayGameControlValues();
15103 #else
15104 DrawGameValue_Time(TimeLeft);
15105 #endif
15106 }
15107 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15108 {
15109 player->shield_normal_time_left += level.shield_normal_time;
15110 if (element == EL_SHIELD_DEADLY)
15111 player->shield_deadly_time_left += level.shield_deadly_time;
15112 }
15113 else if (element == EL_DYNAMITE ||
15114 element == EL_EM_DYNAMITE ||
15115 element == EL_SP_DISK_RED)
15116 {
15117 if (player->inventory_size < MAX_INVENTORY_SIZE)
15118 player->inventory_element[player->inventory_size++] = element;
15119
15120 DrawGameDoorValues();
15121 }
15122 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15123 {
15124 player->dynabomb_count++;
15125 player->dynabombs_left++;
15126 }
15127 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15128 {
15129 player->dynabomb_size++;
15130 }
15131 else if (element == EL_DYNABOMB_INCREASE_POWER)
15132 {
15133 player->dynabomb_xl = TRUE;
15134 }
15135 else if (IS_KEY(element))
15136 {
15137 player->key[KEY_NR(element)] = TRUE;
15138
15139 DrawGameDoorValues();
15140 }
15141 else if (element == EL_DC_KEY_WHITE)
15142 {
15143 player->num_white_keys++;
15144
15145 /* display white keys? */
15146 /* DrawGameDoorValues(); */
15147 }
15148 else if (IS_ENVELOPE(element))
15149 {
15150 player->show_envelope = element;
15151 }
15152 else if (element == EL_EMC_LENSES)
15153 {
15154 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15155
15156 RedrawAllInvisibleElementsForLenses();
15157 }
15158 else if (element == EL_EMC_MAGNIFIER)
15159 {
15160 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15161
15162 RedrawAllInvisibleElementsForMagnifier();
15163 }
15164 else if (IS_DROPPABLE(element) ||
15165 IS_THROWABLE(element)) /* can be collected and dropped */
15166 {
15167 int i;
15168
15169 if (collect_count == 0)
15170 player->inventory_infinite_element = element;
15171 else
15172 for (i = 0; i < collect_count; i++)
15173 if (player->inventory_size < MAX_INVENTORY_SIZE)
15174 player->inventory_element[player->inventory_size++] = element;
15175
15176 DrawGameDoorValues();
15177 }
15178 else if (collect_count > 0)
15179 {
15180 local_player->gems_still_needed -= collect_count;
15181 if (local_player->gems_still_needed < 0)
15182 local_player->gems_still_needed = 0;
15183
15184 #if 1
15185 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15186
15187 DisplayGameControlValues();
15188 #else
15189 DrawGameValue_Emeralds(local_player->gems_still_needed);
15190 #endif
15191 }
15192
15193 RaiseScoreElement(element);
15194 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15195
15196 if (is_player)
15197 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15198 player->index_bit, dig_side);
15199
15200 if (mode == DF_SNAP)
15201 {
15202 #if USE_NEW_SNAP_DELAY
15203 if (level.block_snap_field)
15204 setFieldForSnapping(x, y, element, move_direction);
15205 else
15206 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15207 #else
15208 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15209 #endif
15210
15211 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15212 player->index_bit, dig_side);
15213 }
15214 }
15215 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15216 {
15217 if (mode == DF_SNAP && element != EL_BD_ROCK)
15218 return MP_NO_ACTION;
15219
15220 if (CAN_FALL(element) && dy)
15221 return MP_NO_ACTION;
15222
15223 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15224 !(element == EL_SPRING && level.use_spring_bug))
15225 return MP_NO_ACTION;
15226
15227 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15228 ((move_direction & MV_VERTICAL &&
15229 ((element_info[element].move_pattern & MV_LEFT &&
15230 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15231 (element_info[element].move_pattern & MV_RIGHT &&
15232 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15233 (move_direction & MV_HORIZONTAL &&
15234 ((element_info[element].move_pattern & MV_UP &&
15235 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15236 (element_info[element].move_pattern & MV_DOWN &&
15237 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15238 return MP_NO_ACTION;
15239
15240 /* do not push elements already moving away faster than player */
15241 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15242 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15243 return MP_NO_ACTION;
15244
15245 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15246 {
15247 if (player->push_delay_value == -1 || !player_was_pushing)
15248 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15249 }
15250 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15251 {
15252 if (player->push_delay_value == -1)
15253 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15254 }
15255 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15256 {
15257 if (!player->is_pushing)
15258 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15259 }
15260
15261 player->is_pushing = TRUE;
15262 player->is_active = TRUE;
15263
15264 if (!(IN_LEV_FIELD(nextx, nexty) &&
15265 (IS_FREE(nextx, nexty) ||
15266 (IS_SB_ELEMENT(element) &&
15267 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15268 (IS_CUSTOM_ELEMENT(element) &&
15269 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15270 return MP_NO_ACTION;
15271
15272 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15273 return MP_NO_ACTION;
15274
15275 if (player->push_delay == -1) /* new pushing; restart delay */
15276 player->push_delay = 0;
15277
15278 if (player->push_delay < player->push_delay_value &&
15279 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15280 element != EL_SPRING && element != EL_BALLOON)
15281 {
15282 /* make sure that there is no move delay before next try to push */
15283 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15284 player->move_delay = 0;
15285
15286 return MP_NO_ACTION;
15287 }
15288
15289 if (IS_CUSTOM_ELEMENT(element) &&
15290 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15291 {
15292 if (!DigFieldByCE(nextx, nexty, element))
15293 return MP_NO_ACTION;
15294 }
15295
15296 if (IS_SB_ELEMENT(element))
15297 {
15298 if (element == EL_SOKOBAN_FIELD_FULL)
15299 {
15300 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15301 local_player->sokobanfields_still_needed++;
15302 }
15303
15304 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15305 {
15306 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15307 local_player->sokobanfields_still_needed--;
15308 }
15309
15310 Feld[x][y] = EL_SOKOBAN_OBJECT;
15311
15312 if (Back[x][y] == Back[nextx][nexty])
15313 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15314 else if (Back[x][y] != 0)
15315 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15316 ACTION_EMPTYING);
15317 else
15318 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15319 ACTION_FILLING);
15320
15321 #if 1
15322 if (local_player->sokobanfields_still_needed == 0 &&
15323 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15324 #else
15325 if (local_player->sokobanfields_still_needed == 0 &&
15326 game.emulation == EMU_SOKOBAN)
15327 #endif
15328 {
15329 PlayerWins(player);
15330
15331 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15332 }
15333 }
15334 else
15335 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15336
15337 InitMovingField(x, y, move_direction);
15338 GfxAction[x][y] = ACTION_PUSHING;
15339
15340 if (mode == DF_SNAP)
15341 ContinueMoving(x, y);
15342 else
15343 MovPos[x][y] = (dx != 0 ? dx : dy);
15344
15345 Pushed[x][y] = TRUE;
15346 Pushed[nextx][nexty] = TRUE;
15347
15348 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15349 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15350 else
15351 player->push_delay_value = -1; /* get new value later */
15352
15353 /* check for element change _after_ element has been pushed */
15354 if (game.use_change_when_pushing_bug)
15355 {
15356 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15357 player->index_bit, dig_side);
15358 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15359 player->index_bit, dig_side);
15360 }
15361 }
15362 else if (IS_SWITCHABLE(element))
15363 {
15364 if (PLAYER_SWITCHING(player, x, y))
15365 {
15366 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367 player->index_bit, dig_side);
15368
15369 return MP_ACTION;
15370 }
15371
15372 player->is_switching = TRUE;
15373 player->switch_x = x;
15374 player->switch_y = y;
15375
15376 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15377
15378 if (element == EL_ROBOT_WHEEL)
15379 {
15380 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15381 ZX = x;
15382 ZY = y;
15383
15384 game.robot_wheel_active = TRUE;
15385
15386 TEST_DrawLevelField(x, y);
15387 }
15388 else if (element == EL_SP_TERMINAL)
15389 {
15390 int xx, yy;
15391
15392 SCAN_PLAYFIELD(xx, yy)
15393 {
15394 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15395 Bang(xx, yy);
15396 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15397 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15398 }
15399 }
15400 else if (IS_BELT_SWITCH(element))
15401 {
15402 ToggleBeltSwitch(x, y);
15403 }
15404 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15405 element == EL_SWITCHGATE_SWITCH_DOWN ||
15406 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15407 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15408 {
15409 ToggleSwitchgateSwitch(x, y);
15410 }
15411 else if (element == EL_LIGHT_SWITCH ||
15412 element == EL_LIGHT_SWITCH_ACTIVE)
15413 {
15414 ToggleLightSwitch(x, y);
15415 }
15416 else if (element == EL_TIMEGATE_SWITCH ||
15417 element == EL_DC_TIMEGATE_SWITCH)
15418 {
15419 ActivateTimegateSwitch(x, y);
15420 }
15421 else if (element == EL_BALLOON_SWITCH_LEFT ||
15422 element == EL_BALLOON_SWITCH_RIGHT ||
15423 element == EL_BALLOON_SWITCH_UP ||
15424 element == EL_BALLOON_SWITCH_DOWN ||
15425 element == EL_BALLOON_SWITCH_NONE ||
15426 element == EL_BALLOON_SWITCH_ANY)
15427 {
15428 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15429 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15430 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15431 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15432 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15433 move_direction);
15434 }
15435 else if (element == EL_LAMP)
15436 {
15437 Feld[x][y] = EL_LAMP_ACTIVE;
15438 local_player->lights_still_needed--;
15439
15440 ResetGfxAnimation(x, y);
15441 TEST_DrawLevelField(x, y);
15442 }
15443 else if (element == EL_TIME_ORB_FULL)
15444 {
15445 Feld[x][y] = EL_TIME_ORB_EMPTY;
15446
15447 if (level.time > 0 || level.use_time_orb_bug)
15448 {
15449 TimeLeft += level.time_orb_time;
15450 game.no_time_limit = FALSE;
15451
15452 #if 1
15453 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15454
15455 DisplayGameControlValues();
15456 #else
15457 DrawGameValue_Time(TimeLeft);
15458 #endif
15459 }
15460
15461 ResetGfxAnimation(x, y);
15462 TEST_DrawLevelField(x, y);
15463 }
15464 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15465 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15466 {
15467 int xx, yy;
15468
15469 game.ball_state = !game.ball_state;
15470
15471 SCAN_PLAYFIELD(xx, yy)
15472 {
15473 int e = Feld[xx][yy];
15474
15475 if (game.ball_state)
15476 {
15477 if (e == EL_EMC_MAGIC_BALL)
15478 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15479 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15480 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15481 }
15482 else
15483 {
15484 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15485 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15486 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15487 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15488 }
15489 }
15490 }
15491
15492 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15493 player->index_bit, dig_side);
15494
15495 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15496 player->index_bit, dig_side);
15497
15498 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15499 player->index_bit, dig_side);
15500
15501 return MP_ACTION;
15502 }
15503 else
15504 {
15505 if (!PLAYER_SWITCHING(player, x, y))
15506 {
15507 player->is_switching = TRUE;
15508 player->switch_x = x;
15509 player->switch_y = y;
15510
15511 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15512 player->index_bit, dig_side);
15513 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15514 player->index_bit, dig_side);
15515
15516 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15517 player->index_bit, dig_side);
15518 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15519 player->index_bit, dig_side);
15520 }
15521
15522 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15523 player->index_bit, dig_side);
15524 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15525 player->index_bit, dig_side);
15526
15527 return MP_NO_ACTION;
15528 }
15529
15530 player->push_delay = -1;
15531
15532 if (is_player) /* function can also be called by EL_PENGUIN */
15533 {
15534 if (Feld[x][y] != element) /* really digged/collected something */
15535 {
15536 player->is_collecting = !player->is_digging;
15537 player->is_active = TRUE;
15538 }
15539 }
15540
15541 return MP_MOVING;
15542 }
15543
DigFieldByCE(int x,int y,int digging_element)15544 static boolean DigFieldByCE(int x, int y, int digging_element)
15545 {
15546 int element = Feld[x][y];
15547
15548 if (!IS_FREE(x, y))
15549 {
15550 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15551 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15552 ACTION_BREAKING);
15553
15554 /* no element can dig solid indestructible elements */
15555 if (IS_INDESTRUCTIBLE(element) &&
15556 !IS_DIGGABLE(element) &&
15557 !IS_COLLECTIBLE(element))
15558 return FALSE;
15559
15560 if (AmoebaNr[x][y] &&
15561 (element == EL_AMOEBA_FULL ||
15562 element == EL_BD_AMOEBA ||
15563 element == EL_AMOEBA_GROWING))
15564 {
15565 AmoebaCnt[AmoebaNr[x][y]]--;
15566 AmoebaCnt2[AmoebaNr[x][y]]--;
15567 }
15568
15569 if (IS_MOVING(x, y))
15570 RemoveMovingField(x, y);
15571 else
15572 {
15573 RemoveField(x, y);
15574 TEST_DrawLevelField(x, y);
15575 }
15576
15577 /* if digged element was about to explode, prevent the explosion */
15578 ExplodeField[x][y] = EX_TYPE_NONE;
15579
15580 PlayLevelSoundAction(x, y, action);
15581 }
15582
15583 Store[x][y] = EL_EMPTY;
15584
15585 #if 1
15586 /* this makes it possible to leave the removed element again */
15587 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15588 Store[x][y] = element;
15589 #else
15590 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15591 {
15592 int move_leave_element = element_info[digging_element].move_leave_element;
15593
15594 /* this makes it possible to leave the removed element again */
15595 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15596 element : move_leave_element);
15597 }
15598 #endif
15599
15600 return TRUE;
15601 }
15602
SnapField(struct PlayerInfo * player,int dx,int dy)15603 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15604 {
15605 int jx = player->jx, jy = player->jy;
15606 int x = jx + dx, y = jy + dy;
15607 int snap_direction = (dx == -1 ? MV_LEFT :
15608 dx == +1 ? MV_RIGHT :
15609 dy == -1 ? MV_UP :
15610 dy == +1 ? MV_DOWN : MV_NONE);
15611 boolean can_continue_snapping = (level.continuous_snapping &&
15612 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15613
15614 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15615 return FALSE;
15616
15617 if (!player->active || !IN_LEV_FIELD(x, y))
15618 return FALSE;
15619
15620 if (dx && dy)
15621 return FALSE;
15622
15623 if (!dx && !dy)
15624 {
15625 if (player->MovPos == 0)
15626 player->is_pushing = FALSE;
15627
15628 player->is_snapping = FALSE;
15629
15630 if (player->MovPos == 0)
15631 {
15632 player->is_moving = FALSE;
15633 player->is_digging = FALSE;
15634 player->is_collecting = FALSE;
15635 }
15636
15637 return FALSE;
15638 }
15639
15640 #if USE_NEW_CONTINUOUS_SNAPPING
15641 /* prevent snapping with already pressed snap key when not allowed */
15642 if (player->is_snapping && !can_continue_snapping)
15643 return FALSE;
15644 #else
15645 if (player->is_snapping)
15646 return FALSE;
15647 #endif
15648
15649 player->MovDir = snap_direction;
15650
15651 if (player->MovPos == 0)
15652 {
15653 player->is_moving = FALSE;
15654 player->is_digging = FALSE;
15655 player->is_collecting = FALSE;
15656 }
15657
15658 player->is_dropping = FALSE;
15659 player->is_dropping_pressed = FALSE;
15660 player->drop_pressed_delay = 0;
15661
15662 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15663 return FALSE;
15664
15665 player->is_snapping = TRUE;
15666 player->is_active = TRUE;
15667
15668 if (player->MovPos == 0)
15669 {
15670 player->is_moving = FALSE;
15671 player->is_digging = FALSE;
15672 player->is_collecting = FALSE;
15673 }
15674
15675 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15676 TEST_DrawLevelField(player->last_jx, player->last_jy);
15677
15678 TEST_DrawLevelField(x, y);
15679
15680 return TRUE;
15681 }
15682
DropElement(struct PlayerInfo * player)15683 static boolean DropElement(struct PlayerInfo *player)
15684 {
15685 int old_element, new_element;
15686 int dropx = player->jx, dropy = player->jy;
15687 int drop_direction = player->MovDir;
15688 int drop_side = drop_direction;
15689 #if 1
15690 int drop_element = get_next_dropped_element(player);
15691 #else
15692 int drop_element = (player->inventory_size > 0 ?
15693 player->inventory_element[player->inventory_size - 1] :
15694 player->inventory_infinite_element != EL_UNDEFINED ?
15695 player->inventory_infinite_element :
15696 player->dynabombs_left > 0 ?
15697 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15698 EL_UNDEFINED);
15699 #endif
15700
15701 player->is_dropping_pressed = TRUE;
15702
15703 /* do not drop an element on top of another element; when holding drop key
15704 pressed without moving, dropped element must move away before the next
15705 element can be dropped (this is especially important if the next element
15706 is dynamite, which can be placed on background for historical reasons) */
15707 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15708 return MP_ACTION;
15709
15710 if (IS_THROWABLE(drop_element))
15711 {
15712 dropx += GET_DX_FROM_DIR(drop_direction);
15713 dropy += GET_DY_FROM_DIR(drop_direction);
15714
15715 if (!IN_LEV_FIELD(dropx, dropy))
15716 return FALSE;
15717 }
15718
15719 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15720 new_element = drop_element; /* default: no change when dropping */
15721
15722 /* check if player is active, not moving and ready to drop */
15723 if (!player->active || player->MovPos || player->drop_delay > 0)
15724 return FALSE;
15725
15726 /* check if player has anything that can be dropped */
15727 if (new_element == EL_UNDEFINED)
15728 return FALSE;
15729
15730 /* check if drop key was pressed long enough for EM style dynamite */
15731 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15732 return FALSE;
15733
15734 /* check if anything can be dropped at the current position */
15735 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15736 return FALSE;
15737
15738 /* collected custom elements can only be dropped on empty fields */
15739 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15740 return FALSE;
15741
15742 if (old_element != EL_EMPTY)
15743 Back[dropx][dropy] = old_element; /* store old element on this field */
15744
15745 ResetGfxAnimation(dropx, dropy);
15746 ResetRandomAnimationValue(dropx, dropy);
15747
15748 if (player->inventory_size > 0 ||
15749 player->inventory_infinite_element != EL_UNDEFINED)
15750 {
15751 if (player->inventory_size > 0)
15752 {
15753 player->inventory_size--;
15754
15755 DrawGameDoorValues();
15756
15757 if (new_element == EL_DYNAMITE)
15758 new_element = EL_DYNAMITE_ACTIVE;
15759 else if (new_element == EL_EM_DYNAMITE)
15760 new_element = EL_EM_DYNAMITE_ACTIVE;
15761 else if (new_element == EL_SP_DISK_RED)
15762 new_element = EL_SP_DISK_RED_ACTIVE;
15763 }
15764
15765 Feld[dropx][dropy] = new_element;
15766
15767 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15768 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15769 el2img(Feld[dropx][dropy]), 0);
15770
15771 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15772
15773 /* needed if previous element just changed to "empty" in the last frame */
15774 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15775
15776 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15777 player->index_bit, drop_side);
15778 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15779 CE_PLAYER_DROPS_X,
15780 player->index_bit, drop_side);
15781
15782 TestIfElementTouchesCustomElement(dropx, dropy);
15783 }
15784 else /* player is dropping a dyna bomb */
15785 {
15786 player->dynabombs_left--;
15787
15788 Feld[dropx][dropy] = new_element;
15789
15790 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15791 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15792 el2img(Feld[dropx][dropy]), 0);
15793
15794 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15795 }
15796
15797 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15798 InitField_WithBug1(dropx, dropy, FALSE);
15799
15800 new_element = Feld[dropx][dropy]; /* element might have changed */
15801
15802 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15803 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15804 {
15805 #if 0
15806 int move_direction;
15807 int nextx, nexty;
15808 #endif
15809
15810 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15811 MovDir[dropx][dropy] = drop_direction;
15812
15813 #if 0
15814 move_direction = MovDir[dropx][dropy];
15815 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15816 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15817 #endif
15818
15819 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15820
15821 #if USE_FIX_IMPACT_COLLISION
15822 /* do not cause impact style collision by dropping elements that can fall */
15823 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15824 #else
15825 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15826 #endif
15827 }
15828
15829 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15830 player->is_dropping = TRUE;
15831
15832 player->drop_pressed_delay = 0;
15833 player->is_dropping_pressed = FALSE;
15834
15835 player->drop_x = dropx;
15836 player->drop_y = dropy;
15837
15838 return TRUE;
15839 }
15840
15841 /* ------------------------------------------------------------------------- */
15842 /* game sound playing functions */
15843 /* ------------------------------------------------------------------------- */
15844
15845 static int *loop_sound_frame = NULL;
15846 static int *loop_sound_volume = NULL;
15847
InitPlayLevelSound()15848 void InitPlayLevelSound()
15849 {
15850 int num_sounds = getSoundListSize();
15851
15852 checked_free(loop_sound_frame);
15853 checked_free(loop_sound_volume);
15854
15855 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15856 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15857 }
15858
PlayLevelSound(int x,int y,int nr)15859 static void PlayLevelSound(int x, int y, int nr)
15860 {
15861 int sx = SCREENX(x), sy = SCREENY(y);
15862 int volume, stereo_position;
15863 int max_distance = 8;
15864 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15865
15866 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15867 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15868 return;
15869
15870 if (!IN_LEV_FIELD(x, y) ||
15871 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15872 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15873 return;
15874
15875 volume = SOUND_MAX_VOLUME;
15876
15877 if (!IN_SCR_FIELD(sx, sy))
15878 {
15879 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15880 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15881
15882 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15883 }
15884
15885 stereo_position = (SOUND_MAX_LEFT +
15886 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15887 (SCR_FIELDX + 2 * max_distance));
15888
15889 if (IS_LOOP_SOUND(nr))
15890 {
15891 /* This assures that quieter loop sounds do not overwrite louder ones,
15892 while restarting sound volume comparison with each new game frame. */
15893
15894 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15895 return;
15896
15897 loop_sound_volume[nr] = volume;
15898 loop_sound_frame[nr] = FrameCounter;
15899 }
15900
15901 PlaySoundExt(nr, volume, stereo_position, type);
15902 }
15903
PlayLevelSoundNearest(int x,int y,int sound_action)15904 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15905 {
15906 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15907 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15908 y < LEVELY(BY1) ? LEVELY(BY1) :
15909 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15910 sound_action);
15911 }
15912
PlayLevelSoundAction(int x,int y,int action)15913 static void PlayLevelSoundAction(int x, int y, int action)
15914 {
15915 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15916 }
15917
PlayLevelSoundElementAction(int x,int y,int element,int action)15918 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15919 {
15920 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15921
15922 if (sound_effect != SND_UNDEFINED)
15923 PlayLevelSound(x, y, sound_effect);
15924 }
15925
PlayLevelSoundElementActionIfLoop(int x,int y,int element,int action)15926 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15927 int action)
15928 {
15929 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15930
15931 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15932 PlayLevelSound(x, y, sound_effect);
15933 }
15934
PlayLevelSoundActionIfLoop(int x,int y,int action)15935 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15936 {
15937 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15938
15939 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15940 PlayLevelSound(x, y, sound_effect);
15941 }
15942
StopLevelSoundActionIfLoop(int x,int y,int action)15943 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15944 {
15945 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15946
15947 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15948 StopSound(sound_effect);
15949 }
15950
PlayLevelMusic()15951 static void PlayLevelMusic()
15952 {
15953 if (levelset.music[level_nr] != MUS_UNDEFINED)
15954 PlayMusic(levelset.music[level_nr]); /* from config file */
15955 else
15956 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15957 }
15958
PlayLevelSound_EM(int xx,int yy,int element_em,int sample)15959 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15960 {
15961 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15962 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15963 int x = xx - 1 - offset;
15964 int y = yy - 1 - offset;
15965
15966 switch (sample)
15967 {
15968 case SAMPLE_blank:
15969 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15970 break;
15971
15972 case SAMPLE_roll:
15973 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15974 break;
15975
15976 case SAMPLE_stone:
15977 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15978 break;
15979
15980 case SAMPLE_nut:
15981 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15982 break;
15983
15984 case SAMPLE_crack:
15985 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15986 break;
15987
15988 case SAMPLE_bug:
15989 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15990 break;
15991
15992 case SAMPLE_tank:
15993 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15994 break;
15995
15996 case SAMPLE_android_clone:
15997 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15998 break;
15999
16000 case SAMPLE_android_move:
16001 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16002 break;
16003
16004 case SAMPLE_spring:
16005 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16006 break;
16007
16008 case SAMPLE_slurp:
16009 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16010 break;
16011
16012 case SAMPLE_eater:
16013 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16014 break;
16015
16016 case SAMPLE_eater_eat:
16017 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16018 break;
16019
16020 case SAMPLE_alien:
16021 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16022 break;
16023
16024 case SAMPLE_collect:
16025 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16026 break;
16027
16028 case SAMPLE_diamond:
16029 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16030 break;
16031
16032 case SAMPLE_squash:
16033 /* !!! CHECK THIS !!! */
16034 #if 1
16035 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16036 #else
16037 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16038 #endif
16039 break;
16040
16041 case SAMPLE_wonderfall:
16042 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16043 break;
16044
16045 case SAMPLE_drip:
16046 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16047 break;
16048
16049 case SAMPLE_push:
16050 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16051 break;
16052
16053 case SAMPLE_dirt:
16054 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16055 break;
16056
16057 case SAMPLE_acid:
16058 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16059 break;
16060
16061 case SAMPLE_ball:
16062 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16063 break;
16064
16065 case SAMPLE_grow:
16066 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16067 break;
16068
16069 case SAMPLE_wonder:
16070 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16071 break;
16072
16073 case SAMPLE_door:
16074 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16075 break;
16076
16077 case SAMPLE_exit_open:
16078 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16079 break;
16080
16081 case SAMPLE_exit_leave:
16082 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16083 break;
16084
16085 case SAMPLE_dynamite:
16086 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16087 break;
16088
16089 case SAMPLE_tick:
16090 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16091 break;
16092
16093 case SAMPLE_press:
16094 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16095 break;
16096
16097 case SAMPLE_wheel:
16098 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16099 break;
16100
16101 case SAMPLE_boom:
16102 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16103 break;
16104
16105 case SAMPLE_die:
16106 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16107 break;
16108
16109 case SAMPLE_time:
16110 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16111 break;
16112
16113 default:
16114 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16115 break;
16116 }
16117 }
16118
PlayLevelSound_SP(int xx,int yy,int element_sp,int action_sp)16119 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16120 {
16121 int element = map_element_SP_to_RND(element_sp);
16122 int action = map_action_SP_to_RND(action_sp);
16123 int offset = (setup.sp_show_border_elements ? 0 : 1);
16124 int x = xx - offset;
16125 int y = yy - offset;
16126
16127 #if 0
16128 printf("::: %d -> %d\n", element_sp, action_sp);
16129 #endif
16130
16131 PlayLevelSoundElementAction(x, y, element, action);
16132 }
16133
RaiseScore(int value)16134 void RaiseScore(int value)
16135 {
16136 local_player->score += value;
16137
16138 #if 1
16139 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16140
16141 DisplayGameControlValues();
16142 #else
16143 DrawGameValue_Score(local_player->score);
16144 #endif
16145 }
16146
RaiseScoreElement(int element)16147 void RaiseScoreElement(int element)
16148 {
16149 switch (element)
16150 {
16151 case EL_EMERALD:
16152 case EL_BD_DIAMOND:
16153 case EL_EMERALD_YELLOW:
16154 case EL_EMERALD_RED:
16155 case EL_EMERALD_PURPLE:
16156 case EL_SP_INFOTRON:
16157 RaiseScore(level.score[SC_EMERALD]);
16158 break;
16159 case EL_DIAMOND:
16160 RaiseScore(level.score[SC_DIAMOND]);
16161 break;
16162 case EL_CRYSTAL:
16163 RaiseScore(level.score[SC_CRYSTAL]);
16164 break;
16165 case EL_PEARL:
16166 RaiseScore(level.score[SC_PEARL]);
16167 break;
16168 case EL_BUG:
16169 case EL_BD_BUTTERFLY:
16170 case EL_SP_ELECTRON:
16171 RaiseScore(level.score[SC_BUG]);
16172 break;
16173 case EL_SPACESHIP:
16174 case EL_BD_FIREFLY:
16175 case EL_SP_SNIKSNAK:
16176 RaiseScore(level.score[SC_SPACESHIP]);
16177 break;
16178 case EL_YAMYAM:
16179 case EL_DARK_YAMYAM:
16180 RaiseScore(level.score[SC_YAMYAM]);
16181 break;
16182 case EL_ROBOT:
16183 RaiseScore(level.score[SC_ROBOT]);
16184 break;
16185 case EL_PACMAN:
16186 RaiseScore(level.score[SC_PACMAN]);
16187 break;
16188 case EL_NUT:
16189 RaiseScore(level.score[SC_NUT]);
16190 break;
16191 case EL_DYNAMITE:
16192 case EL_EM_DYNAMITE:
16193 case EL_SP_DISK_RED:
16194 case EL_DYNABOMB_INCREASE_NUMBER:
16195 case EL_DYNABOMB_INCREASE_SIZE:
16196 case EL_DYNABOMB_INCREASE_POWER:
16197 RaiseScore(level.score[SC_DYNAMITE]);
16198 break;
16199 case EL_SHIELD_NORMAL:
16200 case EL_SHIELD_DEADLY:
16201 RaiseScore(level.score[SC_SHIELD]);
16202 break;
16203 case EL_EXTRA_TIME:
16204 RaiseScore(level.extra_time_score);
16205 break;
16206 case EL_KEY_1:
16207 case EL_KEY_2:
16208 case EL_KEY_3:
16209 case EL_KEY_4:
16210 case EL_EM_KEY_1:
16211 case EL_EM_KEY_2:
16212 case EL_EM_KEY_3:
16213 case EL_EM_KEY_4:
16214 case EL_EMC_KEY_5:
16215 case EL_EMC_KEY_6:
16216 case EL_EMC_KEY_7:
16217 case EL_EMC_KEY_8:
16218 case EL_DC_KEY_WHITE:
16219 RaiseScore(level.score[SC_KEY]);
16220 break;
16221 default:
16222 RaiseScore(element_info[element].collect_score);
16223 break;
16224 }
16225 }
16226
RequestQuitGameExt(boolean skip_request,boolean quick_quit,char * message)16227 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16228 {
16229 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16230 {
16231 #if defined(NETWORK_AVALIABLE)
16232 if (options.network)
16233 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16234 else
16235 #endif
16236 {
16237 if (quick_quit)
16238 {
16239 #if 1
16240
16241 #if 1
16242 FadeSkipNextFadeIn();
16243 #else
16244 fading = fading_none;
16245 #endif
16246
16247 #else
16248 OpenDoor(DOOR_CLOSE_1);
16249 #endif
16250
16251 game_status = GAME_MODE_MAIN;
16252
16253 #if 1
16254 DrawAndFadeInMainMenu(REDRAW_FIELD);
16255 #else
16256 DrawMainMenu();
16257 #endif
16258 }
16259 else
16260 {
16261 #if 0
16262 FadeOut(REDRAW_FIELD);
16263 #endif
16264
16265 game_status = GAME_MODE_MAIN;
16266
16267 DrawAndFadeInMainMenu(REDRAW_FIELD);
16268 }
16269 }
16270 }
16271 else /* continue playing the game */
16272 {
16273 if (tape.playing && tape.deactivate_display)
16274 TapeDeactivateDisplayOff(TRUE);
16275
16276 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16277
16278 if (tape.playing && tape.deactivate_display)
16279 TapeDeactivateDisplayOn();
16280 }
16281 }
16282
RequestQuitGame(boolean ask_if_really_quit)16283 void RequestQuitGame(boolean ask_if_really_quit)
16284 {
16285 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16286 boolean skip_request = AllPlayersGone || quick_quit;
16287
16288 RequestQuitGameExt(skip_request, quick_quit,
16289 "Do you really want to quit the game ?");
16290 }
16291
16292
16293 /* ------------------------------------------------------------------------- */
16294 /* random generator functions */
16295 /* ------------------------------------------------------------------------- */
16296
InitEngineRandom_RND(int seed)16297 unsigned int InitEngineRandom_RND(int seed)
16298 {
16299 game.num_random_calls = 0;
16300
16301 #if 0
16302 unsigned int rnd_seed = InitEngineRandom(seed);
16303
16304 printf("::: START RND: %d\n", rnd_seed);
16305
16306 return rnd_seed;
16307 #else
16308
16309 return InitEngineRandom(seed);
16310
16311 #endif
16312
16313 }
16314
RND(int max)16315 unsigned int RND(int max)
16316 {
16317 if (max > 0)
16318 {
16319 game.num_random_calls++;
16320
16321 return GetEngineRandom(max);
16322 }
16323
16324 return 0;
16325 }
16326
16327
16328 /* ------------------------------------------------------------------------- */
16329 /* game engine snapshot handling functions */
16330 /* ------------------------------------------------------------------------- */
16331
16332 struct EngineSnapshotInfo
16333 {
16334 /* runtime values for custom element collect score */
16335 int collect_score[NUM_CUSTOM_ELEMENTS];
16336
16337 /* runtime values for group element choice position */
16338 int choice_pos[NUM_GROUP_ELEMENTS];
16339
16340 /* runtime values for belt position animations */
16341 int belt_graphic[4][NUM_BELT_PARTS];
16342 int belt_anim_mode[4][NUM_BELT_PARTS];
16343 };
16344
16345 static struct EngineSnapshotInfo engine_snapshot_rnd;
16346 static char *snapshot_level_identifier = NULL;
16347 static int snapshot_level_nr = -1;
16348
SaveEngineSnapshotValues_RND()16349 static void SaveEngineSnapshotValues_RND()
16350 {
16351 static int belt_base_active_element[4] =
16352 {
16353 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16354 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16355 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16356 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16357 };
16358 int i, j;
16359
16360 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16361 {
16362 int element = EL_CUSTOM_START + i;
16363
16364 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16365 }
16366
16367 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16368 {
16369 int element = EL_GROUP_START + i;
16370
16371 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16372 }
16373
16374 for (i = 0; i < 4; i++)
16375 {
16376 for (j = 0; j < NUM_BELT_PARTS; j++)
16377 {
16378 int element = belt_base_active_element[i] + j;
16379 int graphic = el2img(element);
16380 int anim_mode = graphic_info[graphic].anim_mode;
16381
16382 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16383 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16384 }
16385 }
16386 }
16387
LoadEngineSnapshotValues_RND()16388 static void LoadEngineSnapshotValues_RND()
16389 {
16390 unsigned int num_random_calls = game.num_random_calls;
16391 int i, j;
16392
16393 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16394 {
16395 int element = EL_CUSTOM_START + i;
16396
16397 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16398 }
16399
16400 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16401 {
16402 int element = EL_GROUP_START + i;
16403
16404 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16405 }
16406
16407 for (i = 0; i < 4; i++)
16408 {
16409 for (j = 0; j < NUM_BELT_PARTS; j++)
16410 {
16411 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16412 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16413
16414 graphic_info[graphic].anim_mode = anim_mode;
16415 }
16416 }
16417
16418 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16419 {
16420 InitRND(tape.random_seed);
16421 for (i = 0; i < num_random_calls; i++)
16422 RND(1);
16423 }
16424
16425 if (game.num_random_calls != num_random_calls)
16426 {
16427 Error(ERR_INFO, "number of random calls out of sync");
16428 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16429 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16430 Error(ERR_EXIT, "this should not happen -- please debug");
16431 }
16432 }
16433
SaveEngineSnapshot()16434 void SaveEngineSnapshot()
16435 {
16436 /* do not save snapshots from editor */
16437 if (level_editor_test_game)
16438 return;
16439
16440 /* free previous snapshot buffers, if needed */
16441 FreeEngineSnapshotBuffers();
16442
16443 /* copy some special values to a structure better suited for the snapshot */
16444
16445 SaveEngineSnapshotValues_RND();
16446 SaveEngineSnapshotValues_EM();
16447 SaveEngineSnapshotValues_SP();
16448
16449 /* save values stored in special snapshot structure */
16450
16451 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16452 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16453 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16454
16455 /* save further RND engine values */
16456
16457 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16458 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16459 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16460
16461 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16462 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16463 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16464 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16465
16466 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16467 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16468 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16469 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16470 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16471
16472 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16473 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16474 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16475
16476 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16477
16478 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16479
16480 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16481 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16482
16483 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16484 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16485 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16486 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16487 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16488 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16489 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16490 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16491 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16492 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16493 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16494 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16495 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16496 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16497 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16498 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16499 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16500 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16501
16502 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16503 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16504
16505 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16506 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16507 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16508
16509 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16511
16512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16513 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16514 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16516 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16517
16518 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16519 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16520
16521 /* save level identification information */
16522
16523 setString(&snapshot_level_identifier, leveldir_current->identifier);
16524 snapshot_level_nr = level_nr;
16525
16526 #if 0
16527 ListNode *node = engine_snapshot_list_rnd;
16528 int num_bytes = 0;
16529
16530 while (node != NULL)
16531 {
16532 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16533
16534 node = node->next;
16535 }
16536
16537 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16538 #endif
16539 }
16540
LoadEngineSnapshot()16541 void LoadEngineSnapshot()
16542 {
16543 /* restore generically stored snapshot buffers */
16544
16545 LoadEngineSnapshotBuffers();
16546
16547 /* restore special values from snapshot structure */
16548
16549 LoadEngineSnapshotValues_RND();
16550 LoadEngineSnapshotValues_EM();
16551 LoadEngineSnapshotValues_SP();
16552 }
16553
CheckEngineSnapshot()16554 boolean CheckEngineSnapshot()
16555 {
16556 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16557 snapshot_level_nr == level_nr);
16558 }
16559
16560
16561 /* ---------- new game button stuff ---------------------------------------- */
16562
16563 static struct
16564 {
16565 int graphic;
16566 struct Rect *pos;
16567 int gadget_id;
16568 char *infotext;
16569 } gamebutton_info[NUM_GAME_BUTTONS] =
16570 {
16571 {
16572 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16573 GAME_CTRL_ID_STOP, "stop game"
16574 },
16575 {
16576 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16577 GAME_CTRL_ID_PAUSE, "pause game"
16578 },
16579 {
16580 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16581 GAME_CTRL_ID_PLAY, "play game"
16582 },
16583 {
16584 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16585 SOUND_CTRL_ID_MUSIC, "background music on/off"
16586 },
16587 {
16588 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16589 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16590 },
16591 {
16592 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16593 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16594 }
16595 };
16596
CreateGameButtons()16597 void CreateGameButtons()
16598 {
16599 int i;
16600
16601 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16602 {
16603 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16604 struct Rect *pos = gamebutton_info[i].pos;
16605 struct GadgetInfo *gi;
16606 int button_type;
16607 boolean checked;
16608 unsigned int event_mask;
16609 int gd_x = gfx->src_x;
16610 int gd_y = gfx->src_y;
16611 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16612 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16613 int gd_xa = gfx->src_x + gfx->active_xoffset;
16614 int gd_ya = gfx->src_y + gfx->active_yoffset;
16615 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16616 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16617 int id = i;
16618
16619 if (id == GAME_CTRL_ID_STOP ||
16620 id == GAME_CTRL_ID_PAUSE ||
16621 id == GAME_CTRL_ID_PLAY)
16622 {
16623 button_type = GD_TYPE_NORMAL_BUTTON;
16624 checked = FALSE;
16625 event_mask = GD_EVENT_RELEASED;
16626 }
16627 else
16628 {
16629 button_type = GD_TYPE_CHECK_BUTTON;
16630 checked =
16631 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16632 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16633 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16634 event_mask = GD_EVENT_PRESSED;
16635 }
16636
16637 gi = CreateGadget(GDI_CUSTOM_ID, id,
16638 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16639 GDI_X, DX + pos->x,
16640 GDI_Y, DY + pos->y,
16641 GDI_WIDTH, gfx->width,
16642 GDI_HEIGHT, gfx->height,
16643 GDI_TYPE, button_type,
16644 GDI_STATE, GD_BUTTON_UNPRESSED,
16645 GDI_CHECKED, checked,
16646 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16647 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16648 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16649 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16650 GDI_DIRECT_DRAW, FALSE,
16651 GDI_EVENT_MASK, event_mask,
16652 GDI_CALLBACK_ACTION, HandleGameButtons,
16653 GDI_END);
16654
16655 if (gi == NULL)
16656 Error(ERR_EXIT, "cannot create gadget");
16657
16658 game_gadget[id] = gi;
16659 }
16660 }
16661
FreeGameButtons()16662 void FreeGameButtons()
16663 {
16664 int i;
16665
16666 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16667 FreeGadget(game_gadget[i]);
16668 }
16669
MapGameButtons()16670 static void MapGameButtons()
16671 {
16672 int i;
16673
16674 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16675 MapGadget(game_gadget[i]);
16676 }
16677
UnmapGameButtons()16678 void UnmapGameButtons()
16679 {
16680 int i;
16681
16682 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16683 UnmapGadget(game_gadget[i]);
16684 }
16685
RedrawGameButtons()16686 void RedrawGameButtons()
16687 {
16688 int i;
16689
16690 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16691 RedrawGadget(game_gadget[i]);
16692 }
16693
HandleGameButtonsExt(int id)16694 static void HandleGameButtonsExt(int id)
16695 {
16696 if (game_status != GAME_MODE_PLAYING)
16697 return;
16698
16699 switch (id)
16700 {
16701 case GAME_CTRL_ID_STOP:
16702 if (tape.playing)
16703 TapeStop();
16704 else
16705 RequestQuitGame(TRUE);
16706 break;
16707
16708 case GAME_CTRL_ID_PAUSE:
16709 if (options.network)
16710 {
16711 #if defined(NETWORK_AVALIABLE)
16712 if (tape.pausing)
16713 SendToServer_ContinuePlaying();
16714 else
16715 SendToServer_PausePlaying();
16716 #endif
16717 }
16718 else
16719 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16720 break;
16721
16722 case GAME_CTRL_ID_PLAY:
16723 if (tape.pausing)
16724 {
16725 #if defined(NETWORK_AVALIABLE)
16726 if (options.network)
16727 SendToServer_ContinuePlaying();
16728 else
16729 #endif
16730 {
16731 tape.pausing = FALSE;
16732 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16733 }
16734 }
16735 break;
16736
16737 case SOUND_CTRL_ID_MUSIC:
16738 if (setup.sound_music)
16739 {
16740 setup.sound_music = FALSE;
16741
16742 FadeMusic();
16743 }
16744 else if (audio.music_available)
16745 {
16746 setup.sound = setup.sound_music = TRUE;
16747
16748 SetAudioMode(setup.sound);
16749
16750 PlayLevelMusic();
16751 }
16752 break;
16753
16754 case SOUND_CTRL_ID_LOOPS:
16755 if (setup.sound_loops)
16756 setup.sound_loops = FALSE;
16757 else if (audio.loops_available)
16758 {
16759 setup.sound = setup.sound_loops = TRUE;
16760
16761 SetAudioMode(setup.sound);
16762 }
16763 break;
16764
16765 case SOUND_CTRL_ID_SIMPLE:
16766 if (setup.sound_simple)
16767 setup.sound_simple = FALSE;
16768 else if (audio.sound_available)
16769 {
16770 setup.sound = setup.sound_simple = TRUE;
16771
16772 SetAudioMode(setup.sound);
16773 }
16774 break;
16775
16776 default:
16777 break;
16778 }
16779 }
16780
HandleGameButtons(struct GadgetInfo * gi)16781 static void HandleGameButtons(struct GadgetInfo *gi)
16782 {
16783 HandleGameButtonsExt(gi->custom_id);
16784 }
16785
HandleSoundButtonKeys(Key key)16786 void HandleSoundButtonKeys(Key key)
16787 {
16788 #if 1
16789 if (key == setup.shortcut.sound_simple)
16790 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16791 else if (key == setup.shortcut.sound_loops)
16792 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16793 else if (key == setup.shortcut.sound_music)
16794 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16795 #else
16796 if (key == setup.shortcut.sound_simple)
16797 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16798 else if (key == setup.shortcut.sound_loops)
16799 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16800 else if (key == setup.shortcut.sound_music)
16801 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16802 #endif
16803 }
16804