b80d8585b9fc63889a907efb28bc5163715ea823
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
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 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
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(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (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             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 static void InitGameControlValues(void)
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 static void UpdatePlayfieldElementCount(void)
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 static void UpdateGameControlValues(void)
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
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                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* used instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 static void DisplayGameControlValues(void)
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues(void)
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 #if 0
2771 static void UpdateGameDoorValues(void)
2772 {
2773   UpdateGameControlValues();
2774 }
2775 #endif
2776
2777 void DrawGameDoorValues(void)
2778 {
2779   DisplayGameControlValues();
2780 }
2781
2782
2783 /*
2784   =============================================================================
2785   InitGameEngine()
2786   -----------------------------------------------------------------------------
2787   initialize game engine due to level / tape version number
2788   =============================================================================
2789 */
2790
2791 static void InitGameEngine(void)
2792 {
2793   int i, j, k, l, x, y;
2794
2795   /* set game engine from tape file when re-playing, else from level file */
2796   game.engine_version = (tape.playing ? tape.engine_version :
2797                          level.game_version);
2798
2799   /* set single or multi-player game mode (needed for re-playing tapes) */
2800   game.team_mode = setup.team_mode;
2801
2802   if (tape.playing)
2803   {
2804     int num_players = 0;
2805
2806     for (i = 0; i < MAX_PLAYERS; i++)
2807       if (tape.player_participates[i])
2808         num_players++;
2809
2810     /* multi-player tapes contain input data for more than one player */
2811     game.team_mode = (num_players > 1);
2812   }
2813
2814   /* ---------------------------------------------------------------------- */
2815   /* set flags for bugs and changes according to active game engine version */
2816   /* ---------------------------------------------------------------------- */
2817
2818   /*
2819     Summary of bugfix/change:
2820     Fixed handling for custom elements that change when pushed by the player.
2821
2822     Fixed/changed in version:
2823     3.1.0
2824
2825     Description:
2826     Before 3.1.0, custom elements that "change when pushing" changed directly
2827     after the player started pushing them (until then handled in "DigField()").
2828     Since 3.1.0, these custom elements are not changed until the "pushing"
2829     move of the element is finished (now handled in "ContinueMoving()").
2830
2831     Affected levels/tapes:
2832     The first condition is generally needed for all levels/tapes before version
2833     3.1.0, which might use the old behaviour before it was changed; known tapes
2834     that are affected are some tapes from the level set "Walpurgis Gardens" by
2835     Jamie Cullen.
2836     The second condition is an exception from the above case and is needed for
2837     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2838     above (including some development versions of 3.1.0), but before it was
2839     known that this change would break tapes like the above and was fixed in
2840     3.1.1, so that the changed behaviour was active although the engine version
2841     while recording maybe was before 3.1.0. There is at least one tape that is
2842     affected by this exception, which is the tape for the one-level set "Bug
2843     Machine" by Juergen Bonhagen.
2844   */
2845
2846   game.use_change_when_pushing_bug =
2847     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2848      !(tape.playing &&
2849        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2850        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2851
2852   /*
2853     Summary of bugfix/change:
2854     Fixed handling for blocking the field the player leaves when moving.
2855
2856     Fixed/changed in version:
2857     3.1.1
2858
2859     Description:
2860     Before 3.1.1, when "block last field when moving" was enabled, the field
2861     the player is leaving when moving was blocked for the time of the move,
2862     and was directly unblocked afterwards. This resulted in the last field
2863     being blocked for exactly one less than the number of frames of one player
2864     move. Additionally, even when blocking was disabled, the last field was
2865     blocked for exactly one frame.
2866     Since 3.1.1, due to changes in player movement handling, the last field
2867     is not blocked at all when blocking is disabled. When blocking is enabled,
2868     the last field is blocked for exactly the number of frames of one player
2869     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2870     last field is blocked for exactly one more than the number of frames of
2871     one player move.
2872
2873     Affected levels/tapes:
2874     (!!! yet to be determined -- probably many !!!)
2875   */
2876
2877   game.use_block_last_field_bug =
2878     (game.engine_version < VERSION_IDENT(3,1,1,0));
2879
2880   game_em.use_single_button =
2881     (game.engine_version > VERSION_IDENT(4,0,0,2));
2882
2883   game_em.use_snap_key_bug =
2884     (game.engine_version < VERSION_IDENT(4,0,1,0));
2885
2886   /* ---------------------------------------------------------------------- */
2887
2888   /* set maximal allowed number of custom element changes per game frame */
2889   game.max_num_changes_per_frame = 1;
2890
2891   /* default scan direction: scan playfield from top/left to bottom/right */
2892   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2893
2894   /* dynamically adjust element properties according to game engine version */
2895   InitElementPropertiesEngine(game.engine_version);
2896
2897 #if 0
2898   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2899   printf("          tape version == %06d [%s] [file: %06d]\n",
2900          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2901          tape.file_version);
2902   printf("       => game.engine_version == %06d\n", game.engine_version);
2903 #endif
2904
2905   /* ---------- initialize player's initial move delay --------------------- */
2906
2907   /* dynamically adjust player properties according to level information */
2908   for (i = 0; i < MAX_PLAYERS; i++)
2909     game.initial_move_delay_value[i] =
2910       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2911
2912   /* dynamically adjust player properties according to game engine version */
2913   for (i = 0; i < MAX_PLAYERS; i++)
2914     game.initial_move_delay[i] =
2915       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2916        game.initial_move_delay_value[i] : 0);
2917
2918   /* ---------- initialize player's initial push delay --------------------- */
2919
2920   /* dynamically adjust player properties according to game engine version */
2921   game.initial_push_delay_value =
2922     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2923
2924   /* ---------- initialize changing elements ------------------------------- */
2925
2926   /* initialize changing elements information */
2927   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2928   {
2929     struct ElementInfo *ei = &element_info[i];
2930
2931     /* this pointer might have been changed in the level editor */
2932     ei->change = &ei->change_page[0];
2933
2934     if (!IS_CUSTOM_ELEMENT(i))
2935     {
2936       ei->change->target_element = EL_EMPTY_SPACE;
2937       ei->change->delay_fixed = 0;
2938       ei->change->delay_random = 0;
2939       ei->change->delay_frames = 1;
2940     }
2941
2942     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2943     {
2944       ei->has_change_event[j] = FALSE;
2945
2946       ei->event_page_nr[j] = 0;
2947       ei->event_page[j] = &ei->change_page[0];
2948     }
2949   }
2950
2951   /* add changing elements from pre-defined list */
2952   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2953   {
2954     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2955     struct ElementInfo *ei = &element_info[ch_delay->element];
2956
2957     ei->change->target_element       = ch_delay->target_element;
2958     ei->change->delay_fixed          = ch_delay->change_delay;
2959
2960     ei->change->pre_change_function  = ch_delay->pre_change_function;
2961     ei->change->change_function      = ch_delay->change_function;
2962     ei->change->post_change_function = ch_delay->post_change_function;
2963
2964     ei->change->can_change = TRUE;
2965     ei->change->can_change_or_has_action = TRUE;
2966
2967     ei->has_change_event[CE_DELAY] = TRUE;
2968
2969     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2970     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2971   }
2972
2973   /* ---------- initialize internal run-time variables --------------------- */
2974
2975   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2976   {
2977     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2978
2979     for (j = 0; j < ei->num_change_pages; j++)
2980     {
2981       ei->change_page[j].can_change_or_has_action =
2982         (ei->change_page[j].can_change |
2983          ei->change_page[j].has_action);
2984     }
2985   }
2986
2987   /* add change events from custom element configuration */
2988   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2989   {
2990     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2991
2992     for (j = 0; j < ei->num_change_pages; j++)
2993     {
2994       if (!ei->change_page[j].can_change_or_has_action)
2995         continue;
2996
2997       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2998       {
2999         /* only add event page for the first page found with this event */
3000         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3001         {
3002           ei->has_change_event[k] = TRUE;
3003
3004           ei->event_page_nr[k] = j;
3005           ei->event_page[k] = &ei->change_page[j];
3006         }
3007       }
3008     }
3009   }
3010
3011   /* ---------- initialize reference elements in change conditions --------- */
3012
3013   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3014   {
3015     int element = EL_CUSTOM_START + i;
3016     struct ElementInfo *ei = &element_info[element];
3017
3018     for (j = 0; j < ei->num_change_pages; j++)
3019     {
3020       int trigger_element = ei->change_page[j].initial_trigger_element;
3021
3022       if (trigger_element >= EL_PREV_CE_8 &&
3023           trigger_element <= EL_NEXT_CE_8)
3024         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3025
3026       ei->change_page[j].trigger_element = trigger_element;
3027     }
3028   }
3029
3030   /* ---------- initialize run-time trigger player and element ------------- */
3031
3032   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3033   {
3034     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3035
3036     for (j = 0; j < ei->num_change_pages; j++)
3037     {
3038       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3039       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3040       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3041       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3042       ei->change_page[j].actual_trigger_ce_value = 0;
3043       ei->change_page[j].actual_trigger_ce_score = 0;
3044     }
3045   }
3046
3047   /* ---------- initialize trigger events ---------------------------------- */
3048
3049   /* initialize trigger events information */
3050   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3051     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3052       trigger_events[i][j] = FALSE;
3053
3054   /* add trigger events from element change event properties */
3055   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3056   {
3057     struct ElementInfo *ei = &element_info[i];
3058
3059     for (j = 0; j < ei->num_change_pages; j++)
3060     {
3061       if (!ei->change_page[j].can_change_or_has_action)
3062         continue;
3063
3064       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3065       {
3066         int trigger_element = ei->change_page[j].trigger_element;
3067
3068         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3069         {
3070           if (ei->change_page[j].has_event[k])
3071           {
3072             if (IS_GROUP_ELEMENT(trigger_element))
3073             {
3074               struct ElementGroupInfo *group =
3075                 element_info[trigger_element].group;
3076
3077               for (l = 0; l < group->num_elements_resolved; l++)
3078                 trigger_events[group->element_resolved[l]][k] = TRUE;
3079             }
3080             else if (trigger_element == EL_ANY_ELEMENT)
3081               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3082                 trigger_events[l][k] = TRUE;
3083             else
3084               trigger_events[trigger_element][k] = TRUE;
3085           }
3086         }
3087       }
3088     }
3089   }
3090
3091   /* ---------- initialize push delay -------------------------------------- */
3092
3093   /* initialize push delay values to default */
3094   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3095   {
3096     if (!IS_CUSTOM_ELEMENT(i))
3097     {
3098       /* set default push delay values (corrected since version 3.0.7-1) */
3099       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3100       {
3101         element_info[i].push_delay_fixed = 2;
3102         element_info[i].push_delay_random = 8;
3103       }
3104       else
3105       {
3106         element_info[i].push_delay_fixed = 8;
3107         element_info[i].push_delay_random = 8;
3108       }
3109     }
3110   }
3111
3112   /* set push delay value for certain elements from pre-defined list */
3113   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3114   {
3115     int e = push_delay_list[i].element;
3116
3117     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3118     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3119   }
3120
3121   /* set push delay value for Supaplex elements for newer engine versions */
3122   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3123   {
3124     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3125     {
3126       if (IS_SP_ELEMENT(i))
3127       {
3128         /* set SP push delay to just enough to push under a falling zonk */
3129         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3130
3131         element_info[i].push_delay_fixed  = delay;
3132         element_info[i].push_delay_random = 0;
3133       }
3134     }
3135   }
3136
3137   /* ---------- initialize move stepsize ----------------------------------- */
3138
3139   /* initialize move stepsize values to default */
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141     if (!IS_CUSTOM_ELEMENT(i))
3142       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3143
3144   /* set move stepsize value for certain elements from pre-defined list */
3145   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3146   {
3147     int e = move_stepsize_list[i].element;
3148
3149     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3150   }
3151
3152   /* ---------- initialize collect score ----------------------------------- */
3153
3154   /* initialize collect score values for custom elements from initial value */
3155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3156     if (IS_CUSTOM_ELEMENT(i))
3157       element_info[i].collect_score = element_info[i].collect_score_initial;
3158
3159   /* ---------- initialize collect count ----------------------------------- */
3160
3161   /* initialize collect count values for non-custom elements */
3162   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3163     if (!IS_CUSTOM_ELEMENT(i))
3164       element_info[i].collect_count_initial = 0;
3165
3166   /* add collect count values for all elements from pre-defined list */
3167   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3168     element_info[collect_count_list[i].element].collect_count_initial =
3169       collect_count_list[i].count;
3170
3171   /* ---------- initialize access direction -------------------------------- */
3172
3173   /* initialize access direction values to default (access from every side) */
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3177
3178   /* set access direction value for certain elements from pre-defined list */
3179   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3180     element_info[access_direction_list[i].element].access_direction =
3181       access_direction_list[i].direction;
3182
3183   /* ---------- initialize explosion content ------------------------------- */
3184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185   {
3186     if (IS_CUSTOM_ELEMENT(i))
3187       continue;
3188
3189     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3190     {
3191       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3192
3193       element_info[i].content.e[x][y] =
3194         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3195          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3196          i == EL_PLAYER_3 ? EL_EMERALD :
3197          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3198          i == EL_MOLE ? EL_EMERALD_RED :
3199          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3200          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3201          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3202          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3203          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3204          i == EL_WALL_EMERALD ? EL_EMERALD :
3205          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3206          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3207          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3208          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3209          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3210          i == EL_WALL_PEARL ? EL_PEARL :
3211          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212          EL_EMPTY);
3213     }
3214   }
3215
3216   /* ---------- initialize recursion detection ------------------------------ */
3217   recursion_loop_depth = 0;
3218   recursion_loop_detected = FALSE;
3219   recursion_loop_element = EL_UNDEFINED;
3220
3221   /* ---------- initialize graphics engine ---------------------------------- */
3222   game.scroll_delay_value =
3223     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3224      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3225   game.scroll_delay_value =
3226     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3227
3228   /* ---------- initialize game engine snapshots ---------------------------- */
3229   for (i = 0; i < MAX_PLAYERS; i++)
3230     game.snapshot.last_action[i] = 0;
3231   game.snapshot.changed_action = FALSE;
3232   game.snapshot.collected_item = FALSE;
3233   game.snapshot.mode =
3234     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3235      SNAPSHOT_MODE_EVERY_STEP :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3237      SNAPSHOT_MODE_EVERY_MOVE :
3238      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3239      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3240   game.snapshot.save_snapshot = FALSE;
3241
3242   /* ---------- initialize level time for Supaplex engine ------------------- */
3243   /* Supaplex levels with time limit currently unsupported -- should be added */
3244   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3245     level.time = 0;
3246 }
3247
3248 static int get_num_special_action(int element, int action_first,
3249                                   int action_last)
3250 {
3251   int num_special_action = 0;
3252   int i, j;
3253
3254   for (i = action_first; i <= action_last; i++)
3255   {
3256     boolean found = FALSE;
3257
3258     for (j = 0; j < NUM_DIRECTIONS; j++)
3259       if (el_act_dir2img(element, i, j) !=
3260           el_act_dir2img(element, ACTION_DEFAULT, j))
3261         found = TRUE;
3262
3263     if (found)
3264       num_special_action++;
3265     else
3266       break;
3267   }
3268
3269   return num_special_action;
3270 }
3271
3272
3273 /*
3274   =============================================================================
3275   InitGame()
3276   -----------------------------------------------------------------------------
3277   initialize and start new game
3278   =============================================================================
3279 */
3280
3281 #if DEBUG_INIT_PLAYER
3282 static void DebugPrintPlayerStatus(char *message)
3283 {
3284   int i;
3285
3286   if (!options.debug)
3287     return;
3288
3289   printf("%s:\n", message);
3290
3291   for (i = 0; i < MAX_PLAYERS; i++)
3292   {
3293     struct PlayerInfo *player = &stored_player[i];
3294
3295     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3296            i + 1,
3297            player->present,
3298            player->connected,
3299            player->connected_locally,
3300            player->connected_network,
3301            player->active);
3302
3303     if (local_player == player)
3304       printf(" (local player)");
3305
3306     printf("\n");
3307   }
3308 }
3309 #endif
3310
3311 void InitGame(void)
3312 {
3313   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3314   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3315   int fade_mask = REDRAW_FIELD;
3316
3317   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3318   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3319   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3320   int initial_move_dir = MV_DOWN;
3321   int i, j, x, y;
3322
3323   // required here to update video display before fading (FIX THIS)
3324   DrawMaskedBorder(REDRAW_DOOR_2);
3325
3326   if (!game.restart_level)
3327     CloseDoor(DOOR_CLOSE_1);
3328
3329   SetGameStatus(GAME_MODE_PLAYING);
3330
3331   if (level_editor_test_game)
3332     FadeSkipNextFadeIn();
3333   else
3334     FadeSetEnterScreen();
3335
3336   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3337     fade_mask = REDRAW_ALL;
3338
3339   FadeLevelSoundsAndMusic();
3340
3341   ExpireSoundLoops(TRUE);
3342
3343   FadeOut(fade_mask);
3344
3345   /* needed if different viewport properties defined for playing */
3346   ChangeViewportPropertiesIfNeeded();
3347
3348   ClearField();
3349
3350   DrawCompleteVideoDisplay();
3351
3352   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3353
3354   InitGameEngine();
3355   InitGameControlValues();
3356
3357   /* don't play tapes over network */
3358   network_playing = (network.enabled && !tape.playing);
3359
3360   for (i = 0; i < MAX_PLAYERS; i++)
3361   {
3362     struct PlayerInfo *player = &stored_player[i];
3363
3364     player->index_nr = i;
3365     player->index_bit = (1 << i);
3366     player->element_nr = EL_PLAYER_1 + i;
3367
3368     player->present = FALSE;
3369     player->active = FALSE;
3370     player->mapped = FALSE;
3371
3372     player->killed = FALSE;
3373     player->reanimated = FALSE;
3374
3375     player->action = 0;
3376     player->effective_action = 0;
3377     player->programmed_action = 0;
3378
3379     player->mouse_action.lx = 0;
3380     player->mouse_action.ly = 0;
3381     player->mouse_action.button = 0;
3382     player->mouse_action.button_hint = 0;
3383
3384     player->effective_mouse_action.lx = 0;
3385     player->effective_mouse_action.ly = 0;
3386     player->effective_mouse_action.button = 0;
3387     player->effective_mouse_action.button_hint = 0;
3388
3389     player->score = 0;
3390     player->score_final = 0;
3391
3392     player->health = MAX_HEALTH;
3393     player->health_final = MAX_HEALTH;
3394
3395     player->gems_still_needed = level.gems_needed;
3396     player->sokobanfields_still_needed = 0;
3397     player->lights_still_needed = 0;
3398     player->players_still_needed = 0;
3399     player->friends_still_needed = 0;
3400
3401     for (j = 0; j < MAX_NUM_KEYS; j++)
3402       player->key[j] = FALSE;
3403
3404     player->num_white_keys = 0;
3405
3406     player->dynabomb_count = 0;
3407     player->dynabomb_size = 1;
3408     player->dynabombs_left = 0;
3409     player->dynabomb_xl = FALSE;
3410
3411     player->MovDir = initial_move_dir;
3412     player->MovPos = 0;
3413     player->GfxPos = 0;
3414     player->GfxDir = initial_move_dir;
3415     player->GfxAction = ACTION_DEFAULT;
3416     player->Frame = 0;
3417     player->StepFrame = 0;
3418
3419     player->initial_element = player->element_nr;
3420     player->artwork_element =
3421       (level.use_artwork_element[i] ? level.artwork_element[i] :
3422        player->element_nr);
3423     player->use_murphy = FALSE;
3424
3425     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3426     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3427
3428     player->gravity = level.initial_player_gravity[i];
3429
3430     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3431
3432     player->actual_frame_counter = 0;
3433
3434     player->step_counter = 0;
3435
3436     player->last_move_dir = initial_move_dir;
3437
3438     player->is_active = FALSE;
3439
3440     player->is_waiting = FALSE;
3441     player->is_moving = FALSE;
3442     player->is_auto_moving = FALSE;
3443     player->is_digging = FALSE;
3444     player->is_snapping = FALSE;
3445     player->is_collecting = FALSE;
3446     player->is_pushing = FALSE;
3447     player->is_switching = FALSE;
3448     player->is_dropping = FALSE;
3449     player->is_dropping_pressed = FALSE;
3450
3451     player->is_bored = FALSE;
3452     player->is_sleeping = FALSE;
3453
3454     player->was_waiting = TRUE;
3455     player->was_moving = FALSE;
3456     player->was_snapping = FALSE;
3457     player->was_dropping = FALSE;
3458
3459     player->force_dropping = FALSE;
3460
3461     player->frame_counter_bored = -1;
3462     player->frame_counter_sleeping = -1;
3463
3464     player->anim_delay_counter = 0;
3465     player->post_delay_counter = 0;
3466
3467     player->dir_waiting = initial_move_dir;
3468     player->action_waiting = ACTION_DEFAULT;
3469     player->last_action_waiting = ACTION_DEFAULT;
3470     player->special_action_bored = ACTION_DEFAULT;
3471     player->special_action_sleeping = ACTION_DEFAULT;
3472
3473     player->switch_x = -1;
3474     player->switch_y = -1;
3475
3476     player->drop_x = -1;
3477     player->drop_y = -1;
3478
3479     player->show_envelope = 0;
3480
3481     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3482
3483     player->push_delay       = -1;      /* initialized when pushing starts */
3484     player->push_delay_value = game.initial_push_delay_value;
3485
3486     player->drop_delay = 0;
3487     player->drop_pressed_delay = 0;
3488
3489     player->last_jx = -1;
3490     player->last_jy = -1;
3491     player->jx = -1;
3492     player->jy = -1;
3493
3494     player->shield_normal_time_left = 0;
3495     player->shield_deadly_time_left = 0;
3496
3497     player->inventory_infinite_element = EL_UNDEFINED;
3498     player->inventory_size = 0;
3499
3500     if (level.use_initial_inventory[i])
3501     {
3502       for (j = 0; j < level.initial_inventory_size[i]; j++)
3503       {
3504         int element = level.initial_inventory_content[i][j];
3505         int collect_count = element_info[element].collect_count_initial;
3506         int k;
3507
3508         if (!IS_CUSTOM_ELEMENT(element))
3509           collect_count = 1;
3510
3511         if (collect_count == 0)
3512           player->inventory_infinite_element = element;
3513         else
3514           for (k = 0; k < collect_count; k++)
3515             if (player->inventory_size < MAX_INVENTORY_SIZE)
3516               player->inventory_element[player->inventory_size++] = element;
3517       }
3518     }
3519
3520     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3521     SnapField(player, 0, 0);
3522
3523     player->LevelSolved = FALSE;
3524     player->GameOver = FALSE;
3525
3526     player->LevelSolved_GameWon = FALSE;
3527     player->LevelSolved_GameEnd = FALSE;
3528     player->LevelSolved_SaveTape = FALSE;
3529     player->LevelSolved_SaveScore = FALSE;
3530
3531     player->LevelSolved_CountingTime = 0;
3532     player->LevelSolved_CountingScore = 0;
3533     player->LevelSolved_CountingHealth = 0;
3534
3535     map_player_action[i] = i;
3536   }
3537
3538   network_player_action_received = FALSE;
3539
3540   /* initial null action */
3541   if (network_playing)
3542     SendToServer_MovePlayer(MV_NONE);
3543
3544   ZX = ZY = -1;
3545   ExitX = ExitY = -1;
3546
3547   FrameCounter = 0;
3548   TimeFrames = 0;
3549   TimePlayed = 0;
3550   TimeLeft = level.time;
3551   TapeTime = 0;
3552
3553   ScreenMovDir = MV_NONE;
3554   ScreenMovPos = 0;
3555   ScreenGfxPos = 0;
3556
3557   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3558
3559   AllPlayersGone = FALSE;
3560
3561   game.panel.active = TRUE;
3562
3563   game.no_time_limit = (level.time == 0);
3564
3565   game.yamyam_content_nr = 0;
3566   game.robot_wheel_active = FALSE;
3567   game.magic_wall_active = FALSE;
3568   game.magic_wall_time_left = 0;
3569   game.light_time_left = 0;
3570   game.timegate_time_left = 0;
3571   game.switchgate_pos = 0;
3572   game.wind_direction = level.wind_direction_initial;
3573
3574   game.lenses_time_left = 0;
3575   game.magnify_time_left = 0;
3576
3577   game.ball_state = level.ball_state_initial;
3578   game.ball_content_nr = 0;
3579
3580   game.explosions_delayed = TRUE;
3581
3582   game.envelope_active = FALSE;
3583
3584   for (i = 0; i < NUM_BELTS; i++)
3585   {
3586     game.belt_dir[i] = MV_NONE;
3587     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3588   }
3589
3590   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3591     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3592
3593 #if DEBUG_INIT_PLAYER
3594   DebugPrintPlayerStatus("Player status at level initialization");
3595 #endif
3596
3597   SCAN_PLAYFIELD(x, y)
3598   {
3599     Feld[x][y] = Last[x][y] = level.field[x][y];
3600     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3601     ChangeDelay[x][y] = 0;
3602     ChangePage[x][y] = -1;
3603     CustomValue[x][y] = 0;              /* initialized in InitField() */
3604     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3605     AmoebaNr[x][y] = 0;
3606     WasJustMoving[x][y] = 0;
3607     WasJustFalling[x][y] = 0;
3608     CheckCollision[x][y] = 0;
3609     CheckImpact[x][y] = 0;
3610     Stop[x][y] = FALSE;
3611     Pushed[x][y] = FALSE;
3612
3613     ChangeCount[x][y] = 0;
3614     ChangeEvent[x][y] = -1;
3615
3616     ExplodePhase[x][y] = 0;
3617     ExplodeDelay[x][y] = 0;
3618     ExplodeField[x][y] = EX_TYPE_NONE;
3619
3620     RunnerVisit[x][y] = 0;
3621     PlayerVisit[x][y] = 0;
3622
3623     GfxFrame[x][y] = 0;
3624     GfxRandom[x][y] = INIT_GFX_RANDOM();
3625     GfxElement[x][y] = EL_UNDEFINED;
3626     GfxAction[x][y] = ACTION_DEFAULT;
3627     GfxDir[x][y] = MV_NONE;
3628     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3629   }
3630
3631   SCAN_PLAYFIELD(x, y)
3632   {
3633     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3634       emulate_bd = FALSE;
3635     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3636       emulate_sb = FALSE;
3637     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3638       emulate_sp = FALSE;
3639
3640     InitField(x, y, TRUE);
3641
3642     ResetGfxAnimation(x, y);
3643   }
3644
3645   InitBeltMovement();
3646
3647   for (i = 0; i < MAX_PLAYERS; i++)
3648   {
3649     struct PlayerInfo *player = &stored_player[i];
3650
3651     /* set number of special actions for bored and sleeping animation */
3652     player->num_special_action_bored =
3653       get_num_special_action(player->artwork_element,
3654                              ACTION_BORING_1, ACTION_BORING_LAST);
3655     player->num_special_action_sleeping =
3656       get_num_special_action(player->artwork_element,
3657                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3658   }
3659
3660   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3661                     emulate_sb ? EMU_SOKOBAN :
3662                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3663
3664   /* initialize type of slippery elements */
3665   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3666   {
3667     if (!IS_CUSTOM_ELEMENT(i))
3668     {
3669       /* default: elements slip down either to the left or right randomly */
3670       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3671
3672       /* SP style elements prefer to slip down on the left side */
3673       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3674         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3675
3676       /* BD style elements prefer to slip down on the left side */
3677       if (game.emulation == EMU_BOULDERDASH)
3678         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3679     }
3680   }
3681
3682   /* initialize explosion and ignition delay */
3683   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3684   {
3685     if (!IS_CUSTOM_ELEMENT(i))
3686     {
3687       int num_phase = 8;
3688       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3689                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3690                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3691       int last_phase = (num_phase + 1) * delay;
3692       int half_phase = (num_phase / 2) * delay;
3693
3694       element_info[i].explosion_delay = last_phase - 1;
3695       element_info[i].ignition_delay = half_phase;
3696
3697       if (i == EL_BLACK_ORB)
3698         element_info[i].ignition_delay = 1;
3699     }
3700   }
3701
3702   /* correct non-moving belts to start moving left */
3703   for (i = 0; i < NUM_BELTS; i++)
3704     if (game.belt_dir[i] == MV_NONE)
3705       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3706
3707 #if USE_NEW_PLAYER_ASSIGNMENTS
3708   for (i = 0; i < MAX_PLAYERS; i++)
3709   {
3710     stored_player[i].connected = FALSE;
3711
3712     /* in network game mode, the local player might not be the first player */
3713     if (stored_player[i].connected_locally)
3714       local_player = &stored_player[i];
3715   }
3716
3717   if (!network.enabled)
3718     local_player->connected = TRUE;
3719
3720   if (tape.playing)
3721   {
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       stored_player[i].connected = tape.player_participates[i];
3724   }
3725   else if (network.enabled)
3726   {
3727     /* add team mode players connected over the network (needed for correct
3728        assignment of player figures from level to locally playing players) */
3729
3730     for (i = 0; i < MAX_PLAYERS; i++)
3731       if (stored_player[i].connected_network)
3732         stored_player[i].connected = TRUE;
3733   }
3734   else if (game.team_mode)
3735   {
3736     /* try to guess locally connected team mode players (needed for correct
3737        assignment of player figures from level to locally playing players) */
3738
3739     for (i = 0; i < MAX_PLAYERS; i++)
3740       if (setup.input[i].use_joystick ||
3741           setup.input[i].key.left != KSYM_UNDEFINED)
3742         stored_player[i].connected = TRUE;
3743   }
3744
3745 #if DEBUG_INIT_PLAYER
3746   DebugPrintPlayerStatus("Player status after level initialization");
3747 #endif
3748
3749 #if DEBUG_INIT_PLAYER
3750   if (options.debug)
3751     printf("Reassigning players ...\n");
3752 #endif
3753
3754   /* check if any connected player was not found in playfield */
3755   for (i = 0; i < MAX_PLAYERS; i++)
3756   {
3757     struct PlayerInfo *player = &stored_player[i];
3758
3759     if (player->connected && !player->present)
3760     {
3761       struct PlayerInfo *field_player = NULL;
3762
3763 #if DEBUG_INIT_PLAYER
3764       if (options.debug)
3765         printf("- looking for field player for player %d ...\n", i + 1);
3766 #endif
3767
3768       /* assign first free player found that is present in the playfield */
3769
3770       /* first try: look for unmapped playfield player that is not connected */
3771       for (j = 0; j < MAX_PLAYERS; j++)
3772         if (field_player == NULL &&
3773             stored_player[j].present &&
3774             !stored_player[j].mapped &&
3775             !stored_player[j].connected)
3776           field_player = &stored_player[j];
3777
3778       /* second try: look for *any* unmapped playfield player */
3779       for (j = 0; j < MAX_PLAYERS; j++)
3780         if (field_player == NULL &&
3781             stored_player[j].present &&
3782             !stored_player[j].mapped)
3783           field_player = &stored_player[j];
3784
3785       if (field_player != NULL)
3786       {
3787         int jx = field_player->jx, jy = field_player->jy;
3788
3789 #if DEBUG_INIT_PLAYER
3790         if (options.debug)
3791           printf("- found player %d\n", field_player->index_nr + 1);
3792 #endif
3793
3794         player->present = FALSE;
3795         player->active = FALSE;
3796
3797         field_player->present = TRUE;
3798         field_player->active = TRUE;
3799
3800         /*
3801         player->initial_element = field_player->initial_element;
3802         player->artwork_element = field_player->artwork_element;
3803
3804         player->block_last_field       = field_player->block_last_field;
3805         player->block_delay_adjustment = field_player->block_delay_adjustment;
3806         */
3807
3808         StorePlayer[jx][jy] = field_player->element_nr;
3809
3810         field_player->jx = field_player->last_jx = jx;
3811         field_player->jy = field_player->last_jy = jy;
3812
3813         if (local_player == player)
3814           local_player = field_player;
3815
3816         map_player_action[field_player->index_nr] = i;
3817
3818         field_player->mapped = TRUE;
3819
3820 #if DEBUG_INIT_PLAYER
3821         if (options.debug)
3822           printf("- map_player_action[%d] == %d\n",
3823                  field_player->index_nr + 1, i + 1);
3824 #endif
3825       }
3826     }
3827
3828     if (player->connected && player->present)
3829       player->mapped = TRUE;
3830   }
3831
3832 #if DEBUG_INIT_PLAYER
3833   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3834 #endif
3835
3836 #else
3837
3838   /* check if any connected player was not found in playfield */
3839   for (i = 0; i < MAX_PLAYERS; i++)
3840   {
3841     struct PlayerInfo *player = &stored_player[i];
3842
3843     if (player->connected && !player->present)
3844     {
3845       for (j = 0; j < MAX_PLAYERS; j++)
3846       {
3847         struct PlayerInfo *field_player = &stored_player[j];
3848         int jx = field_player->jx, jy = field_player->jy;
3849
3850         /* assign first free player found that is present in the playfield */
3851         if (field_player->present && !field_player->connected)
3852         {
3853           player->present = TRUE;
3854           player->active = TRUE;
3855
3856           field_player->present = FALSE;
3857           field_player->active = FALSE;
3858
3859           player->initial_element = field_player->initial_element;
3860           player->artwork_element = field_player->artwork_element;
3861
3862           player->block_last_field       = field_player->block_last_field;
3863           player->block_delay_adjustment = field_player->block_delay_adjustment;
3864
3865           StorePlayer[jx][jy] = player->element_nr;
3866
3867           player->jx = player->last_jx = jx;
3868           player->jy = player->last_jy = jy;
3869
3870           break;
3871         }
3872       }
3873     }
3874   }
3875 #endif
3876
3877 #if 0
3878   printf("::: local_player->present == %d\n", local_player->present);
3879 #endif
3880
3881   /* set focus to local player for network games, else to all players */
3882   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3883   game.centered_player_nr_next = game.centered_player_nr;
3884   game.set_centered_player = FALSE;
3885
3886   if (network_playing && tape.recording)
3887   {
3888     /* store client dependent player focus when recording network games */
3889     tape.centered_player_nr_next = game.centered_player_nr_next;
3890     tape.set_centered_player = TRUE;
3891   }
3892
3893   if (tape.playing)
3894   {
3895     /* when playing a tape, eliminate all players who do not participate */
3896
3897 #if USE_NEW_PLAYER_ASSIGNMENTS
3898
3899     if (!game.team_mode)
3900     {
3901       for (i = 0; i < MAX_PLAYERS; i++)
3902       {
3903         if (stored_player[i].active &&
3904             !tape.player_participates[map_player_action[i]])
3905         {
3906           struct PlayerInfo *player = &stored_player[i];
3907           int jx = player->jx, jy = player->jy;
3908
3909 #if DEBUG_INIT_PLAYER
3910           if (options.debug)
3911             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3912 #endif
3913
3914           player->active = FALSE;
3915           StorePlayer[jx][jy] = 0;
3916           Feld[jx][jy] = EL_EMPTY;
3917         }
3918       }
3919     }
3920
3921 #else
3922
3923     for (i = 0; i < MAX_PLAYERS; i++)
3924     {
3925       if (stored_player[i].active &&
3926           !tape.player_participates[i])
3927       {
3928         struct PlayerInfo *player = &stored_player[i];
3929         int jx = player->jx, jy = player->jy;
3930
3931         player->active = FALSE;
3932         StorePlayer[jx][jy] = 0;
3933         Feld[jx][jy] = EL_EMPTY;
3934       }
3935     }
3936 #endif
3937   }
3938   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3939   {
3940     /* when in single player mode, eliminate all but the local player */
3941
3942     for (i = 0; i < MAX_PLAYERS; i++)
3943     {
3944       struct PlayerInfo *player = &stored_player[i];
3945
3946       if (player->active && player != local_player)
3947       {
3948         int jx = player->jx, jy = player->jy;
3949
3950         player->active = FALSE;
3951         player->present = FALSE;
3952
3953         StorePlayer[jx][jy] = 0;
3954         Feld[jx][jy] = EL_EMPTY;
3955       }
3956     }
3957   }
3958
3959   for (i = 0; i < MAX_PLAYERS; i++)
3960     if (stored_player[i].active)
3961       local_player->players_still_needed++;
3962
3963   if (level.solved_by_one_player)
3964     local_player->players_still_needed = 1;
3965
3966   /* when recording the game, store which players take part in the game */
3967   if (tape.recording)
3968   {
3969 #if USE_NEW_PLAYER_ASSIGNMENTS
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971       if (stored_player[i].connected)
3972         tape.player_participates[i] = TRUE;
3973 #else
3974     for (i = 0; i < MAX_PLAYERS; i++)
3975       if (stored_player[i].active)
3976         tape.player_participates[i] = TRUE;
3977 #endif
3978   }
3979
3980 #if DEBUG_INIT_PLAYER
3981   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3982 #endif
3983
3984   if (BorderElement == EL_EMPTY)
3985   {
3986     SBX_Left = 0;
3987     SBX_Right = lev_fieldx - SCR_FIELDX;
3988     SBY_Upper = 0;
3989     SBY_Lower = lev_fieldy - SCR_FIELDY;
3990   }
3991   else
3992   {
3993     SBX_Left = -1;
3994     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3995     SBY_Upper = -1;
3996     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3997   }
3998
3999   if (full_lev_fieldx <= SCR_FIELDX)
4000     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4001   if (full_lev_fieldy <= SCR_FIELDY)
4002     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4003
4004   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4005     SBX_Left--;
4006   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4007     SBY_Upper--;
4008
4009   /* if local player not found, look for custom element that might create
4010      the player (make some assumptions about the right custom element) */
4011   if (!local_player->present)
4012   {
4013     int start_x = 0, start_y = 0;
4014     int found_rating = 0;
4015     int found_element = EL_UNDEFINED;
4016     int player_nr = local_player->index_nr;
4017
4018     SCAN_PLAYFIELD(x, y)
4019     {
4020       int element = Feld[x][y];
4021       int content;
4022       int xx, yy;
4023       boolean is_player;
4024
4025       if (level.use_start_element[player_nr] &&
4026           level.start_element[player_nr] == element &&
4027           found_rating < 4)
4028       {
4029         start_x = x;
4030         start_y = y;
4031
4032         found_rating = 4;
4033         found_element = element;
4034       }
4035
4036       if (!IS_CUSTOM_ELEMENT(element))
4037         continue;
4038
4039       if (CAN_CHANGE(element))
4040       {
4041         for (i = 0; i < element_info[element].num_change_pages; i++)
4042         {
4043           /* check for player created from custom element as single target */
4044           content = element_info[element].change_page[i].target_element;
4045           is_player = ELEM_IS_PLAYER(content);
4046
4047           if (is_player && (found_rating < 3 ||
4048                             (found_rating == 3 && element < found_element)))
4049           {
4050             start_x = x;
4051             start_y = y;
4052
4053             found_rating = 3;
4054             found_element = element;
4055           }
4056         }
4057       }
4058
4059       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4060       {
4061         /* check for player created from custom element as explosion content */
4062         content = element_info[element].content.e[xx][yy];
4063         is_player = ELEM_IS_PLAYER(content);
4064
4065         if (is_player && (found_rating < 2 ||
4066                           (found_rating == 2 && element < found_element)))
4067         {
4068           start_x = x + xx - 1;
4069           start_y = y + yy - 1;
4070
4071           found_rating = 2;
4072           found_element = element;
4073         }
4074
4075         if (!CAN_CHANGE(element))
4076           continue;
4077
4078         for (i = 0; i < element_info[element].num_change_pages; i++)
4079         {
4080           /* check for player created from custom element as extended target */
4081           content =
4082             element_info[element].change_page[i].target_content.e[xx][yy];
4083
4084           is_player = ELEM_IS_PLAYER(content);
4085
4086           if (is_player && (found_rating < 1 ||
4087                             (found_rating == 1 && element < found_element)))
4088           {
4089             start_x = x + xx - 1;
4090             start_y = y + yy - 1;
4091
4092             found_rating = 1;
4093             found_element = element;
4094           }
4095         }
4096       }
4097     }
4098
4099     scroll_x = SCROLL_POSITION_X(start_x);
4100     scroll_y = SCROLL_POSITION_Y(start_y);
4101   }
4102   else
4103   {
4104     scroll_x = SCROLL_POSITION_X(local_player->jx);
4105     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4106   }
4107
4108   /* !!! FIX THIS (START) !!! */
4109   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4110   {
4111     InitGameEngine_EM();
4112   }
4113   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4114   {
4115     InitGameEngine_SP();
4116   }
4117   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4118   {
4119     InitGameEngine_MM();
4120   }
4121   else
4122   {
4123     DrawLevel(REDRAW_FIELD);
4124     DrawAllPlayers();
4125
4126     /* after drawing the level, correct some elements */
4127     if (game.timegate_time_left == 0)
4128       CloseAllOpenTimegates();
4129   }
4130
4131   /* blit playfield from scroll buffer to normal back buffer for fading in */
4132   BlitScreenToBitmap(backbuffer);
4133   /* !!! FIX THIS (END) !!! */
4134
4135   DrawMaskedBorder(fade_mask);
4136
4137   FadeIn(fade_mask);
4138
4139 #if 1
4140   // full screen redraw is required at this point in the following cases:
4141   // - special editor door undrawn when game was started from level editor
4142   // - drawing area (playfield) was changed and has to be removed completely
4143   redraw_mask = REDRAW_ALL;
4144   BackToFront();
4145 #endif
4146
4147   if (!game.restart_level)
4148   {
4149     /* copy default game door content to main double buffer */
4150
4151     /* !!! CHECK AGAIN !!! */
4152     SetPanelBackground();
4153     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4154     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4155   }
4156
4157   SetPanelBackground();
4158   SetDrawBackgroundMask(REDRAW_DOOR_1);
4159
4160   UpdateAndDisplayGameControlValues();
4161
4162   if (!game.restart_level)
4163   {
4164     UnmapGameButtons();
4165     UnmapTapeButtons();
4166
4167     FreeGameButtons();
4168     CreateGameButtons();
4169
4170     MapGameButtons();
4171     MapTapeButtons();
4172
4173     /* copy actual game door content to door double buffer for OpenDoor() */
4174     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4175
4176     OpenDoor(DOOR_OPEN_ALL);
4177
4178     KeyboardAutoRepeatOffUnlessAutoplay();
4179
4180 #if DEBUG_INIT_PLAYER
4181     DebugPrintPlayerStatus("Player status (final)");
4182 #endif
4183   }
4184
4185   UnmapAllGadgets();
4186
4187   MapGameButtons();
4188   MapTapeButtons();
4189
4190   if (!game.restart_level && !tape.playing)
4191   {
4192     LevelStats_incPlayed(level_nr);
4193
4194     SaveLevelSetup_SeriesInfo();
4195   }
4196
4197   game.restart_level = FALSE;
4198   game.restart_game_message = NULL;
4199
4200   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4201     InitGameActions_MM();
4202
4203   SaveEngineSnapshotToListInitial();
4204
4205   if (!game.restart_level)
4206   {
4207     PlaySound(SND_GAME_STARTING);
4208
4209     if (setup.sound_music)
4210       PlayLevelMusic();
4211   }
4212 }
4213
4214 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4215                         int actual_player_x, int actual_player_y)
4216 {
4217   /* this is used for non-R'n'D game engines to update certain engine values */
4218
4219   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4220   {
4221     actual_player_x = correctLevelPosX_EM(actual_player_x);
4222     actual_player_y = correctLevelPosY_EM(actual_player_y);
4223   }
4224
4225   /* needed to determine if sounds are played within the visible screen area */
4226   scroll_x = actual_scroll_x;
4227   scroll_y = actual_scroll_y;
4228
4229   /* needed to get player position for "follow finger" playing input method */
4230   local_player->jx = actual_player_x;
4231   local_player->jy = actual_player_y;
4232 }
4233
4234 void InitMovDir(int x, int y)
4235 {
4236   int i, element = Feld[x][y];
4237   static int xy[4][2] =
4238   {
4239     {  0, +1 },
4240     { +1,  0 },
4241     {  0, -1 },
4242     { -1,  0 }
4243   };
4244   static int direction[3][4] =
4245   {
4246     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4247     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4248     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4249   };
4250
4251   switch (element)
4252   {
4253     case EL_BUG_RIGHT:
4254     case EL_BUG_UP:
4255     case EL_BUG_LEFT:
4256     case EL_BUG_DOWN:
4257       Feld[x][y] = EL_BUG;
4258       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4259       break;
4260
4261     case EL_SPACESHIP_RIGHT:
4262     case EL_SPACESHIP_UP:
4263     case EL_SPACESHIP_LEFT:
4264     case EL_SPACESHIP_DOWN:
4265       Feld[x][y] = EL_SPACESHIP;
4266       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4267       break;
4268
4269     case EL_BD_BUTTERFLY_RIGHT:
4270     case EL_BD_BUTTERFLY_UP:
4271     case EL_BD_BUTTERFLY_LEFT:
4272     case EL_BD_BUTTERFLY_DOWN:
4273       Feld[x][y] = EL_BD_BUTTERFLY;
4274       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4275       break;
4276
4277     case EL_BD_FIREFLY_RIGHT:
4278     case EL_BD_FIREFLY_UP:
4279     case EL_BD_FIREFLY_LEFT:
4280     case EL_BD_FIREFLY_DOWN:
4281       Feld[x][y] = EL_BD_FIREFLY;
4282       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4283       break;
4284
4285     case EL_PACMAN_RIGHT:
4286     case EL_PACMAN_UP:
4287     case EL_PACMAN_LEFT:
4288     case EL_PACMAN_DOWN:
4289       Feld[x][y] = EL_PACMAN;
4290       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4291       break;
4292
4293     case EL_YAMYAM_LEFT:
4294     case EL_YAMYAM_RIGHT:
4295     case EL_YAMYAM_UP:
4296     case EL_YAMYAM_DOWN:
4297       Feld[x][y] = EL_YAMYAM;
4298       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4299       break;
4300
4301     case EL_SP_SNIKSNAK:
4302       MovDir[x][y] = MV_UP;
4303       break;
4304
4305     case EL_SP_ELECTRON:
4306       MovDir[x][y] = MV_LEFT;
4307       break;
4308
4309     case EL_MOLE_LEFT:
4310     case EL_MOLE_RIGHT:
4311     case EL_MOLE_UP:
4312     case EL_MOLE_DOWN:
4313       Feld[x][y] = EL_MOLE;
4314       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4315       break;
4316
4317     default:
4318       if (IS_CUSTOM_ELEMENT(element))
4319       {
4320         struct ElementInfo *ei = &element_info[element];
4321         int move_direction_initial = ei->move_direction_initial;
4322         int move_pattern = ei->move_pattern;
4323
4324         if (move_direction_initial == MV_START_PREVIOUS)
4325         {
4326           if (MovDir[x][y] != MV_NONE)
4327             return;
4328
4329           move_direction_initial = MV_START_AUTOMATIC;
4330         }
4331
4332         if (move_direction_initial == MV_START_RANDOM)
4333           MovDir[x][y] = 1 << RND(4);
4334         else if (move_direction_initial & MV_ANY_DIRECTION)
4335           MovDir[x][y] = move_direction_initial;
4336         else if (move_pattern == MV_ALL_DIRECTIONS ||
4337                  move_pattern == MV_TURNING_LEFT ||
4338                  move_pattern == MV_TURNING_RIGHT ||
4339                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4340                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4341                  move_pattern == MV_TURNING_RANDOM)
4342           MovDir[x][y] = 1 << RND(4);
4343         else if (move_pattern == MV_HORIZONTAL)
4344           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4345         else if (move_pattern == MV_VERTICAL)
4346           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4347         else if (move_pattern & MV_ANY_DIRECTION)
4348           MovDir[x][y] = element_info[element].move_pattern;
4349         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4350                  move_pattern == MV_ALONG_RIGHT_SIDE)
4351         {
4352           /* use random direction as default start direction */
4353           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4354             MovDir[x][y] = 1 << RND(4);
4355
4356           for (i = 0; i < NUM_DIRECTIONS; i++)
4357           {
4358             int x1 = x + xy[i][0];
4359             int y1 = y + xy[i][1];
4360
4361             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4362             {
4363               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4364                 MovDir[x][y] = direction[0][i];
4365               else
4366                 MovDir[x][y] = direction[1][i];
4367
4368               break;
4369             }
4370           }
4371         }                
4372       }
4373       else
4374       {
4375         MovDir[x][y] = 1 << RND(4);
4376
4377         if (element != EL_BUG &&
4378             element != EL_SPACESHIP &&
4379             element != EL_BD_BUTTERFLY &&
4380             element != EL_BD_FIREFLY)
4381           break;
4382
4383         for (i = 0; i < NUM_DIRECTIONS; i++)
4384         {
4385           int x1 = x + xy[i][0];
4386           int y1 = y + xy[i][1];
4387
4388           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4389           {
4390             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4391             {
4392               MovDir[x][y] = direction[0][i];
4393               break;
4394             }
4395             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4396                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4397             {
4398               MovDir[x][y] = direction[1][i];
4399               break;
4400             }
4401           }
4402         }
4403       }
4404       break;
4405   }
4406
4407   GfxDir[x][y] = MovDir[x][y];
4408 }
4409
4410 void InitAmoebaNr(int x, int y)
4411 {
4412   int i;
4413   int group_nr = AmoebeNachbarNr(x, y);
4414
4415   if (group_nr == 0)
4416   {
4417     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4418     {
4419       if (AmoebaCnt[i] == 0)
4420       {
4421         group_nr = i;
4422         break;
4423       }
4424     }
4425   }
4426
4427   AmoebaNr[x][y] = group_nr;
4428   AmoebaCnt[group_nr]++;
4429   AmoebaCnt2[group_nr]++;
4430 }
4431
4432 static void PlayerWins(struct PlayerInfo *player)
4433 {
4434   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4435       local_player->players_still_needed > 0)
4436     return;
4437
4438   player->LevelSolved = TRUE;
4439   player->GameOver = TRUE;
4440
4441   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4442                          level.native_em_level->lev->score :
4443                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4444                          game_mm.score :
4445                          player->score);
4446   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4447                           MM_HEALTH(game_mm.laser_overload_value) :
4448                           player->health);
4449
4450   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4451                                       TimeLeft);
4452   player->LevelSolved_CountingScore = player->score_final;
4453   player->LevelSolved_CountingHealth = player->health_final;
4454 }
4455
4456 void GameWon(void)
4457 {
4458   static int time_count_steps;
4459   static int time, time_final;
4460   static int score, score_final;
4461   static int health, health_final;
4462   static int game_over_delay_1 = 0;
4463   static int game_over_delay_2 = 0;
4464   static int game_over_delay_3 = 0;
4465   int game_over_delay_value_1 = 50;
4466   int game_over_delay_value_2 = 25;
4467   int game_over_delay_value_3 = 50;
4468
4469   if (!local_player->LevelSolved_GameWon)
4470   {
4471     int i;
4472
4473     /* do not start end game actions before the player stops moving (to exit) */
4474     if (local_player->MovPos)
4475       return;
4476
4477     local_player->LevelSolved_GameWon = TRUE;
4478     local_player->LevelSolved_SaveTape = tape.recording;
4479     local_player->LevelSolved_SaveScore = !tape.playing;
4480
4481     if (!tape.playing)
4482     {
4483       LevelStats_incSolved(level_nr);
4484
4485       SaveLevelSetup_SeriesInfo();
4486     }
4487
4488     if (tape.auto_play)         /* tape might already be stopped here */
4489       tape.auto_play_level_solved = TRUE;
4490
4491     TapeStop();
4492
4493     game_over_delay_1 = 0;
4494     game_over_delay_2 = 0;
4495     game_over_delay_3 = game_over_delay_value_3;
4496
4497     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4498     score = score_final = local_player->score_final;
4499     health = health_final = local_player->health_final;
4500
4501     if (level.score[SC_TIME_BONUS] > 0)
4502     {
4503       if (TimeLeft > 0)
4504       {
4505         time_final = 0;
4506         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4507       }
4508       else if (game.no_time_limit && TimePlayed < 999)
4509       {
4510         time_final = 999;
4511         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4512       }
4513
4514       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4515
4516       game_over_delay_1 = game_over_delay_value_1;
4517
4518       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4519       {
4520         health_final = 0;
4521         score_final += health * level.score[SC_TIME_BONUS];
4522
4523         game_over_delay_2 = game_over_delay_value_2;
4524       }
4525
4526       local_player->score_final = score_final;
4527       local_player->health_final = health_final;
4528     }
4529
4530     if (level_editor_test_game)
4531     {
4532       time = time_final;
4533       score = score_final;
4534
4535       local_player->LevelSolved_CountingTime = time;
4536       local_player->LevelSolved_CountingScore = score;
4537
4538       game_panel_controls[GAME_PANEL_TIME].value = time;
4539       game_panel_controls[GAME_PANEL_SCORE].value = score;
4540
4541       DisplayGameControlValues();
4542     }
4543
4544     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4545     {
4546       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4547       {
4548         /* close exit door after last player */
4549         if ((AllPlayersGone &&
4550              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4551               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4552               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4553             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4554             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4555         {
4556           int element = Feld[ExitX][ExitY];
4557
4558           Feld[ExitX][ExitY] =
4559             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4560              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4561              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4562              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4563              EL_EM_STEEL_EXIT_CLOSING);
4564
4565           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4566         }
4567
4568         /* player disappears */
4569         DrawLevelField(ExitX, ExitY);
4570       }
4571
4572       for (i = 0; i < MAX_PLAYERS; i++)
4573       {
4574         struct PlayerInfo *player = &stored_player[i];
4575
4576         if (player->present)
4577         {
4578           RemovePlayer(player);
4579
4580           /* player disappears */
4581           DrawLevelField(player->jx, player->jy);
4582         }
4583       }
4584     }
4585
4586     PlaySound(SND_GAME_WINNING);
4587   }
4588
4589   if (game_over_delay_1 > 0)
4590   {
4591     game_over_delay_1--;
4592
4593     return;
4594   }
4595
4596   if (time != time_final)
4597   {
4598     int time_to_go = ABS(time_final - time);
4599     int time_count_dir = (time < time_final ? +1 : -1);
4600
4601     if (time_to_go < time_count_steps)
4602       time_count_steps = 1;
4603
4604     time  += time_count_steps * time_count_dir;
4605     score += time_count_steps * level.score[SC_TIME_BONUS];
4606
4607     local_player->LevelSolved_CountingTime = time;
4608     local_player->LevelSolved_CountingScore = score;
4609
4610     game_panel_controls[GAME_PANEL_TIME].value = time;
4611     game_panel_controls[GAME_PANEL_SCORE].value = score;
4612
4613     DisplayGameControlValues();
4614
4615     if (time == time_final)
4616       StopSound(SND_GAME_LEVELTIME_BONUS);
4617     else if (setup.sound_loops)
4618       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4619     else
4620       PlaySound(SND_GAME_LEVELTIME_BONUS);
4621
4622     return;
4623   }
4624
4625   if (game_over_delay_2 > 0)
4626   {
4627     game_over_delay_2--;
4628
4629     return;
4630   }
4631
4632   if (health != health_final)
4633   {
4634     int health_count_dir = (health < health_final ? +1 : -1);
4635
4636     health += health_count_dir;
4637     score  += level.score[SC_TIME_BONUS];
4638
4639     local_player->LevelSolved_CountingHealth = health;
4640     local_player->LevelSolved_CountingScore = score;
4641
4642     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4643     game_panel_controls[GAME_PANEL_SCORE].value = score;
4644
4645     DisplayGameControlValues();
4646
4647     if (health == health_final)
4648       StopSound(SND_GAME_LEVELTIME_BONUS);
4649     else if (setup.sound_loops)
4650       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4651     else
4652       PlaySound(SND_GAME_LEVELTIME_BONUS);
4653
4654     return;
4655   }
4656
4657   game.panel.active = FALSE;
4658
4659   if (game_over_delay_3 > 0)
4660   {
4661     game_over_delay_3--;
4662
4663     return;
4664   }
4665
4666   GameEnd();
4667 }
4668
4669 void GameEnd(void)
4670 {
4671   /* used instead of "level_nr" (needed for network games) */
4672   int last_level_nr = levelset.level_nr;
4673   int hi_pos;
4674
4675   local_player->LevelSolved_GameEnd = TRUE;
4676
4677   if (local_player->LevelSolved_SaveTape)
4678   {
4679     /* make sure that request dialog to save tape does not open door again */
4680     if (!global.use_envelope_request)
4681       CloseDoor(DOOR_CLOSE_1);
4682
4683     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4684   }
4685
4686   /* if no tape is to be saved, close both doors simultaneously */
4687   CloseDoor(DOOR_CLOSE_ALL);
4688
4689   if (level_editor_test_game)
4690   {
4691     SetGameStatus(GAME_MODE_MAIN);
4692
4693     DrawMainMenu();
4694
4695     return;
4696   }
4697
4698   if (!local_player->LevelSolved_SaveScore)
4699   {
4700     SetGameStatus(GAME_MODE_MAIN);
4701
4702     DrawMainMenu();
4703
4704     return;
4705   }
4706
4707   if (level_nr == leveldir_current->handicap_level)
4708   {
4709     leveldir_current->handicap_level++;
4710
4711     SaveLevelSetup_SeriesInfo();
4712   }
4713
4714   if (setup.increment_levels &&
4715       level_nr < leveldir_current->last_level &&
4716       !network_playing)
4717   {
4718     level_nr++;         /* advance to next level */
4719     TapeErase();        /* start with empty tape */
4720
4721     if (setup.auto_play_next_level)
4722     {
4723       LoadLevel(level_nr);
4724
4725       SaveLevelSetup_SeriesInfo();
4726     }
4727   }
4728
4729   hi_pos = NewHiScore(last_level_nr);
4730
4731   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4732   {
4733     SetGameStatus(GAME_MODE_SCORES);
4734
4735     DrawHallOfFame(last_level_nr, hi_pos);
4736   }
4737   else if (setup.auto_play_next_level && setup.increment_levels &&
4738            last_level_nr < leveldir_current->last_level &&
4739            !network_playing)
4740   {
4741     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4742   }
4743   else
4744   {
4745     SetGameStatus(GAME_MODE_MAIN);
4746
4747     DrawMainMenu();
4748   }
4749 }
4750
4751 int NewHiScore(int level_nr)
4752 {
4753   int k, l;
4754   int position = -1;
4755   boolean one_score_entry_per_name = !program.many_scores_per_name;
4756
4757   LoadScore(level_nr);
4758
4759   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4760       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4761     return -1;
4762
4763   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4764   {
4765     if (local_player->score_final > highscore[k].Score)
4766     {
4767       /* player has made it to the hall of fame */
4768
4769       if (k < MAX_SCORE_ENTRIES - 1)
4770       {
4771         int m = MAX_SCORE_ENTRIES - 1;
4772
4773         if (one_score_entry_per_name)
4774         {
4775           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4776             if (strEqual(setup.player_name, highscore[l].Name))
4777               m = l;
4778
4779           if (m == k)   /* player's new highscore overwrites his old one */
4780             goto put_into_list;
4781         }
4782
4783         for (l = m; l > k; l--)
4784         {
4785           strcpy(highscore[l].Name, highscore[l - 1].Name);
4786           highscore[l].Score = highscore[l - 1].Score;
4787         }
4788       }
4789
4790       put_into_list:
4791
4792       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4793       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4794       highscore[k].Score = local_player->score_final; 
4795       position = k;
4796
4797       break;
4798     }
4799     else if (one_score_entry_per_name &&
4800              !strncmp(setup.player_name, highscore[k].Name,
4801                       MAX_PLAYER_NAME_LEN))
4802       break;    /* player already there with a higher score */
4803   }
4804
4805   if (position >= 0) 
4806     SaveScore(level_nr);
4807
4808   return position;
4809 }
4810
4811 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4812 {
4813   int element = Feld[x][y];
4814   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4815   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4816   int horiz_move = (dx != 0);
4817   int sign = (horiz_move ? dx : dy);
4818   int step = sign * element_info[element].move_stepsize;
4819
4820   /* special values for move stepsize for spring and things on conveyor belt */
4821   if (horiz_move)
4822   {
4823     if (CAN_FALL(element) &&
4824         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4825       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4826     else if (element == EL_SPRING)
4827       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4828   }
4829
4830   return step;
4831 }
4832
4833 inline static int getElementMoveStepsize(int x, int y)
4834 {
4835   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4836 }
4837
4838 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4839 {
4840   if (player->GfxAction != action || player->GfxDir != dir)
4841   {
4842     player->GfxAction = action;
4843     player->GfxDir = dir;
4844     player->Frame = 0;
4845     player->StepFrame = 0;
4846   }
4847 }
4848
4849 static void ResetGfxFrame(int x, int y)
4850 {
4851   // profiling showed that "autotest" spends 10~20% of its time in this function
4852   if (DrawingDeactivatedField())
4853     return;
4854
4855   int element = Feld[x][y];
4856   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4857
4858   if (graphic_info[graphic].anim_global_sync)
4859     GfxFrame[x][y] = FrameCounter;
4860   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4861     GfxFrame[x][y] = CustomValue[x][y];
4862   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4863     GfxFrame[x][y] = element_info[element].collect_score;
4864   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4865     GfxFrame[x][y] = ChangeDelay[x][y];
4866 }
4867
4868 static void ResetGfxAnimation(int x, int y)
4869 {
4870   GfxAction[x][y] = ACTION_DEFAULT;
4871   GfxDir[x][y] = MovDir[x][y];
4872   GfxFrame[x][y] = 0;
4873
4874   ResetGfxFrame(x, y);
4875 }
4876
4877 static void ResetRandomAnimationValue(int x, int y)
4878 {
4879   GfxRandom[x][y] = INIT_GFX_RANDOM();
4880 }
4881
4882 static void InitMovingField(int x, int y, int direction)
4883 {
4884   int element = Feld[x][y];
4885   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4886   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4887   int newx = x + dx;
4888   int newy = y + dy;
4889   boolean is_moving_before, is_moving_after;
4890
4891   /* check if element was/is moving or being moved before/after mode change */
4892   is_moving_before = (WasJustMoving[x][y] != 0);
4893   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4894
4895   /* reset animation only for moving elements which change direction of moving
4896      or which just started or stopped moving
4897      (else CEs with property "can move" / "not moving" are reset each frame) */
4898   if (is_moving_before != is_moving_after ||
4899       direction != MovDir[x][y])
4900     ResetGfxAnimation(x, y);
4901
4902   MovDir[x][y] = direction;
4903   GfxDir[x][y] = direction;
4904
4905   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4906                      direction == MV_DOWN && CAN_FALL(element) ?
4907                      ACTION_FALLING : ACTION_MOVING);
4908
4909   /* this is needed for CEs with property "can move" / "not moving" */
4910
4911   if (is_moving_after)
4912   {
4913     if (Feld[newx][newy] == EL_EMPTY)
4914       Feld[newx][newy] = EL_BLOCKED;
4915
4916     MovDir[newx][newy] = MovDir[x][y];
4917
4918     CustomValue[newx][newy] = CustomValue[x][y];
4919
4920     GfxFrame[newx][newy] = GfxFrame[x][y];
4921     GfxRandom[newx][newy] = GfxRandom[x][y];
4922     GfxAction[newx][newy] = GfxAction[x][y];
4923     GfxDir[newx][newy] = GfxDir[x][y];
4924   }
4925 }
4926
4927 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4928 {
4929   int direction = MovDir[x][y];
4930   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4931   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4932
4933   *goes_to_x = newx;
4934   *goes_to_y = newy;
4935 }
4936
4937 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4938 {
4939   int oldx = x, oldy = y;
4940   int direction = MovDir[x][y];
4941
4942   if (direction == MV_LEFT)
4943     oldx++;
4944   else if (direction == MV_RIGHT)
4945     oldx--;
4946   else if (direction == MV_UP)
4947     oldy++;
4948   else if (direction == MV_DOWN)
4949     oldy--;
4950
4951   *comes_from_x = oldx;
4952   *comes_from_y = oldy;
4953 }
4954
4955 static int MovingOrBlocked2Element(int x, int y)
4956 {
4957   int element = Feld[x][y];
4958
4959   if (element == EL_BLOCKED)
4960   {
4961     int oldx, oldy;
4962
4963     Blocked2Moving(x, y, &oldx, &oldy);
4964     return Feld[oldx][oldy];
4965   }
4966   else
4967     return element;
4968 }
4969
4970 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4971 {
4972   /* like MovingOrBlocked2Element(), but if element is moving
4973      and (x,y) is the field the moving element is just leaving,
4974      return EL_BLOCKED instead of the element value */
4975   int element = Feld[x][y];
4976
4977   if (IS_MOVING(x, y))
4978   {
4979     if (element == EL_BLOCKED)
4980     {
4981       int oldx, oldy;
4982
4983       Blocked2Moving(x, y, &oldx, &oldy);
4984       return Feld[oldx][oldy];
4985     }
4986     else
4987       return EL_BLOCKED;
4988   }
4989   else
4990     return element;
4991 }
4992
4993 static void RemoveField(int x, int y)
4994 {
4995   Feld[x][y] = EL_EMPTY;
4996
4997   MovPos[x][y] = 0;
4998   MovDir[x][y] = 0;
4999   MovDelay[x][y] = 0;
5000
5001   CustomValue[x][y] = 0;
5002
5003   AmoebaNr[x][y] = 0;
5004   ChangeDelay[x][y] = 0;
5005   ChangePage[x][y] = -1;
5006   Pushed[x][y] = FALSE;
5007
5008   GfxElement[x][y] = EL_UNDEFINED;
5009   GfxAction[x][y] = ACTION_DEFAULT;
5010   GfxDir[x][y] = MV_NONE;
5011 }
5012
5013 static void RemoveMovingField(int x, int y)
5014 {
5015   int oldx = x, oldy = y, newx = x, newy = y;
5016   int element = Feld[x][y];
5017   int next_element = EL_UNDEFINED;
5018
5019   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5020     return;
5021
5022   if (IS_MOVING(x, y))
5023   {
5024     Moving2Blocked(x, y, &newx, &newy);
5025
5026     if (Feld[newx][newy] != EL_BLOCKED)
5027     {
5028       /* element is moving, but target field is not free (blocked), but
5029          already occupied by something different (example: acid pool);
5030          in this case, only remove the moving field, but not the target */
5031
5032       RemoveField(oldx, oldy);
5033
5034       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5035
5036       TEST_DrawLevelField(oldx, oldy);
5037
5038       return;
5039     }
5040   }
5041   else if (element == EL_BLOCKED)
5042   {
5043     Blocked2Moving(x, y, &oldx, &oldy);
5044     if (!IS_MOVING(oldx, oldy))
5045       return;
5046   }
5047
5048   if (element == EL_BLOCKED &&
5049       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5050        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5051        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5052        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5053        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5054        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5055     next_element = get_next_element(Feld[oldx][oldy]);
5056
5057   RemoveField(oldx, oldy);
5058   RemoveField(newx, newy);
5059
5060   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5061
5062   if (next_element != EL_UNDEFINED)
5063     Feld[oldx][oldy] = next_element;
5064
5065   TEST_DrawLevelField(oldx, oldy);
5066   TEST_DrawLevelField(newx, newy);
5067 }
5068
5069 void DrawDynamite(int x, int y)
5070 {
5071   int sx = SCREENX(x), sy = SCREENY(y);
5072   int graphic = el2img(Feld[x][y]);
5073   int frame;
5074
5075   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5076     return;
5077
5078   if (IS_WALKABLE_INSIDE(Back[x][y]))
5079     return;
5080
5081   if (Back[x][y])
5082     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5083   else if (Store[x][y])
5084     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5085
5086   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5087
5088   if (Back[x][y] || Store[x][y])
5089     DrawGraphicThruMask(sx, sy, graphic, frame);
5090   else
5091     DrawGraphic(sx, sy, graphic, frame);
5092 }
5093
5094 static void CheckDynamite(int x, int y)
5095 {
5096   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5097   {
5098     MovDelay[x][y]--;
5099
5100     if (MovDelay[x][y] != 0)
5101     {
5102       DrawDynamite(x, y);
5103       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5104
5105       return;
5106     }
5107   }
5108
5109   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5110
5111   Bang(x, y);
5112 }
5113
5114 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5115 {
5116   boolean num_checked_players = 0;
5117   int i;
5118
5119   for (i = 0; i < MAX_PLAYERS; i++)
5120   {
5121     if (stored_player[i].active)
5122     {
5123       int sx = stored_player[i].jx;
5124       int sy = stored_player[i].jy;
5125
5126       if (num_checked_players == 0)
5127       {
5128         *sx1 = *sx2 = sx;
5129         *sy1 = *sy2 = sy;
5130       }
5131       else
5132       {
5133         *sx1 = MIN(*sx1, sx);
5134         *sy1 = MIN(*sy1, sy);
5135         *sx2 = MAX(*sx2, sx);
5136         *sy2 = MAX(*sy2, sy);
5137       }
5138
5139       num_checked_players++;
5140     }
5141   }
5142 }
5143
5144 static boolean checkIfAllPlayersFitToScreen_RND(void)
5145 {
5146   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5147
5148   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5149
5150   return (sx2 - sx1 < SCR_FIELDX &&
5151           sy2 - sy1 < SCR_FIELDY);
5152 }
5153
5154 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5155 {
5156   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5157
5158   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5159
5160   *sx = (sx1 + sx2) / 2;
5161   *sy = (sy1 + sy2) / 2;
5162 }
5163
5164 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5165                                boolean center_screen, boolean quick_relocation)
5166 {
5167   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5168   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5169   boolean no_delay = (tape.warp_forward);
5170   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5171   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5172   int new_scroll_x, new_scroll_y;
5173
5174   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5175   {
5176     /* case 1: quick relocation inside visible screen (without scrolling) */
5177
5178     RedrawPlayfield();
5179
5180     return;
5181   }
5182
5183   if (!level.shifted_relocation || center_screen)
5184   {
5185     /* relocation _with_ centering of screen */
5186
5187     new_scroll_x = SCROLL_POSITION_X(x);
5188     new_scroll_y = SCROLL_POSITION_Y(y);
5189   }
5190   else
5191   {
5192     /* relocation _without_ centering of screen */
5193
5194     int center_scroll_x = SCROLL_POSITION_X(old_x);
5195     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5196     int offset_x = x + (scroll_x - center_scroll_x);
5197     int offset_y = y + (scroll_y - center_scroll_y);
5198
5199     /* for new screen position, apply previous offset to center position */
5200     new_scroll_x = SCROLL_POSITION_X(offset_x);
5201     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5202   }
5203
5204   if (quick_relocation)
5205   {
5206     /* case 2: quick relocation (redraw without visible scrolling) */
5207
5208     scroll_x = new_scroll_x;
5209     scroll_y = new_scroll_y;
5210
5211     RedrawPlayfield();
5212
5213     return;
5214   }
5215
5216   /* case 3: visible relocation (with scrolling to new position) */
5217
5218   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5219
5220   SetVideoFrameDelay(wait_delay_value);
5221
5222   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5223   {
5224     int dx = 0, dy = 0;
5225     int fx = FX, fy = FY;
5226
5227     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5228     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5229
5230     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5231       break;
5232
5233     scroll_x -= dx;
5234     scroll_y -= dy;
5235
5236     fx += dx * TILEX / 2;
5237     fy += dy * TILEY / 2;
5238
5239     ScrollLevel(dx, dy);
5240     DrawAllPlayers();
5241
5242     /* scroll in two steps of half tile size to make things smoother */
5243     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5244
5245     /* scroll second step to align at full tile size */
5246     BlitScreenToBitmap(window);
5247   }
5248
5249   DrawAllPlayers();
5250   BackToFront();
5251
5252   SetVideoFrameDelay(frame_delay_value_old);
5253 }
5254
5255 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5256 {
5257   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5258   int player_nr = GET_PLAYER_NR(el_player);
5259   struct PlayerInfo *player = &stored_player[player_nr];
5260   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5261   boolean no_delay = (tape.warp_forward);
5262   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5263   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5264   int old_jx = player->jx;
5265   int old_jy = player->jy;
5266   int old_element = Feld[old_jx][old_jy];
5267   int element = Feld[jx][jy];
5268   boolean player_relocated = (old_jx != jx || old_jy != jy);
5269
5270   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5271   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5272   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5273   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5274   int leave_side_horiz = move_dir_horiz;
5275   int leave_side_vert  = move_dir_vert;
5276   int enter_side = enter_side_horiz | enter_side_vert;
5277   int leave_side = leave_side_horiz | leave_side_vert;
5278
5279   if (player->GameOver)         /* do not reanimate dead player */
5280     return;
5281
5282   if (!player_relocated)        /* no need to relocate the player */
5283     return;
5284
5285   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5286   {
5287     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5288     DrawLevelField(jx, jy);
5289   }
5290
5291   if (player->present)
5292   {
5293     while (player->MovPos)
5294     {
5295       ScrollPlayer(player, SCROLL_GO_ON);
5296       ScrollScreen(NULL, SCROLL_GO_ON);
5297
5298       AdvanceFrameAndPlayerCounters(player->index_nr);
5299
5300       DrawPlayer(player);
5301
5302       BackToFront_WithFrameDelay(wait_delay_value);
5303     }
5304
5305     DrawPlayer(player);         /* needed here only to cleanup last field */
5306     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5307
5308     player->is_moving = FALSE;
5309   }
5310
5311   if (IS_CUSTOM_ELEMENT(old_element))
5312     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5313                                CE_LEFT_BY_PLAYER,
5314                                player->index_bit, leave_side);
5315
5316   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5317                                       CE_PLAYER_LEAVES_X,
5318                                       player->index_bit, leave_side);
5319
5320   Feld[jx][jy] = el_player;
5321   InitPlayerField(jx, jy, el_player, TRUE);
5322
5323   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5324      possible that the relocation target field did not contain a player element,
5325      but a walkable element, to which the new player was relocated -- in this
5326      case, restore that (already initialized!) element on the player field */
5327   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5328   {
5329     Feld[jx][jy] = element;     /* restore previously existing element */
5330   }
5331
5332   /* only visually relocate centered player */
5333   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5334                      FALSE, level.instant_relocation);
5335
5336   TestIfPlayerTouchesBadThing(jx, jy);
5337   TestIfPlayerTouchesCustomElement(jx, jy);
5338
5339   if (IS_CUSTOM_ELEMENT(element))
5340     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5341                                player->index_bit, enter_side);
5342
5343   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5344                                       player->index_bit, enter_side);
5345
5346   if (player->is_switching)
5347   {
5348     /* ensure that relocation while still switching an element does not cause
5349        a new element to be treated as also switched directly after relocation
5350        (this is important for teleporter switches that teleport the player to
5351        a place where another teleporter switch is in the same direction, which
5352        would then incorrectly be treated as immediately switched before the
5353        direction key that caused the switch was released) */
5354
5355     player->switch_x += jx - old_jx;
5356     player->switch_y += jy - old_jy;
5357   }
5358 }
5359
5360 static void Explode(int ex, int ey, int phase, int mode)
5361 {
5362   int x, y;
5363   int last_phase;
5364   int border_element;
5365
5366   /* !!! eliminate this variable !!! */
5367   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5368
5369   if (game.explosions_delayed)
5370   {
5371     ExplodeField[ex][ey] = mode;
5372     return;
5373   }
5374
5375   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5376   {
5377     int center_element = Feld[ex][ey];
5378     int artwork_element, explosion_element;     /* set these values later */
5379
5380     /* remove things displayed in background while burning dynamite */
5381     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5382       Back[ex][ey] = 0;
5383
5384     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5385     {
5386       /* put moving element to center field (and let it explode there) */
5387       center_element = MovingOrBlocked2Element(ex, ey);
5388       RemoveMovingField(ex, ey);
5389       Feld[ex][ey] = center_element;
5390     }
5391
5392     /* now "center_element" is finally determined -- set related values now */
5393     artwork_element = center_element;           /* for custom player artwork */
5394     explosion_element = center_element;         /* for custom player artwork */
5395
5396     if (IS_PLAYER(ex, ey))
5397     {
5398       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5399
5400       artwork_element = stored_player[player_nr].artwork_element;
5401
5402       if (level.use_explosion_element[player_nr])
5403       {
5404         explosion_element = level.explosion_element[player_nr];
5405         artwork_element = explosion_element;
5406       }
5407     }
5408
5409     if (mode == EX_TYPE_NORMAL ||
5410         mode == EX_TYPE_CENTER ||
5411         mode == EX_TYPE_CROSS)
5412       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5413
5414     last_phase = element_info[explosion_element].explosion_delay + 1;
5415
5416     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5417     {
5418       int xx = x - ex + 1;
5419       int yy = y - ey + 1;
5420       int element;
5421
5422       if (!IN_LEV_FIELD(x, y) ||
5423           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5424           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5425         continue;
5426
5427       element = Feld[x][y];
5428
5429       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5430       {
5431         element = MovingOrBlocked2Element(x, y);
5432
5433         if (!IS_EXPLOSION_PROOF(element))
5434           RemoveMovingField(x, y);
5435       }
5436
5437       /* indestructible elements can only explode in center (but not flames) */
5438       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5439                                            mode == EX_TYPE_BORDER)) ||
5440           element == EL_FLAMES)
5441         continue;
5442
5443       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5444          behaviour, for example when touching a yamyam that explodes to rocks
5445          with active deadly shield, a rock is created under the player !!! */
5446       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5447 #if 0
5448       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5449           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5450            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5451 #else
5452       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5453 #endif
5454       {
5455         if (IS_ACTIVE_BOMB(element))
5456         {
5457           /* re-activate things under the bomb like gate or penguin */
5458           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5459           Back[x][y] = 0;
5460         }
5461
5462         continue;
5463       }
5464
5465       /* save walkable background elements while explosion on same tile */
5466       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5467           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5468         Back[x][y] = element;
5469
5470       /* ignite explodable elements reached by other explosion */
5471       if (element == EL_EXPLOSION)
5472         element = Store2[x][y];
5473
5474       if (AmoebaNr[x][y] &&
5475           (element == EL_AMOEBA_FULL ||
5476            element == EL_BD_AMOEBA ||
5477            element == EL_AMOEBA_GROWING))
5478       {
5479         AmoebaCnt[AmoebaNr[x][y]]--;
5480         AmoebaCnt2[AmoebaNr[x][y]]--;
5481       }
5482
5483       RemoveField(x, y);
5484
5485       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5486       {
5487         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5488
5489         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5490
5491         if (PLAYERINFO(ex, ey)->use_murphy)
5492           Store[x][y] = EL_EMPTY;
5493       }
5494
5495       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5496          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5497       else if (ELEM_IS_PLAYER(center_element))
5498         Store[x][y] = EL_EMPTY;
5499       else if (center_element == EL_YAMYAM)
5500         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5501       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5502         Store[x][y] = element_info[center_element].content.e[xx][yy];
5503 #if 1
5504       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5505          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5506          otherwise) -- FIX THIS !!! */
5507       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5508         Store[x][y] = element_info[element].content.e[1][1];
5509 #else
5510       else if (!CAN_EXPLODE(element))
5511         Store[x][y] = element_info[element].content.e[1][1];
5512 #endif
5513       else
5514         Store[x][y] = EL_EMPTY;
5515
5516       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5517           center_element == EL_AMOEBA_TO_DIAMOND)
5518         Store2[x][y] = element;
5519
5520       Feld[x][y] = EL_EXPLOSION;
5521       GfxElement[x][y] = artwork_element;
5522
5523       ExplodePhase[x][y] = 1;
5524       ExplodeDelay[x][y] = last_phase;
5525
5526       Stop[x][y] = TRUE;
5527     }
5528
5529     if (center_element == EL_YAMYAM)
5530       game.yamyam_content_nr =
5531         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5532
5533     return;
5534   }
5535
5536   if (Stop[ex][ey])
5537     return;
5538
5539   x = ex;
5540   y = ey;
5541
5542   if (phase == 1)
5543     GfxFrame[x][y] = 0;         /* restart explosion animation */
5544
5545   last_phase = ExplodeDelay[x][y];
5546
5547   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5548
5549   /* this can happen if the player leaves an explosion just in time */
5550   if (GfxElement[x][y] == EL_UNDEFINED)
5551     GfxElement[x][y] = EL_EMPTY;
5552
5553   border_element = Store2[x][y];
5554   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5555     border_element = StorePlayer[x][y];
5556
5557   if (phase == element_info[border_element].ignition_delay ||
5558       phase == last_phase)
5559   {
5560     boolean border_explosion = FALSE;
5561
5562     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5563         !PLAYER_EXPLOSION_PROTECTED(x, y))
5564     {
5565       KillPlayerUnlessExplosionProtected(x, y);
5566       border_explosion = TRUE;
5567     }
5568     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5569     {
5570       Feld[x][y] = Store2[x][y];
5571       Store2[x][y] = 0;
5572       Bang(x, y);
5573       border_explosion = TRUE;
5574     }
5575     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5576     {
5577       AmoebeUmwandeln(x, y);
5578       Store2[x][y] = 0;
5579       border_explosion = TRUE;
5580     }
5581
5582     /* if an element just explodes due to another explosion (chain-reaction),
5583        do not immediately end the new explosion when it was the last frame of
5584        the explosion (as it would be done in the following "if"-statement!) */
5585     if (border_explosion && phase == last_phase)
5586       return;
5587   }
5588
5589   if (phase == last_phase)
5590   {
5591     int element;
5592
5593     element = Feld[x][y] = Store[x][y];
5594     Store[x][y] = Store2[x][y] = 0;
5595     GfxElement[x][y] = EL_UNDEFINED;
5596
5597     /* player can escape from explosions and might therefore be still alive */
5598     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5599         element <= EL_PLAYER_IS_EXPLODING_4)
5600     {
5601       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5602       int explosion_element = EL_PLAYER_1 + player_nr;
5603       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5604       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5605
5606       if (level.use_explosion_element[player_nr])
5607         explosion_element = level.explosion_element[player_nr];
5608
5609       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5610                     element_info[explosion_element].content.e[xx][yy]);
5611     }
5612
5613     /* restore probably existing indestructible background element */
5614     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5615       element = Feld[x][y] = Back[x][y];
5616     Back[x][y] = 0;
5617
5618     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5619     GfxDir[x][y] = MV_NONE;
5620     ChangeDelay[x][y] = 0;
5621     ChangePage[x][y] = -1;
5622
5623     CustomValue[x][y] = 0;
5624
5625     InitField_WithBug2(x, y, FALSE);
5626
5627     TEST_DrawLevelField(x, y);
5628
5629     TestIfElementTouchesCustomElement(x, y);
5630
5631     if (GFX_CRUMBLED(element))
5632       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5633
5634     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5635       StorePlayer[x][y] = 0;
5636
5637     if (ELEM_IS_PLAYER(element))
5638       RelocatePlayer(x, y, element);
5639   }
5640   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5641   {
5642     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5643     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5644
5645     if (phase == delay)
5646       TEST_DrawLevelFieldCrumbled(x, y);
5647
5648     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5649     {
5650       DrawLevelElement(x, y, Back[x][y]);
5651       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5652     }
5653     else if (IS_WALKABLE_UNDER(Back[x][y]))
5654     {
5655       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5656       DrawLevelElementThruMask(x, y, Back[x][y]);
5657     }
5658     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5659       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5660   }
5661 }
5662
5663 static void DynaExplode(int ex, int ey)
5664 {
5665   int i, j;
5666   int dynabomb_element = Feld[ex][ey];
5667   int dynabomb_size = 1;
5668   boolean dynabomb_xl = FALSE;
5669   struct PlayerInfo *player;
5670   static int xy[4][2] =
5671   {
5672     { 0, -1 },
5673     { -1, 0 },
5674     { +1, 0 },
5675     { 0, +1 }
5676   };
5677
5678   if (IS_ACTIVE_BOMB(dynabomb_element))
5679   {
5680     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5681     dynabomb_size = player->dynabomb_size;
5682     dynabomb_xl = player->dynabomb_xl;
5683     player->dynabombs_left++;
5684   }
5685
5686   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5687
5688   for (i = 0; i < NUM_DIRECTIONS; i++)
5689   {
5690     for (j = 1; j <= dynabomb_size; j++)
5691     {
5692       int x = ex + j * xy[i][0];
5693       int y = ey + j * xy[i][1];
5694       int element;
5695
5696       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5697         break;
5698
5699       element = Feld[x][y];
5700
5701       /* do not restart explosions of fields with active bombs */
5702       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5703         continue;
5704
5705       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5706
5707       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5708           !IS_DIGGABLE(element) && !dynabomb_xl)
5709         break;
5710     }
5711   }
5712 }
5713
5714 void Bang(int x, int y)
5715 {
5716   int element = MovingOrBlocked2Element(x, y);
5717   int explosion_type = EX_TYPE_NORMAL;
5718
5719   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5720   {
5721     struct PlayerInfo *player = PLAYERINFO(x, y);
5722
5723     element = Feld[x][y] = player->initial_element;
5724
5725     if (level.use_explosion_element[player->index_nr])
5726     {
5727       int explosion_element = level.explosion_element[player->index_nr];
5728
5729       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5730         explosion_type = EX_TYPE_CROSS;
5731       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5732         explosion_type = EX_TYPE_CENTER;
5733     }
5734   }
5735
5736   switch (element)
5737   {
5738     case EL_BUG:
5739     case EL_SPACESHIP:
5740     case EL_BD_BUTTERFLY:
5741     case EL_BD_FIREFLY:
5742     case EL_YAMYAM:
5743     case EL_DARK_YAMYAM:
5744     case EL_ROBOT:
5745     case EL_PACMAN:
5746     case EL_MOLE:
5747       RaiseScoreElement(element);
5748       break;
5749
5750     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5751     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5752     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5753     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5754     case EL_DYNABOMB_INCREASE_NUMBER:
5755     case EL_DYNABOMB_INCREASE_SIZE:
5756     case EL_DYNABOMB_INCREASE_POWER:
5757       explosion_type = EX_TYPE_DYNA;
5758       break;
5759
5760     case EL_DC_LANDMINE:
5761       explosion_type = EX_TYPE_CENTER;
5762       break;
5763
5764     case EL_PENGUIN:
5765     case EL_LAMP:
5766     case EL_LAMP_ACTIVE:
5767     case EL_AMOEBA_TO_DIAMOND:
5768       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5769         explosion_type = EX_TYPE_CENTER;
5770       break;
5771
5772     default:
5773       if (element_info[element].explosion_type == EXPLODES_CROSS)
5774         explosion_type = EX_TYPE_CROSS;
5775       else if (element_info[element].explosion_type == EXPLODES_1X1)
5776         explosion_type = EX_TYPE_CENTER;
5777       break;
5778   }
5779
5780   if (explosion_type == EX_TYPE_DYNA)
5781     DynaExplode(x, y);
5782   else
5783     Explode(x, y, EX_PHASE_START, explosion_type);
5784
5785   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5786 }
5787
5788 static void SplashAcid(int x, int y)
5789 {
5790   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5791       (!IN_LEV_FIELD(x - 1, y - 2) ||
5792        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5793     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5794
5795   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5796       (!IN_LEV_FIELD(x + 1, y - 2) ||
5797        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5798     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5799
5800   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5801 }
5802
5803 static void InitBeltMovement(void)
5804 {
5805   static int belt_base_element[4] =
5806   {
5807     EL_CONVEYOR_BELT_1_LEFT,
5808     EL_CONVEYOR_BELT_2_LEFT,
5809     EL_CONVEYOR_BELT_3_LEFT,
5810     EL_CONVEYOR_BELT_4_LEFT
5811   };
5812   static int belt_base_active_element[4] =
5813   {
5814     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5815     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5816     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5817     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5818   };
5819
5820   int x, y, i, j;
5821
5822   /* set frame order for belt animation graphic according to belt direction */
5823   for (i = 0; i < NUM_BELTS; i++)
5824   {
5825     int belt_nr = i;
5826
5827     for (j = 0; j < NUM_BELT_PARTS; j++)
5828     {
5829       int element = belt_base_active_element[belt_nr] + j;
5830       int graphic_1 = el2img(element);
5831       int graphic_2 = el2panelimg(element);
5832
5833       if (game.belt_dir[i] == MV_LEFT)
5834       {
5835         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5836         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5837       }
5838       else
5839       {
5840         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5841         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5842       }
5843     }
5844   }
5845
5846   SCAN_PLAYFIELD(x, y)
5847   {
5848     int element = Feld[x][y];
5849
5850     for (i = 0; i < NUM_BELTS; i++)
5851     {
5852       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5853       {
5854         int e_belt_nr = getBeltNrFromBeltElement(element);
5855         int belt_nr = i;
5856
5857         if (e_belt_nr == belt_nr)
5858         {
5859           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5860
5861           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5862         }
5863       }
5864     }
5865   }
5866 }
5867
5868 static void ToggleBeltSwitch(int x, int y)
5869 {
5870   static int belt_base_element[4] =
5871   {
5872     EL_CONVEYOR_BELT_1_LEFT,
5873     EL_CONVEYOR_BELT_2_LEFT,
5874     EL_CONVEYOR_BELT_3_LEFT,
5875     EL_CONVEYOR_BELT_4_LEFT
5876   };
5877   static int belt_base_active_element[4] =
5878   {
5879     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5880     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5881     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5882     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5883   };
5884   static int belt_base_switch_element[4] =
5885   {
5886     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5887     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5888     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5889     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5890   };
5891   static int belt_move_dir[4] =
5892   {
5893     MV_LEFT,
5894     MV_NONE,
5895     MV_RIGHT,
5896     MV_NONE,
5897   };
5898
5899   int element = Feld[x][y];
5900   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5901   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5902   int belt_dir = belt_move_dir[belt_dir_nr];
5903   int xx, yy, i;
5904
5905   if (!IS_BELT_SWITCH(element))
5906     return;
5907
5908   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5909   game.belt_dir[belt_nr] = belt_dir;
5910
5911   if (belt_dir_nr == 3)
5912     belt_dir_nr = 1;
5913
5914   /* set frame order for belt animation graphic according to belt direction */
5915   for (i = 0; i < NUM_BELT_PARTS; i++)
5916   {
5917     int element = belt_base_active_element[belt_nr] + i;
5918     int graphic_1 = el2img(element);
5919     int graphic_2 = el2panelimg(element);
5920
5921     if (belt_dir == MV_LEFT)
5922     {
5923       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5924       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5925     }
5926     else
5927     {
5928       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5929       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5930     }
5931   }
5932
5933   SCAN_PLAYFIELD(xx, yy)
5934   {
5935     int element = Feld[xx][yy];
5936
5937     if (IS_BELT_SWITCH(element))
5938     {
5939       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5940
5941       if (e_belt_nr == belt_nr)
5942       {
5943         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5944         TEST_DrawLevelField(xx, yy);
5945       }
5946     }
5947     else if (IS_BELT(element) && belt_dir != MV_NONE)
5948     {
5949       int e_belt_nr = getBeltNrFromBeltElement(element);
5950
5951       if (e_belt_nr == belt_nr)
5952       {
5953         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5954
5955         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5956         TEST_DrawLevelField(xx, yy);
5957       }
5958     }
5959     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5960     {
5961       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5962
5963       if (e_belt_nr == belt_nr)
5964       {
5965         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5966
5967         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5968         TEST_DrawLevelField(xx, yy);
5969       }
5970     }
5971   }
5972 }
5973
5974 static void ToggleSwitchgateSwitch(int x, int y)
5975 {
5976   int xx, yy;
5977
5978   game.switchgate_pos = !game.switchgate_pos;
5979
5980   SCAN_PLAYFIELD(xx, yy)
5981   {
5982     int element = Feld[xx][yy];
5983
5984     if (element == EL_SWITCHGATE_SWITCH_UP)
5985     {
5986       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5987       TEST_DrawLevelField(xx, yy);
5988     }
5989     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5990     {
5991       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5992       TEST_DrawLevelField(xx, yy);
5993     }
5994     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5995     {
5996       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5997       TEST_DrawLevelField(xx, yy);
5998     }
5999     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6000     {
6001       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6002       TEST_DrawLevelField(xx, yy);
6003     }
6004     else if (element == EL_SWITCHGATE_OPEN ||
6005              element == EL_SWITCHGATE_OPENING)
6006     {
6007       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6008
6009       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6010     }
6011     else if (element == EL_SWITCHGATE_CLOSED ||
6012              element == EL_SWITCHGATE_CLOSING)
6013     {
6014       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6015
6016       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6017     }
6018   }
6019 }
6020
6021 static int getInvisibleActiveFromInvisibleElement(int element)
6022 {
6023   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6024           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6025           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6026           element);
6027 }
6028
6029 static int getInvisibleFromInvisibleActiveElement(int element)
6030 {
6031   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6032           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6033           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6034           element);
6035 }
6036
6037 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6038 {
6039   int x, y;
6040
6041   SCAN_PLAYFIELD(x, y)
6042   {
6043     int element = Feld[x][y];
6044
6045     if (element == EL_LIGHT_SWITCH &&
6046         game.light_time_left > 0)
6047     {
6048       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6049       TEST_DrawLevelField(x, y);
6050     }
6051     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6052              game.light_time_left == 0)
6053     {
6054       Feld[x][y] = EL_LIGHT_SWITCH;
6055       TEST_DrawLevelField(x, y);
6056     }
6057     else if (element == EL_EMC_DRIPPER &&
6058              game.light_time_left > 0)
6059     {
6060       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6061       TEST_DrawLevelField(x, y);
6062     }
6063     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6064              game.light_time_left == 0)
6065     {
6066       Feld[x][y] = EL_EMC_DRIPPER;
6067       TEST_DrawLevelField(x, y);
6068     }
6069     else if (element == EL_INVISIBLE_STEELWALL ||
6070              element == EL_INVISIBLE_WALL ||
6071              element == EL_INVISIBLE_SAND)
6072     {
6073       if (game.light_time_left > 0)
6074         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6075
6076       TEST_DrawLevelField(x, y);
6077
6078       /* uncrumble neighbour fields, if needed */
6079       if (element == EL_INVISIBLE_SAND)
6080         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6081     }
6082     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6083              element == EL_INVISIBLE_WALL_ACTIVE ||
6084              element == EL_INVISIBLE_SAND_ACTIVE)
6085     {
6086       if (game.light_time_left == 0)
6087         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6088
6089       TEST_DrawLevelField(x, y);
6090
6091       /* re-crumble neighbour fields, if needed */
6092       if (element == EL_INVISIBLE_SAND)
6093         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6094     }
6095   }
6096 }
6097
6098 static void RedrawAllInvisibleElementsForLenses(void)
6099 {
6100   int x, y;
6101
6102   SCAN_PLAYFIELD(x, y)
6103   {
6104     int element = Feld[x][y];
6105
6106     if (element == EL_EMC_DRIPPER &&
6107         game.lenses_time_left > 0)
6108     {
6109       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6110       TEST_DrawLevelField(x, y);
6111     }
6112     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6113              game.lenses_time_left == 0)
6114     {
6115       Feld[x][y] = EL_EMC_DRIPPER;
6116       TEST_DrawLevelField(x, y);
6117     }
6118     else if (element == EL_INVISIBLE_STEELWALL ||
6119              element == EL_INVISIBLE_WALL ||
6120              element == EL_INVISIBLE_SAND)
6121     {
6122       if (game.lenses_time_left > 0)
6123         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6124
6125       TEST_DrawLevelField(x, y);
6126
6127       /* uncrumble neighbour fields, if needed */
6128       if (element == EL_INVISIBLE_SAND)
6129         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6130     }
6131     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6132              element == EL_INVISIBLE_WALL_ACTIVE ||
6133              element == EL_INVISIBLE_SAND_ACTIVE)
6134     {
6135       if (game.lenses_time_left == 0)
6136         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6137
6138       TEST_DrawLevelField(x, y);
6139
6140       /* re-crumble neighbour fields, if needed */
6141       if (element == EL_INVISIBLE_SAND)
6142         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6143     }
6144   }
6145 }
6146
6147 static void RedrawAllInvisibleElementsForMagnifier(void)
6148 {
6149   int x, y;
6150
6151   SCAN_PLAYFIELD(x, y)
6152   {
6153     int element = Feld[x][y];
6154
6155     if (element == EL_EMC_FAKE_GRASS &&
6156         game.magnify_time_left > 0)
6157     {
6158       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6159       TEST_DrawLevelField(x, y);
6160     }
6161     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6162              game.magnify_time_left == 0)
6163     {
6164       Feld[x][y] = EL_EMC_FAKE_GRASS;
6165       TEST_DrawLevelField(x, y);
6166     }
6167     else if (IS_GATE_GRAY(element) &&
6168              game.magnify_time_left > 0)
6169     {
6170       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6171                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6172                     IS_EM_GATE_GRAY(element) ?
6173                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6174                     IS_EMC_GATE_GRAY(element) ?
6175                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6176                     IS_DC_GATE_GRAY(element) ?
6177                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6178                     element);
6179       TEST_DrawLevelField(x, y);
6180     }
6181     else if (IS_GATE_GRAY_ACTIVE(element) &&
6182              game.magnify_time_left == 0)
6183     {
6184       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6185                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6186                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6187                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6188                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6189                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6190                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6191                     EL_DC_GATE_WHITE_GRAY :
6192                     element);
6193       TEST_DrawLevelField(x, y);
6194     }
6195   }
6196 }
6197
6198 static void ToggleLightSwitch(int x, int y)
6199 {
6200   int element = Feld[x][y];
6201
6202   game.light_time_left =
6203     (element == EL_LIGHT_SWITCH ?
6204      level.time_light * FRAMES_PER_SECOND : 0);
6205
6206   RedrawAllLightSwitchesAndInvisibleElements();
6207 }
6208
6209 static void ActivateTimegateSwitch(int x, int y)
6210 {
6211   int xx, yy;
6212
6213   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6214
6215   SCAN_PLAYFIELD(xx, yy)
6216   {
6217     int element = Feld[xx][yy];
6218
6219     if (element == EL_TIMEGATE_CLOSED ||
6220         element == EL_TIMEGATE_CLOSING)
6221     {
6222       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6223       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6224     }
6225
6226     /*
6227     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6228     {
6229       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6230       TEST_DrawLevelField(xx, yy);
6231     }
6232     */
6233
6234   }
6235
6236   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6237                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6238 }
6239
6240 static void Impact(int x, int y)
6241 {
6242   boolean last_line = (y == lev_fieldy - 1);
6243   boolean object_hit = FALSE;
6244   boolean impact = (last_line || object_hit);
6245   int element = Feld[x][y];
6246   int smashed = EL_STEELWALL;
6247
6248   if (!last_line)       /* check if element below was hit */
6249   {
6250     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6251       return;
6252
6253     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6254                                          MovDir[x][y + 1] != MV_DOWN ||
6255                                          MovPos[x][y + 1] <= TILEY / 2));
6256
6257     /* do not smash moving elements that left the smashed field in time */
6258     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6259         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6260       object_hit = FALSE;
6261
6262 #if USE_QUICKSAND_IMPACT_BUGFIX
6263     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6264     {
6265       RemoveMovingField(x, y + 1);
6266       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6267       Feld[x][y + 2] = EL_ROCK;
6268       TEST_DrawLevelField(x, y + 2);
6269
6270       object_hit = TRUE;
6271     }
6272
6273     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6274     {
6275       RemoveMovingField(x, y + 1);
6276       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6277       Feld[x][y + 2] = EL_ROCK;
6278       TEST_DrawLevelField(x, y + 2);
6279
6280       object_hit = TRUE;
6281     }
6282 #endif
6283
6284     if (object_hit)
6285       smashed = MovingOrBlocked2Element(x, y + 1);
6286
6287     impact = (last_line || object_hit);
6288   }
6289
6290   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6291   {
6292     SplashAcid(x, y + 1);
6293     return;
6294   }
6295
6296   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6297   /* only reset graphic animation if graphic really changes after impact */
6298   if (impact &&
6299       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6300   {
6301     ResetGfxAnimation(x, y);
6302     TEST_DrawLevelField(x, y);
6303   }
6304
6305   if (impact && CAN_EXPLODE_IMPACT(element))
6306   {
6307     Bang(x, y);
6308     return;
6309   }
6310   else if (impact && element == EL_PEARL &&
6311            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6312   {
6313     ResetGfxAnimation(x, y);
6314
6315     Feld[x][y] = EL_PEARL_BREAKING;
6316     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6317     return;
6318   }
6319   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6320   {
6321     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6322
6323     return;
6324   }
6325
6326   if (impact && element == EL_AMOEBA_DROP)
6327   {
6328     if (object_hit && IS_PLAYER(x, y + 1))
6329       KillPlayerUnlessEnemyProtected(x, y + 1);
6330     else if (object_hit && smashed == EL_PENGUIN)
6331       Bang(x, y + 1);
6332     else
6333     {
6334       Feld[x][y] = EL_AMOEBA_GROWING;
6335       Store[x][y] = EL_AMOEBA_WET;
6336
6337       ResetRandomAnimationValue(x, y);
6338     }
6339     return;
6340   }
6341
6342   if (object_hit)               /* check which object was hit */
6343   {
6344     if ((CAN_PASS_MAGIC_WALL(element) && 
6345          (smashed == EL_MAGIC_WALL ||
6346           smashed == EL_BD_MAGIC_WALL)) ||
6347         (CAN_PASS_DC_MAGIC_WALL(element) &&
6348          smashed == EL_DC_MAGIC_WALL))
6349     {
6350       int xx, yy;
6351       int activated_magic_wall =
6352         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6353          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6354          EL_DC_MAGIC_WALL_ACTIVE);
6355
6356       /* activate magic wall / mill */
6357       SCAN_PLAYFIELD(xx, yy)
6358       {
6359         if (Feld[xx][yy] == smashed)
6360           Feld[xx][yy] = activated_magic_wall;
6361       }
6362
6363       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6364       game.magic_wall_active = TRUE;
6365
6366       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6367                             SND_MAGIC_WALL_ACTIVATING :
6368                             smashed == EL_BD_MAGIC_WALL ?
6369                             SND_BD_MAGIC_WALL_ACTIVATING :
6370                             SND_DC_MAGIC_WALL_ACTIVATING));
6371     }
6372
6373     if (IS_PLAYER(x, y + 1))
6374     {
6375       if (CAN_SMASH_PLAYER(element))
6376       {
6377         KillPlayerUnlessEnemyProtected(x, y + 1);
6378         return;
6379       }
6380     }
6381     else if (smashed == EL_PENGUIN)
6382     {
6383       if (CAN_SMASH_PLAYER(element))
6384       {
6385         Bang(x, y + 1);
6386         return;
6387       }
6388     }
6389     else if (element == EL_BD_DIAMOND)
6390     {
6391       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6392       {
6393         Bang(x, y + 1);
6394         return;
6395       }
6396     }
6397     else if (((element == EL_SP_INFOTRON ||
6398                element == EL_SP_ZONK) &&
6399               (smashed == EL_SP_SNIKSNAK ||
6400                smashed == EL_SP_ELECTRON ||
6401                smashed == EL_SP_DISK_ORANGE)) ||
6402              (element == EL_SP_INFOTRON &&
6403               smashed == EL_SP_DISK_YELLOW))
6404     {
6405       Bang(x, y + 1);
6406       return;
6407     }
6408     else if (CAN_SMASH_EVERYTHING(element))
6409     {
6410       if (IS_CLASSIC_ENEMY(smashed) ||
6411           CAN_EXPLODE_SMASHED(smashed))
6412       {
6413         Bang(x, y + 1);
6414         return;
6415       }
6416       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6417       {
6418         if (smashed == EL_LAMP ||
6419             smashed == EL_LAMP_ACTIVE)
6420         {
6421           Bang(x, y + 1);
6422           return;
6423         }
6424         else if (smashed == EL_NUT)
6425         {
6426           Feld[x][y + 1] = EL_NUT_BREAKING;
6427           PlayLevelSound(x, y, SND_NUT_BREAKING);
6428           RaiseScoreElement(EL_NUT);
6429           return;
6430         }
6431         else if (smashed == EL_PEARL)
6432         {
6433           ResetGfxAnimation(x, y);
6434
6435           Feld[x][y + 1] = EL_PEARL_BREAKING;
6436           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6437           return;
6438         }
6439         else if (smashed == EL_DIAMOND)
6440         {
6441           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6442           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6443           return;
6444         }
6445         else if (IS_BELT_SWITCH(smashed))
6446         {
6447           ToggleBeltSwitch(x, y + 1);
6448         }
6449         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6450                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6451                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6452                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6453         {
6454           ToggleSwitchgateSwitch(x, y + 1);
6455         }
6456         else if (smashed == EL_LIGHT_SWITCH ||
6457                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6458         {
6459           ToggleLightSwitch(x, y + 1);
6460         }
6461         else
6462         {
6463           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6464
6465           CheckElementChangeBySide(x, y + 1, smashed, element,
6466                                    CE_SWITCHED, CH_SIDE_TOP);
6467           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6468                                             CH_SIDE_TOP);
6469         }
6470       }
6471       else
6472       {
6473         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6474       }
6475     }
6476   }
6477
6478   /* play sound of magic wall / mill */
6479   if (!last_line &&
6480       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6481        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6482        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6483   {
6484     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6485       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6486     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6487       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6488     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6489       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6490
6491     return;
6492   }
6493
6494   /* play sound of object that hits the ground */
6495   if (last_line || object_hit)
6496     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6497 }
6498
6499 inline static void TurnRoundExt(int x, int y)
6500 {
6501   static struct
6502   {
6503     int dx, dy;
6504   } move_xy[] =
6505   {
6506     {  0,  0 },
6507     { -1,  0 },
6508     { +1,  0 },
6509     {  0,  0 },
6510     {  0, -1 },
6511     {  0,  0 }, { 0, 0 }, { 0, 0 },
6512     {  0, +1 }
6513   };
6514   static struct
6515   {
6516     int left, right, back;
6517   } turn[] =
6518   {
6519     { 0,        0,              0        },
6520     { MV_DOWN,  MV_UP,          MV_RIGHT },
6521     { MV_UP,    MV_DOWN,        MV_LEFT  },
6522     { 0,        0,              0        },
6523     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6524     { 0,        0,              0        },
6525     { 0,        0,              0        },
6526     { 0,        0,              0        },
6527     { MV_RIGHT, MV_LEFT,        MV_UP    }
6528   };
6529
6530   int element = Feld[x][y];
6531   int move_pattern = element_info[element].move_pattern;
6532
6533   int old_move_dir = MovDir[x][y];
6534   int left_dir  = turn[old_move_dir].left;
6535   int right_dir = turn[old_move_dir].right;
6536   int back_dir  = turn[old_move_dir].back;
6537
6538   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6539   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6540   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6541   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6542
6543   int left_x  = x + left_dx,  left_y  = y + left_dy;
6544   int right_x = x + right_dx, right_y = y + right_dy;
6545   int move_x  = x + move_dx,  move_y  = y + move_dy;
6546
6547   int xx, yy;
6548
6549   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6550   {
6551     TestIfBadThingTouchesOtherBadThing(x, y);
6552
6553     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6554       MovDir[x][y] = right_dir;
6555     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6556       MovDir[x][y] = left_dir;
6557
6558     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6559       MovDelay[x][y] = 9;
6560     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6561       MovDelay[x][y] = 1;
6562   }
6563   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6564   {
6565     TestIfBadThingTouchesOtherBadThing(x, y);
6566
6567     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6568       MovDir[x][y] = left_dir;
6569     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6570       MovDir[x][y] = right_dir;
6571
6572     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6573       MovDelay[x][y] = 9;
6574     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6575       MovDelay[x][y] = 1;
6576   }
6577   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6578   {
6579     TestIfBadThingTouchesOtherBadThing(x, y);
6580
6581     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6582       MovDir[x][y] = left_dir;
6583     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6584       MovDir[x][y] = right_dir;
6585
6586     if (MovDir[x][y] != old_move_dir)
6587       MovDelay[x][y] = 9;
6588   }
6589   else if (element == EL_YAMYAM)
6590   {
6591     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6592     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6593
6594     if (can_turn_left && can_turn_right)
6595       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6596     else if (can_turn_left)
6597       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6598     else if (can_turn_right)
6599       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6600     else
6601       MovDir[x][y] = back_dir;
6602
6603     MovDelay[x][y] = 16 + 16 * RND(3);
6604   }
6605   else if (element == EL_DARK_YAMYAM)
6606   {
6607     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6608                                                          left_x, left_y);
6609     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6610                                                          right_x, right_y);
6611
6612     if (can_turn_left && can_turn_right)
6613       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6614     else if (can_turn_left)
6615       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6616     else if (can_turn_right)
6617       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6618     else
6619       MovDir[x][y] = back_dir;
6620
6621     MovDelay[x][y] = 16 + 16 * RND(3);
6622   }
6623   else if (element == EL_PACMAN)
6624   {
6625     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6626     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6627
6628     if (can_turn_left && can_turn_right)
6629       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6630     else if (can_turn_left)
6631       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6632     else if (can_turn_right)
6633       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6634     else
6635       MovDir[x][y] = back_dir;
6636
6637     MovDelay[x][y] = 6 + RND(40);
6638   }
6639   else if (element == EL_PIG)
6640   {
6641     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6642     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6643     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6644     boolean should_turn_left, should_turn_right, should_move_on;
6645     int rnd_value = 24;
6646     int rnd = RND(rnd_value);
6647
6648     should_turn_left = (can_turn_left &&
6649                         (!can_move_on ||
6650                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6651                                                    y + back_dy + left_dy)));
6652     should_turn_right = (can_turn_right &&
6653                          (!can_move_on ||
6654                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6655                                                     y + back_dy + right_dy)));
6656     should_move_on = (can_move_on &&
6657                       (!can_turn_left ||
6658                        !can_turn_right ||
6659                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6660                                                  y + move_dy + left_dy) ||
6661                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6662                                                  y + move_dy + right_dy)));
6663
6664     if (should_turn_left || should_turn_right || should_move_on)
6665     {
6666       if (should_turn_left && should_turn_right && should_move_on)
6667         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6668                         rnd < 2 * rnd_value / 3 ? right_dir :
6669                         old_move_dir);
6670       else if (should_turn_left && should_turn_right)
6671         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6672       else if (should_turn_left && should_move_on)
6673         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6674       else if (should_turn_right && should_move_on)
6675         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6676       else if (should_turn_left)
6677         MovDir[x][y] = left_dir;
6678       else if (should_turn_right)
6679         MovDir[x][y] = right_dir;
6680       else if (should_move_on)
6681         MovDir[x][y] = old_move_dir;
6682     }
6683     else if (can_move_on && rnd > rnd_value / 8)
6684       MovDir[x][y] = old_move_dir;
6685     else if (can_turn_left && can_turn_right)
6686       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6687     else if (can_turn_left && rnd > rnd_value / 8)
6688       MovDir[x][y] = left_dir;
6689     else if (can_turn_right && rnd > rnd_value/8)
6690       MovDir[x][y] = right_dir;
6691     else
6692       MovDir[x][y] = back_dir;
6693
6694     xx = x + move_xy[MovDir[x][y]].dx;
6695     yy = y + move_xy[MovDir[x][y]].dy;
6696
6697     if (!IN_LEV_FIELD(xx, yy) ||
6698         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6699       MovDir[x][y] = old_move_dir;
6700
6701     MovDelay[x][y] = 0;
6702   }
6703   else if (element == EL_DRAGON)
6704   {
6705     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6706     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6707     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6708     int rnd_value = 24;
6709     int rnd = RND(rnd_value);
6710
6711     if (can_move_on && rnd > rnd_value / 8)
6712       MovDir[x][y] = old_move_dir;
6713     else if (can_turn_left && can_turn_right)
6714       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6715     else if (can_turn_left && rnd > rnd_value / 8)
6716       MovDir[x][y] = left_dir;
6717     else if (can_turn_right && rnd > rnd_value / 8)
6718       MovDir[x][y] = right_dir;
6719     else
6720       MovDir[x][y] = back_dir;
6721
6722     xx = x + move_xy[MovDir[x][y]].dx;
6723     yy = y + move_xy[MovDir[x][y]].dy;
6724
6725     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6726       MovDir[x][y] = old_move_dir;
6727
6728     MovDelay[x][y] = 0;
6729   }
6730   else if (element == EL_MOLE)
6731   {
6732     boolean can_move_on =
6733       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6734                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6735                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6736     if (!can_move_on)
6737     {
6738       boolean can_turn_left =
6739         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6740                               IS_AMOEBOID(Feld[left_x][left_y])));
6741
6742       boolean can_turn_right =
6743         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6744                               IS_AMOEBOID(Feld[right_x][right_y])));
6745
6746       if (can_turn_left && can_turn_right)
6747         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6748       else if (can_turn_left)
6749         MovDir[x][y] = left_dir;
6750       else
6751         MovDir[x][y] = right_dir;
6752     }
6753
6754     if (MovDir[x][y] != old_move_dir)
6755       MovDelay[x][y] = 9;
6756   }
6757   else if (element == EL_BALLOON)
6758   {
6759     MovDir[x][y] = game.wind_direction;
6760     MovDelay[x][y] = 0;
6761   }
6762   else if (element == EL_SPRING)
6763   {
6764     if (MovDir[x][y] & MV_HORIZONTAL)
6765     {
6766       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6767           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6768       {
6769         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6770         ResetGfxAnimation(move_x, move_y);
6771         TEST_DrawLevelField(move_x, move_y);
6772
6773         MovDir[x][y] = back_dir;
6774       }
6775       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6776                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6777         MovDir[x][y] = MV_NONE;
6778     }
6779
6780     MovDelay[x][y] = 0;
6781   }
6782   else if (element == EL_ROBOT ||
6783            element == EL_SATELLITE ||
6784            element == EL_PENGUIN ||
6785            element == EL_EMC_ANDROID)
6786   {
6787     int attr_x = -1, attr_y = -1;
6788
6789     if (AllPlayersGone)
6790     {
6791       attr_x = ExitX;
6792       attr_y = ExitY;
6793     }
6794     else
6795     {
6796       int i;
6797
6798       for (i = 0; i < MAX_PLAYERS; i++)
6799       {
6800         struct PlayerInfo *player = &stored_player[i];
6801         int jx = player->jx, jy = player->jy;
6802
6803         if (!player->active)
6804           continue;
6805
6806         if (attr_x == -1 ||
6807             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6808         {
6809           attr_x = jx;
6810           attr_y = jy;
6811         }
6812       }
6813     }
6814
6815     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6816         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6817          game.engine_version < VERSION_IDENT(3,1,0,0)))
6818     {
6819       attr_x = ZX;
6820       attr_y = ZY;
6821     }
6822
6823     if (element == EL_PENGUIN)
6824     {
6825       int i;
6826       static int xy[4][2] =
6827       {
6828         { 0, -1 },
6829         { -1, 0 },
6830         { +1, 0 },
6831         { 0, +1 }
6832       };
6833
6834       for (i = 0; i < NUM_DIRECTIONS; i++)
6835       {
6836         int ex = x + xy[i][0];
6837         int ey = y + xy[i][1];
6838
6839         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6840                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6841                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6842                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6843         {
6844           attr_x = ex;
6845           attr_y = ey;
6846           break;
6847         }
6848       }
6849     }
6850
6851     MovDir[x][y] = MV_NONE;
6852     if (attr_x < x)
6853       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6854     else if (attr_x > x)
6855       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6856     if (attr_y < y)
6857       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6858     else if (attr_y > y)
6859       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6860
6861     if (element == EL_ROBOT)
6862     {
6863       int newx, newy;
6864
6865       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6866         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6867       Moving2Blocked(x, y, &newx, &newy);
6868
6869       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6870         MovDelay[x][y] = 8 + 8 * !RND(3);
6871       else
6872         MovDelay[x][y] = 16;
6873     }
6874     else if (element == EL_PENGUIN)
6875     {
6876       int newx, newy;
6877
6878       MovDelay[x][y] = 1;
6879
6880       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6881       {
6882         boolean first_horiz = RND(2);
6883         int new_move_dir = MovDir[x][y];
6884
6885         MovDir[x][y] =
6886           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6887         Moving2Blocked(x, y, &newx, &newy);
6888
6889         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6890           return;
6891
6892         MovDir[x][y] =
6893           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6894         Moving2Blocked(x, y, &newx, &newy);
6895
6896         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6897           return;
6898
6899         MovDir[x][y] = old_move_dir;
6900         return;
6901       }
6902     }
6903     else if (element == EL_SATELLITE)
6904     {
6905       int newx, newy;
6906
6907       MovDelay[x][y] = 1;
6908
6909       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6910       {
6911         boolean first_horiz = RND(2);
6912         int new_move_dir = MovDir[x][y];
6913
6914         MovDir[x][y] =
6915           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6916         Moving2Blocked(x, y, &newx, &newy);
6917
6918         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6919           return;
6920
6921         MovDir[x][y] =
6922           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6923         Moving2Blocked(x, y, &newx, &newy);
6924
6925         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6926           return;
6927
6928         MovDir[x][y] = old_move_dir;
6929         return;
6930       }
6931     }
6932     else if (element == EL_EMC_ANDROID)
6933     {
6934       static int check_pos[16] =
6935       {
6936         -1,             /*  0 => (invalid)          */
6937         7,              /*  1 => MV_LEFT            */
6938         3,              /*  2 => MV_RIGHT           */
6939         -1,             /*  3 => (invalid)          */
6940         1,              /*  4 =>            MV_UP   */
6941         0,              /*  5 => MV_LEFT  | MV_UP   */
6942         2,              /*  6 => MV_RIGHT | MV_UP   */
6943         -1,             /*  7 => (invalid)          */
6944         5,              /*  8 =>            MV_DOWN */
6945         6,              /*  9 => MV_LEFT  | MV_DOWN */
6946         4,              /* 10 => MV_RIGHT | MV_DOWN */
6947         -1,             /* 11 => (invalid)          */
6948         -1,             /* 12 => (invalid)          */
6949         -1,             /* 13 => (invalid)          */
6950         -1,             /* 14 => (invalid)          */
6951         -1,             /* 15 => (invalid)          */
6952       };
6953       static struct
6954       {
6955         int dx, dy;
6956         int dir;
6957       } check_xy[8] =
6958       {
6959         { -1, -1,       MV_LEFT  | MV_UP   },
6960         {  0, -1,                  MV_UP   },
6961         { +1, -1,       MV_RIGHT | MV_UP   },
6962         { +1,  0,       MV_RIGHT           },
6963         { +1, +1,       MV_RIGHT | MV_DOWN },
6964         {  0, +1,                  MV_DOWN },
6965         { -1, +1,       MV_LEFT  | MV_DOWN },
6966         { -1,  0,       MV_LEFT            },
6967       };
6968       int start_pos, check_order;
6969       boolean can_clone = FALSE;
6970       int i;
6971
6972       /* check if there is any free field around current position */
6973       for (i = 0; i < 8; i++)
6974       {
6975         int newx = x + check_xy[i].dx;
6976         int newy = y + check_xy[i].dy;
6977
6978         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6979         {
6980           can_clone = TRUE;
6981
6982           break;
6983         }
6984       }
6985
6986       if (can_clone)            /* randomly find an element to clone */
6987       {
6988         can_clone = FALSE;
6989
6990         start_pos = check_pos[RND(8)];
6991         check_order = (RND(2) ? -1 : +1);
6992
6993         for (i = 0; i < 8; i++)
6994         {
6995           int pos_raw = start_pos + i * check_order;
6996           int pos = (pos_raw + 8) % 8;
6997           int newx = x + check_xy[pos].dx;
6998           int newy = y + check_xy[pos].dy;
6999
7000           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7001           {
7002             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7003             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7004
7005             Store[x][y] = Feld[newx][newy];
7006
7007             can_clone = TRUE;
7008
7009             break;
7010           }
7011         }
7012       }
7013
7014       if (can_clone)            /* randomly find a direction to move */
7015       {
7016         can_clone = FALSE;
7017
7018         start_pos = check_pos[RND(8)];
7019         check_order = (RND(2) ? -1 : +1);
7020
7021         for (i = 0; i < 8; i++)
7022         {
7023           int pos_raw = start_pos + i * check_order;
7024           int pos = (pos_raw + 8) % 8;
7025           int newx = x + check_xy[pos].dx;
7026           int newy = y + check_xy[pos].dy;
7027           int new_move_dir = check_xy[pos].dir;
7028
7029           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7030           {
7031             MovDir[x][y] = new_move_dir;
7032             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7033
7034             can_clone = TRUE;
7035
7036             break;
7037           }
7038         }
7039       }
7040
7041       if (can_clone)            /* cloning and moving successful */
7042         return;
7043
7044       /* cannot clone -- try to move towards player */
7045
7046       start_pos = check_pos[MovDir[x][y] & 0x0f];
7047       check_order = (RND(2) ? -1 : +1);
7048
7049       for (i = 0; i < 3; i++)
7050       {
7051         /* first check start_pos, then previous/next or (next/previous) pos */
7052         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7053         int pos = (pos_raw + 8) % 8;
7054         int newx = x + check_xy[pos].dx;
7055         int newy = y + check_xy[pos].dy;
7056         int new_move_dir = check_xy[pos].dir;
7057
7058         if (IS_PLAYER(newx, newy))
7059           break;
7060
7061         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7062         {
7063           MovDir[x][y] = new_move_dir;
7064           MovDelay[x][y] = level.android_move_time * 8 + 1;
7065
7066           break;
7067         }
7068       }
7069     }
7070   }
7071   else if (move_pattern == MV_TURNING_LEFT ||
7072            move_pattern == MV_TURNING_RIGHT ||
7073            move_pattern == MV_TURNING_LEFT_RIGHT ||
7074            move_pattern == MV_TURNING_RIGHT_LEFT ||
7075            move_pattern == MV_TURNING_RANDOM ||
7076            move_pattern == MV_ALL_DIRECTIONS)
7077   {
7078     boolean can_turn_left =
7079       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7080     boolean can_turn_right =
7081       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7082
7083     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7084       return;
7085
7086     if (move_pattern == MV_TURNING_LEFT)
7087       MovDir[x][y] = left_dir;
7088     else if (move_pattern == MV_TURNING_RIGHT)
7089       MovDir[x][y] = right_dir;
7090     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7091       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7092     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7093       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7094     else if (move_pattern == MV_TURNING_RANDOM)
7095       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7096                       can_turn_right && !can_turn_left ? right_dir :
7097                       RND(2) ? left_dir : right_dir);
7098     else if (can_turn_left && can_turn_right)
7099       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7100     else if (can_turn_left)
7101       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7102     else if (can_turn_right)
7103       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7104     else
7105       MovDir[x][y] = back_dir;
7106
7107     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7108   }
7109   else if (move_pattern == MV_HORIZONTAL ||
7110            move_pattern == MV_VERTICAL)
7111   {
7112     if (move_pattern & old_move_dir)
7113       MovDir[x][y] = back_dir;
7114     else if (move_pattern == MV_HORIZONTAL)
7115       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7116     else if (move_pattern == MV_VERTICAL)
7117       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7118
7119     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7120   }
7121   else if (move_pattern & MV_ANY_DIRECTION)
7122   {
7123     MovDir[x][y] = move_pattern;
7124     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7125   }
7126   else if (move_pattern & MV_WIND_DIRECTION)
7127   {
7128     MovDir[x][y] = game.wind_direction;
7129     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7130   }
7131   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7132   {
7133     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7134       MovDir[x][y] = left_dir;
7135     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7136       MovDir[x][y] = right_dir;
7137
7138     if (MovDir[x][y] != old_move_dir)
7139       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7140   }
7141   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7142   {
7143     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7144       MovDir[x][y] = right_dir;
7145     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7146       MovDir[x][y] = left_dir;
7147
7148     if (MovDir[x][y] != old_move_dir)
7149       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7150   }
7151   else if (move_pattern == MV_TOWARDS_PLAYER ||
7152            move_pattern == MV_AWAY_FROM_PLAYER)
7153   {
7154     int attr_x = -1, attr_y = -1;
7155     int newx, newy;
7156     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7157
7158     if (AllPlayersGone)
7159     {
7160       attr_x = ExitX;
7161       attr_y = ExitY;
7162     }
7163     else
7164     {
7165       int i;
7166
7167       for (i = 0; i < MAX_PLAYERS; i++)
7168       {
7169         struct PlayerInfo *player = &stored_player[i];
7170         int jx = player->jx, jy = player->jy;
7171
7172         if (!player->active)
7173           continue;
7174
7175         if (attr_x == -1 ||
7176             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7177         {
7178           attr_x = jx;
7179           attr_y = jy;
7180         }
7181       }
7182     }
7183
7184     MovDir[x][y] = MV_NONE;
7185     if (attr_x < x)
7186       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7187     else if (attr_x > x)
7188       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7189     if (attr_y < y)
7190       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7191     else if (attr_y > y)
7192       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7193
7194     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7195
7196     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7197     {
7198       boolean first_horiz = RND(2);
7199       int new_move_dir = MovDir[x][y];
7200
7201       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7202       {
7203         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7204         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205
7206         return;
7207       }
7208
7209       MovDir[x][y] =
7210         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7211       Moving2Blocked(x, y, &newx, &newy);
7212
7213       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7214         return;
7215
7216       MovDir[x][y] =
7217         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7218       Moving2Blocked(x, y, &newx, &newy);
7219
7220       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7221         return;
7222
7223       MovDir[x][y] = old_move_dir;
7224     }
7225   }
7226   else if (move_pattern == MV_WHEN_PUSHED ||
7227            move_pattern == MV_WHEN_DROPPED)
7228   {
7229     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7230       MovDir[x][y] = MV_NONE;
7231
7232     MovDelay[x][y] = 0;
7233   }
7234   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7235   {
7236     static int test_xy[7][2] =
7237     {
7238       { 0, -1 },
7239       { -1, 0 },
7240       { +1, 0 },
7241       { 0, +1 },
7242       { 0, -1 },
7243       { -1, 0 },
7244       { +1, 0 },
7245     };
7246     static int test_dir[7] =
7247     {
7248       MV_UP,
7249       MV_LEFT,
7250       MV_RIGHT,
7251       MV_DOWN,
7252       MV_UP,
7253       MV_LEFT,
7254       MV_RIGHT,
7255     };
7256     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7257     int move_preference = -1000000;     /* start with very low preference */
7258     int new_move_dir = MV_NONE;
7259     int start_test = RND(4);
7260     int i;
7261
7262     for (i = 0; i < NUM_DIRECTIONS; i++)
7263     {
7264       int move_dir = test_dir[start_test + i];
7265       int move_dir_preference;
7266
7267       xx = x + test_xy[start_test + i][0];
7268       yy = y + test_xy[start_test + i][1];
7269
7270       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7271           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7272       {
7273         new_move_dir = move_dir;
7274
7275         break;
7276       }
7277
7278       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7279         continue;
7280
7281       move_dir_preference = -1 * RunnerVisit[xx][yy];
7282       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7283         move_dir_preference = PlayerVisit[xx][yy];
7284
7285       if (move_dir_preference > move_preference)
7286       {
7287         /* prefer field that has not been visited for the longest time */
7288         move_preference = move_dir_preference;
7289         new_move_dir = move_dir;
7290       }
7291       else if (move_dir_preference == move_preference &&
7292                move_dir == old_move_dir)
7293       {
7294         /* prefer last direction when all directions are preferred equally */
7295         move_preference = move_dir_preference;
7296         new_move_dir = move_dir;
7297       }
7298     }
7299
7300     MovDir[x][y] = new_move_dir;
7301     if (old_move_dir != new_move_dir)
7302       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7303   }
7304 }
7305
7306 static void TurnRound(int x, int y)
7307 {
7308   int direction = MovDir[x][y];
7309
7310   TurnRoundExt(x, y);
7311
7312   GfxDir[x][y] = MovDir[x][y];
7313
7314   if (direction != MovDir[x][y])
7315     GfxFrame[x][y] = 0;
7316
7317   if (MovDelay[x][y])
7318     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7319
7320   ResetGfxFrame(x, y);
7321 }
7322
7323 static boolean JustBeingPushed(int x, int y)
7324 {
7325   int i;
7326
7327   for (i = 0; i < MAX_PLAYERS; i++)
7328   {
7329     struct PlayerInfo *player = &stored_player[i];
7330
7331     if (player->active && player->is_pushing && player->MovPos)
7332     {
7333       int next_jx = player->jx + (player->jx - player->last_jx);
7334       int next_jy = player->jy + (player->jy - player->last_jy);
7335
7336       if (x == next_jx && y == next_jy)
7337         return TRUE;
7338     }
7339   }
7340
7341   return FALSE;
7342 }
7343
7344 static void StartMoving(int x, int y)
7345 {
7346   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7347   int element = Feld[x][y];
7348
7349   if (Stop[x][y])
7350     return;
7351
7352   if (MovDelay[x][y] == 0)
7353     GfxAction[x][y] = ACTION_DEFAULT;
7354
7355   if (CAN_FALL(element) && y < lev_fieldy - 1)
7356   {
7357     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7358         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7359       if (JustBeingPushed(x, y))
7360         return;
7361
7362     if (element == EL_QUICKSAND_FULL)
7363     {
7364       if (IS_FREE(x, y + 1))
7365       {
7366         InitMovingField(x, y, MV_DOWN);
7367         started_moving = TRUE;
7368
7369         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7370 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7371         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7372           Store[x][y] = EL_ROCK;
7373 #else
7374         Store[x][y] = EL_ROCK;
7375 #endif
7376
7377         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7378       }
7379       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7380       {
7381         if (!MovDelay[x][y])
7382         {
7383           MovDelay[x][y] = TILEY + 1;
7384
7385           ResetGfxAnimation(x, y);
7386           ResetGfxAnimation(x, y + 1);
7387         }
7388
7389         if (MovDelay[x][y])
7390         {
7391           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7392           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7393
7394           MovDelay[x][y]--;
7395           if (MovDelay[x][y])
7396             return;
7397         }
7398
7399         Feld[x][y] = EL_QUICKSAND_EMPTY;
7400         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7401         Store[x][y + 1] = Store[x][y];
7402         Store[x][y] = 0;
7403
7404         PlayLevelSoundAction(x, y, ACTION_FILLING);
7405       }
7406       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7407       {
7408         if (!MovDelay[x][y])
7409         {
7410           MovDelay[x][y] = TILEY + 1;
7411
7412           ResetGfxAnimation(x, y);
7413           ResetGfxAnimation(x, y + 1);
7414         }
7415
7416         if (MovDelay[x][y])
7417         {
7418           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7419           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7420
7421           MovDelay[x][y]--;
7422           if (MovDelay[x][y])
7423             return;
7424         }
7425
7426         Feld[x][y] = EL_QUICKSAND_EMPTY;
7427         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7428         Store[x][y + 1] = Store[x][y];
7429         Store[x][y] = 0;
7430
7431         PlayLevelSoundAction(x, y, ACTION_FILLING);
7432       }
7433     }
7434     else if (element == EL_QUICKSAND_FAST_FULL)
7435     {
7436       if (IS_FREE(x, y + 1))
7437       {
7438         InitMovingField(x, y, MV_DOWN);
7439         started_moving = TRUE;
7440
7441         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7442 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7443         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7444           Store[x][y] = EL_ROCK;
7445 #else
7446         Store[x][y] = EL_ROCK;
7447 #endif
7448
7449         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7450       }
7451       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7452       {
7453         if (!MovDelay[x][y])
7454         {
7455           MovDelay[x][y] = TILEY + 1;
7456
7457           ResetGfxAnimation(x, y);
7458           ResetGfxAnimation(x, y + 1);
7459         }
7460
7461         if (MovDelay[x][y])
7462         {
7463           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7464           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7465
7466           MovDelay[x][y]--;
7467           if (MovDelay[x][y])
7468             return;
7469         }
7470
7471         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7472         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7473         Store[x][y + 1] = Store[x][y];
7474         Store[x][y] = 0;
7475
7476         PlayLevelSoundAction(x, y, ACTION_FILLING);
7477       }
7478       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7479       {
7480         if (!MovDelay[x][y])
7481         {
7482           MovDelay[x][y] = TILEY + 1;
7483
7484           ResetGfxAnimation(x, y);
7485           ResetGfxAnimation(x, y + 1);
7486         }
7487
7488         if (MovDelay[x][y])
7489         {
7490           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7491           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7492
7493           MovDelay[x][y]--;
7494           if (MovDelay[x][y])
7495             return;
7496         }
7497
7498         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7499         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7500         Store[x][y + 1] = Store[x][y];
7501         Store[x][y] = 0;
7502
7503         PlayLevelSoundAction(x, y, ACTION_FILLING);
7504       }
7505     }
7506     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7507              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7508     {
7509       InitMovingField(x, y, MV_DOWN);
7510       started_moving = TRUE;
7511
7512       Feld[x][y] = EL_QUICKSAND_FILLING;
7513       Store[x][y] = element;
7514
7515       PlayLevelSoundAction(x, y, ACTION_FILLING);
7516     }
7517     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7518              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7519     {
7520       InitMovingField(x, y, MV_DOWN);
7521       started_moving = TRUE;
7522
7523       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7524       Store[x][y] = element;
7525
7526       PlayLevelSoundAction(x, y, ACTION_FILLING);
7527     }
7528     else if (element == EL_MAGIC_WALL_FULL)
7529     {
7530       if (IS_FREE(x, y + 1))
7531       {
7532         InitMovingField(x, y, MV_DOWN);
7533         started_moving = TRUE;
7534
7535         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7536         Store[x][y] = EL_CHANGED(Store[x][y]);
7537       }
7538       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7539       {
7540         if (!MovDelay[x][y])
7541           MovDelay[x][y] = TILEY / 4 + 1;
7542
7543         if (MovDelay[x][y])
7544         {
7545           MovDelay[x][y]--;
7546           if (MovDelay[x][y])
7547             return;
7548         }
7549
7550         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7551         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7552         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7553         Store[x][y] = 0;
7554       }
7555     }
7556     else if (element == EL_BD_MAGIC_WALL_FULL)
7557     {
7558       if (IS_FREE(x, y + 1))
7559       {
7560         InitMovingField(x, y, MV_DOWN);
7561         started_moving = TRUE;
7562
7563         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7564         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7565       }
7566       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7567       {
7568         if (!MovDelay[x][y])
7569           MovDelay[x][y] = TILEY / 4 + 1;
7570
7571         if (MovDelay[x][y])
7572         {
7573           MovDelay[x][y]--;
7574           if (MovDelay[x][y])
7575             return;
7576         }
7577
7578         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7579         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7580         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7581         Store[x][y] = 0;
7582       }
7583     }
7584     else if (element == EL_DC_MAGIC_WALL_FULL)
7585     {
7586       if (IS_FREE(x, y + 1))
7587       {
7588         InitMovingField(x, y, MV_DOWN);
7589         started_moving = TRUE;
7590
7591         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7592         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7593       }
7594       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7595       {
7596         if (!MovDelay[x][y])
7597           MovDelay[x][y] = TILEY / 4 + 1;
7598
7599         if (MovDelay[x][y])
7600         {
7601           MovDelay[x][y]--;
7602           if (MovDelay[x][y])
7603             return;
7604         }
7605
7606         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7607         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7608         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7609         Store[x][y] = 0;
7610       }
7611     }
7612     else if ((CAN_PASS_MAGIC_WALL(element) &&
7613               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7614                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7615              (CAN_PASS_DC_MAGIC_WALL(element) &&
7616               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7617
7618     {
7619       InitMovingField(x, y, MV_DOWN);
7620       started_moving = TRUE;
7621
7622       Feld[x][y] =
7623         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7624          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7625          EL_DC_MAGIC_WALL_FILLING);
7626       Store[x][y] = element;
7627     }
7628     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7629     {
7630       SplashAcid(x, y + 1);
7631
7632       InitMovingField(x, y, MV_DOWN);
7633       started_moving = TRUE;
7634
7635       Store[x][y] = EL_ACID;
7636     }
7637     else if (
7638              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7639               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7640              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7641               CAN_FALL(element) && WasJustFalling[x][y] &&
7642               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7643
7644              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7645               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7646               (Feld[x][y + 1] == EL_BLOCKED)))
7647     {
7648       /* this is needed for a special case not covered by calling "Impact()"
7649          from "ContinueMoving()": if an element moves to a tile directly below
7650          another element which was just falling on that tile (which was empty
7651          in the previous frame), the falling element above would just stop
7652          instead of smashing the element below (in previous version, the above
7653          element was just checked for "moving" instead of "falling", resulting
7654          in incorrect smashes caused by horizontal movement of the above
7655          element; also, the case of the player being the element to smash was
7656          simply not covered here... :-/ ) */
7657
7658       CheckCollision[x][y] = 0;
7659       CheckImpact[x][y] = 0;
7660
7661       Impact(x, y);
7662     }
7663     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7664     {
7665       if (MovDir[x][y] == MV_NONE)
7666       {
7667         InitMovingField(x, y, MV_DOWN);
7668         started_moving = TRUE;
7669       }
7670     }
7671     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7672     {
7673       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7674         MovDir[x][y] = MV_DOWN;
7675
7676       InitMovingField(x, y, MV_DOWN);
7677       started_moving = TRUE;
7678     }
7679     else if (element == EL_AMOEBA_DROP)
7680     {
7681       Feld[x][y] = EL_AMOEBA_GROWING;
7682       Store[x][y] = EL_AMOEBA_WET;
7683     }
7684     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7685               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7686              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7687              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7688     {
7689       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7690                                 (IS_FREE(x - 1, y + 1) ||
7691                                  Feld[x - 1][y + 1] == EL_ACID));
7692       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7693                                 (IS_FREE(x + 1, y + 1) ||
7694                                  Feld[x + 1][y + 1] == EL_ACID));
7695       boolean can_fall_any  = (can_fall_left || can_fall_right);
7696       boolean can_fall_both = (can_fall_left && can_fall_right);
7697       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7698
7699       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7700       {
7701         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7702           can_fall_right = FALSE;
7703         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7704           can_fall_left = FALSE;
7705         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7706           can_fall_right = FALSE;
7707         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7708           can_fall_left = FALSE;
7709
7710         can_fall_any  = (can_fall_left || can_fall_right);
7711         can_fall_both = FALSE;
7712       }
7713
7714       if (can_fall_both)
7715       {
7716         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7717           can_fall_right = FALSE;       /* slip down on left side */
7718         else
7719           can_fall_left = !(can_fall_right = RND(2));
7720
7721         can_fall_both = FALSE;
7722       }
7723
7724       if (can_fall_any)
7725       {
7726         /* if not determined otherwise, prefer left side for slipping down */
7727         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7728         started_moving = TRUE;
7729       }
7730     }
7731     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7732     {
7733       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7734       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7735       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7736       int belt_dir = game.belt_dir[belt_nr];
7737
7738       if ((belt_dir == MV_LEFT  && left_is_free) ||
7739           (belt_dir == MV_RIGHT && right_is_free))
7740       {
7741         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7742
7743         InitMovingField(x, y, belt_dir);
7744         started_moving = TRUE;
7745
7746         Pushed[x][y] = TRUE;
7747         Pushed[nextx][y] = TRUE;
7748
7749         GfxAction[x][y] = ACTION_DEFAULT;
7750       }
7751       else
7752       {
7753         MovDir[x][y] = 0;       /* if element was moving, stop it */
7754       }
7755     }
7756   }
7757
7758   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7759   if (CAN_MOVE(element) && !started_moving)
7760   {
7761     int move_pattern = element_info[element].move_pattern;
7762     int newx, newy;
7763
7764     Moving2Blocked(x, y, &newx, &newy);
7765
7766     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7767       return;
7768
7769     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7770         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7771     {
7772       WasJustMoving[x][y] = 0;
7773       CheckCollision[x][y] = 0;
7774
7775       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7776
7777       if (Feld[x][y] != element)        /* element has changed */
7778         return;
7779     }
7780
7781     if (!MovDelay[x][y])        /* start new movement phase */
7782     {
7783       /* all objects that can change their move direction after each step
7784          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7785
7786       if (element != EL_YAMYAM &&
7787           element != EL_DARK_YAMYAM &&
7788           element != EL_PACMAN &&
7789           !(move_pattern & MV_ANY_DIRECTION) &&
7790           move_pattern != MV_TURNING_LEFT &&
7791           move_pattern != MV_TURNING_RIGHT &&
7792           move_pattern != MV_TURNING_LEFT_RIGHT &&
7793           move_pattern != MV_TURNING_RIGHT_LEFT &&
7794           move_pattern != MV_TURNING_RANDOM)
7795       {
7796         TurnRound(x, y);
7797
7798         if (MovDelay[x][y] && (element == EL_BUG ||
7799                                element == EL_SPACESHIP ||
7800                                element == EL_SP_SNIKSNAK ||
7801                                element == EL_SP_ELECTRON ||
7802                                element == EL_MOLE))
7803           TEST_DrawLevelField(x, y);
7804       }
7805     }
7806
7807     if (MovDelay[x][y])         /* wait some time before next movement */
7808     {
7809       MovDelay[x][y]--;
7810
7811       if (element == EL_ROBOT ||
7812           element == EL_YAMYAM ||
7813           element == EL_DARK_YAMYAM)
7814       {
7815         DrawLevelElementAnimationIfNeeded(x, y, element);
7816         PlayLevelSoundAction(x, y, ACTION_WAITING);
7817       }
7818       else if (element == EL_SP_ELECTRON)
7819         DrawLevelElementAnimationIfNeeded(x, y, element);
7820       else if (element == EL_DRAGON)
7821       {
7822         int i;
7823         int dir = MovDir[x][y];
7824         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7825         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7826         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7827                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7828                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7829                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7830         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7831
7832         GfxAction[x][y] = ACTION_ATTACKING;
7833
7834         if (IS_PLAYER(x, y))
7835           DrawPlayerField(x, y);
7836         else
7837           TEST_DrawLevelField(x, y);
7838
7839         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7840
7841         for (i = 1; i <= 3; i++)
7842         {
7843           int xx = x + i * dx;
7844           int yy = y + i * dy;
7845           int sx = SCREENX(xx);
7846           int sy = SCREENY(yy);
7847           int flame_graphic = graphic + (i - 1);
7848
7849           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7850             break;
7851
7852           if (MovDelay[x][y])
7853           {
7854             int flamed = MovingOrBlocked2Element(xx, yy);
7855
7856             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7857               Bang(xx, yy);
7858             else
7859               RemoveMovingField(xx, yy);
7860
7861             ChangeDelay[xx][yy] = 0;
7862
7863             Feld[xx][yy] = EL_FLAMES;
7864
7865             if (IN_SCR_FIELD(sx, sy))
7866             {
7867               TEST_DrawLevelFieldCrumbled(xx, yy);
7868               DrawGraphic(sx, sy, flame_graphic, frame);
7869             }
7870           }
7871           else
7872           {
7873             if (Feld[xx][yy] == EL_FLAMES)
7874               Feld[xx][yy] = EL_EMPTY;
7875             TEST_DrawLevelField(xx, yy);
7876           }
7877         }
7878       }
7879
7880       if (MovDelay[x][y])       /* element still has to wait some time */
7881       {
7882         PlayLevelSoundAction(x, y, ACTION_WAITING);
7883
7884         return;
7885       }
7886     }
7887
7888     /* now make next step */
7889
7890     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7891
7892     if (DONT_COLLIDE_WITH(element) &&
7893         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7894         !PLAYER_ENEMY_PROTECTED(newx, newy))
7895     {
7896       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7897
7898       return;
7899     }
7900
7901     else if (CAN_MOVE_INTO_ACID(element) &&
7902              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7903              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7904              (MovDir[x][y] == MV_DOWN ||
7905               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7906     {
7907       SplashAcid(newx, newy);
7908       Store[x][y] = EL_ACID;
7909     }
7910     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7911     {
7912       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7913           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7914           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7915           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7916       {
7917         RemoveField(x, y);
7918         TEST_DrawLevelField(x, y);
7919
7920         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7921         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7922           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7923
7924         local_player->friends_still_needed--;
7925         if (!local_player->friends_still_needed &&
7926             !local_player->GameOver && AllPlayersGone)
7927           PlayerWins(local_player);
7928
7929         return;
7930       }
7931       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7932       {
7933         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7934           TEST_DrawLevelField(newx, newy);
7935         else
7936           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7937       }
7938       else if (!IS_FREE(newx, newy))
7939       {
7940         GfxAction[x][y] = ACTION_WAITING;
7941
7942         if (IS_PLAYER(x, y))
7943           DrawPlayerField(x, y);
7944         else
7945           TEST_DrawLevelField(x, y);
7946
7947         return;
7948       }
7949     }
7950     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7951     {
7952       if (IS_FOOD_PIG(Feld[newx][newy]))
7953       {
7954         if (IS_MOVING(newx, newy))
7955           RemoveMovingField(newx, newy);
7956         else
7957         {
7958           Feld[newx][newy] = EL_EMPTY;
7959           TEST_DrawLevelField(newx, newy);
7960         }
7961
7962         PlayLevelSound(x, y, SND_PIG_DIGGING);
7963       }
7964       else if (!IS_FREE(newx, newy))
7965       {
7966         if (IS_PLAYER(x, y))
7967           DrawPlayerField(x, y);
7968         else
7969           TEST_DrawLevelField(x, y);
7970
7971         return;
7972       }
7973     }
7974     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7975     {
7976       if (Store[x][y] != EL_EMPTY)
7977       {
7978         boolean can_clone = FALSE;
7979         int xx, yy;
7980
7981         /* check if element to clone is still there */
7982         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7983         {
7984           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7985           {
7986             can_clone = TRUE;
7987
7988             break;
7989           }
7990         }
7991
7992         /* cannot clone or target field not free anymore -- do not clone */
7993         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7994           Store[x][y] = EL_EMPTY;
7995       }
7996
7997       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7998       {
7999         if (IS_MV_DIAGONAL(MovDir[x][y]))
8000         {
8001           int diagonal_move_dir = MovDir[x][y];
8002           int stored = Store[x][y];
8003           int change_delay = 8;
8004           int graphic;
8005
8006           /* android is moving diagonally */
8007
8008           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8009
8010           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8011           GfxElement[x][y] = EL_EMC_ANDROID;
8012           GfxAction[x][y] = ACTION_SHRINKING;
8013           GfxDir[x][y] = diagonal_move_dir;
8014           ChangeDelay[x][y] = change_delay;
8015
8016           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8017                                    GfxDir[x][y]);
8018
8019           DrawLevelGraphicAnimation(x, y, graphic);
8020           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8021
8022           if (Feld[newx][newy] == EL_ACID)
8023           {
8024             SplashAcid(newx, newy);
8025
8026             return;
8027           }
8028
8029           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8030
8031           Store[newx][newy] = EL_EMC_ANDROID;
8032           GfxElement[newx][newy] = EL_EMC_ANDROID;
8033           GfxAction[newx][newy] = ACTION_GROWING;
8034           GfxDir[newx][newy] = diagonal_move_dir;
8035           ChangeDelay[newx][newy] = change_delay;
8036
8037           graphic = el_act_dir2img(GfxElement[newx][newy],
8038                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8039
8040           DrawLevelGraphicAnimation(newx, newy, graphic);
8041           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8042
8043           return;
8044         }
8045         else
8046         {
8047           Feld[newx][newy] = EL_EMPTY;
8048           TEST_DrawLevelField(newx, newy);
8049
8050           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8051         }
8052       }
8053       else if (!IS_FREE(newx, newy))
8054       {
8055         return;
8056       }
8057     }
8058     else if (IS_CUSTOM_ELEMENT(element) &&
8059              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8060     {
8061       if (!DigFieldByCE(newx, newy, element))
8062         return;
8063
8064       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8065       {
8066         RunnerVisit[x][y] = FrameCounter;
8067         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8068       }
8069     }
8070     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8071     {
8072       if (!IS_FREE(newx, newy))
8073       {
8074         if (IS_PLAYER(x, y))
8075           DrawPlayerField(x, y);
8076         else
8077           TEST_DrawLevelField(x, y);
8078
8079         return;
8080       }
8081       else
8082       {
8083         boolean wanna_flame = !RND(10);
8084         int dx = newx - x, dy = newy - y;
8085         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8086         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8087         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8088                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8089         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8090                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8091
8092         if ((wanna_flame ||
8093              IS_CLASSIC_ENEMY(element1) ||
8094              IS_CLASSIC_ENEMY(element2)) &&
8095             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8096             element1 != EL_FLAMES && element2 != EL_FLAMES)
8097         {
8098           ResetGfxAnimation(x, y);
8099           GfxAction[x][y] = ACTION_ATTACKING;
8100
8101           if (IS_PLAYER(x, y))
8102             DrawPlayerField(x, y);
8103           else
8104             TEST_DrawLevelField(x, y);
8105
8106           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8107
8108           MovDelay[x][y] = 50;
8109
8110           Feld[newx][newy] = EL_FLAMES;
8111           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8112             Feld[newx1][newy1] = EL_FLAMES;
8113           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8114             Feld[newx2][newy2] = EL_FLAMES;
8115
8116           return;
8117         }
8118       }
8119     }
8120     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8121              Feld[newx][newy] == EL_DIAMOND)
8122     {
8123       if (IS_MOVING(newx, newy))
8124         RemoveMovingField(newx, newy);
8125       else
8126       {
8127         Feld[newx][newy] = EL_EMPTY;
8128         TEST_DrawLevelField(newx, newy);
8129       }
8130
8131       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8132     }
8133     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8134              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8135     {
8136       if (AmoebaNr[newx][newy])
8137       {
8138         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8139         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8140             Feld[newx][newy] == EL_BD_AMOEBA)
8141           AmoebaCnt[AmoebaNr[newx][newy]]--;
8142       }
8143
8144       if (IS_MOVING(newx, newy))
8145       {
8146         RemoveMovingField(newx, newy);
8147       }
8148       else
8149       {
8150         Feld[newx][newy] = EL_EMPTY;
8151         TEST_DrawLevelField(newx, newy);
8152       }
8153
8154       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8155     }
8156     else if ((element == EL_PACMAN || element == EL_MOLE)
8157              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8158     {
8159       if (AmoebaNr[newx][newy])
8160       {
8161         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8162         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8163             Feld[newx][newy] == EL_BD_AMOEBA)
8164           AmoebaCnt[AmoebaNr[newx][newy]]--;
8165       }
8166
8167       if (element == EL_MOLE)
8168       {
8169         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8170         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8171
8172         ResetGfxAnimation(x, y);
8173         GfxAction[x][y] = ACTION_DIGGING;
8174         TEST_DrawLevelField(x, y);
8175
8176         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8177
8178         return;                         /* wait for shrinking amoeba */
8179       }
8180       else      /* element == EL_PACMAN */
8181       {
8182         Feld[newx][newy] = EL_EMPTY;
8183         TEST_DrawLevelField(newx, newy);
8184         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8185       }
8186     }
8187     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8188              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8189               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8190     {
8191       /* wait for shrinking amoeba to completely disappear */
8192       return;
8193     }
8194     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8195     {
8196       /* object was running against a wall */
8197
8198       TurnRound(x, y);
8199
8200       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8201         DrawLevelElementAnimation(x, y, element);
8202
8203       if (DONT_TOUCH(element))
8204         TestIfBadThingTouchesPlayer(x, y);
8205
8206       return;
8207     }
8208
8209     InitMovingField(x, y, MovDir[x][y]);
8210
8211     PlayLevelSoundAction(x, y, ACTION_MOVING);
8212   }
8213
8214   if (MovDir[x][y])
8215     ContinueMoving(x, y);
8216 }
8217
8218 void ContinueMoving(int x, int y)
8219 {
8220   int element = Feld[x][y];
8221   struct ElementInfo *ei = &element_info[element];
8222   int direction = MovDir[x][y];
8223   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8224   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8225   int newx = x + dx, newy = y + dy;
8226   int stored = Store[x][y];
8227   int stored_new = Store[newx][newy];
8228   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8229   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8230   boolean last_line = (newy == lev_fieldy - 1);
8231
8232   MovPos[x][y] += getElementMoveStepsize(x, y);
8233
8234   if (pushed_by_player) /* special case: moving object pushed by player */
8235     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8236
8237   if (ABS(MovPos[x][y]) < TILEX)
8238   {
8239     TEST_DrawLevelField(x, y);
8240
8241     return;     /* element is still moving */
8242   }
8243
8244   /* element reached destination field */
8245
8246   Feld[x][y] = EL_EMPTY;
8247   Feld[newx][newy] = element;
8248   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8249
8250   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8251   {
8252     element = Feld[newx][newy] = EL_ACID;
8253   }
8254   else if (element == EL_MOLE)
8255   {
8256     Feld[x][y] = EL_SAND;
8257
8258     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8259   }
8260   else if (element == EL_QUICKSAND_FILLING)
8261   {
8262     element = Feld[newx][newy] = get_next_element(element);
8263     Store[newx][newy] = Store[x][y];
8264   }
8265   else if (element == EL_QUICKSAND_EMPTYING)
8266   {
8267     Feld[x][y] = get_next_element(element);
8268     element = Feld[newx][newy] = Store[x][y];
8269   }
8270   else if (element == EL_QUICKSAND_FAST_FILLING)
8271   {
8272     element = Feld[newx][newy] = get_next_element(element);
8273     Store[newx][newy] = Store[x][y];
8274   }
8275   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8276   {
8277     Feld[x][y] = get_next_element(element);
8278     element = Feld[newx][newy] = Store[x][y];
8279   }
8280   else if (element == EL_MAGIC_WALL_FILLING)
8281   {
8282     element = Feld[newx][newy] = get_next_element(element);
8283     if (!game.magic_wall_active)
8284       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8285     Store[newx][newy] = Store[x][y];
8286   }
8287   else if (element == EL_MAGIC_WALL_EMPTYING)
8288   {
8289     Feld[x][y] = get_next_element(element);
8290     if (!game.magic_wall_active)
8291       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8292     element = Feld[newx][newy] = Store[x][y];
8293
8294     InitField(newx, newy, FALSE);
8295   }
8296   else if (element == EL_BD_MAGIC_WALL_FILLING)
8297   {
8298     element = Feld[newx][newy] = get_next_element(element);
8299     if (!game.magic_wall_active)
8300       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8301     Store[newx][newy] = Store[x][y];
8302   }
8303   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8304   {
8305     Feld[x][y] = get_next_element(element);
8306     if (!game.magic_wall_active)
8307       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8308     element = Feld[newx][newy] = Store[x][y];
8309
8310     InitField(newx, newy, FALSE);
8311   }
8312   else if (element == EL_DC_MAGIC_WALL_FILLING)
8313   {
8314     element = Feld[newx][newy] = get_next_element(element);
8315     if (!game.magic_wall_active)
8316       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8317     Store[newx][newy] = Store[x][y];
8318   }
8319   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8320   {
8321     Feld[x][y] = get_next_element(element);
8322     if (!game.magic_wall_active)
8323       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8324     element = Feld[newx][newy] = Store[x][y];
8325
8326     InitField(newx, newy, FALSE);
8327   }
8328   else if (element == EL_AMOEBA_DROPPING)
8329   {
8330     Feld[x][y] = get_next_element(element);
8331     element = Feld[newx][newy] = Store[x][y];
8332   }
8333   else if (element == EL_SOKOBAN_OBJECT)
8334   {
8335     if (Back[x][y])
8336       Feld[x][y] = Back[x][y];
8337
8338     if (Back[newx][newy])
8339       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8340
8341     Back[x][y] = Back[newx][newy] = 0;
8342   }
8343
8344   Store[x][y] = EL_EMPTY;
8345   MovPos[x][y] = 0;
8346   MovDir[x][y] = 0;
8347   MovDelay[x][y] = 0;
8348
8349   MovDelay[newx][newy] = 0;
8350
8351   if (CAN_CHANGE_OR_HAS_ACTION(element))
8352   {
8353     /* copy element change control values to new field */
8354     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8355     ChangePage[newx][newy]  = ChangePage[x][y];
8356     ChangeCount[newx][newy] = ChangeCount[x][y];
8357     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8358   }
8359
8360   CustomValue[newx][newy] = CustomValue[x][y];
8361
8362   ChangeDelay[x][y] = 0;
8363   ChangePage[x][y] = -1;
8364   ChangeCount[x][y] = 0;
8365   ChangeEvent[x][y] = -1;
8366
8367   CustomValue[x][y] = 0;
8368
8369   /* copy animation control values to new field */
8370   GfxFrame[newx][newy]  = GfxFrame[x][y];
8371   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8372   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8373   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8374
8375   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8376
8377   /* some elements can leave other elements behind after moving */
8378   if (ei->move_leave_element != EL_EMPTY &&
8379       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8380       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8381   {
8382     int move_leave_element = ei->move_leave_element;
8383
8384     /* this makes it possible to leave the removed element again */
8385     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8386       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8387
8388     Feld[x][y] = move_leave_element;
8389
8390     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8391       MovDir[x][y] = direction;
8392
8393     InitField(x, y, FALSE);
8394
8395     if (GFX_CRUMBLED(Feld[x][y]))
8396       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8397
8398     if (ELEM_IS_PLAYER(move_leave_element))
8399       RelocatePlayer(x, y, move_leave_element);
8400   }
8401
8402   /* do this after checking for left-behind element */
8403   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8404
8405   if (!CAN_MOVE(element) ||
8406       (CAN_FALL(element) && direction == MV_DOWN &&
8407        (element == EL_SPRING ||
8408         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8409         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8410     GfxDir[x][y] = MovDir[newx][newy] = 0;
8411
8412   TEST_DrawLevelField(x, y);
8413   TEST_DrawLevelField(newx, newy);
8414
8415   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8416
8417   /* prevent pushed element from moving on in pushed direction */
8418   if (pushed_by_player && CAN_MOVE(element) &&
8419       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8420       !(element_info[element].move_pattern & direction))
8421     TurnRound(newx, newy);
8422
8423   /* prevent elements on conveyor belt from moving on in last direction */
8424   if (pushed_by_conveyor && CAN_FALL(element) &&
8425       direction & MV_HORIZONTAL)
8426     MovDir[newx][newy] = 0;
8427
8428   if (!pushed_by_player)
8429   {
8430     int nextx = newx + dx, nexty = newy + dy;
8431     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8432
8433     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8434
8435     if (CAN_FALL(element) && direction == MV_DOWN)
8436       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8437
8438     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8439       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8440
8441     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8442       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8443   }
8444
8445   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8446   {
8447     TestIfBadThingTouchesPlayer(newx, newy);
8448     TestIfBadThingTouchesFriend(newx, newy);
8449
8450     if (!IS_CUSTOM_ELEMENT(element))
8451       TestIfBadThingTouchesOtherBadThing(newx, newy);
8452   }
8453   else if (element == EL_PENGUIN)
8454     TestIfFriendTouchesBadThing(newx, newy);
8455
8456   if (DONT_GET_HIT_BY(element))
8457   {
8458     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8459   }
8460
8461   /* give the player one last chance (one more frame) to move away */
8462   if (CAN_FALL(element) && direction == MV_DOWN &&
8463       (last_line || (!IS_FREE(x, newy + 1) &&
8464                      (!IS_PLAYER(x, newy + 1) ||
8465                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8466     Impact(x, newy);
8467
8468   if (pushed_by_player && !game.use_change_when_pushing_bug)
8469   {
8470     int push_side = MV_DIR_OPPOSITE(direction);
8471     struct PlayerInfo *player = PLAYERINFO(x, y);
8472
8473     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8474                                player->index_bit, push_side);
8475     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8476                                         player->index_bit, push_side);
8477   }
8478
8479   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8480     MovDelay[newx][newy] = 1;
8481
8482   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8483
8484   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8485   TestIfElementHitsCustomElement(newx, newy, direction);
8486   TestIfPlayerTouchesCustomElement(newx, newy);
8487   TestIfElementTouchesCustomElement(newx, newy);
8488
8489   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8490       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8491     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8492                              MV_DIR_OPPOSITE(direction));
8493 }
8494
8495 int AmoebeNachbarNr(int ax, int ay)
8496 {
8497   int i;
8498   int element = Feld[ax][ay];
8499   int group_nr = 0;
8500   static int xy[4][2] =
8501   {
8502     { 0, -1 },
8503     { -1, 0 },
8504     { +1, 0 },
8505     { 0, +1 }
8506   };
8507
8508   for (i = 0; i < NUM_DIRECTIONS; i++)
8509   {
8510     int x = ax + xy[i][0];
8511     int y = ay + xy[i][1];
8512
8513     if (!IN_LEV_FIELD(x, y))
8514       continue;
8515
8516     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8517       group_nr = AmoebaNr[x][y];
8518   }
8519
8520   return group_nr;
8521 }
8522
8523 static void AmoebenVereinigen(int ax, int ay)
8524 {
8525   int i, x, y, xx, yy;
8526   int new_group_nr = AmoebaNr[ax][ay];
8527   static int xy[4][2] =
8528   {
8529     { 0, -1 },
8530     { -1, 0 },
8531     { +1, 0 },
8532     { 0, +1 }
8533   };
8534
8535   if (new_group_nr == 0)
8536     return;
8537
8538   for (i = 0; i < NUM_DIRECTIONS; i++)
8539   {
8540     x = ax + xy[i][0];
8541     y = ay + xy[i][1];
8542
8543     if (!IN_LEV_FIELD(x, y))
8544       continue;
8545
8546     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8547          Feld[x][y] == EL_BD_AMOEBA ||
8548          Feld[x][y] == EL_AMOEBA_DEAD) &&
8549         AmoebaNr[x][y] != new_group_nr)
8550     {
8551       int old_group_nr = AmoebaNr[x][y];
8552
8553       if (old_group_nr == 0)
8554         return;
8555
8556       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8557       AmoebaCnt[old_group_nr] = 0;
8558       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8559       AmoebaCnt2[old_group_nr] = 0;
8560
8561       SCAN_PLAYFIELD(xx, yy)
8562       {
8563         if (AmoebaNr[xx][yy] == old_group_nr)
8564           AmoebaNr[xx][yy] = new_group_nr;
8565       }
8566     }
8567   }
8568 }
8569
8570 void AmoebeUmwandeln(int ax, int ay)
8571 {
8572   int i, x, y;
8573
8574   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8575   {
8576     int group_nr = AmoebaNr[ax][ay];
8577
8578 #ifdef DEBUG
8579     if (group_nr == 0)
8580     {
8581       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8582       printf("AmoebeUmwandeln(): This should never happen!\n");
8583       return;
8584     }
8585 #endif
8586
8587     SCAN_PLAYFIELD(x, y)
8588     {
8589       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8590       {
8591         AmoebaNr[x][y] = 0;
8592         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8593       }
8594     }
8595
8596     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8597                             SND_AMOEBA_TURNING_TO_GEM :
8598                             SND_AMOEBA_TURNING_TO_ROCK));
8599     Bang(ax, ay);
8600   }
8601   else
8602   {
8603     static int xy[4][2] =
8604     {
8605       { 0, -1 },
8606       { -1, 0 },
8607       { +1, 0 },
8608       { 0, +1 }
8609     };
8610
8611     for (i = 0; i < NUM_DIRECTIONS; i++)
8612     {
8613       x = ax + xy[i][0];
8614       y = ay + xy[i][1];
8615
8616       if (!IN_LEV_FIELD(x, y))
8617         continue;
8618
8619       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8620       {
8621         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8622                               SND_AMOEBA_TURNING_TO_GEM :
8623                               SND_AMOEBA_TURNING_TO_ROCK));
8624         Bang(x, y);
8625       }
8626     }
8627   }
8628 }
8629
8630 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8631 {
8632   int x, y;
8633   int group_nr = AmoebaNr[ax][ay];
8634   boolean done = FALSE;
8635
8636 #ifdef DEBUG
8637   if (group_nr == 0)
8638   {
8639     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8640     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8641     return;
8642   }
8643 #endif
8644
8645   SCAN_PLAYFIELD(x, y)
8646   {
8647     if (AmoebaNr[x][y] == group_nr &&
8648         (Feld[x][y] == EL_AMOEBA_DEAD ||
8649          Feld[x][y] == EL_BD_AMOEBA ||
8650          Feld[x][y] == EL_AMOEBA_GROWING))
8651     {
8652       AmoebaNr[x][y] = 0;
8653       Feld[x][y] = new_element;
8654       InitField(x, y, FALSE);
8655       TEST_DrawLevelField(x, y);
8656       done = TRUE;
8657     }
8658   }
8659
8660   if (done)
8661     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8662                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8663                             SND_BD_AMOEBA_TURNING_TO_GEM));
8664 }
8665
8666 static void AmoebeWaechst(int x, int y)
8667 {
8668   static unsigned int sound_delay = 0;
8669   static unsigned int sound_delay_value = 0;
8670
8671   if (!MovDelay[x][y])          /* start new growing cycle */
8672   {
8673     MovDelay[x][y] = 7;
8674
8675     if (DelayReached(&sound_delay, sound_delay_value))
8676     {
8677       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8678       sound_delay_value = 30;
8679     }
8680   }
8681
8682   if (MovDelay[x][y])           /* wait some time before growing bigger */
8683   {
8684     MovDelay[x][y]--;
8685     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8686     {
8687       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8688                                            6 - MovDelay[x][y]);
8689
8690       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8691     }
8692
8693     if (!MovDelay[x][y])
8694     {
8695       Feld[x][y] = Store[x][y];
8696       Store[x][y] = 0;
8697       TEST_DrawLevelField(x, y);
8698     }
8699   }
8700 }
8701
8702 static void AmoebaDisappearing(int x, int y)
8703 {
8704   static unsigned int sound_delay = 0;
8705   static unsigned int sound_delay_value = 0;
8706
8707   if (!MovDelay[x][y])          /* start new shrinking cycle */
8708   {
8709     MovDelay[x][y] = 7;
8710
8711     if (DelayReached(&sound_delay, sound_delay_value))
8712       sound_delay_value = 30;
8713   }
8714
8715   if (MovDelay[x][y])           /* wait some time before shrinking */
8716   {
8717     MovDelay[x][y]--;
8718     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8719     {
8720       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8721                                            6 - MovDelay[x][y]);
8722
8723       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8724     }
8725
8726     if (!MovDelay[x][y])
8727     {
8728       Feld[x][y] = EL_EMPTY;
8729       TEST_DrawLevelField(x, y);
8730
8731       /* don't let mole enter this field in this cycle;
8732          (give priority to objects falling to this field from above) */
8733       Stop[x][y] = TRUE;
8734     }
8735   }
8736 }
8737
8738 static void AmoebeAbleger(int ax, int ay)
8739 {
8740   int i;
8741   int element = Feld[ax][ay];
8742   int graphic = el2img(element);
8743   int newax = ax, neway = ay;
8744   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8745   static int xy[4][2] =
8746   {
8747     { 0, -1 },
8748     { -1, 0 },
8749     { +1, 0 },
8750     { 0, +1 }
8751   };
8752
8753   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8754   {
8755     Feld[ax][ay] = EL_AMOEBA_DEAD;
8756     TEST_DrawLevelField(ax, ay);
8757     return;
8758   }
8759
8760   if (IS_ANIMATED(graphic))
8761     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8762
8763   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8764     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8765
8766   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8767   {
8768     MovDelay[ax][ay]--;
8769     if (MovDelay[ax][ay])
8770       return;
8771   }
8772
8773   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8774   {
8775     int start = RND(4);
8776     int x = ax + xy[start][0];
8777     int y = ay + xy[start][1];
8778
8779     if (!IN_LEV_FIELD(x, y))
8780       return;
8781
8782     if (IS_FREE(x, y) ||
8783         CAN_GROW_INTO(Feld[x][y]) ||
8784         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8785         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8786     {
8787       newax = x;
8788       neway = y;
8789     }
8790
8791     if (newax == ax && neway == ay)
8792       return;
8793   }
8794   else                          /* normal or "filled" (BD style) amoeba */
8795   {
8796     int start = RND(4);
8797     boolean waiting_for_player = FALSE;
8798
8799     for (i = 0; i < NUM_DIRECTIONS; i++)
8800     {
8801       int j = (start + i) % 4;
8802       int x = ax + xy[j][0];
8803       int y = ay + xy[j][1];
8804
8805       if (!IN_LEV_FIELD(x, y))
8806         continue;
8807
8808       if (IS_FREE(x, y) ||
8809           CAN_GROW_INTO(Feld[x][y]) ||
8810           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8811           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8812       {
8813         newax = x;
8814         neway = y;
8815         break;
8816       }
8817       else if (IS_PLAYER(x, y))
8818         waiting_for_player = TRUE;
8819     }
8820
8821     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8822     {
8823       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8824       {
8825         Feld[ax][ay] = EL_AMOEBA_DEAD;
8826         TEST_DrawLevelField(ax, ay);
8827         AmoebaCnt[AmoebaNr[ax][ay]]--;
8828
8829         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8830         {
8831           if (element == EL_AMOEBA_FULL)
8832             AmoebeUmwandeln(ax, ay);
8833           else if (element == EL_BD_AMOEBA)
8834             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8835         }
8836       }
8837       return;
8838     }
8839     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8840     {
8841       /* amoeba gets larger by growing in some direction */
8842
8843       int new_group_nr = AmoebaNr[ax][ay];
8844
8845 #ifdef DEBUG
8846   if (new_group_nr == 0)
8847   {
8848     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8849     printf("AmoebeAbleger(): This should never happen!\n");
8850     return;
8851   }
8852 #endif
8853
8854       AmoebaNr[newax][neway] = new_group_nr;
8855       AmoebaCnt[new_group_nr]++;
8856       AmoebaCnt2[new_group_nr]++;
8857
8858       /* if amoeba touches other amoeba(s) after growing, unify them */
8859       AmoebenVereinigen(newax, neway);
8860
8861       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8862       {
8863         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8864         return;
8865       }
8866     }
8867   }
8868
8869   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8870       (neway == lev_fieldy - 1 && newax != ax))
8871   {
8872     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8873     Store[newax][neway] = element;
8874   }
8875   else if (neway == ay || element == EL_EMC_DRIPPER)
8876   {
8877     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8878
8879     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8880   }
8881   else
8882   {
8883     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8884     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8885     Store[ax][ay] = EL_AMOEBA_DROP;
8886     ContinueMoving(ax, ay);
8887     return;
8888   }
8889
8890   TEST_DrawLevelField(newax, neway);
8891 }
8892
8893 static void Life(int ax, int ay)
8894 {
8895   int x1, y1, x2, y2;
8896   int life_time = 40;
8897   int element = Feld[ax][ay];
8898   int graphic = el2img(element);
8899   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8900                          level.biomaze);
8901   boolean changed = FALSE;
8902
8903   if (IS_ANIMATED(graphic))
8904     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8905
8906   if (Stop[ax][ay])
8907     return;
8908
8909   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8910     MovDelay[ax][ay] = life_time;
8911
8912   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8913   {
8914     MovDelay[ax][ay]--;
8915     if (MovDelay[ax][ay])
8916       return;
8917   }
8918
8919   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8920   {
8921     int xx = ax+x1, yy = ay+y1;
8922     int old_element = Feld[xx][yy];
8923     int num_neighbours = 0;
8924
8925     if (!IN_LEV_FIELD(xx, yy))
8926       continue;
8927
8928     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8929     {
8930       int x = xx+x2, y = yy+y2;
8931
8932       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8933         continue;
8934
8935       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8936       boolean is_neighbour = FALSE;
8937
8938       if (level.use_life_bugs)
8939         is_neighbour =
8940           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8941            (IS_FREE(x, y)                             &&  Stop[x][y]));
8942       else
8943         is_neighbour =
8944           (Last[x][y] == element || is_player_cell);
8945
8946       if (is_neighbour)
8947         num_neighbours++;
8948     }
8949
8950     boolean is_free = FALSE;
8951
8952     if (level.use_life_bugs)
8953       is_free = (IS_FREE(xx, yy));
8954     else
8955       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8956
8957     if (xx == ax && yy == ay)           /* field in the middle */
8958     {
8959       if (num_neighbours < life_parameter[0] ||
8960           num_neighbours > life_parameter[1])
8961       {
8962         Feld[xx][yy] = EL_EMPTY;
8963         if (Feld[xx][yy] != old_element)
8964           TEST_DrawLevelField(xx, yy);
8965         Stop[xx][yy] = TRUE;
8966         changed = TRUE;
8967       }
8968     }
8969     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8970     {                                   /* free border field */
8971       if (num_neighbours >= life_parameter[2] &&
8972           num_neighbours <= life_parameter[3])
8973       {
8974         Feld[xx][yy] = element;
8975         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8976         if (Feld[xx][yy] != old_element)
8977           TEST_DrawLevelField(xx, yy);
8978         Stop[xx][yy] = TRUE;
8979         changed = TRUE;
8980       }
8981     }
8982   }
8983
8984   if (changed)
8985     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8986                    SND_GAME_OF_LIFE_GROWING);
8987 }
8988
8989 static void InitRobotWheel(int x, int y)
8990 {
8991   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8992 }
8993
8994 static void RunRobotWheel(int x, int y)
8995 {
8996   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8997 }
8998
8999 static void StopRobotWheel(int x, int y)
9000 {
9001   if (ZX == x && ZY == y)
9002   {
9003     ZX = ZY = -1;
9004
9005     game.robot_wheel_active = FALSE;
9006   }
9007 }
9008
9009 static void InitTimegateWheel(int x, int y)
9010 {
9011   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9012 }
9013
9014 static void RunTimegateWheel(int x, int y)
9015 {
9016   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9017 }
9018
9019 static void InitMagicBallDelay(int x, int y)
9020 {
9021   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9022 }
9023
9024 static void ActivateMagicBall(int bx, int by)
9025 {
9026   int x, y;
9027
9028   if (level.ball_random)
9029   {
9030     int pos_border = RND(8);    /* select one of the eight border elements */
9031     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9032     int xx = pos_content % 3;
9033     int yy = pos_content / 3;
9034
9035     x = bx - 1 + xx;
9036     y = by - 1 + yy;
9037
9038     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9039       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9040   }
9041   else
9042   {
9043     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9044     {
9045       int xx = x - bx + 1;
9046       int yy = y - by + 1;
9047
9048       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9049         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9050     }
9051   }
9052
9053   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9054 }
9055
9056 static void CheckExit(int x, int y)
9057 {
9058   if (local_player->gems_still_needed > 0 ||
9059       local_player->sokobanfields_still_needed > 0 ||
9060       local_player->lights_still_needed > 0)
9061   {
9062     int element = Feld[x][y];
9063     int graphic = el2img(element);
9064
9065     if (IS_ANIMATED(graphic))
9066       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9067
9068     return;
9069   }
9070
9071   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9072     return;
9073
9074   Feld[x][y] = EL_EXIT_OPENING;
9075
9076   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9077 }
9078
9079 static void CheckExitEM(int x, int y)
9080 {
9081   if (local_player->gems_still_needed > 0 ||
9082       local_player->sokobanfields_still_needed > 0 ||
9083       local_player->lights_still_needed > 0)
9084   {
9085     int element = Feld[x][y];
9086     int graphic = el2img(element);
9087
9088     if (IS_ANIMATED(graphic))
9089       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9090
9091     return;
9092   }
9093
9094   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9095     return;
9096
9097   Feld[x][y] = EL_EM_EXIT_OPENING;
9098
9099   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9100 }
9101
9102 static void CheckExitSteel(int x, int y)
9103 {
9104   if (local_player->gems_still_needed > 0 ||
9105       local_player->sokobanfields_still_needed > 0 ||
9106       local_player->lights_still_needed > 0)
9107   {
9108     int element = Feld[x][y];
9109     int graphic = el2img(element);
9110
9111     if (IS_ANIMATED(graphic))
9112       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9113
9114     return;
9115   }
9116
9117   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9118     return;
9119
9120   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9121
9122   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9123 }
9124
9125 static void CheckExitSteelEM(int x, int y)
9126 {
9127   if (local_player->gems_still_needed > 0 ||
9128       local_player->sokobanfields_still_needed > 0 ||
9129       local_player->lights_still_needed > 0)
9130   {
9131     int element = Feld[x][y];
9132     int graphic = el2img(element);
9133
9134     if (IS_ANIMATED(graphic))
9135       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136
9137     return;
9138   }
9139
9140   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9141     return;
9142
9143   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9144
9145   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9146 }
9147
9148 static void CheckExitSP(int x, int y)
9149 {
9150   if (local_player->gems_still_needed > 0)
9151   {
9152     int element = Feld[x][y];
9153     int graphic = el2img(element);
9154
9155     if (IS_ANIMATED(graphic))
9156       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9157
9158     return;
9159   }
9160
9161   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9162     return;
9163
9164   Feld[x][y] = EL_SP_EXIT_OPENING;
9165
9166   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9167 }
9168
9169 static void CloseAllOpenTimegates(void)
9170 {
9171   int x, y;
9172
9173   SCAN_PLAYFIELD(x, y)
9174   {
9175     int element = Feld[x][y];
9176
9177     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9178     {
9179       Feld[x][y] = EL_TIMEGATE_CLOSING;
9180
9181       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9182     }
9183   }
9184 }
9185
9186 static void DrawTwinkleOnField(int x, int y)
9187 {
9188   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9189     return;
9190
9191   if (Feld[x][y] == EL_BD_DIAMOND)
9192     return;
9193
9194   if (MovDelay[x][y] == 0)      /* next animation frame */
9195     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9196
9197   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9198   {
9199     MovDelay[x][y]--;
9200
9201     DrawLevelElementAnimation(x, y, Feld[x][y]);
9202
9203     if (MovDelay[x][y] != 0)
9204     {
9205       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9206                                            10 - MovDelay[x][y]);
9207
9208       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9209     }
9210   }
9211 }
9212
9213 static void MauerWaechst(int x, int y)
9214 {
9215   int delay = 6;
9216
9217   if (!MovDelay[x][y])          /* next animation frame */
9218     MovDelay[x][y] = 3 * delay;
9219
9220   if (MovDelay[x][y])           /* wait some time before next frame */
9221   {
9222     MovDelay[x][y]--;
9223
9224     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9225     {
9226       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9227       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9228
9229       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9230     }
9231
9232     if (!MovDelay[x][y])
9233     {
9234       if (MovDir[x][y] == MV_LEFT)
9235       {
9236         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9237           TEST_DrawLevelField(x - 1, y);
9238       }
9239       else if (MovDir[x][y] == MV_RIGHT)
9240       {
9241         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9242           TEST_DrawLevelField(x + 1, y);
9243       }
9244       else if (MovDir[x][y] == MV_UP)
9245       {
9246         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9247           TEST_DrawLevelField(x, y - 1);
9248       }
9249       else
9250       {
9251         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9252           TEST_DrawLevelField(x, y + 1);
9253       }
9254
9255       Feld[x][y] = Store[x][y];
9256       Store[x][y] = 0;
9257       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9258       TEST_DrawLevelField(x, y);
9259     }
9260   }
9261 }
9262
9263 static void MauerAbleger(int ax, int ay)
9264 {
9265   int element = Feld[ax][ay];
9266   int graphic = el2img(element);
9267   boolean oben_frei = FALSE, unten_frei = FALSE;
9268   boolean links_frei = FALSE, rechts_frei = FALSE;
9269   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9270   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9271   boolean new_wall = FALSE;
9272
9273   if (IS_ANIMATED(graphic))
9274     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9275
9276   if (!MovDelay[ax][ay])        /* start building new wall */
9277     MovDelay[ax][ay] = 6;
9278
9279   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9280   {
9281     MovDelay[ax][ay]--;
9282     if (MovDelay[ax][ay])
9283       return;
9284   }
9285
9286   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9287     oben_frei = TRUE;
9288   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9289     unten_frei = TRUE;
9290   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9291     links_frei = TRUE;
9292   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9293     rechts_frei = TRUE;
9294
9295   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9296       element == EL_EXPANDABLE_WALL_ANY)
9297   {
9298     if (oben_frei)
9299     {
9300       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9301       Store[ax][ay-1] = element;
9302       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9303       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9304         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9305                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9306       new_wall = TRUE;
9307     }
9308     if (unten_frei)
9309     {
9310       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9311       Store[ax][ay+1] = element;
9312       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9313       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9314         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9315                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9316       new_wall = TRUE;
9317     }
9318   }
9319
9320   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9321       element == EL_EXPANDABLE_WALL_ANY ||
9322       element == EL_EXPANDABLE_WALL ||
9323       element == EL_BD_EXPANDABLE_WALL)
9324   {
9325     if (links_frei)
9326     {
9327       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9328       Store[ax-1][ay] = element;
9329       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9330       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9331         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9332                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9333       new_wall = TRUE;
9334     }
9335
9336     if (rechts_frei)
9337     {
9338       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9339       Store[ax+1][ay] = element;
9340       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9341       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9342         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9343                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9344       new_wall = TRUE;
9345     }
9346   }
9347
9348   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9349     TEST_DrawLevelField(ax, ay);
9350
9351   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9352     oben_massiv = TRUE;
9353   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9354     unten_massiv = TRUE;
9355   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9356     links_massiv = TRUE;
9357   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9358     rechts_massiv = TRUE;
9359
9360   if (((oben_massiv && unten_massiv) ||
9361        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9362        element == EL_EXPANDABLE_WALL) &&
9363       ((links_massiv && rechts_massiv) ||
9364        element == EL_EXPANDABLE_WALL_VERTICAL))
9365     Feld[ax][ay] = EL_WALL;
9366
9367   if (new_wall)
9368     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9369 }
9370
9371 static void MauerAblegerStahl(int ax, int ay)
9372 {
9373   int element = Feld[ax][ay];
9374   int graphic = el2img(element);
9375   boolean oben_frei = FALSE, unten_frei = FALSE;
9376   boolean links_frei = FALSE, rechts_frei = FALSE;
9377   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9378   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9379   boolean new_wall = FALSE;
9380
9381   if (IS_ANIMATED(graphic))
9382     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9383
9384   if (!MovDelay[ax][ay])        /* start building new wall */
9385     MovDelay[ax][ay] = 6;
9386
9387   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9388   {
9389     MovDelay[ax][ay]--;
9390     if (MovDelay[ax][ay])
9391       return;
9392   }
9393
9394   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9395     oben_frei = TRUE;
9396   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9397     unten_frei = TRUE;
9398   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9399     links_frei = TRUE;
9400   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9401     rechts_frei = TRUE;
9402
9403   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9404       element == EL_EXPANDABLE_STEELWALL_ANY)
9405   {
9406     if (oben_frei)
9407     {
9408       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9409       Store[ax][ay-1] = element;
9410       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9411       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9412         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9413                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9414       new_wall = TRUE;
9415     }
9416     if (unten_frei)
9417     {
9418       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9419       Store[ax][ay+1] = element;
9420       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9421       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9422         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9423                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9424       new_wall = TRUE;
9425     }
9426   }
9427
9428   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9429       element == EL_EXPANDABLE_STEELWALL_ANY)
9430   {
9431     if (links_frei)
9432     {
9433       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9434       Store[ax-1][ay] = element;
9435       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9436       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9437         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9438                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9439       new_wall = TRUE;
9440     }
9441
9442     if (rechts_frei)
9443     {
9444       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9445       Store[ax+1][ay] = element;
9446       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9447       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9448         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9449                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9450       new_wall = TRUE;
9451     }
9452   }
9453
9454   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9455     oben_massiv = TRUE;
9456   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9457     unten_massiv = TRUE;
9458   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9459     links_massiv = TRUE;
9460   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9461     rechts_massiv = TRUE;
9462
9463   if (((oben_massiv && unten_massiv) ||
9464        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9465       ((links_massiv && rechts_massiv) ||
9466        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9467     Feld[ax][ay] = EL_STEELWALL;
9468
9469   if (new_wall)
9470     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9471 }
9472
9473 static void CheckForDragon(int x, int y)
9474 {
9475   int i, j;
9476   boolean dragon_found = FALSE;
9477   static int xy[4][2] =
9478   {
9479     { 0, -1 },
9480     { -1, 0 },
9481     { +1, 0 },
9482     { 0, +1 }
9483   };
9484
9485   for (i = 0; i < NUM_DIRECTIONS; i++)
9486   {
9487     for (j = 0; j < 4; j++)
9488     {
9489       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9490
9491       if (IN_LEV_FIELD(xx, yy) &&
9492           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9493       {
9494         if (Feld[xx][yy] == EL_DRAGON)
9495           dragon_found = TRUE;
9496       }
9497       else
9498         break;
9499     }
9500   }
9501
9502   if (!dragon_found)
9503   {
9504     for (i = 0; i < NUM_DIRECTIONS; i++)
9505     {
9506       for (j = 0; j < 3; j++)
9507       {
9508         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9509   
9510         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9511         {
9512           Feld[xx][yy] = EL_EMPTY;
9513           TEST_DrawLevelField(xx, yy);
9514         }
9515         else
9516           break;
9517       }
9518     }
9519   }
9520 }
9521
9522 static void InitBuggyBase(int x, int y)
9523 {
9524   int element = Feld[x][y];
9525   int activating_delay = FRAMES_PER_SECOND / 4;
9526
9527   ChangeDelay[x][y] =
9528     (element == EL_SP_BUGGY_BASE ?
9529      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9530      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9531      activating_delay :
9532      element == EL_SP_BUGGY_BASE_ACTIVE ?
9533      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9534 }
9535
9536 static void WarnBuggyBase(int x, int y)
9537 {
9538   int i;
9539   static int xy[4][2] =
9540   {
9541     { 0, -1 },
9542     { -1, 0 },
9543     { +1, 0 },
9544     { 0, +1 }
9545   };
9546
9547   for (i = 0; i < NUM_DIRECTIONS; i++)
9548   {
9549     int xx = x + xy[i][0];
9550     int yy = y + xy[i][1];
9551
9552     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9553     {
9554       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9555
9556       break;
9557     }
9558   }
9559 }
9560
9561 static void InitTrap(int x, int y)
9562 {
9563   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9564 }
9565
9566 static void ActivateTrap(int x, int y)
9567 {
9568   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9569 }
9570
9571 static void ChangeActiveTrap(int x, int y)
9572 {
9573   int graphic = IMG_TRAP_ACTIVE;
9574
9575   /* if new animation frame was drawn, correct crumbled sand border */
9576   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9577     TEST_DrawLevelFieldCrumbled(x, y);
9578 }
9579
9580 static int getSpecialActionElement(int element, int number, int base_element)
9581 {
9582   return (element != EL_EMPTY ? element :
9583           number != -1 ? base_element + number - 1 :
9584           EL_EMPTY);
9585 }
9586
9587 static int getModifiedActionNumber(int value_old, int operator, int operand,
9588                                    int value_min, int value_max)
9589 {
9590   int value_new = (operator == CA_MODE_SET      ? operand :
9591                    operator == CA_MODE_ADD      ? value_old + operand :
9592                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9593                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9594                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9595                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9596                    value_old);
9597
9598   return (value_new < value_min ? value_min :
9599           value_new > value_max ? value_max :
9600           value_new);
9601 }
9602
9603 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9604 {
9605   struct ElementInfo *ei = &element_info[element];
9606   struct ElementChangeInfo *change = &ei->change_page[page];
9607   int target_element = change->target_element;
9608   int action_type = change->action_type;
9609   int action_mode = change->action_mode;
9610   int action_arg = change->action_arg;
9611   int action_element = change->action_element;
9612   int i;
9613
9614   if (!change->has_action)
9615     return;
9616
9617   /* ---------- determine action paramater values -------------------------- */
9618
9619   int level_time_value =
9620     (level.time > 0 ? TimeLeft :
9621      TimePlayed);
9622
9623   int action_arg_element_raw =
9624     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9625      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9626      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9627      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9628      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9629      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9630      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9631      EL_EMPTY);
9632   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9633
9634   int action_arg_direction =
9635     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9636      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9637      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9638      change->actual_trigger_side :
9639      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9640      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9641      MV_NONE);
9642
9643   int action_arg_number_min =
9644     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9645      CA_ARG_MIN);
9646
9647   int action_arg_number_max =
9648     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9649      action_type == CA_SET_LEVEL_GEMS ? 999 :
9650      action_type == CA_SET_LEVEL_TIME ? 9999 :
9651      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9652      action_type == CA_SET_CE_VALUE ? 9999 :
9653      action_type == CA_SET_CE_SCORE ? 9999 :
9654      CA_ARG_MAX);
9655
9656   int action_arg_number_reset =
9657     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9658      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9659      action_type == CA_SET_LEVEL_TIME ? level.time :
9660      action_type == CA_SET_LEVEL_SCORE ? 0 :
9661      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9662      action_type == CA_SET_CE_SCORE ? 0 :
9663      0);
9664
9665   int action_arg_number =
9666     (action_arg <= CA_ARG_MAX ? action_arg :
9667      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9668      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9669      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9670      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9671      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9672      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9673      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9674      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9675      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9676      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9677      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9678      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9679      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9680      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9681      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9682      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9683      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9684      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9685      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9686      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9687      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9688      -1);
9689
9690   int action_arg_number_old =
9691     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9692      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9693      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9694      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9695      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9696      0);
9697
9698   int action_arg_number_new =
9699     getModifiedActionNumber(action_arg_number_old,
9700                             action_mode, action_arg_number,
9701                             action_arg_number_min, action_arg_number_max);
9702
9703   int trigger_player_bits =
9704     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9705      change->actual_trigger_player_bits : change->trigger_player);
9706
9707   int action_arg_player_bits =
9708     (action_arg >= CA_ARG_PLAYER_1 &&
9709      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9710      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9711      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9712      PLAYER_BITS_ANY);
9713
9714   /* ---------- execute action  -------------------------------------------- */
9715
9716   switch (action_type)
9717   {
9718     case CA_NO_ACTION:
9719     {
9720       return;
9721     }
9722
9723     /* ---------- level actions  ------------------------------------------- */
9724
9725     case CA_RESTART_LEVEL:
9726     {
9727       game.restart_level = TRUE;
9728
9729       break;
9730     }
9731
9732     case CA_SHOW_ENVELOPE:
9733     {
9734       int element = getSpecialActionElement(action_arg_element,
9735                                             action_arg_number, EL_ENVELOPE_1);
9736
9737       if (IS_ENVELOPE(element))
9738         local_player->show_envelope = element;
9739
9740       break;
9741     }
9742
9743     case CA_SET_LEVEL_TIME:
9744     {
9745       if (level.time > 0)       /* only modify limited time value */
9746       {
9747         TimeLeft = action_arg_number_new;
9748
9749         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9750
9751         DisplayGameControlValues();
9752
9753         if (!TimeLeft && setup.time_limit)
9754           for (i = 0; i < MAX_PLAYERS; i++)
9755             KillPlayer(&stored_player[i]);
9756       }
9757
9758       break;
9759     }
9760
9761     case CA_SET_LEVEL_SCORE:
9762     {
9763       local_player->score = action_arg_number_new;
9764
9765       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9766
9767       DisplayGameControlValues();
9768
9769       break;
9770     }
9771
9772     case CA_SET_LEVEL_GEMS:
9773     {
9774       local_player->gems_still_needed = action_arg_number_new;
9775
9776       game.snapshot.collected_item = TRUE;
9777
9778       game_panel_controls[GAME_PANEL_GEMS].value =
9779         local_player->gems_still_needed;
9780
9781       DisplayGameControlValues();
9782
9783       break;
9784     }
9785
9786     case CA_SET_LEVEL_WIND:
9787     {
9788       game.wind_direction = action_arg_direction;
9789
9790       break;
9791     }
9792
9793     case CA_SET_LEVEL_RANDOM_SEED:
9794     {
9795       /* ensure that setting a new random seed while playing is predictable */
9796       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9797
9798       break;
9799     }
9800
9801     /* ---------- player actions  ------------------------------------------ */
9802
9803     case CA_MOVE_PLAYER:
9804     {
9805       /* automatically move to the next field in specified direction */
9806       for (i = 0; i < MAX_PLAYERS; i++)
9807         if (trigger_player_bits & (1 << i))
9808           stored_player[i].programmed_action = action_arg_direction;
9809
9810       break;
9811     }
9812
9813     case CA_EXIT_PLAYER:
9814     {
9815       for (i = 0; i < MAX_PLAYERS; i++)
9816         if (action_arg_player_bits & (1 << i))
9817           ExitPlayer(&stored_player[i]);
9818
9819       if (AllPlayersGone)
9820         PlayerWins(local_player);
9821
9822       break;
9823     }
9824
9825     case CA_KILL_PLAYER:
9826     {
9827       for (i = 0; i < MAX_PLAYERS; i++)
9828         if (action_arg_player_bits & (1 << i))
9829           KillPlayer(&stored_player[i]);
9830
9831       break;
9832     }
9833
9834     case CA_SET_PLAYER_KEYS:
9835     {
9836       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9837       int element = getSpecialActionElement(action_arg_element,
9838                                             action_arg_number, EL_KEY_1);
9839
9840       if (IS_KEY(element))
9841       {
9842         for (i = 0; i < MAX_PLAYERS; i++)
9843         {
9844           if (trigger_player_bits & (1 << i))
9845           {
9846             stored_player[i].key[KEY_NR(element)] = key_state;
9847
9848             DrawGameDoorValues();
9849           }
9850         }
9851       }
9852
9853       break;
9854     }
9855
9856     case CA_SET_PLAYER_SPEED:
9857     {
9858       for (i = 0; i < MAX_PLAYERS; i++)
9859       {
9860         if (trigger_player_bits & (1 << i))
9861         {
9862           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9863
9864           if (action_arg == CA_ARG_SPEED_FASTER &&
9865               stored_player[i].cannot_move)
9866           {
9867             action_arg_number = STEPSIZE_VERY_SLOW;
9868           }
9869           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9870                    action_arg == CA_ARG_SPEED_FASTER)
9871           {
9872             action_arg_number = 2;
9873             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9874                            CA_MODE_MULTIPLY);
9875           }
9876           else if (action_arg == CA_ARG_NUMBER_RESET)
9877           {
9878             action_arg_number = level.initial_player_stepsize[i];
9879           }
9880
9881           move_stepsize =
9882             getModifiedActionNumber(move_stepsize,
9883                                     action_mode,
9884                                     action_arg_number,
9885                                     action_arg_number_min,
9886                                     action_arg_number_max);
9887
9888           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9889         }
9890       }
9891
9892       break;
9893     }
9894
9895     case CA_SET_PLAYER_SHIELD:
9896     {
9897       for (i = 0; i < MAX_PLAYERS; i++)
9898       {
9899         if (trigger_player_bits & (1 << i))
9900         {
9901           if (action_arg == CA_ARG_SHIELD_OFF)
9902           {
9903             stored_player[i].shield_normal_time_left = 0;
9904             stored_player[i].shield_deadly_time_left = 0;
9905           }
9906           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9907           {
9908             stored_player[i].shield_normal_time_left = 999999;
9909           }
9910           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9911           {
9912             stored_player[i].shield_normal_time_left = 999999;
9913             stored_player[i].shield_deadly_time_left = 999999;
9914           }
9915         }
9916       }
9917
9918       break;
9919     }
9920
9921     case CA_SET_PLAYER_GRAVITY:
9922     {
9923       for (i = 0; i < MAX_PLAYERS; i++)
9924       {
9925         if (trigger_player_bits & (1 << i))
9926         {
9927           stored_player[i].gravity =
9928             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9929              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9930              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9931              stored_player[i].gravity);
9932         }
9933       }
9934
9935       break;
9936     }
9937
9938     case CA_SET_PLAYER_ARTWORK:
9939     {
9940       for (i = 0; i < MAX_PLAYERS; i++)
9941       {
9942         if (trigger_player_bits & (1 << i))
9943         {
9944           int artwork_element = action_arg_element;
9945
9946           if (action_arg == CA_ARG_ELEMENT_RESET)
9947             artwork_element =
9948               (level.use_artwork_element[i] ? level.artwork_element[i] :
9949                stored_player[i].element_nr);
9950
9951           if (stored_player[i].artwork_element != artwork_element)
9952             stored_player[i].Frame = 0;
9953
9954           stored_player[i].artwork_element = artwork_element;
9955
9956           SetPlayerWaiting(&stored_player[i], FALSE);
9957
9958           /* set number of special actions for bored and sleeping animation */
9959           stored_player[i].num_special_action_bored =
9960             get_num_special_action(artwork_element,
9961                                    ACTION_BORING_1, ACTION_BORING_LAST);
9962           stored_player[i].num_special_action_sleeping =
9963             get_num_special_action(artwork_element,
9964                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9965         }
9966       }
9967
9968       break;
9969     }
9970
9971     case CA_SET_PLAYER_INVENTORY:
9972     {
9973       for (i = 0; i < MAX_PLAYERS; i++)
9974       {
9975         struct PlayerInfo *player = &stored_player[i];
9976         int j, k;
9977
9978         if (trigger_player_bits & (1 << i))
9979         {
9980           int inventory_element = action_arg_element;
9981
9982           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9983               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9984               action_arg == CA_ARG_ELEMENT_ACTION)
9985           {
9986             int element = inventory_element;
9987             int collect_count = element_info[element].collect_count_initial;
9988
9989             if (!IS_CUSTOM_ELEMENT(element))
9990               collect_count = 1;
9991
9992             if (collect_count == 0)
9993               player->inventory_infinite_element = element;
9994             else
9995               for (k = 0; k < collect_count; k++)
9996                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9997                   player->inventory_element[player->inventory_size++] =
9998                     element;
9999           }
10000           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10001                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10002                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10003           {
10004             if (player->inventory_infinite_element != EL_UNDEFINED &&
10005                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10006                                      action_arg_element_raw))
10007               player->inventory_infinite_element = EL_UNDEFINED;
10008
10009             for (k = 0, j = 0; j < player->inventory_size; j++)
10010             {
10011               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10012                                         action_arg_element_raw))
10013                 player->inventory_element[k++] = player->inventory_element[j];
10014             }
10015
10016             player->inventory_size = k;
10017           }
10018           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10019           {
10020             if (player->inventory_size > 0)
10021             {
10022               for (j = 0; j < player->inventory_size - 1; j++)
10023                 player->inventory_element[j] = player->inventory_element[j + 1];
10024
10025               player->inventory_size--;
10026             }
10027           }
10028           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10029           {
10030             if (player->inventory_size > 0)
10031               player->inventory_size--;
10032           }
10033           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10034           {
10035             player->inventory_infinite_element = EL_UNDEFINED;
10036             player->inventory_size = 0;
10037           }
10038           else if (action_arg == CA_ARG_INVENTORY_RESET)
10039           {
10040             player->inventory_infinite_element = EL_UNDEFINED;
10041             player->inventory_size = 0;
10042
10043             if (level.use_initial_inventory[i])
10044             {
10045               for (j = 0; j < level.initial_inventory_size[i]; j++)
10046               {
10047                 int element = level.initial_inventory_content[i][j];
10048                 int collect_count = element_info[element].collect_count_initial;
10049
10050                 if (!IS_CUSTOM_ELEMENT(element))
10051                   collect_count = 1;
10052
10053                 if (collect_count == 0)
10054                   player->inventory_infinite_element = element;
10055                 else
10056                   for (k = 0; k < collect_count; k++)
10057                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10058                       player->inventory_element[player->inventory_size++] =
10059                         element;
10060               }
10061             }
10062           }
10063         }
10064       }
10065
10066       break;
10067     }
10068
10069     /* ---------- CE actions  ---------------------------------------------- */
10070
10071     case CA_SET_CE_VALUE:
10072     {
10073       int last_ce_value = CustomValue[x][y];
10074
10075       CustomValue[x][y] = action_arg_number_new;
10076
10077       if (CustomValue[x][y] != last_ce_value)
10078       {
10079         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10080         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10081
10082         if (CustomValue[x][y] == 0)
10083         {
10084           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10085           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10086         }
10087       }
10088
10089       break;
10090     }
10091
10092     case CA_SET_CE_SCORE:
10093     {
10094       int last_ce_score = ei->collect_score;
10095
10096       ei->collect_score = action_arg_number_new;
10097
10098       if (ei->collect_score != last_ce_score)
10099       {
10100         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10101         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10102
10103         if (ei->collect_score == 0)
10104         {
10105           int xx, yy;
10106
10107           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10108           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10109
10110           /*
10111             This is a very special case that seems to be a mixture between
10112             CheckElementChange() and CheckTriggeredElementChange(): while
10113             the first one only affects single elements that are triggered
10114             directly, the second one affects multiple elements in the playfield
10115             that are triggered indirectly by another element. This is a third
10116             case: Changing the CE score always affects multiple identical CEs,
10117             so every affected CE must be checked, not only the single CE for
10118             which the CE score was changed in the first place (as every instance
10119             of that CE shares the same CE score, and therefore also can change)!
10120           */
10121           SCAN_PLAYFIELD(xx, yy)
10122           {
10123             if (Feld[xx][yy] == element)
10124               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10125                                  CE_SCORE_GETS_ZERO);
10126           }
10127         }
10128       }
10129
10130       break;
10131     }
10132
10133     case CA_SET_CE_ARTWORK:
10134     {
10135       int artwork_element = action_arg_element;
10136       boolean reset_frame = FALSE;
10137       int xx, yy;
10138
10139       if (action_arg == CA_ARG_ELEMENT_RESET)
10140         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10141                            element);
10142
10143       if (ei->gfx_element != artwork_element)
10144         reset_frame = TRUE;
10145
10146       ei->gfx_element = artwork_element;
10147
10148       SCAN_PLAYFIELD(xx, yy)
10149       {
10150         if (Feld[xx][yy] == element)
10151         {
10152           if (reset_frame)
10153           {
10154             ResetGfxAnimation(xx, yy);
10155             ResetRandomAnimationValue(xx, yy);
10156           }
10157
10158           TEST_DrawLevelField(xx, yy);
10159         }
10160       }
10161
10162       break;
10163     }
10164
10165     /* ---------- engine actions  ------------------------------------------ */
10166
10167     case CA_SET_ENGINE_SCAN_MODE:
10168     {
10169       InitPlayfieldScanMode(action_arg);
10170
10171       break;
10172     }
10173
10174     default:
10175       break;
10176   }
10177 }
10178
10179 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10180 {
10181   int old_element = Feld[x][y];
10182   int new_element = GetElementFromGroupElement(element);
10183   int previous_move_direction = MovDir[x][y];
10184   int last_ce_value = CustomValue[x][y];
10185   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10186   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10187   boolean add_player_onto_element = (new_element_is_player &&
10188                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10189                                      IS_WALKABLE(old_element));
10190
10191   if (!add_player_onto_element)
10192   {
10193     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10194       RemoveMovingField(x, y);
10195     else
10196       RemoveField(x, y);
10197
10198     Feld[x][y] = new_element;
10199
10200     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10201       MovDir[x][y] = previous_move_direction;
10202
10203     if (element_info[new_element].use_last_ce_value)
10204       CustomValue[x][y] = last_ce_value;
10205
10206     InitField_WithBug1(x, y, FALSE);
10207
10208     new_element = Feld[x][y];   /* element may have changed */
10209
10210     ResetGfxAnimation(x, y);
10211     ResetRandomAnimationValue(x, y);
10212
10213     TEST_DrawLevelField(x, y);
10214
10215     if (GFX_CRUMBLED(new_element))
10216       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10217   }
10218
10219   /* check if element under the player changes from accessible to unaccessible
10220      (needed for special case of dropping element which then changes) */
10221   /* (must be checked after creating new element for walkable group elements) */
10222   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10223       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10224   {
10225     Bang(x, y);
10226
10227     return;
10228   }
10229
10230   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10231   if (new_element_is_player)
10232     RelocatePlayer(x, y, new_element);
10233
10234   if (is_change)
10235     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10236
10237   TestIfBadThingTouchesPlayer(x, y);
10238   TestIfPlayerTouchesCustomElement(x, y);
10239   TestIfElementTouchesCustomElement(x, y);
10240 }
10241
10242 static void CreateField(int x, int y, int element)
10243 {
10244   CreateFieldExt(x, y, element, FALSE);
10245 }
10246
10247 static void CreateElementFromChange(int x, int y, int element)
10248 {
10249   element = GET_VALID_RUNTIME_ELEMENT(element);
10250
10251   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10252   {
10253     int old_element = Feld[x][y];
10254
10255     /* prevent changed element from moving in same engine frame
10256        unless both old and new element can either fall or move */
10257     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10258         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10259       Stop[x][y] = TRUE;
10260   }
10261
10262   CreateFieldExt(x, y, element, TRUE);
10263 }
10264
10265 static boolean ChangeElement(int x, int y, int element, int page)
10266 {
10267   struct ElementInfo *ei = &element_info[element];
10268   struct ElementChangeInfo *change = &ei->change_page[page];
10269   int ce_value = CustomValue[x][y];
10270   int ce_score = ei->collect_score;
10271   int target_element;
10272   int old_element = Feld[x][y];
10273
10274   /* always use default change event to prevent running into a loop */
10275   if (ChangeEvent[x][y] == -1)
10276     ChangeEvent[x][y] = CE_DELAY;
10277
10278   if (ChangeEvent[x][y] == CE_DELAY)
10279   {
10280     /* reset actual trigger element, trigger player and action element */
10281     change->actual_trigger_element = EL_EMPTY;
10282     change->actual_trigger_player = EL_EMPTY;
10283     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10284     change->actual_trigger_side = CH_SIDE_NONE;
10285     change->actual_trigger_ce_value = 0;
10286     change->actual_trigger_ce_score = 0;
10287   }
10288
10289   /* do not change elements more than a specified maximum number of changes */
10290   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10291     return FALSE;
10292
10293   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10294
10295   if (change->explode)
10296   {
10297     Bang(x, y);
10298
10299     return TRUE;
10300   }
10301
10302   if (change->use_target_content)
10303   {
10304     boolean complete_replace = TRUE;
10305     boolean can_replace[3][3];
10306     int xx, yy;
10307
10308     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10309     {
10310       boolean is_empty;
10311       boolean is_walkable;
10312       boolean is_diggable;
10313       boolean is_collectible;
10314       boolean is_removable;
10315       boolean is_destructible;
10316       int ex = x + xx - 1;
10317       int ey = y + yy - 1;
10318       int content_element = change->target_content.e[xx][yy];
10319       int e;
10320
10321       can_replace[xx][yy] = TRUE;
10322
10323       if (ex == x && ey == y)   /* do not check changing element itself */
10324         continue;
10325
10326       if (content_element == EL_EMPTY_SPACE)
10327       {
10328         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10329
10330         continue;
10331       }
10332
10333       if (!IN_LEV_FIELD(ex, ey))
10334       {
10335         can_replace[xx][yy] = FALSE;
10336         complete_replace = FALSE;
10337
10338         continue;
10339       }
10340
10341       e = Feld[ex][ey];
10342
10343       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10344         e = MovingOrBlocked2Element(ex, ey);
10345
10346       is_empty = (IS_FREE(ex, ey) ||
10347                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10348
10349       is_walkable     = (is_empty || IS_WALKABLE(e));
10350       is_diggable     = (is_empty || IS_DIGGABLE(e));
10351       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10352       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10353       is_removable    = (is_diggable || is_collectible);
10354
10355       can_replace[xx][yy] =
10356         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10357           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10358           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10359           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10360           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10361           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10362          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10363
10364       if (!can_replace[xx][yy])
10365         complete_replace = FALSE;
10366     }
10367
10368     if (!change->only_if_complete || complete_replace)
10369     {
10370       boolean something_has_changed = FALSE;
10371
10372       if (change->only_if_complete && change->use_random_replace &&
10373           RND(100) < change->random_percentage)
10374         return FALSE;
10375
10376       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10377       {
10378         int ex = x + xx - 1;
10379         int ey = y + yy - 1;
10380         int content_element;
10381
10382         if (can_replace[xx][yy] && (!change->use_random_replace ||
10383                                     RND(100) < change->random_percentage))
10384         {
10385           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10386             RemoveMovingField(ex, ey);
10387
10388           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10389
10390           content_element = change->target_content.e[xx][yy];
10391           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10392                                               ce_value, ce_score);
10393
10394           CreateElementFromChange(ex, ey, target_element);
10395
10396           something_has_changed = TRUE;
10397
10398           /* for symmetry reasons, freeze newly created border elements */
10399           if (ex != x || ey != y)
10400             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10401         }
10402       }
10403
10404       if (something_has_changed)
10405       {
10406         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10407         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10408       }
10409     }
10410   }
10411   else
10412   {
10413     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10414                                         ce_value, ce_score);
10415
10416     if (element == EL_DIAGONAL_GROWING ||
10417         element == EL_DIAGONAL_SHRINKING)
10418     {
10419       target_element = Store[x][y];
10420
10421       Store[x][y] = EL_EMPTY;
10422     }
10423
10424     CreateElementFromChange(x, y, target_element);
10425
10426     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10427     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10428   }
10429
10430   /* this uses direct change before indirect change */
10431   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10432
10433   return TRUE;
10434 }
10435
10436 static void HandleElementChange(int x, int y, int page)
10437 {
10438   int element = MovingOrBlocked2Element(x, y);
10439   struct ElementInfo *ei = &element_info[element];
10440   struct ElementChangeInfo *change = &ei->change_page[page];
10441   boolean handle_action_before_change = FALSE;
10442
10443 #ifdef DEBUG
10444   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10445       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10446   {
10447     printf("\n\n");
10448     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10449            x, y, element, element_info[element].token_name);
10450     printf("HandleElementChange(): This should never happen!\n");
10451     printf("\n\n");
10452   }
10453 #endif
10454
10455   /* this can happen with classic bombs on walkable, changing elements */
10456   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10457   {
10458     return;
10459   }
10460
10461   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10462   {
10463     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10464
10465     if (change->can_change)
10466     {
10467       /* !!! not clear why graphic animation should be reset at all here !!! */
10468       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10469       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10470
10471       /*
10472         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10473
10474         When using an animation frame delay of 1 (this only happens with
10475         "sp_zonk.moving.left/right" in the classic graphics), the default
10476         (non-moving) animation shows wrong animation frames (while the
10477         moving animation, like "sp_zonk.moving.left/right", is correct,
10478         so this graphical bug never shows up with the classic graphics).
10479         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10480         be drawn instead of the correct frames 0,1,2,3. This is caused by
10481         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10482         an element change: First when the change delay ("ChangeDelay[][]")
10483         counter has reached zero after decrementing, then a second time in
10484         the next frame (after "GfxFrame[][]" was already incremented) when
10485         "ChangeDelay[][]" is reset to the initial delay value again.
10486
10487         This causes frame 0 to be drawn twice, while the last frame won't
10488         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10489
10490         As some animations may already be cleverly designed around this bug
10491         (at least the "Snake Bite" snake tail animation does this), it cannot
10492         simply be fixed here without breaking such existing animations.
10493         Unfortunately, it cannot easily be detected if a graphics set was
10494         designed "before" or "after" the bug was fixed. As a workaround,
10495         a new graphics set option "game.graphics_engine_version" was added
10496         to be able to specify the game's major release version for which the
10497         graphics set was designed, which can then be used to decide if the
10498         bugfix should be used (version 4 and above) or not (version 3 or
10499         below, or if no version was specified at all, as with old sets).
10500
10501         (The wrong/fixed animation frames can be tested with the test level set
10502         "test_gfxframe" and level "000", which contains a specially prepared
10503         custom element at level position (x/y) == (11/9) which uses the zonk
10504         animation mentioned above. Using "game.graphics_engine_version: 4"
10505         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10506         This can also be seen from the debug output for this test element.)
10507       */
10508
10509       /* when a custom element is about to change (for example by change delay),
10510          do not reset graphic animation when the custom element is moving */
10511       if (game.graphics_engine_version < 4 &&
10512           !IS_MOVING(x, y))
10513       {
10514         ResetGfxAnimation(x, y);
10515         ResetRandomAnimationValue(x, y);
10516       }
10517
10518       if (change->pre_change_function)
10519         change->pre_change_function(x, y);
10520     }
10521   }
10522
10523   ChangeDelay[x][y]--;
10524
10525   if (ChangeDelay[x][y] != 0)           /* continue element change */
10526   {
10527     if (change->can_change)
10528     {
10529       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10530
10531       if (IS_ANIMATED(graphic))
10532         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10533
10534       if (change->change_function)
10535         change->change_function(x, y);
10536     }
10537   }
10538   else                                  /* finish element change */
10539   {
10540     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10541     {
10542       page = ChangePage[x][y];
10543       ChangePage[x][y] = -1;
10544
10545       change = &ei->change_page[page];
10546     }
10547
10548     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10549     {
10550       ChangeDelay[x][y] = 1;            /* try change after next move step */
10551       ChangePage[x][y] = page;          /* remember page to use for change */
10552
10553       return;
10554     }
10555
10556     /* special case: set new level random seed before changing element */
10557     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10558       handle_action_before_change = TRUE;
10559
10560     if (change->has_action && handle_action_before_change)
10561       ExecuteCustomElementAction(x, y, element, page);
10562
10563     if (change->can_change)
10564     {
10565       if (ChangeElement(x, y, element, page))
10566       {
10567         if (change->post_change_function)
10568           change->post_change_function(x, y);
10569       }
10570     }
10571
10572     if (change->has_action && !handle_action_before_change)
10573       ExecuteCustomElementAction(x, y, element, page);
10574   }
10575 }
10576
10577 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10578                                               int trigger_element,
10579                                               int trigger_event,
10580                                               int trigger_player,
10581                                               int trigger_side,
10582                                               int trigger_page)
10583 {
10584   boolean change_done_any = FALSE;
10585   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10586   int i;
10587
10588   if (!(trigger_events[trigger_element][trigger_event]))
10589     return FALSE;
10590
10591   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10592
10593   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10594   {
10595     int element = EL_CUSTOM_START + i;
10596     boolean change_done = FALSE;
10597     int p;
10598
10599     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10600         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10601       continue;
10602
10603     for (p = 0; p < element_info[element].num_change_pages; p++)
10604     {
10605       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10606
10607       if (change->can_change_or_has_action &&
10608           change->has_event[trigger_event] &&
10609           change->trigger_side & trigger_side &&
10610           change->trigger_player & trigger_player &&
10611           change->trigger_page & trigger_page_bits &&
10612           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10613       {
10614         change->actual_trigger_element = trigger_element;
10615         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10616         change->actual_trigger_player_bits = trigger_player;
10617         change->actual_trigger_side = trigger_side;
10618         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10619         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10620
10621         if ((change->can_change && !change_done) || change->has_action)
10622         {
10623           int x, y;
10624
10625           SCAN_PLAYFIELD(x, y)
10626           {
10627             if (Feld[x][y] == element)
10628             {
10629               if (change->can_change && !change_done)
10630               {
10631                 /* if element already changed in this frame, not only prevent
10632                    another element change (checked in ChangeElement()), but
10633                    also prevent additional element actions for this element */
10634
10635                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10636                     !level.use_action_after_change_bug)
10637                   continue;
10638
10639                 ChangeDelay[x][y] = 1;
10640                 ChangeEvent[x][y] = trigger_event;
10641
10642                 HandleElementChange(x, y, p);
10643               }
10644               else if (change->has_action)
10645               {
10646                 /* if element already changed in this frame, not only prevent
10647                    another element change (checked in ChangeElement()), but
10648                    also prevent additional element actions for this element */
10649
10650                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10651                     !level.use_action_after_change_bug)
10652                   continue;
10653
10654                 ExecuteCustomElementAction(x, y, element, p);
10655                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10656               }
10657             }
10658           }
10659
10660           if (change->can_change)
10661           {
10662             change_done = TRUE;
10663             change_done_any = TRUE;
10664           }
10665         }
10666       }
10667     }
10668   }
10669
10670   RECURSION_LOOP_DETECTION_END();
10671
10672   return change_done_any;
10673 }
10674
10675 static boolean CheckElementChangeExt(int x, int y,
10676                                      int element,
10677                                      int trigger_element,
10678                                      int trigger_event,
10679                                      int trigger_player,
10680                                      int trigger_side)
10681 {
10682   boolean change_done = FALSE;
10683   int p;
10684
10685   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10686       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10687     return FALSE;
10688
10689   if (Feld[x][y] == EL_BLOCKED)
10690   {
10691     Blocked2Moving(x, y, &x, &y);
10692     element = Feld[x][y];
10693   }
10694
10695   /* check if element has already changed or is about to change after moving */
10696   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10697        Feld[x][y] != element) ||
10698
10699       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10700        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10701         ChangePage[x][y] != -1)))
10702     return FALSE;
10703
10704   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10705
10706   for (p = 0; p < element_info[element].num_change_pages; p++)
10707   {
10708     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10709
10710     /* check trigger element for all events where the element that is checked
10711        for changing interacts with a directly adjacent element -- this is
10712        different to element changes that affect other elements to change on the
10713        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10714     boolean check_trigger_element =
10715       (trigger_event == CE_TOUCHING_X ||
10716        trigger_event == CE_HITTING_X ||
10717        trigger_event == CE_HIT_BY_X ||
10718        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10719
10720     if (change->can_change_or_has_action &&
10721         change->has_event[trigger_event] &&
10722         change->trigger_side & trigger_side &&
10723         change->trigger_player & trigger_player &&
10724         (!check_trigger_element ||
10725          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10726     {
10727       change->actual_trigger_element = trigger_element;
10728       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10729       change->actual_trigger_player_bits = trigger_player;
10730       change->actual_trigger_side = trigger_side;
10731       change->actual_trigger_ce_value = CustomValue[x][y];
10732       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10733
10734       /* special case: trigger element not at (x,y) position for some events */
10735       if (check_trigger_element)
10736       {
10737         static struct
10738         {
10739           int dx, dy;
10740         } move_xy[] =
10741           {
10742             {  0,  0 },
10743             { -1,  0 },
10744             { +1,  0 },
10745             {  0,  0 },
10746             {  0, -1 },
10747             {  0,  0 }, { 0, 0 }, { 0, 0 },
10748             {  0, +1 }
10749           };
10750
10751         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10752         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10753
10754         change->actual_trigger_ce_value = CustomValue[xx][yy];
10755         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10756       }
10757
10758       if (change->can_change && !change_done)
10759       {
10760         ChangeDelay[x][y] = 1;
10761         ChangeEvent[x][y] = trigger_event;
10762
10763         HandleElementChange(x, y, p);
10764
10765         change_done = TRUE;
10766       }
10767       else if (change->has_action)
10768       {
10769         ExecuteCustomElementAction(x, y, element, p);
10770         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10771       }
10772     }
10773   }
10774
10775   RECURSION_LOOP_DETECTION_END();
10776
10777   return change_done;
10778 }
10779
10780 static void PlayPlayerSound(struct PlayerInfo *player)
10781 {
10782   int jx = player->jx, jy = player->jy;
10783   int sound_element = player->artwork_element;
10784   int last_action = player->last_action_waiting;
10785   int action = player->action_waiting;
10786
10787   if (player->is_waiting)
10788   {
10789     if (action != last_action)
10790       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10791     else
10792       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10793   }
10794   else
10795   {
10796     if (action != last_action)
10797       StopSound(element_info[sound_element].sound[last_action]);
10798
10799     if (last_action == ACTION_SLEEPING)
10800       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10801   }
10802 }
10803
10804 static void PlayAllPlayersSound(void)
10805 {
10806   int i;
10807
10808   for (i = 0; i < MAX_PLAYERS; i++)
10809     if (stored_player[i].active)
10810       PlayPlayerSound(&stored_player[i]);
10811 }
10812
10813 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10814 {
10815   boolean last_waiting = player->is_waiting;
10816   int move_dir = player->MovDir;
10817
10818   player->dir_waiting = move_dir;
10819   player->last_action_waiting = player->action_waiting;
10820
10821   if (is_waiting)
10822   {
10823     if (!last_waiting)          /* not waiting -> waiting */
10824     {
10825       player->is_waiting = TRUE;
10826
10827       player->frame_counter_bored =
10828         FrameCounter +
10829         game.player_boring_delay_fixed +
10830         GetSimpleRandom(game.player_boring_delay_random);
10831       player->frame_counter_sleeping =
10832         FrameCounter +
10833         game.player_sleeping_delay_fixed +
10834         GetSimpleRandom(game.player_sleeping_delay_random);
10835
10836       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10837     }
10838
10839     if (game.player_sleeping_delay_fixed +
10840         game.player_sleeping_delay_random > 0 &&
10841         player->anim_delay_counter == 0 &&
10842         player->post_delay_counter == 0 &&
10843         FrameCounter >= player->frame_counter_sleeping)
10844       player->is_sleeping = TRUE;
10845     else if (game.player_boring_delay_fixed +
10846              game.player_boring_delay_random > 0 &&
10847              FrameCounter >= player->frame_counter_bored)
10848       player->is_bored = TRUE;
10849
10850     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10851                               player->is_bored ? ACTION_BORING :
10852                               ACTION_WAITING);
10853
10854     if (player->is_sleeping && player->use_murphy)
10855     {
10856       /* special case for sleeping Murphy when leaning against non-free tile */
10857
10858       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10859           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10860            !IS_MOVING(player->jx - 1, player->jy)))
10861         move_dir = MV_LEFT;
10862       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10863                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10864                 !IS_MOVING(player->jx + 1, player->jy)))
10865         move_dir = MV_RIGHT;
10866       else
10867         player->is_sleeping = FALSE;
10868
10869       player->dir_waiting = move_dir;
10870     }
10871
10872     if (player->is_sleeping)
10873     {
10874       if (player->num_special_action_sleeping > 0)
10875       {
10876         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10877         {
10878           int last_special_action = player->special_action_sleeping;
10879           int num_special_action = player->num_special_action_sleeping;
10880           int special_action =
10881             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10882              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10883              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10884              last_special_action + 1 : ACTION_SLEEPING);
10885           int special_graphic =
10886             el_act_dir2img(player->artwork_element, special_action, move_dir);
10887
10888           player->anim_delay_counter =
10889             graphic_info[special_graphic].anim_delay_fixed +
10890             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10891           player->post_delay_counter =
10892             graphic_info[special_graphic].post_delay_fixed +
10893             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10894
10895           player->special_action_sleeping = special_action;
10896         }
10897
10898         if (player->anim_delay_counter > 0)
10899         {
10900           player->action_waiting = player->special_action_sleeping;
10901           player->anim_delay_counter--;
10902         }
10903         else if (player->post_delay_counter > 0)
10904         {
10905           player->post_delay_counter--;
10906         }
10907       }
10908     }
10909     else if (player->is_bored)
10910     {
10911       if (player->num_special_action_bored > 0)
10912       {
10913         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10914         {
10915           int special_action =
10916             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10917           int special_graphic =
10918             el_act_dir2img(player->artwork_element, special_action, move_dir);
10919
10920           player->anim_delay_counter =
10921             graphic_info[special_graphic].anim_delay_fixed +
10922             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10923           player->post_delay_counter =
10924             graphic_info[special_graphic].post_delay_fixed +
10925             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10926
10927           player->special_action_bored = special_action;
10928         }
10929
10930         if (player->anim_delay_counter > 0)
10931         {
10932           player->action_waiting = player->special_action_bored;
10933           player->anim_delay_counter--;
10934         }
10935         else if (player->post_delay_counter > 0)
10936         {
10937           player->post_delay_counter--;
10938         }
10939       }
10940     }
10941   }
10942   else if (last_waiting)        /* waiting -> not waiting */
10943   {
10944     player->is_waiting = FALSE;
10945     player->is_bored = FALSE;
10946     player->is_sleeping = FALSE;
10947
10948     player->frame_counter_bored = -1;
10949     player->frame_counter_sleeping = -1;
10950
10951     player->anim_delay_counter = 0;
10952     player->post_delay_counter = 0;
10953
10954     player->dir_waiting = player->MovDir;
10955     player->action_waiting = ACTION_DEFAULT;
10956
10957     player->special_action_bored = ACTION_DEFAULT;
10958     player->special_action_sleeping = ACTION_DEFAULT;
10959   }
10960 }
10961
10962 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10963 {
10964   if ((!player->is_moving  && player->was_moving) ||
10965       (player->MovPos == 0 && player->was_moving) ||
10966       (player->is_snapping && !player->was_snapping) ||
10967       (player->is_dropping && !player->was_dropping))
10968   {
10969     if (!CheckSaveEngineSnapshotToList())
10970       return;
10971
10972     player->was_moving = FALSE;
10973     player->was_snapping = TRUE;
10974     player->was_dropping = TRUE;
10975   }
10976   else
10977   {
10978     if (player->is_moving)
10979       player->was_moving = TRUE;
10980
10981     if (!player->is_snapping)
10982       player->was_snapping = FALSE;
10983
10984     if (!player->is_dropping)
10985       player->was_dropping = FALSE;
10986   }
10987 }
10988
10989 static void CheckSingleStepMode(struct PlayerInfo *player)
10990 {
10991   if (tape.single_step && tape.recording && !tape.pausing)
10992   {
10993     /* as it is called "single step mode", just return to pause mode when the
10994        player stopped moving after one tile (or never starts moving at all) */
10995     if (!player->is_moving &&
10996         !player->is_pushing &&
10997         !player->is_dropping_pressed)
10998     {
10999       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11000       SnapField(player, 0, 0);                  /* stop snapping */
11001     }
11002   }
11003
11004   CheckSaveEngineSnapshot(player);
11005 }
11006
11007 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11008 {
11009   int left      = player_action & JOY_LEFT;
11010   int right     = player_action & JOY_RIGHT;
11011   int up        = player_action & JOY_UP;
11012   int down      = player_action & JOY_DOWN;
11013   int button1   = player_action & JOY_BUTTON_1;
11014   int button2   = player_action & JOY_BUTTON_2;
11015   int dx        = (left ? -1 : right ? 1 : 0);
11016   int dy        = (up   ? -1 : down  ? 1 : 0);
11017
11018   if (!player->active || tape.pausing)
11019     return 0;
11020
11021   if (player_action)
11022   {
11023     if (button1)
11024       SnapField(player, dx, dy);
11025     else
11026     {
11027       if (button2)
11028         DropElement(player);
11029
11030       MovePlayer(player, dx, dy);
11031     }
11032
11033     CheckSingleStepMode(player);
11034
11035     SetPlayerWaiting(player, FALSE);
11036
11037     return player_action;
11038   }
11039   else
11040   {
11041     /* no actions for this player (no input at player's configured device) */
11042
11043     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11044     SnapField(player, 0, 0);
11045     CheckGravityMovementWhenNotMoving(player);
11046
11047     if (player->MovPos == 0)
11048       SetPlayerWaiting(player, TRUE);
11049
11050     if (player->MovPos == 0)    /* needed for tape.playing */
11051       player->is_moving = FALSE;
11052
11053     player->is_dropping = FALSE;
11054     player->is_dropping_pressed = FALSE;
11055     player->drop_pressed_delay = 0;
11056
11057     CheckSingleStepMode(player);
11058
11059     return 0;
11060   }
11061 }
11062
11063 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11064                                          byte *tape_action)
11065 {
11066   if (!tape.use_mouse)
11067     return;
11068
11069   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11070   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11071   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11072 }
11073
11074 static void SetTapeActionFromMouseAction(byte *tape_action,
11075                                          struct MouseActionInfo *mouse_action)
11076 {
11077   if (!tape.use_mouse)
11078     return;
11079
11080   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11081   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11082   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11083 }
11084
11085 static void CheckLevelSolved(void)
11086 {
11087   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11088   {
11089     if (level.native_em_level->lev->home == 0)  /* all players at home */
11090     {
11091       PlayerWins(local_player);
11092
11093       AllPlayersGone = TRUE;
11094
11095       level.native_em_level->lev->home = -1;
11096     }
11097
11098     if (level.native_em_level->ply[0]->alive == 0 &&
11099         level.native_em_level->ply[1]->alive == 0 &&
11100         level.native_em_level->ply[2]->alive == 0 &&
11101         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11102       AllPlayersGone = TRUE;
11103   }
11104   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11105   {
11106     if (game_sp.LevelSolved &&
11107         !game_sp.GameOver)                              /* game won */
11108     {
11109       PlayerWins(local_player);
11110
11111       game_sp.GameOver = TRUE;
11112
11113       AllPlayersGone = TRUE;
11114     }
11115
11116     if (game_sp.GameOver)                               /* game lost */
11117       AllPlayersGone = TRUE;
11118   }
11119   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11120   {
11121     if (game_mm.level_solved &&
11122         !game_mm.game_over)                             /* game won */
11123     {
11124       PlayerWins(local_player);
11125
11126       game_mm.game_over = TRUE;
11127
11128       AllPlayersGone = TRUE;
11129     }
11130
11131     if (game_mm.game_over)                              /* game lost */
11132       AllPlayersGone = TRUE;
11133   }
11134 }
11135
11136 static void CheckLevelTime(void)
11137 {
11138   int i;
11139
11140   if (TimeFrames >= FRAMES_PER_SECOND)
11141   {
11142     TimeFrames = 0;
11143     TapeTime++;
11144
11145     for (i = 0; i < MAX_PLAYERS; i++)
11146     {
11147       struct PlayerInfo *player = &stored_player[i];
11148
11149       if (SHIELD_ON(player))
11150       {
11151         player->shield_normal_time_left--;
11152
11153         if (player->shield_deadly_time_left > 0)
11154           player->shield_deadly_time_left--;
11155       }
11156     }
11157
11158     if (!local_player->LevelSolved && !level.use_step_counter)
11159     {
11160       TimePlayed++;
11161
11162       if (TimeLeft > 0)
11163       {
11164         TimeLeft--;
11165
11166         if (TimeLeft <= 10 && setup.time_limit)
11167           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11168
11169         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11170            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11171
11172         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11173
11174         if (!TimeLeft && setup.time_limit)
11175         {
11176           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11177             level.native_em_level->lev->killed_out_of_time = TRUE;
11178           else
11179             for (i = 0; i < MAX_PLAYERS; i++)
11180               KillPlayer(&stored_player[i]);
11181         }
11182       }
11183       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11184       {
11185         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11186       }
11187
11188       level.native_em_level->lev->time =
11189         (game.no_time_limit ? TimePlayed : TimeLeft);
11190     }
11191
11192     if (tape.recording || tape.playing)
11193       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11194   }
11195
11196   if (tape.recording || tape.playing)
11197     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11198
11199   UpdateAndDisplayGameControlValues();
11200 }
11201
11202 void AdvanceFrameAndPlayerCounters(int player_nr)
11203 {
11204   int i;
11205
11206   /* advance frame counters (global frame counter and time frame counter) */
11207   FrameCounter++;
11208   TimeFrames++;
11209
11210   /* advance player counters (counters for move delay, move animation etc.) */
11211   for (i = 0; i < MAX_PLAYERS; i++)
11212   {
11213     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11214     int move_delay_value = stored_player[i].move_delay_value;
11215     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11216
11217     if (!advance_player_counters)       /* not all players may be affected */
11218       continue;
11219
11220     if (move_frames == 0)       /* less than one move per game frame */
11221     {
11222       int stepsize = TILEX / move_delay_value;
11223       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11224       int count = (stored_player[i].is_moving ?
11225                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11226
11227       if (count % delay == 0)
11228         move_frames = 1;
11229     }
11230
11231     stored_player[i].Frame += move_frames;
11232
11233     if (stored_player[i].MovPos != 0)
11234       stored_player[i].StepFrame += move_frames;
11235
11236     if (stored_player[i].move_delay > 0)
11237       stored_player[i].move_delay--;
11238
11239     /* due to bugs in previous versions, counter must count up, not down */
11240     if (stored_player[i].push_delay != -1)
11241       stored_player[i].push_delay++;
11242
11243     if (stored_player[i].drop_delay > 0)
11244       stored_player[i].drop_delay--;
11245
11246     if (stored_player[i].is_dropping_pressed)
11247       stored_player[i].drop_pressed_delay++;
11248   }
11249 }
11250
11251 void StartGameActions(boolean init_network_game, boolean record_tape,
11252                       int random_seed)
11253 {
11254   unsigned int new_random_seed = InitRND(random_seed);
11255
11256   if (record_tape)
11257     TapeStartRecording(new_random_seed);
11258
11259   if (init_network_game)
11260   {
11261     SendToServer_LevelFile();
11262     SendToServer_StartPlaying();
11263
11264     return;
11265   }
11266
11267   InitGame();
11268 }
11269
11270 static void GameActionsExt(void)
11271 {
11272 #if 0
11273   static unsigned int game_frame_delay = 0;
11274 #endif
11275   unsigned int game_frame_delay_value;
11276   byte *recorded_player_action;
11277   byte summarized_player_action = 0;
11278   byte tape_action[MAX_PLAYERS];
11279   int i;
11280
11281   /* detect endless loops, caused by custom element programming */
11282   if (recursion_loop_detected && recursion_loop_depth == 0)
11283   {
11284     char *message = getStringCat3("Internal Error! Element ",
11285                                   EL_NAME(recursion_loop_element),
11286                                   " caused endless loop! Quit the game?");
11287
11288     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11289           EL_NAME(recursion_loop_element));
11290
11291     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11292
11293     recursion_loop_detected = FALSE;    /* if game should be continued */
11294
11295     free(message);
11296
11297     return;
11298   }
11299
11300   if (game.restart_level)
11301     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11302
11303   CheckLevelSolved();
11304
11305   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11306     GameWon();
11307
11308   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11309     TapeStop();
11310
11311   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11312     return;
11313
11314   game_frame_delay_value =
11315     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11316
11317   if (tape.playing && tape.warp_forward && !tape.pausing)
11318     game_frame_delay_value = 0;
11319
11320   SetVideoFrameDelay(game_frame_delay_value);
11321
11322 #if 0
11323 #if 0
11324   /* ---------- main game synchronization point ---------- */
11325
11326   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11327
11328   printf("::: skip == %d\n", skip);
11329
11330 #else
11331   /* ---------- main game synchronization point ---------- */
11332
11333   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11334 #endif
11335 #endif
11336
11337   if (network_playing && !network_player_action_received)
11338   {
11339     /* try to get network player actions in time */
11340
11341     /* last chance to get network player actions without main loop delay */
11342     HandleNetworking();
11343
11344     /* game was quit by network peer */
11345     if (game_status != GAME_MODE_PLAYING)
11346       return;
11347
11348     if (!network_player_action_received)
11349       return;           /* failed to get network player actions in time */
11350
11351     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11352   }
11353
11354   if (tape.pausing)
11355     return;
11356
11357   /* at this point we know that we really continue executing the game */
11358
11359   network_player_action_received = FALSE;
11360
11361   /* when playing tape, read previously recorded player input from tape data */
11362   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11363
11364   local_player->effective_mouse_action = local_player->mouse_action;
11365
11366   if (recorded_player_action != NULL)
11367     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11368                                  recorded_player_action);
11369
11370   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11371   if (tape.pausing)
11372     return;
11373
11374   if (tape.set_centered_player)
11375   {
11376     game.centered_player_nr_next = tape.centered_player_nr_next;
11377     game.set_centered_player = TRUE;
11378   }
11379
11380   for (i = 0; i < MAX_PLAYERS; i++)
11381   {
11382     summarized_player_action |= stored_player[i].action;
11383
11384     if (!network_playing && (game.team_mode || tape.playing))
11385       stored_player[i].effective_action = stored_player[i].action;
11386   }
11387
11388   if (network_playing)
11389     SendToServer_MovePlayer(summarized_player_action);
11390
11391   // summarize all actions at local players mapped input device position
11392   // (this allows using different input devices in single player mode)
11393   if (!network.enabled && !game.team_mode)
11394     stored_player[map_player_action[local_player->index_nr]].effective_action =
11395       summarized_player_action;
11396
11397   if (tape.recording &&
11398       setup.team_mode &&
11399       setup.input_on_focus &&
11400       game.centered_player_nr != -1)
11401   {
11402     for (i = 0; i < MAX_PLAYERS; i++)
11403       stored_player[i].effective_action =
11404         (i == game.centered_player_nr ? summarized_player_action : 0);
11405   }
11406
11407   if (recorded_player_action != NULL)
11408     for (i = 0; i < MAX_PLAYERS; i++)
11409       stored_player[i].effective_action = recorded_player_action[i];
11410
11411   for (i = 0; i < MAX_PLAYERS; i++)
11412   {
11413     tape_action[i] = stored_player[i].effective_action;
11414
11415     /* (this may happen in the RND game engine if a player was not present on
11416        the playfield on level start, but appeared later from a custom element */
11417     if (setup.team_mode &&
11418         tape.recording &&
11419         tape_action[i] &&
11420         !tape.player_participates[i])
11421       tape.player_participates[i] = TRUE;
11422   }
11423
11424   SetTapeActionFromMouseAction(tape_action,
11425                                &local_player->effective_mouse_action);
11426
11427   /* only record actions from input devices, but not programmed actions */
11428   if (tape.recording)
11429     TapeRecordAction(tape_action);
11430
11431 #if USE_NEW_PLAYER_ASSIGNMENTS
11432   // !!! also map player actions in single player mode !!!
11433   // if (game.team_mode)
11434   if (1)
11435   {
11436     byte mapped_action[MAX_PLAYERS];
11437
11438 #if DEBUG_PLAYER_ACTIONS
11439     printf(":::");
11440     for (i = 0; i < MAX_PLAYERS; i++)
11441       printf(" %d, ", stored_player[i].effective_action);
11442 #endif
11443
11444     for (i = 0; i < MAX_PLAYERS; i++)
11445       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11446
11447     for (i = 0; i < MAX_PLAYERS; i++)
11448       stored_player[i].effective_action = mapped_action[i];
11449
11450 #if DEBUG_PLAYER_ACTIONS
11451     printf(" =>");
11452     for (i = 0; i < MAX_PLAYERS; i++)
11453       printf(" %d, ", stored_player[i].effective_action);
11454     printf("\n");
11455 #endif
11456   }
11457 #if DEBUG_PLAYER_ACTIONS
11458   else
11459   {
11460     printf(":::");
11461     for (i = 0; i < MAX_PLAYERS; i++)
11462       printf(" %d, ", stored_player[i].effective_action);
11463     printf("\n");
11464   }
11465 #endif
11466 #endif
11467
11468   for (i = 0; i < MAX_PLAYERS; i++)
11469   {
11470     // allow engine snapshot in case of changed movement attempt
11471     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11472         (stored_player[i].effective_action & KEY_MOTION))
11473       game.snapshot.changed_action = TRUE;
11474
11475     // allow engine snapshot in case of snapping/dropping attempt
11476     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11477         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11478       game.snapshot.changed_action = TRUE;
11479
11480     game.snapshot.last_action[i] = stored_player[i].effective_action;
11481   }
11482
11483   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11484   {
11485     GameActions_EM_Main();
11486   }
11487   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11488   {
11489     GameActions_SP_Main();
11490   }
11491   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11492   {
11493     GameActions_MM_Main();
11494   }
11495   else
11496   {
11497     GameActions_RND_Main();
11498   }
11499
11500   BlitScreenToBitmap(backbuffer);
11501
11502   CheckLevelSolved();
11503   CheckLevelTime();
11504
11505   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11506
11507   if (global.show_frames_per_second)
11508   {
11509     static unsigned int fps_counter = 0;
11510     static int fps_frames = 0;
11511     unsigned int fps_delay_ms = Counter() - fps_counter;
11512
11513     fps_frames++;
11514
11515     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11516     {
11517       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11518
11519       fps_frames = 0;
11520       fps_counter = Counter();
11521
11522       /* always draw FPS to screen after FPS value was updated */
11523       redraw_mask |= REDRAW_FPS;
11524     }
11525
11526     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11527     if (GetDrawDeactivationMask() == REDRAW_NONE)
11528       redraw_mask |= REDRAW_FPS;
11529   }
11530 }
11531
11532 static void GameActions_CheckSaveEngineSnapshot(void)
11533 {
11534   if (!game.snapshot.save_snapshot)
11535     return;
11536
11537   // clear flag for saving snapshot _before_ saving snapshot
11538   game.snapshot.save_snapshot = FALSE;
11539
11540   SaveEngineSnapshotToList();
11541 }
11542
11543 void GameActions(void)
11544 {
11545   GameActionsExt();
11546
11547   GameActions_CheckSaveEngineSnapshot();
11548 }
11549
11550 void GameActions_EM_Main(void)
11551 {
11552   byte effective_action[MAX_PLAYERS];
11553   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11554   int i;
11555
11556   for (i = 0; i < MAX_PLAYERS; i++)
11557     effective_action[i] = stored_player[i].effective_action;
11558
11559   GameActions_EM(effective_action, warp_mode);
11560 }
11561
11562 void GameActions_SP_Main(void)
11563 {
11564   byte effective_action[MAX_PLAYERS];
11565   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11566   int i;
11567
11568   for (i = 0; i < MAX_PLAYERS; i++)
11569     effective_action[i] = stored_player[i].effective_action;
11570
11571   GameActions_SP(effective_action, warp_mode);
11572
11573   for (i = 0; i < MAX_PLAYERS; i++)
11574   {
11575     if (stored_player[i].force_dropping)
11576       stored_player[i].action |= KEY_BUTTON_DROP;
11577
11578     stored_player[i].force_dropping = FALSE;
11579   }
11580 }
11581
11582 void GameActions_MM_Main(void)
11583 {
11584   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11585
11586   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11587 }
11588
11589 void GameActions_RND_Main(void)
11590 {
11591   GameActions_RND();
11592 }
11593
11594 void GameActions_RND(void)
11595 {
11596   int magic_wall_x = 0, magic_wall_y = 0;
11597   int i, x, y, element, graphic, last_gfx_frame;
11598
11599   InitPlayfieldScanModeVars();
11600
11601   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11602   {
11603     SCAN_PLAYFIELD(x, y)
11604     {
11605       ChangeCount[x][y] = 0;
11606       ChangeEvent[x][y] = -1;
11607     }
11608   }
11609
11610   if (game.set_centered_player)
11611   {
11612     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11613
11614     /* switching to "all players" only possible if all players fit to screen */
11615     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11616     {
11617       game.centered_player_nr_next = game.centered_player_nr;
11618       game.set_centered_player = FALSE;
11619     }
11620
11621     /* do not switch focus to non-existing (or non-active) player */
11622     if (game.centered_player_nr_next >= 0 &&
11623         !stored_player[game.centered_player_nr_next].active)
11624     {
11625       game.centered_player_nr_next = game.centered_player_nr;
11626       game.set_centered_player = FALSE;
11627     }
11628   }
11629
11630   if (game.set_centered_player &&
11631       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11632   {
11633     int sx, sy;
11634
11635     if (game.centered_player_nr_next == -1)
11636     {
11637       setScreenCenteredToAllPlayers(&sx, &sy);
11638     }
11639     else
11640     {
11641       sx = stored_player[game.centered_player_nr_next].jx;
11642       sy = stored_player[game.centered_player_nr_next].jy;
11643     }
11644
11645     game.centered_player_nr = game.centered_player_nr_next;
11646     game.set_centered_player = FALSE;
11647
11648     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11649     DrawGameDoorValues();
11650   }
11651
11652   for (i = 0; i < MAX_PLAYERS; i++)
11653   {
11654     int actual_player_action = stored_player[i].effective_action;
11655
11656 #if 1
11657     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11658        - rnd_equinox_tetrachloride 048
11659        - rnd_equinox_tetrachloride_ii 096
11660        - rnd_emanuel_schmieg 002
11661        - doctor_sloan_ww 001, 020
11662     */
11663     if (stored_player[i].MovPos == 0)
11664       CheckGravityMovement(&stored_player[i]);
11665 #endif
11666
11667     /* overwrite programmed action with tape action */
11668     if (stored_player[i].programmed_action)
11669       actual_player_action = stored_player[i].programmed_action;
11670
11671     PlayerActions(&stored_player[i], actual_player_action);
11672
11673     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11674   }
11675
11676   ScrollScreen(NULL, SCROLL_GO_ON);
11677
11678   /* for backwards compatibility, the following code emulates a fixed bug that
11679      occured when pushing elements (causing elements that just made their last
11680      pushing step to already (if possible) make their first falling step in the
11681      same game frame, which is bad); this code is also needed to use the famous
11682      "spring push bug" which is used in older levels and might be wanted to be
11683      used also in newer levels, but in this case the buggy pushing code is only
11684      affecting the "spring" element and no other elements */
11685
11686   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11687   {
11688     for (i = 0; i < MAX_PLAYERS; i++)
11689     {
11690       struct PlayerInfo *player = &stored_player[i];
11691       int x = player->jx;
11692       int y = player->jy;
11693
11694       if (player->active && player->is_pushing && player->is_moving &&
11695           IS_MOVING(x, y) &&
11696           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11697            Feld[x][y] == EL_SPRING))
11698       {
11699         ContinueMoving(x, y);
11700
11701         /* continue moving after pushing (this is actually a bug) */
11702         if (!IS_MOVING(x, y))
11703           Stop[x][y] = FALSE;
11704       }
11705     }
11706   }
11707
11708   SCAN_PLAYFIELD(x, y)
11709   {
11710     Last[x][y] = Feld[x][y];
11711
11712     ChangeCount[x][y] = 0;
11713     ChangeEvent[x][y] = -1;
11714
11715     /* this must be handled before main playfield loop */
11716     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11717     {
11718       MovDelay[x][y]--;
11719       if (MovDelay[x][y] <= 0)
11720         RemoveField(x, y);
11721     }
11722
11723     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11724     {
11725       MovDelay[x][y]--;
11726       if (MovDelay[x][y] <= 0)
11727       {
11728         RemoveField(x, y);
11729         TEST_DrawLevelField(x, y);
11730
11731         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11732       }
11733     }
11734
11735 #if DEBUG
11736     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11737     {
11738       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11739       printf("GameActions(): This should never happen!\n");
11740
11741       ChangePage[x][y] = -1;
11742     }
11743 #endif
11744
11745     Stop[x][y] = FALSE;
11746     if (WasJustMoving[x][y] > 0)
11747       WasJustMoving[x][y]--;
11748     if (WasJustFalling[x][y] > 0)
11749       WasJustFalling[x][y]--;
11750     if (CheckCollision[x][y] > 0)
11751       CheckCollision[x][y]--;
11752     if (CheckImpact[x][y] > 0)
11753       CheckImpact[x][y]--;
11754
11755     GfxFrame[x][y]++;
11756
11757     /* reset finished pushing action (not done in ContinueMoving() to allow
11758        continuous pushing animation for elements with zero push delay) */
11759     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11760     {
11761       ResetGfxAnimation(x, y);
11762       TEST_DrawLevelField(x, y);
11763     }
11764
11765 #if DEBUG
11766     if (IS_BLOCKED(x, y))
11767     {
11768       int oldx, oldy;
11769
11770       Blocked2Moving(x, y, &oldx, &oldy);
11771       if (!IS_MOVING(oldx, oldy))
11772       {
11773         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11774         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11775         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11776         printf("GameActions(): This should never happen!\n");
11777       }
11778     }
11779 #endif
11780   }
11781
11782   SCAN_PLAYFIELD(x, y)
11783   {
11784     element = Feld[x][y];
11785     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11786     last_gfx_frame = GfxFrame[x][y];
11787
11788     ResetGfxFrame(x, y);
11789
11790     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11791       DrawLevelGraphicAnimation(x, y, graphic);
11792
11793     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11794         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11795       ResetRandomAnimationValue(x, y);
11796
11797     SetRandomAnimationValue(x, y);
11798
11799     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11800
11801     if (IS_INACTIVE(element))
11802     {
11803       if (IS_ANIMATED(graphic))
11804         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11805
11806       continue;
11807     }
11808
11809     /* this may take place after moving, so 'element' may have changed */
11810     if (IS_CHANGING(x, y) &&
11811         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11812     {
11813       int page = element_info[element].event_page_nr[CE_DELAY];
11814
11815       HandleElementChange(x, y, page);
11816
11817       element = Feld[x][y];
11818       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11819     }
11820
11821     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11822     {
11823       StartMoving(x, y);
11824
11825       element = Feld[x][y];
11826       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11827
11828       if (IS_ANIMATED(graphic) &&
11829           !IS_MOVING(x, y) &&
11830           !Stop[x][y])
11831         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11832
11833       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11834         TEST_DrawTwinkleOnField(x, y);
11835     }
11836     else if (element == EL_ACID)
11837     {
11838       if (!Stop[x][y])
11839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11840     }
11841     else if ((element == EL_EXIT_OPEN ||
11842               element == EL_EM_EXIT_OPEN ||
11843               element == EL_SP_EXIT_OPEN ||
11844               element == EL_STEEL_EXIT_OPEN ||
11845               element == EL_EM_STEEL_EXIT_OPEN ||
11846               element == EL_SP_TERMINAL ||
11847               element == EL_SP_TERMINAL_ACTIVE ||
11848               element == EL_EXTRA_TIME ||
11849               element == EL_SHIELD_NORMAL ||
11850               element == EL_SHIELD_DEADLY) &&
11851              IS_ANIMATED(graphic))
11852       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11853     else if (IS_MOVING(x, y))
11854       ContinueMoving(x, y);
11855     else if (IS_ACTIVE_BOMB(element))
11856       CheckDynamite(x, y);
11857     else if (element == EL_AMOEBA_GROWING)
11858       AmoebeWaechst(x, y);
11859     else if (element == EL_AMOEBA_SHRINKING)
11860       AmoebaDisappearing(x, y);
11861
11862 #if !USE_NEW_AMOEBA_CODE
11863     else if (IS_AMOEBALIVE(element))
11864       AmoebeAbleger(x, y);
11865 #endif
11866
11867     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11868       Life(x, y);
11869     else if (element == EL_EXIT_CLOSED)
11870       CheckExit(x, y);
11871     else if (element == EL_EM_EXIT_CLOSED)
11872       CheckExitEM(x, y);
11873     else if (element == EL_STEEL_EXIT_CLOSED)
11874       CheckExitSteel(x, y);
11875     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11876       CheckExitSteelEM(x, y);
11877     else if (element == EL_SP_EXIT_CLOSED)
11878       CheckExitSP(x, y);
11879     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11880              element == EL_EXPANDABLE_STEELWALL_GROWING)
11881       MauerWaechst(x, y);
11882     else if (element == EL_EXPANDABLE_WALL ||
11883              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11884              element == EL_EXPANDABLE_WALL_VERTICAL ||
11885              element == EL_EXPANDABLE_WALL_ANY ||
11886              element == EL_BD_EXPANDABLE_WALL)
11887       MauerAbleger(x, y);
11888     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11889              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11890              element == EL_EXPANDABLE_STEELWALL_ANY)
11891       MauerAblegerStahl(x, y);
11892     else if (element == EL_FLAMES)
11893       CheckForDragon(x, y);
11894     else if (element == EL_EXPLOSION)
11895       ; /* drawing of correct explosion animation is handled separately */
11896     else if (element == EL_ELEMENT_SNAPPING ||
11897              element == EL_DIAGONAL_SHRINKING ||
11898              element == EL_DIAGONAL_GROWING)
11899     {
11900       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11901
11902       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11903     }
11904     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11905       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11906
11907     if (IS_BELT_ACTIVE(element))
11908       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11909
11910     if (game.magic_wall_active)
11911     {
11912       int jx = local_player->jx, jy = local_player->jy;
11913
11914       /* play the element sound at the position nearest to the player */
11915       if ((element == EL_MAGIC_WALL_FULL ||
11916            element == EL_MAGIC_WALL_ACTIVE ||
11917            element == EL_MAGIC_WALL_EMPTYING ||
11918            element == EL_BD_MAGIC_WALL_FULL ||
11919            element == EL_BD_MAGIC_WALL_ACTIVE ||
11920            element == EL_BD_MAGIC_WALL_EMPTYING ||
11921            element == EL_DC_MAGIC_WALL_FULL ||
11922            element == EL_DC_MAGIC_WALL_ACTIVE ||
11923            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11924           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11925       {
11926         magic_wall_x = x;
11927         magic_wall_y = y;
11928       }
11929     }
11930   }
11931
11932 #if USE_NEW_AMOEBA_CODE
11933   /* new experimental amoeba growth stuff */
11934   if (!(FrameCounter % 8))
11935   {
11936     static unsigned int random = 1684108901;
11937
11938     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11939     {
11940       x = RND(lev_fieldx);
11941       y = RND(lev_fieldy);
11942       element = Feld[x][y];
11943
11944       if (!IS_PLAYER(x,y) &&
11945           (element == EL_EMPTY ||
11946            CAN_GROW_INTO(element) ||
11947            element == EL_QUICKSAND_EMPTY ||
11948            element == EL_QUICKSAND_FAST_EMPTY ||
11949            element == EL_ACID_SPLASH_LEFT ||
11950            element == EL_ACID_SPLASH_RIGHT))
11951       {
11952         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11953             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11954             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11955             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11956           Feld[x][y] = EL_AMOEBA_DROP;
11957       }
11958
11959       random = random * 129 + 1;
11960     }
11961   }
11962 #endif
11963
11964   game.explosions_delayed = FALSE;
11965
11966   SCAN_PLAYFIELD(x, y)
11967   {
11968     element = Feld[x][y];
11969
11970     if (ExplodeField[x][y])
11971       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11972     else if (element == EL_EXPLOSION)
11973       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11974
11975     ExplodeField[x][y] = EX_TYPE_NONE;
11976   }
11977
11978   game.explosions_delayed = TRUE;
11979
11980   if (game.magic_wall_active)
11981   {
11982     if (!(game.magic_wall_time_left % 4))
11983     {
11984       int element = Feld[magic_wall_x][magic_wall_y];
11985
11986       if (element == EL_BD_MAGIC_WALL_FULL ||
11987           element == EL_BD_MAGIC_WALL_ACTIVE ||
11988           element == EL_BD_MAGIC_WALL_EMPTYING)
11989         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11990       else if (element == EL_DC_MAGIC_WALL_FULL ||
11991                element == EL_DC_MAGIC_WALL_ACTIVE ||
11992                element == EL_DC_MAGIC_WALL_EMPTYING)
11993         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11994       else
11995         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11996     }
11997
11998     if (game.magic_wall_time_left > 0)
11999     {
12000       game.magic_wall_time_left--;
12001
12002       if (!game.magic_wall_time_left)
12003       {
12004         SCAN_PLAYFIELD(x, y)
12005         {
12006           element = Feld[x][y];
12007
12008           if (element == EL_MAGIC_WALL_ACTIVE ||
12009               element == EL_MAGIC_WALL_FULL)
12010           {
12011             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12012             TEST_DrawLevelField(x, y);
12013           }
12014           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12015                    element == EL_BD_MAGIC_WALL_FULL)
12016           {
12017             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12018             TEST_DrawLevelField(x, y);
12019           }
12020           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12021                    element == EL_DC_MAGIC_WALL_FULL)
12022           {
12023             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12024             TEST_DrawLevelField(x, y);
12025           }
12026         }
12027
12028         game.magic_wall_active = FALSE;
12029       }
12030     }
12031   }
12032
12033   if (game.light_time_left > 0)
12034   {
12035     game.light_time_left--;
12036
12037     if (game.light_time_left == 0)
12038       RedrawAllLightSwitchesAndInvisibleElements();
12039   }
12040
12041   if (game.timegate_time_left > 0)
12042   {
12043     game.timegate_time_left--;
12044
12045     if (game.timegate_time_left == 0)
12046       CloseAllOpenTimegates();
12047   }
12048
12049   if (game.lenses_time_left > 0)
12050   {
12051     game.lenses_time_left--;
12052
12053     if (game.lenses_time_left == 0)
12054       RedrawAllInvisibleElementsForLenses();
12055   }
12056
12057   if (game.magnify_time_left > 0)
12058   {
12059     game.magnify_time_left--;
12060
12061     if (game.magnify_time_left == 0)
12062       RedrawAllInvisibleElementsForMagnifier();
12063   }
12064
12065   for (i = 0; i < MAX_PLAYERS; i++)
12066   {
12067     struct PlayerInfo *player = &stored_player[i];
12068
12069     if (SHIELD_ON(player))
12070     {
12071       if (player->shield_deadly_time_left)
12072         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12073       else if (player->shield_normal_time_left)
12074         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12075     }
12076   }
12077
12078 #if USE_DELAYED_GFX_REDRAW
12079   SCAN_PLAYFIELD(x, y)
12080   {
12081     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12082     {
12083       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12084          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12085
12086       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12087         DrawLevelField(x, y);
12088
12089       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12090         DrawLevelFieldCrumbled(x, y);
12091
12092       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12093         DrawLevelFieldCrumbledNeighbours(x, y);
12094
12095       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12096         DrawTwinkleOnField(x, y);
12097     }
12098
12099     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12100   }
12101 #endif
12102
12103   DrawAllPlayers();
12104   PlayAllPlayersSound();
12105
12106   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12107   {
12108     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12109
12110     local_player->show_envelope = 0;
12111   }
12112
12113   /* use random number generator in every frame to make it less predictable */
12114   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12115     RND(1);
12116 }
12117
12118 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12119 {
12120   int min_x = x, min_y = y, max_x = x, max_y = y;
12121   int i;
12122
12123   for (i = 0; i < MAX_PLAYERS; i++)
12124   {
12125     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12126
12127     if (!stored_player[i].active || &stored_player[i] == player)
12128       continue;
12129
12130     min_x = MIN(min_x, jx);
12131     min_y = MIN(min_y, jy);
12132     max_x = MAX(max_x, jx);
12133     max_y = MAX(max_y, jy);
12134   }
12135
12136   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12137 }
12138
12139 static boolean AllPlayersInVisibleScreen(void)
12140 {
12141   int i;
12142
12143   for (i = 0; i < MAX_PLAYERS; i++)
12144   {
12145     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12146
12147     if (!stored_player[i].active)
12148       continue;
12149
12150     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12151       return FALSE;
12152   }
12153
12154   return TRUE;
12155 }
12156
12157 void ScrollLevel(int dx, int dy)
12158 {
12159   int scroll_offset = 2 * TILEX_VAR;
12160   int x, y;
12161
12162   BlitBitmap(drawto_field, drawto_field,
12163              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12164              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12165              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12166              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12167              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12168              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12169
12170   if (dx != 0)
12171   {
12172     x = (dx == 1 ? BX1 : BX2);
12173     for (y = BY1; y <= BY2; y++)
12174       DrawScreenField(x, y);
12175   }
12176
12177   if (dy != 0)
12178   {
12179     y = (dy == 1 ? BY1 : BY2);
12180     for (x = BX1; x <= BX2; x++)
12181       DrawScreenField(x, y);
12182   }
12183
12184   redraw_mask |= REDRAW_FIELD;
12185 }
12186
12187 static boolean canFallDown(struct PlayerInfo *player)
12188 {
12189   int jx = player->jx, jy = player->jy;
12190
12191   return (IN_LEV_FIELD(jx, jy + 1) &&
12192           (IS_FREE(jx, jy + 1) ||
12193            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12194           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12195           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12196 }
12197
12198 static boolean canPassField(int x, int y, int move_dir)
12199 {
12200   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12201   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12202   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12203   int nextx = x + dx;
12204   int nexty = y + dy;
12205   int element = Feld[x][y];
12206
12207   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12208           !CAN_MOVE(element) &&
12209           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12210           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12211           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12212 }
12213
12214 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12215 {
12216   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12217   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12218   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12219   int newx = x + dx;
12220   int newy = y + dy;
12221
12222   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12223           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12224           (IS_DIGGABLE(Feld[newx][newy]) ||
12225            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12226            canPassField(newx, newy, move_dir)));
12227 }
12228
12229 static void CheckGravityMovement(struct PlayerInfo *player)
12230 {
12231   if (player->gravity && !player->programmed_action)
12232   {
12233     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12234     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12235     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12236     int jx = player->jx, jy = player->jy;
12237     boolean player_is_moving_to_valid_field =
12238       (!player_is_snapping &&
12239        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12240         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12241     boolean player_can_fall_down = canFallDown(player);
12242
12243     if (player_can_fall_down &&
12244         !player_is_moving_to_valid_field)
12245       player->programmed_action = MV_DOWN;
12246   }
12247 }
12248
12249 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12250 {
12251   return CheckGravityMovement(player);
12252
12253   if (player->gravity && !player->programmed_action)
12254   {
12255     int jx = player->jx, jy = player->jy;
12256     boolean field_under_player_is_free =
12257       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12258     boolean player_is_standing_on_valid_field =
12259       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12260        (IS_WALKABLE(Feld[jx][jy]) &&
12261         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12262
12263     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12264       player->programmed_action = MV_DOWN;
12265   }
12266 }
12267
12268 /*
12269   MovePlayerOneStep()
12270   -----------------------------------------------------------------------------
12271   dx, dy:               direction (non-diagonal) to try to move the player to
12272   real_dx, real_dy:     direction as read from input device (can be diagonal)
12273 */
12274
12275 boolean MovePlayerOneStep(struct PlayerInfo *player,
12276                           int dx, int dy, int real_dx, int real_dy)
12277 {
12278   int jx = player->jx, jy = player->jy;
12279   int new_jx = jx + dx, new_jy = jy + dy;
12280   int can_move;
12281   boolean player_can_move = !player->cannot_move;
12282
12283   if (!player->active || (!dx && !dy))
12284     return MP_NO_ACTION;
12285
12286   player->MovDir = (dx < 0 ? MV_LEFT :
12287                     dx > 0 ? MV_RIGHT :
12288                     dy < 0 ? MV_UP :
12289                     dy > 0 ? MV_DOWN :  MV_NONE);
12290
12291   if (!IN_LEV_FIELD(new_jx, new_jy))
12292     return MP_NO_ACTION;
12293
12294   if (!player_can_move)
12295   {
12296     if (player->MovPos == 0)
12297     {
12298       player->is_moving = FALSE;
12299       player->is_digging = FALSE;
12300       player->is_collecting = FALSE;
12301       player->is_snapping = FALSE;
12302       player->is_pushing = FALSE;
12303     }
12304   }
12305
12306   if (!network.enabled && game.centered_player_nr == -1 &&
12307       !AllPlayersInSight(player, new_jx, new_jy))
12308     return MP_NO_ACTION;
12309
12310   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12311   if (can_move != MP_MOVING)
12312     return can_move;
12313
12314   /* check if DigField() has caused relocation of the player */
12315   if (player->jx != jx || player->jy != jy)
12316     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12317
12318   StorePlayer[jx][jy] = 0;
12319   player->last_jx = jx;
12320   player->last_jy = jy;
12321   player->jx = new_jx;
12322   player->jy = new_jy;
12323   StorePlayer[new_jx][new_jy] = player->element_nr;
12324
12325   if (player->move_delay_value_next != -1)
12326   {
12327     player->move_delay_value = player->move_delay_value_next;
12328     player->move_delay_value_next = -1;
12329   }
12330
12331   player->MovPos =
12332     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12333
12334   player->step_counter++;
12335
12336   PlayerVisit[jx][jy] = FrameCounter;
12337
12338   player->is_moving = TRUE;
12339
12340 #if 1
12341   /* should better be called in MovePlayer(), but this breaks some tapes */
12342   ScrollPlayer(player, SCROLL_INIT);
12343 #endif
12344
12345   return MP_MOVING;
12346 }
12347
12348 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12349 {
12350   int jx = player->jx, jy = player->jy;
12351   int old_jx = jx, old_jy = jy;
12352   int moved = MP_NO_ACTION;
12353
12354   if (!player->active)
12355     return FALSE;
12356
12357   if (!dx && !dy)
12358   {
12359     if (player->MovPos == 0)
12360     {
12361       player->is_moving = FALSE;
12362       player->is_digging = FALSE;
12363       player->is_collecting = FALSE;
12364       player->is_snapping = FALSE;
12365       player->is_pushing = FALSE;
12366     }
12367
12368     return FALSE;
12369   }
12370
12371   if (player->move_delay > 0)
12372     return FALSE;
12373
12374   player->move_delay = -1;              /* set to "uninitialized" value */
12375
12376   /* store if player is automatically moved to next field */
12377   player->is_auto_moving = (player->programmed_action != MV_NONE);
12378
12379   /* remove the last programmed player action */
12380   player->programmed_action = 0;
12381
12382   if (player->MovPos)
12383   {
12384     /* should only happen if pre-1.2 tape recordings are played */
12385     /* this is only for backward compatibility */
12386
12387     int original_move_delay_value = player->move_delay_value;
12388
12389 #if DEBUG
12390     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12391            tape.counter);
12392 #endif
12393
12394     /* scroll remaining steps with finest movement resolution */
12395     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12396
12397     while (player->MovPos)
12398     {
12399       ScrollPlayer(player, SCROLL_GO_ON);
12400       ScrollScreen(NULL, SCROLL_GO_ON);
12401
12402       AdvanceFrameAndPlayerCounters(player->index_nr);
12403
12404       DrawAllPlayers();
12405       BackToFront_WithFrameDelay(0);
12406     }
12407
12408     player->move_delay_value = original_move_delay_value;
12409   }
12410
12411   player->is_active = FALSE;
12412
12413   if (player->last_move_dir & MV_HORIZONTAL)
12414   {
12415     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12416       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12417   }
12418   else
12419   {
12420     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12421       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12422   }
12423
12424   if (!moved && !player->is_active)
12425   {
12426     player->is_moving = FALSE;
12427     player->is_digging = FALSE;
12428     player->is_collecting = FALSE;
12429     player->is_snapping = FALSE;
12430     player->is_pushing = FALSE;
12431   }
12432
12433   jx = player->jx;
12434   jy = player->jy;
12435
12436   if (moved & MP_MOVING && !ScreenMovPos &&
12437       (player->index_nr == game.centered_player_nr ||
12438        game.centered_player_nr == -1))
12439   {
12440     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12441     int offset = game.scroll_delay_value;
12442
12443     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12444     {
12445       /* actual player has left the screen -- scroll in that direction */
12446       if (jx != old_jx)         /* player has moved horizontally */
12447         scroll_x += (jx - old_jx);
12448       else                      /* player has moved vertically */
12449         scroll_y += (jy - old_jy);
12450     }
12451     else
12452     {
12453       if (jx != old_jx)         /* player has moved horizontally */
12454       {
12455         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12456             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12457           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12458
12459         /* don't scroll over playfield boundaries */
12460         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12461           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12462
12463         /* don't scroll more than one field at a time */
12464         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12465
12466         /* don't scroll against the player's moving direction */
12467         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12468             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12469           scroll_x = old_scroll_x;
12470       }
12471       else                      /* player has moved vertically */
12472       {
12473         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12474             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12475           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12476
12477         /* don't scroll over playfield boundaries */
12478         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12479           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12480
12481         /* don't scroll more than one field at a time */
12482         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12483
12484         /* don't scroll against the player's moving direction */
12485         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12486             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12487           scroll_y = old_scroll_y;
12488       }
12489     }
12490
12491     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12492     {
12493       if (!network.enabled && game.centered_player_nr == -1 &&
12494           !AllPlayersInVisibleScreen())
12495       {
12496         scroll_x = old_scroll_x;
12497         scroll_y = old_scroll_y;
12498       }
12499       else
12500       {
12501         ScrollScreen(player, SCROLL_INIT);
12502         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12503       }
12504     }
12505   }
12506
12507   player->StepFrame = 0;
12508
12509   if (moved & MP_MOVING)
12510   {
12511     if (old_jx != jx && old_jy == jy)
12512       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12513     else if (old_jx == jx && old_jy != jy)
12514       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12515
12516     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12517
12518     player->last_move_dir = player->MovDir;
12519     player->is_moving = TRUE;
12520     player->is_snapping = FALSE;
12521     player->is_switching = FALSE;
12522     player->is_dropping = FALSE;
12523     player->is_dropping_pressed = FALSE;
12524     player->drop_pressed_delay = 0;
12525
12526 #if 0
12527     /* should better be called here than above, but this breaks some tapes */
12528     ScrollPlayer(player, SCROLL_INIT);
12529 #endif
12530   }
12531   else
12532   {
12533     CheckGravityMovementWhenNotMoving(player);
12534
12535     player->is_moving = FALSE;
12536
12537     /* at this point, the player is allowed to move, but cannot move right now
12538        (e.g. because of something blocking the way) -- ensure that the player
12539        is also allowed to move in the next frame (in old versions before 3.1.1,
12540        the player was forced to wait again for eight frames before next try) */
12541
12542     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12543       player->move_delay = 0;   /* allow direct movement in the next frame */
12544   }
12545
12546   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12547     player->move_delay = player->move_delay_value;
12548
12549   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12550   {
12551     TestIfPlayerTouchesBadThing(jx, jy);
12552     TestIfPlayerTouchesCustomElement(jx, jy);
12553   }
12554
12555   if (!player->active)
12556     RemovePlayer(player);
12557
12558   return moved;
12559 }
12560
12561 void ScrollPlayer(struct PlayerInfo *player, int mode)
12562 {
12563   int jx = player->jx, jy = player->jy;
12564   int last_jx = player->last_jx, last_jy = player->last_jy;
12565   int move_stepsize = TILEX / player->move_delay_value;
12566
12567   if (!player->active)
12568     return;
12569
12570   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12571     return;
12572
12573   if (mode == SCROLL_INIT)
12574   {
12575     player->actual_frame_counter = FrameCounter;
12576     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12577
12578     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12579         Feld[last_jx][last_jy] == EL_EMPTY)
12580     {
12581       int last_field_block_delay = 0;   /* start with no blocking at all */
12582       int block_delay_adjustment = player->block_delay_adjustment;
12583
12584       /* if player blocks last field, add delay for exactly one move */
12585       if (player->block_last_field)
12586       {
12587         last_field_block_delay += player->move_delay_value;
12588
12589         /* when blocking enabled, prevent moving up despite gravity */
12590         if (player->gravity && player->MovDir == MV_UP)
12591           block_delay_adjustment = -1;
12592       }
12593
12594       /* add block delay adjustment (also possible when not blocking) */
12595       last_field_block_delay += block_delay_adjustment;
12596
12597       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12598       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12599     }
12600
12601     if (player->MovPos != 0)    /* player has not yet reached destination */
12602       return;
12603   }
12604   else if (!FrameReached(&player->actual_frame_counter, 1))
12605     return;
12606
12607   if (player->MovPos != 0)
12608   {
12609     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12610     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12611
12612     /* before DrawPlayer() to draw correct player graphic for this case */
12613     if (player->MovPos == 0)
12614       CheckGravityMovement(player);
12615   }
12616
12617   if (player->MovPos == 0)      /* player reached destination field */
12618   {
12619     if (player->move_delay_reset_counter > 0)
12620     {
12621       player->move_delay_reset_counter--;
12622
12623       if (player->move_delay_reset_counter == 0)
12624       {
12625         /* continue with normal speed after quickly moving through gate */
12626         HALVE_PLAYER_SPEED(player);
12627
12628         /* be able to make the next move without delay */
12629         player->move_delay = 0;
12630       }
12631     }
12632
12633     player->last_jx = jx;
12634     player->last_jy = jy;
12635
12636     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12637         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12638         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12639         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12640         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12641         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12642         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12643         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12644     {
12645       ExitPlayer(player);
12646
12647       if ((local_player->friends_still_needed == 0 ||
12648            IS_SP_ELEMENT(Feld[jx][jy])) &&
12649           AllPlayersGone)
12650         PlayerWins(local_player);
12651     }
12652
12653     /* this breaks one level: "machine", level 000 */
12654     {
12655       int move_direction = player->MovDir;
12656       int enter_side = MV_DIR_OPPOSITE(move_direction);
12657       int leave_side = move_direction;
12658       int old_jx = last_jx;
12659       int old_jy = last_jy;
12660       int old_element = Feld[old_jx][old_jy];
12661       int new_element = Feld[jx][jy];
12662
12663       if (IS_CUSTOM_ELEMENT(old_element))
12664         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12665                                    CE_LEFT_BY_PLAYER,
12666                                    player->index_bit, leave_side);
12667
12668       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12669                                           CE_PLAYER_LEAVES_X,
12670                                           player->index_bit, leave_side);
12671
12672       if (IS_CUSTOM_ELEMENT(new_element))
12673         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12674                                    player->index_bit, enter_side);
12675
12676       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12677                                           CE_PLAYER_ENTERS_X,
12678                                           player->index_bit, enter_side);
12679
12680       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12681                                         CE_MOVE_OF_X, move_direction);
12682     }
12683
12684     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12685     {
12686       TestIfPlayerTouchesBadThing(jx, jy);
12687       TestIfPlayerTouchesCustomElement(jx, jy);
12688
12689       /* needed because pushed element has not yet reached its destination,
12690          so it would trigger a change event at its previous field location */
12691       if (!player->is_pushing)
12692         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12693
12694       if (!player->active)
12695         RemovePlayer(player);
12696     }
12697
12698     if (!local_player->LevelSolved && level.use_step_counter)
12699     {
12700       int i;
12701
12702       TimePlayed++;
12703
12704       if (TimeLeft > 0)
12705       {
12706         TimeLeft--;
12707
12708         if (TimeLeft <= 10 && setup.time_limit)
12709           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12710
12711         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12712
12713         DisplayGameControlValues();
12714
12715         if (!TimeLeft && setup.time_limit)
12716           for (i = 0; i < MAX_PLAYERS; i++)
12717             KillPlayer(&stored_player[i]);
12718       }
12719       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12720       {
12721         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12722
12723         DisplayGameControlValues();
12724       }
12725     }
12726
12727     if (tape.single_step && tape.recording && !tape.pausing &&
12728         !player->programmed_action)
12729       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12730
12731     if (!player->programmed_action)
12732       CheckSaveEngineSnapshot(player);
12733   }
12734 }
12735
12736 void ScrollScreen(struct PlayerInfo *player, int mode)
12737 {
12738   static unsigned int screen_frame_counter = 0;
12739
12740   if (mode == SCROLL_INIT)
12741   {
12742     /* set scrolling step size according to actual player's moving speed */
12743     ScrollStepSize = TILEX / player->move_delay_value;
12744
12745     screen_frame_counter = FrameCounter;
12746     ScreenMovDir = player->MovDir;
12747     ScreenMovPos = player->MovPos;
12748     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12749     return;
12750   }
12751   else if (!FrameReached(&screen_frame_counter, 1))
12752     return;
12753
12754   if (ScreenMovPos)
12755   {
12756     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12757     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12758     redraw_mask |= REDRAW_FIELD;
12759   }
12760   else
12761     ScreenMovDir = MV_NONE;
12762 }
12763
12764 void TestIfPlayerTouchesCustomElement(int x, int y)
12765 {
12766   static int xy[4][2] =
12767   {
12768     { 0, -1 },
12769     { -1, 0 },
12770     { +1, 0 },
12771     { 0, +1 }
12772   };
12773   static int trigger_sides[4][2] =
12774   {
12775     /* center side       border side */
12776     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12777     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12778     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12779     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12780   };
12781   static int touch_dir[4] =
12782   {
12783     MV_LEFT | MV_RIGHT,
12784     MV_UP   | MV_DOWN,
12785     MV_UP   | MV_DOWN,
12786     MV_LEFT | MV_RIGHT
12787   };
12788   int center_element = Feld[x][y];      /* should always be non-moving! */
12789   int i;
12790
12791   for (i = 0; i < NUM_DIRECTIONS; i++)
12792   {
12793     int xx = x + xy[i][0];
12794     int yy = y + xy[i][1];
12795     int center_side = trigger_sides[i][0];
12796     int border_side = trigger_sides[i][1];
12797     int border_element;
12798
12799     if (!IN_LEV_FIELD(xx, yy))
12800       continue;
12801
12802     if (IS_PLAYER(x, y))                /* player found at center element */
12803     {
12804       struct PlayerInfo *player = PLAYERINFO(x, y);
12805
12806       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12807         border_element = Feld[xx][yy];          /* may be moving! */
12808       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12809         border_element = Feld[xx][yy];
12810       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12811         border_element = MovingOrBlocked2Element(xx, yy);
12812       else
12813         continue;               /* center and border element do not touch */
12814
12815       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12816                                  player->index_bit, border_side);
12817       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12818                                           CE_PLAYER_TOUCHES_X,
12819                                           player->index_bit, border_side);
12820
12821       {
12822         /* use player element that is initially defined in the level playfield,
12823            not the player element that corresponds to the runtime player number
12824            (example: a level that contains EL_PLAYER_3 as the only player would
12825            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12826         int player_element = PLAYERINFO(x, y)->initial_element;
12827
12828         CheckElementChangeBySide(xx, yy, border_element, player_element,
12829                                  CE_TOUCHING_X, border_side);
12830       }
12831     }
12832     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12833     {
12834       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12835
12836       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12837       {
12838         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12839           continue;             /* center and border element do not touch */
12840       }
12841
12842       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12843                                  player->index_bit, center_side);
12844       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12845                                           CE_PLAYER_TOUCHES_X,
12846                                           player->index_bit, center_side);
12847
12848       {
12849         /* use player element that is initially defined in the level playfield,
12850            not the player element that corresponds to the runtime player number
12851            (example: a level that contains EL_PLAYER_3 as the only player would
12852            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12853         int player_element = PLAYERINFO(xx, yy)->initial_element;
12854
12855         CheckElementChangeBySide(x, y, center_element, player_element,
12856                                  CE_TOUCHING_X, center_side);
12857       }
12858
12859       break;
12860     }
12861   }
12862 }
12863
12864 void TestIfElementTouchesCustomElement(int x, int y)
12865 {
12866   static int xy[4][2] =
12867   {
12868     { 0, -1 },
12869     { -1, 0 },
12870     { +1, 0 },
12871     { 0, +1 }
12872   };
12873   static int trigger_sides[4][2] =
12874   {
12875     /* center side      border side */
12876     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12877     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12878     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12879     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12880   };
12881   static int touch_dir[4] =
12882   {
12883     MV_LEFT | MV_RIGHT,
12884     MV_UP   | MV_DOWN,
12885     MV_UP   | MV_DOWN,
12886     MV_LEFT | MV_RIGHT
12887   };
12888   boolean change_center_element = FALSE;
12889   int center_element = Feld[x][y];      /* should always be non-moving! */
12890   int border_element_old[NUM_DIRECTIONS];
12891   int i;
12892
12893   for (i = 0; i < NUM_DIRECTIONS; i++)
12894   {
12895     int xx = x + xy[i][0];
12896     int yy = y + xy[i][1];
12897     int border_element;
12898
12899     border_element_old[i] = -1;
12900
12901     if (!IN_LEV_FIELD(xx, yy))
12902       continue;
12903
12904     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12905       border_element = Feld[xx][yy];    /* may be moving! */
12906     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12907       border_element = Feld[xx][yy];
12908     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12909       border_element = MovingOrBlocked2Element(xx, yy);
12910     else
12911       continue;                 /* center and border element do not touch */
12912
12913     border_element_old[i] = border_element;
12914   }
12915
12916   for (i = 0; i < NUM_DIRECTIONS; i++)
12917   {
12918     int xx = x + xy[i][0];
12919     int yy = y + xy[i][1];
12920     int center_side = trigger_sides[i][0];
12921     int border_element = border_element_old[i];
12922
12923     if (border_element == -1)
12924       continue;
12925
12926     /* check for change of border element */
12927     CheckElementChangeBySide(xx, yy, border_element, center_element,
12928                              CE_TOUCHING_X, center_side);
12929
12930     /* (center element cannot be player, so we dont have to check this here) */
12931   }
12932
12933   for (i = 0; i < NUM_DIRECTIONS; i++)
12934   {
12935     int xx = x + xy[i][0];
12936     int yy = y + xy[i][1];
12937     int border_side = trigger_sides[i][1];
12938     int border_element = border_element_old[i];
12939
12940     if (border_element == -1)
12941       continue;
12942
12943     /* check for change of center element (but change it only once) */
12944     if (!change_center_element)
12945       change_center_element =
12946         CheckElementChangeBySide(x, y, center_element, border_element,
12947                                  CE_TOUCHING_X, border_side);
12948
12949     if (IS_PLAYER(xx, yy))
12950     {
12951       /* use player element that is initially defined in the level playfield,
12952          not the player element that corresponds to the runtime player number
12953          (example: a level that contains EL_PLAYER_3 as the only player would
12954          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12955       int player_element = PLAYERINFO(xx, yy)->initial_element;
12956
12957       CheckElementChangeBySide(x, y, center_element, player_element,
12958                                CE_TOUCHING_X, border_side);
12959     }
12960   }
12961 }
12962
12963 void TestIfElementHitsCustomElement(int x, int y, int direction)
12964 {
12965   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12966   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12967   int hitx = x + dx, hity = y + dy;
12968   int hitting_element = Feld[x][y];
12969   int touched_element;
12970
12971   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12972     return;
12973
12974   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12975                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12976
12977   if (IN_LEV_FIELD(hitx, hity))
12978   {
12979     int opposite_direction = MV_DIR_OPPOSITE(direction);
12980     int hitting_side = direction;
12981     int touched_side = opposite_direction;
12982     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12983                           MovDir[hitx][hity] != direction ||
12984                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12985
12986     object_hit = TRUE;
12987
12988     if (object_hit)
12989     {
12990       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12991                                CE_HITTING_X, touched_side);
12992
12993       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12994                                CE_HIT_BY_X, hitting_side);
12995
12996       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12997                                CE_HIT_BY_SOMETHING, opposite_direction);
12998
12999       if (IS_PLAYER(hitx, hity))
13000       {
13001         /* use player element that is initially defined in the level playfield,
13002            not the player element that corresponds to the runtime player number
13003            (example: a level that contains EL_PLAYER_3 as the only player would
13004            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13005         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13006
13007         CheckElementChangeBySide(x, y, hitting_element, player_element,
13008                                  CE_HITTING_X, touched_side);
13009       }
13010     }
13011   }
13012
13013   /* "hitting something" is also true when hitting the playfield border */
13014   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13015                            CE_HITTING_SOMETHING, direction);
13016 }
13017
13018 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13019 {
13020   int i, kill_x = -1, kill_y = -1;
13021
13022   int bad_element = -1;
13023   static int test_xy[4][2] =
13024   {
13025     { 0, -1 },
13026     { -1, 0 },
13027     { +1, 0 },
13028     { 0, +1 }
13029   };
13030   static int test_dir[4] =
13031   {
13032     MV_UP,
13033     MV_LEFT,
13034     MV_RIGHT,
13035     MV_DOWN
13036   };
13037
13038   for (i = 0; i < NUM_DIRECTIONS; i++)
13039   {
13040     int test_x, test_y, test_move_dir, test_element;
13041
13042     test_x = good_x + test_xy[i][0];
13043     test_y = good_y + test_xy[i][1];
13044
13045     if (!IN_LEV_FIELD(test_x, test_y))
13046       continue;
13047
13048     test_move_dir =
13049       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13050
13051     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13052
13053     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13054        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13055     */
13056     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13057         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13058     {
13059       kill_x = test_x;
13060       kill_y = test_y;
13061       bad_element = test_element;
13062
13063       break;
13064     }
13065   }
13066
13067   if (kill_x != -1 || kill_y != -1)
13068   {
13069     if (IS_PLAYER(good_x, good_y))
13070     {
13071       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13072
13073       if (player->shield_deadly_time_left > 0 &&
13074           !IS_INDESTRUCTIBLE(bad_element))
13075         Bang(kill_x, kill_y);
13076       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13077         KillPlayer(player);
13078     }
13079     else
13080       Bang(good_x, good_y);
13081   }
13082 }
13083
13084 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13085 {
13086   int i, kill_x = -1, kill_y = -1;
13087   int bad_element = Feld[bad_x][bad_y];
13088   static int test_xy[4][2] =
13089   {
13090     { 0, -1 },
13091     { -1, 0 },
13092     { +1, 0 },
13093     { 0, +1 }
13094   };
13095   static int touch_dir[4] =
13096   {
13097     MV_LEFT | MV_RIGHT,
13098     MV_UP   | MV_DOWN,
13099     MV_UP   | MV_DOWN,
13100     MV_LEFT | MV_RIGHT
13101   };
13102   static int test_dir[4] =
13103   {
13104     MV_UP,
13105     MV_LEFT,
13106     MV_RIGHT,
13107     MV_DOWN
13108   };
13109
13110   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13111     return;
13112
13113   for (i = 0; i < NUM_DIRECTIONS; i++)
13114   {
13115     int test_x, test_y, test_move_dir, test_element;
13116
13117     test_x = bad_x + test_xy[i][0];
13118     test_y = bad_y + test_xy[i][1];
13119
13120     if (!IN_LEV_FIELD(test_x, test_y))
13121       continue;
13122
13123     test_move_dir =
13124       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13125
13126     test_element = Feld[test_x][test_y];
13127
13128     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13129        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13130     */
13131     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13132         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13133     {
13134       /* good thing is player or penguin that does not move away */
13135       if (IS_PLAYER(test_x, test_y))
13136       {
13137         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13138
13139         if (bad_element == EL_ROBOT && player->is_moving)
13140           continue;     /* robot does not kill player if he is moving */
13141
13142         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13143         {
13144           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13145             continue;           /* center and border element do not touch */
13146         }
13147
13148         kill_x = test_x;
13149         kill_y = test_y;
13150
13151         break;
13152       }
13153       else if (test_element == EL_PENGUIN)
13154       {
13155         kill_x = test_x;
13156         kill_y = test_y;
13157
13158         break;
13159       }
13160     }
13161   }
13162
13163   if (kill_x != -1 || kill_y != -1)
13164   {
13165     if (IS_PLAYER(kill_x, kill_y))
13166     {
13167       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13168
13169       if (player->shield_deadly_time_left > 0 &&
13170           !IS_INDESTRUCTIBLE(bad_element))
13171         Bang(bad_x, bad_y);
13172       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13173         KillPlayer(player);
13174     }
13175     else
13176       Bang(kill_x, kill_y);
13177   }
13178 }
13179
13180 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13181 {
13182   int bad_element = Feld[bad_x][bad_y];
13183   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13184   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13185   int test_x = bad_x + dx, test_y = bad_y + dy;
13186   int test_move_dir, test_element;
13187   int kill_x = -1, kill_y = -1;
13188
13189   if (!IN_LEV_FIELD(test_x, test_y))
13190     return;
13191
13192   test_move_dir =
13193     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13194
13195   test_element = Feld[test_x][test_y];
13196
13197   if (test_move_dir != bad_move_dir)
13198   {
13199     /* good thing can be player or penguin that does not move away */
13200     if (IS_PLAYER(test_x, test_y))
13201     {
13202       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13203
13204       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13205          player as being hit when he is moving towards the bad thing, because
13206          the "get hit by" condition would be lost after the player stops) */
13207       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13208         return;         /* player moves away from bad thing */
13209
13210       kill_x = test_x;
13211       kill_y = test_y;
13212     }
13213     else if (test_element == EL_PENGUIN)
13214     {
13215       kill_x = test_x;
13216       kill_y = test_y;
13217     }
13218   }
13219
13220   if (kill_x != -1 || kill_y != -1)
13221   {
13222     if (IS_PLAYER(kill_x, kill_y))
13223     {
13224       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13225
13226       if (player->shield_deadly_time_left > 0 &&
13227           !IS_INDESTRUCTIBLE(bad_element))
13228         Bang(bad_x, bad_y);
13229       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13230         KillPlayer(player);
13231     }
13232     else
13233       Bang(kill_x, kill_y);
13234   }
13235 }
13236
13237 void TestIfPlayerTouchesBadThing(int x, int y)
13238 {
13239   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13240 }
13241
13242 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13243 {
13244   TestIfGoodThingHitsBadThing(x, y, move_dir);
13245 }
13246
13247 void TestIfBadThingTouchesPlayer(int x, int y)
13248 {
13249   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13250 }
13251
13252 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13253 {
13254   TestIfBadThingHitsGoodThing(x, y, move_dir);
13255 }
13256
13257 void TestIfFriendTouchesBadThing(int x, int y)
13258 {
13259   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13260 }
13261
13262 void TestIfBadThingTouchesFriend(int x, int y)
13263 {
13264   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13265 }
13266
13267 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13268 {
13269   int i, kill_x = bad_x, kill_y = bad_y;
13270   static int xy[4][2] =
13271   {
13272     { 0, -1 },
13273     { -1, 0 },
13274     { +1, 0 },
13275     { 0, +1 }
13276   };
13277
13278   for (i = 0; i < NUM_DIRECTIONS; i++)
13279   {
13280     int x, y, element;
13281
13282     x = bad_x + xy[i][0];
13283     y = bad_y + xy[i][1];
13284     if (!IN_LEV_FIELD(x, y))
13285       continue;
13286
13287     element = Feld[x][y];
13288     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13289         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13290     {
13291       kill_x = x;
13292       kill_y = y;
13293       break;
13294     }
13295   }
13296
13297   if (kill_x != bad_x || kill_y != bad_y)
13298     Bang(bad_x, bad_y);
13299 }
13300
13301 void KillPlayer(struct PlayerInfo *player)
13302 {
13303   int jx = player->jx, jy = player->jy;
13304
13305   if (!player->active)
13306     return;
13307
13308 #if 0
13309   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13310          player->killed, player->active, player->reanimated);
13311 #endif
13312
13313   /* the following code was introduced to prevent an infinite loop when calling
13314      -> Bang()
13315      -> CheckTriggeredElementChangeExt()
13316      -> ExecuteCustomElementAction()
13317      -> KillPlayer()
13318      -> (infinitely repeating the above sequence of function calls)
13319      which occurs when killing the player while having a CE with the setting
13320      "kill player X when explosion of <player X>"; the solution using a new
13321      field "player->killed" was chosen for backwards compatibility, although
13322      clever use of the fields "player->active" etc. would probably also work */
13323 #if 1
13324   if (player->killed)
13325     return;
13326 #endif
13327
13328   player->killed = TRUE;
13329
13330   /* remove accessible field at the player's position */
13331   Feld[jx][jy] = EL_EMPTY;
13332
13333   /* deactivate shield (else Bang()/Explode() would not work right) */
13334   player->shield_normal_time_left = 0;
13335   player->shield_deadly_time_left = 0;
13336
13337 #if 0
13338   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13339          player->killed, player->active, player->reanimated);
13340 #endif
13341
13342   Bang(jx, jy);
13343
13344 #if 0
13345   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13346          player->killed, player->active, player->reanimated);
13347 #endif
13348
13349   if (player->reanimated)       /* killed player may have been reanimated */
13350     player->killed = player->reanimated = FALSE;
13351   else
13352     BuryPlayer(player);
13353 }
13354
13355 static void KillPlayerUnlessEnemyProtected(int x, int y)
13356 {
13357   if (!PLAYER_ENEMY_PROTECTED(x, y))
13358     KillPlayer(PLAYERINFO(x, y));
13359 }
13360
13361 static void KillPlayerUnlessExplosionProtected(int x, int y)
13362 {
13363   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13364     KillPlayer(PLAYERINFO(x, y));
13365 }
13366
13367 void BuryPlayer(struct PlayerInfo *player)
13368 {
13369   int jx = player->jx, jy = player->jy;
13370
13371   if (!player->active)
13372     return;
13373
13374   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13375   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13376
13377   player->GameOver = TRUE;
13378   RemovePlayer(player);
13379 }
13380
13381 void RemovePlayer(struct PlayerInfo *player)
13382 {
13383   int jx = player->jx, jy = player->jy;
13384   int i, found = FALSE;
13385
13386   player->present = FALSE;
13387   player->active = FALSE;
13388
13389   if (!ExplodeField[jx][jy])
13390     StorePlayer[jx][jy] = 0;
13391
13392   if (player->is_moving)
13393     TEST_DrawLevelField(player->last_jx, player->last_jy);
13394
13395   for (i = 0; i < MAX_PLAYERS; i++)
13396     if (stored_player[i].active)
13397       found = TRUE;
13398
13399   if (!found)
13400     AllPlayersGone = TRUE;
13401
13402   ExitX = ZX = jx;
13403   ExitY = ZY = jy;
13404 }
13405
13406 void ExitPlayer(struct PlayerInfo *player)
13407 {
13408   DrawPlayer(player);   /* needed here only to cleanup last field */
13409   RemovePlayer(player);
13410
13411   if (local_player->players_still_needed > 0)
13412     local_player->players_still_needed--;
13413
13414   /* also set if some players not yet gone, but not needed to solve level */
13415   if (local_player->players_still_needed == 0)
13416     AllPlayersGone = TRUE;
13417 }
13418
13419 static void setFieldForSnapping(int x, int y, int element, int direction)
13420 {
13421   struct ElementInfo *ei = &element_info[element];
13422   int direction_bit = MV_DIR_TO_BIT(direction);
13423   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13424   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13425                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13426
13427   Feld[x][y] = EL_ELEMENT_SNAPPING;
13428   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13429
13430   ResetGfxAnimation(x, y);
13431
13432   GfxElement[x][y] = element;
13433   GfxAction[x][y] = action;
13434   GfxDir[x][y] = direction;
13435   GfxFrame[x][y] = -1;
13436 }
13437
13438 /*
13439   =============================================================================
13440   checkDiagonalPushing()
13441   -----------------------------------------------------------------------------
13442   check if diagonal input device direction results in pushing of object
13443   (by checking if the alternative direction is walkable, diggable, ...)
13444   =============================================================================
13445 */
13446
13447 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13448                                     int x, int y, int real_dx, int real_dy)
13449 {
13450   int jx, jy, dx, dy, xx, yy;
13451
13452   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13453     return TRUE;
13454
13455   /* diagonal direction: check alternative direction */
13456   jx = player->jx;
13457   jy = player->jy;
13458   dx = x - jx;
13459   dy = y - jy;
13460   xx = jx + (dx == 0 ? real_dx : 0);
13461   yy = jy + (dy == 0 ? real_dy : 0);
13462
13463   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13464 }
13465
13466 /*
13467   =============================================================================
13468   DigField()
13469   -----------------------------------------------------------------------------
13470   x, y:                 field next to player (non-diagonal) to try to dig to
13471   real_dx, real_dy:     direction as read from input device (can be diagonal)
13472   =============================================================================
13473 */
13474
13475 static int DigField(struct PlayerInfo *player,
13476                     int oldx, int oldy, int x, int y,
13477                     int real_dx, int real_dy, int mode)
13478 {
13479   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13480   boolean player_was_pushing = player->is_pushing;
13481   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13482   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13483   int jx = oldx, jy = oldy;
13484   int dx = x - jx, dy = y - jy;
13485   int nextx = x + dx, nexty = y + dy;
13486   int move_direction = (dx == -1 ? MV_LEFT  :
13487                         dx == +1 ? MV_RIGHT :
13488                         dy == -1 ? MV_UP    :
13489                         dy == +1 ? MV_DOWN  : MV_NONE);
13490   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13491   int dig_side = MV_DIR_OPPOSITE(move_direction);
13492   int old_element = Feld[jx][jy];
13493   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13494   int collect_count;
13495
13496   if (is_player)                /* function can also be called by EL_PENGUIN */
13497   {
13498     if (player->MovPos == 0)
13499     {
13500       player->is_digging = FALSE;
13501       player->is_collecting = FALSE;
13502     }
13503
13504     if (player->MovPos == 0)    /* last pushing move finished */
13505       player->is_pushing = FALSE;
13506
13507     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13508     {
13509       player->is_switching = FALSE;
13510       player->push_delay = -1;
13511
13512       return MP_NO_ACTION;
13513     }
13514   }
13515
13516   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13517     old_element = Back[jx][jy];
13518
13519   /* in case of element dropped at player position, check background */
13520   else if (Back[jx][jy] != EL_EMPTY &&
13521            game.engine_version >= VERSION_IDENT(2,2,0,0))
13522     old_element = Back[jx][jy];
13523
13524   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13525     return MP_NO_ACTION;        /* field has no opening in this direction */
13526
13527   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13528     return MP_NO_ACTION;        /* field has no opening in this direction */
13529
13530   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13531   {
13532     SplashAcid(x, y);
13533
13534     Feld[jx][jy] = player->artwork_element;
13535     InitMovingField(jx, jy, MV_DOWN);
13536     Store[jx][jy] = EL_ACID;
13537     ContinueMoving(jx, jy);
13538     BuryPlayer(player);
13539
13540     return MP_DONT_RUN_INTO;
13541   }
13542
13543   if (player_can_move && DONT_RUN_INTO(element))
13544   {
13545     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13546
13547     return MP_DONT_RUN_INTO;
13548   }
13549
13550   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13551     return MP_NO_ACTION;
13552
13553   collect_count = element_info[element].collect_count_initial;
13554
13555   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13556     return MP_NO_ACTION;
13557
13558   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13559     player_can_move = player_can_move_or_snap;
13560
13561   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13562       game.engine_version >= VERSION_IDENT(2,2,0,0))
13563   {
13564     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13565                                player->index_bit, dig_side);
13566     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13567                                         player->index_bit, dig_side);
13568
13569     if (element == EL_DC_LANDMINE)
13570       Bang(x, y);
13571
13572     if (Feld[x][y] != element)          /* field changed by snapping */
13573       return MP_ACTION;
13574
13575     return MP_NO_ACTION;
13576   }
13577
13578   if (player->gravity && is_player && !player->is_auto_moving &&
13579       canFallDown(player) && move_direction != MV_DOWN &&
13580       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13581     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13582
13583   if (player_can_move &&
13584       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13585   {
13586     int sound_element = SND_ELEMENT(element);
13587     int sound_action = ACTION_WALKING;
13588
13589     if (IS_RND_GATE(element))
13590     {
13591       if (!player->key[RND_GATE_NR(element)])
13592         return MP_NO_ACTION;
13593     }
13594     else if (IS_RND_GATE_GRAY(element))
13595     {
13596       if (!player->key[RND_GATE_GRAY_NR(element)])
13597         return MP_NO_ACTION;
13598     }
13599     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13600     {
13601       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13602         return MP_NO_ACTION;
13603     }
13604     else if (element == EL_EXIT_OPEN ||
13605              element == EL_EM_EXIT_OPEN ||
13606              element == EL_EM_EXIT_OPENING ||
13607              element == EL_STEEL_EXIT_OPEN ||
13608              element == EL_EM_STEEL_EXIT_OPEN ||
13609              element == EL_EM_STEEL_EXIT_OPENING ||
13610              element == EL_SP_EXIT_OPEN ||
13611              element == EL_SP_EXIT_OPENING)
13612     {
13613       sound_action = ACTION_PASSING;    /* player is passing exit */
13614     }
13615     else if (element == EL_EMPTY)
13616     {
13617       sound_action = ACTION_MOVING;             /* nothing to walk on */
13618     }
13619
13620     /* play sound from background or player, whatever is available */
13621     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13622       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13623     else
13624       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13625   }
13626   else if (player_can_move &&
13627            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13628   {
13629     if (!ACCESS_FROM(element, opposite_direction))
13630       return MP_NO_ACTION;      /* field not accessible from this direction */
13631
13632     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13633       return MP_NO_ACTION;
13634
13635     if (IS_EM_GATE(element))
13636     {
13637       if (!player->key[EM_GATE_NR(element)])
13638         return MP_NO_ACTION;
13639     }
13640     else if (IS_EM_GATE_GRAY(element))
13641     {
13642       if (!player->key[EM_GATE_GRAY_NR(element)])
13643         return MP_NO_ACTION;
13644     }
13645     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13646     {
13647       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13648         return MP_NO_ACTION;
13649     }
13650     else if (IS_EMC_GATE(element))
13651     {
13652       if (!player->key[EMC_GATE_NR(element)])
13653         return MP_NO_ACTION;
13654     }
13655     else if (IS_EMC_GATE_GRAY(element))
13656     {
13657       if (!player->key[EMC_GATE_GRAY_NR(element)])
13658         return MP_NO_ACTION;
13659     }
13660     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13661     {
13662       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13663         return MP_NO_ACTION;
13664     }
13665     else if (element == EL_DC_GATE_WHITE ||
13666              element == EL_DC_GATE_WHITE_GRAY ||
13667              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13668     {
13669       if (player->num_white_keys == 0)
13670         return MP_NO_ACTION;
13671
13672       player->num_white_keys--;
13673     }
13674     else if (IS_SP_PORT(element))
13675     {
13676       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13677           element == EL_SP_GRAVITY_PORT_RIGHT ||
13678           element == EL_SP_GRAVITY_PORT_UP ||
13679           element == EL_SP_GRAVITY_PORT_DOWN)
13680         player->gravity = !player->gravity;
13681       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13682                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13683                element == EL_SP_GRAVITY_ON_PORT_UP ||
13684                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13685         player->gravity = TRUE;
13686       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13687                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13688                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13689                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13690         player->gravity = FALSE;
13691     }
13692
13693     /* automatically move to the next field with double speed */
13694     player->programmed_action = move_direction;
13695
13696     if (player->move_delay_reset_counter == 0)
13697     {
13698       player->move_delay_reset_counter = 2;     /* two double speed steps */
13699
13700       DOUBLE_PLAYER_SPEED(player);
13701     }
13702
13703     PlayLevelSoundAction(x, y, ACTION_PASSING);
13704   }
13705   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13706   {
13707     RemoveField(x, y);
13708
13709     if (mode != DF_SNAP)
13710     {
13711       GfxElement[x][y] = GFX_ELEMENT(element);
13712       player->is_digging = TRUE;
13713     }
13714
13715     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13716
13717     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13718                                         player->index_bit, dig_side);
13719
13720     if (mode == DF_SNAP)
13721     {
13722       if (level.block_snap_field)
13723         setFieldForSnapping(x, y, element, move_direction);
13724       else
13725         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13726
13727       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13728                                           player->index_bit, dig_side);
13729     }
13730   }
13731   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13732   {
13733     RemoveField(x, y);
13734
13735     if (is_player && mode != DF_SNAP)
13736     {
13737       GfxElement[x][y] = element;
13738       player->is_collecting = TRUE;
13739     }
13740
13741     if (element == EL_SPEED_PILL)
13742     {
13743       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13744     }
13745     else if (element == EL_EXTRA_TIME && level.time > 0)
13746     {
13747       TimeLeft += level.extra_time;
13748
13749       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13750
13751       DisplayGameControlValues();
13752     }
13753     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13754     {
13755       player->shield_normal_time_left += level.shield_normal_time;
13756       if (element == EL_SHIELD_DEADLY)
13757         player->shield_deadly_time_left += level.shield_deadly_time;
13758     }
13759     else if (element == EL_DYNAMITE ||
13760              element == EL_EM_DYNAMITE ||
13761              element == EL_SP_DISK_RED)
13762     {
13763       if (player->inventory_size < MAX_INVENTORY_SIZE)
13764         player->inventory_element[player->inventory_size++] = element;
13765
13766       DrawGameDoorValues();
13767     }
13768     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13769     {
13770       player->dynabomb_count++;
13771       player->dynabombs_left++;
13772     }
13773     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13774     {
13775       player->dynabomb_size++;
13776     }
13777     else if (element == EL_DYNABOMB_INCREASE_POWER)
13778     {
13779       player->dynabomb_xl = TRUE;
13780     }
13781     else if (IS_KEY(element))
13782     {
13783       player->key[KEY_NR(element)] = TRUE;
13784
13785       DrawGameDoorValues();
13786     }
13787     else if (element == EL_DC_KEY_WHITE)
13788     {
13789       player->num_white_keys++;
13790
13791       /* display white keys? */
13792       /* DrawGameDoorValues(); */
13793     }
13794     else if (IS_ENVELOPE(element))
13795     {
13796       player->show_envelope = element;
13797     }
13798     else if (element == EL_EMC_LENSES)
13799     {
13800       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13801
13802       RedrawAllInvisibleElementsForLenses();
13803     }
13804     else if (element == EL_EMC_MAGNIFIER)
13805     {
13806       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13807
13808       RedrawAllInvisibleElementsForMagnifier();
13809     }
13810     else if (IS_DROPPABLE(element) ||
13811              IS_THROWABLE(element))     /* can be collected and dropped */
13812     {
13813       int i;
13814
13815       if (collect_count == 0)
13816         player->inventory_infinite_element = element;
13817       else
13818         for (i = 0; i < collect_count; i++)
13819           if (player->inventory_size < MAX_INVENTORY_SIZE)
13820             player->inventory_element[player->inventory_size++] = element;
13821
13822       DrawGameDoorValues();
13823     }
13824     else if (collect_count > 0)
13825     {
13826       local_player->gems_still_needed -= collect_count;
13827       if (local_player->gems_still_needed < 0)
13828         local_player->gems_still_needed = 0;
13829
13830       game.snapshot.collected_item = TRUE;
13831
13832       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13833
13834       DisplayGameControlValues();
13835     }
13836
13837     RaiseScoreElement(element);
13838     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13839
13840     if (is_player)
13841       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13842                                           player->index_bit, dig_side);
13843
13844     if (mode == DF_SNAP)
13845     {
13846       if (level.block_snap_field)
13847         setFieldForSnapping(x, y, element, move_direction);
13848       else
13849         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13850
13851       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13852                                           player->index_bit, dig_side);
13853     }
13854   }
13855   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13856   {
13857     if (mode == DF_SNAP && element != EL_BD_ROCK)
13858       return MP_NO_ACTION;
13859
13860     if (CAN_FALL(element) && dy)
13861       return MP_NO_ACTION;
13862
13863     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13864         !(element == EL_SPRING && level.use_spring_bug))
13865       return MP_NO_ACTION;
13866
13867     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13868         ((move_direction & MV_VERTICAL &&
13869           ((element_info[element].move_pattern & MV_LEFT &&
13870             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13871            (element_info[element].move_pattern & MV_RIGHT &&
13872             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13873          (move_direction & MV_HORIZONTAL &&
13874           ((element_info[element].move_pattern & MV_UP &&
13875             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13876            (element_info[element].move_pattern & MV_DOWN &&
13877             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13878       return MP_NO_ACTION;
13879
13880     /* do not push elements already moving away faster than player */
13881     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13882         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13883       return MP_NO_ACTION;
13884
13885     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13886     {
13887       if (player->push_delay_value == -1 || !player_was_pushing)
13888         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13889     }
13890     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13891     {
13892       if (player->push_delay_value == -1)
13893         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13894     }
13895     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13896     {
13897       if (!player->is_pushing)
13898         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13899     }
13900
13901     player->is_pushing = TRUE;
13902     player->is_active = TRUE;
13903
13904     if (!(IN_LEV_FIELD(nextx, nexty) &&
13905           (IS_FREE(nextx, nexty) ||
13906            (IS_SB_ELEMENT(element) &&
13907             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13908            (IS_CUSTOM_ELEMENT(element) &&
13909             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13910       return MP_NO_ACTION;
13911
13912     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13913       return MP_NO_ACTION;
13914
13915     if (player->push_delay == -1)       /* new pushing; restart delay */
13916       player->push_delay = 0;
13917
13918     if (player->push_delay < player->push_delay_value &&
13919         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13920         element != EL_SPRING && element != EL_BALLOON)
13921     {
13922       /* make sure that there is no move delay before next try to push */
13923       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13924         player->move_delay = 0;
13925
13926       return MP_NO_ACTION;
13927     }
13928
13929     if (IS_CUSTOM_ELEMENT(element) &&
13930         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13931     {
13932       if (!DigFieldByCE(nextx, nexty, element))
13933         return MP_NO_ACTION;
13934     }
13935
13936     if (IS_SB_ELEMENT(element))
13937     {
13938       if (element == EL_SOKOBAN_FIELD_FULL)
13939       {
13940         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13941         local_player->sokobanfields_still_needed++;
13942       }
13943
13944       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13945       {
13946         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13947         local_player->sokobanfields_still_needed--;
13948       }
13949
13950       Feld[x][y] = EL_SOKOBAN_OBJECT;
13951
13952       if (Back[x][y] == Back[nextx][nexty])
13953         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13954       else if (Back[x][y] != 0)
13955         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13956                                     ACTION_EMPTYING);
13957       else
13958         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13959                                     ACTION_FILLING);
13960
13961       if (local_player->sokobanfields_still_needed == 0 &&
13962           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13963       {
13964         local_player->players_still_needed = 0;
13965
13966         PlayerWins(player);
13967
13968         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13969       }
13970     }
13971     else
13972       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13973
13974     InitMovingField(x, y, move_direction);
13975     GfxAction[x][y] = ACTION_PUSHING;
13976
13977     if (mode == DF_SNAP)
13978       ContinueMoving(x, y);
13979     else
13980       MovPos[x][y] = (dx != 0 ? dx : dy);
13981
13982     Pushed[x][y] = TRUE;
13983     Pushed[nextx][nexty] = TRUE;
13984
13985     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13986       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13987     else
13988       player->push_delay_value = -1;    /* get new value later */
13989
13990     /* check for element change _after_ element has been pushed */
13991     if (game.use_change_when_pushing_bug)
13992     {
13993       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13994                                  player->index_bit, dig_side);
13995       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13996                                           player->index_bit, dig_side);
13997     }
13998   }
13999   else if (IS_SWITCHABLE(element))
14000   {
14001     if (PLAYER_SWITCHING(player, x, y))
14002     {
14003       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14004                                           player->index_bit, dig_side);
14005
14006       return MP_ACTION;
14007     }
14008
14009     player->is_switching = TRUE;
14010     player->switch_x = x;
14011     player->switch_y = y;
14012
14013     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14014
14015     if (element == EL_ROBOT_WHEEL)
14016     {
14017       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14018       ZX = x;
14019       ZY = y;
14020
14021       game.robot_wheel_active = TRUE;
14022
14023       TEST_DrawLevelField(x, y);
14024     }
14025     else if (element == EL_SP_TERMINAL)
14026     {
14027       int xx, yy;
14028
14029       SCAN_PLAYFIELD(xx, yy)
14030       {
14031         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14032         {
14033           Bang(xx, yy);
14034         }
14035         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14036         {
14037           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14038
14039           ResetGfxAnimation(xx, yy);
14040           TEST_DrawLevelField(xx, yy);
14041         }
14042       }
14043     }
14044     else if (IS_BELT_SWITCH(element))
14045     {
14046       ToggleBeltSwitch(x, y);
14047     }
14048     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14049              element == EL_SWITCHGATE_SWITCH_DOWN ||
14050              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14051              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14052     {
14053       ToggleSwitchgateSwitch(x, y);
14054     }
14055     else if (element == EL_LIGHT_SWITCH ||
14056              element == EL_LIGHT_SWITCH_ACTIVE)
14057     {
14058       ToggleLightSwitch(x, y);
14059     }
14060     else if (element == EL_TIMEGATE_SWITCH ||
14061              element == EL_DC_TIMEGATE_SWITCH)
14062     {
14063       ActivateTimegateSwitch(x, y);
14064     }
14065     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14066              element == EL_BALLOON_SWITCH_RIGHT ||
14067              element == EL_BALLOON_SWITCH_UP    ||
14068              element == EL_BALLOON_SWITCH_DOWN  ||
14069              element == EL_BALLOON_SWITCH_NONE  ||
14070              element == EL_BALLOON_SWITCH_ANY)
14071     {
14072       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14073                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14074                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14075                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14076                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14077                              move_direction);
14078     }
14079     else if (element == EL_LAMP)
14080     {
14081       Feld[x][y] = EL_LAMP_ACTIVE;
14082       local_player->lights_still_needed--;
14083
14084       ResetGfxAnimation(x, y);
14085       TEST_DrawLevelField(x, y);
14086     }
14087     else if (element == EL_TIME_ORB_FULL)
14088     {
14089       Feld[x][y] = EL_TIME_ORB_EMPTY;
14090
14091       if (level.time > 0 || level.use_time_orb_bug)
14092       {
14093         TimeLeft += level.time_orb_time;
14094         game.no_time_limit = FALSE;
14095
14096         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14097
14098         DisplayGameControlValues();
14099       }
14100
14101       ResetGfxAnimation(x, y);
14102       TEST_DrawLevelField(x, y);
14103     }
14104     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14105              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14106     {
14107       int xx, yy;
14108
14109       game.ball_state = !game.ball_state;
14110
14111       SCAN_PLAYFIELD(xx, yy)
14112       {
14113         int e = Feld[xx][yy];
14114
14115         if (game.ball_state)
14116         {
14117           if (e == EL_EMC_MAGIC_BALL)
14118             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14119           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14120             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14121         }
14122         else
14123         {
14124           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14125             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14126           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14127             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14128         }
14129       }
14130     }
14131
14132     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14133                                         player->index_bit, dig_side);
14134
14135     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14136                                         player->index_bit, dig_side);
14137
14138     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14139                                         player->index_bit, dig_side);
14140
14141     return MP_ACTION;
14142   }
14143   else
14144   {
14145     if (!PLAYER_SWITCHING(player, x, y))
14146     {
14147       player->is_switching = TRUE;
14148       player->switch_x = x;
14149       player->switch_y = y;
14150
14151       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14152                                  player->index_bit, dig_side);
14153       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14154                                           player->index_bit, dig_side);
14155
14156       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14157                                  player->index_bit, dig_side);
14158       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14159                                           player->index_bit, dig_side);
14160     }
14161
14162     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14163                                player->index_bit, dig_side);
14164     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14165                                         player->index_bit, dig_side);
14166
14167     return MP_NO_ACTION;
14168   }
14169
14170   player->push_delay = -1;
14171
14172   if (is_player)                /* function can also be called by EL_PENGUIN */
14173   {
14174     if (Feld[x][y] != element)          /* really digged/collected something */
14175     {
14176       player->is_collecting = !player->is_digging;
14177       player->is_active = TRUE;
14178     }
14179   }
14180
14181   return MP_MOVING;
14182 }
14183
14184 static boolean DigFieldByCE(int x, int y, int digging_element)
14185 {
14186   int element = Feld[x][y];
14187
14188   if (!IS_FREE(x, y))
14189   {
14190     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14191                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14192                   ACTION_BREAKING);
14193
14194     /* no element can dig solid indestructible elements */
14195     if (IS_INDESTRUCTIBLE(element) &&
14196         !IS_DIGGABLE(element) &&
14197         !IS_COLLECTIBLE(element))
14198       return FALSE;
14199
14200     if (AmoebaNr[x][y] &&
14201         (element == EL_AMOEBA_FULL ||
14202          element == EL_BD_AMOEBA ||
14203          element == EL_AMOEBA_GROWING))
14204     {
14205       AmoebaCnt[AmoebaNr[x][y]]--;
14206       AmoebaCnt2[AmoebaNr[x][y]]--;
14207     }
14208
14209     if (IS_MOVING(x, y))
14210       RemoveMovingField(x, y);
14211     else
14212     {
14213       RemoveField(x, y);
14214       TEST_DrawLevelField(x, y);
14215     }
14216
14217     /* if digged element was about to explode, prevent the explosion */
14218     ExplodeField[x][y] = EX_TYPE_NONE;
14219
14220     PlayLevelSoundAction(x, y, action);
14221   }
14222
14223   Store[x][y] = EL_EMPTY;
14224
14225   /* this makes it possible to leave the removed element again */
14226   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14227     Store[x][y] = element;
14228
14229   return TRUE;
14230 }
14231
14232 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14233 {
14234   int jx = player->jx, jy = player->jy;
14235   int x = jx + dx, y = jy + dy;
14236   int snap_direction = (dx == -1 ? MV_LEFT  :
14237                         dx == +1 ? MV_RIGHT :
14238                         dy == -1 ? MV_UP    :
14239                         dy == +1 ? MV_DOWN  : MV_NONE);
14240   boolean can_continue_snapping = (level.continuous_snapping &&
14241                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14242
14243   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14244     return FALSE;
14245
14246   if (!player->active || !IN_LEV_FIELD(x, y))
14247     return FALSE;
14248
14249   if (dx && dy)
14250     return FALSE;
14251
14252   if (!dx && !dy)
14253   {
14254     if (player->MovPos == 0)
14255       player->is_pushing = FALSE;
14256
14257     player->is_snapping = FALSE;
14258
14259     if (player->MovPos == 0)
14260     {
14261       player->is_moving = FALSE;
14262       player->is_digging = FALSE;
14263       player->is_collecting = FALSE;
14264     }
14265
14266     return FALSE;
14267   }
14268
14269   /* prevent snapping with already pressed snap key when not allowed */
14270   if (player->is_snapping && !can_continue_snapping)
14271     return FALSE;
14272
14273   player->MovDir = snap_direction;
14274
14275   if (player->MovPos == 0)
14276   {
14277     player->is_moving = FALSE;
14278     player->is_digging = FALSE;
14279     player->is_collecting = FALSE;
14280   }
14281
14282   player->is_dropping = FALSE;
14283   player->is_dropping_pressed = FALSE;
14284   player->drop_pressed_delay = 0;
14285
14286   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14287     return FALSE;
14288
14289   player->is_snapping = TRUE;
14290   player->is_active = TRUE;
14291
14292   if (player->MovPos == 0)
14293   {
14294     player->is_moving = FALSE;
14295     player->is_digging = FALSE;
14296     player->is_collecting = FALSE;
14297   }
14298
14299   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14300     TEST_DrawLevelField(player->last_jx, player->last_jy);
14301
14302   TEST_DrawLevelField(x, y);
14303
14304   return TRUE;
14305 }
14306
14307 static boolean DropElement(struct PlayerInfo *player)
14308 {
14309   int old_element, new_element;
14310   int dropx = player->jx, dropy = player->jy;
14311   int drop_direction = player->MovDir;
14312   int drop_side = drop_direction;
14313   int drop_element = get_next_dropped_element(player);
14314
14315   /* do not drop an element on top of another element; when holding drop key
14316      pressed without moving, dropped element must move away before the next
14317      element can be dropped (this is especially important if the next element
14318      is dynamite, which can be placed on background for historical reasons) */
14319   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14320     return MP_ACTION;
14321
14322   if (IS_THROWABLE(drop_element))
14323   {
14324     dropx += GET_DX_FROM_DIR(drop_direction);
14325     dropy += GET_DY_FROM_DIR(drop_direction);
14326
14327     if (!IN_LEV_FIELD(dropx, dropy))
14328       return FALSE;
14329   }
14330
14331   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14332   new_element = drop_element;           /* default: no change when dropping */
14333
14334   /* check if player is active, not moving and ready to drop */
14335   if (!player->active || player->MovPos || player->drop_delay > 0)
14336     return FALSE;
14337
14338   /* check if player has anything that can be dropped */
14339   if (new_element == EL_UNDEFINED)
14340     return FALSE;
14341
14342   /* only set if player has anything that can be dropped */
14343   player->is_dropping_pressed = TRUE;
14344
14345   /* check if drop key was pressed long enough for EM style dynamite */
14346   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14347     return FALSE;
14348
14349   /* check if anything can be dropped at the current position */
14350   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14351     return FALSE;
14352
14353   /* collected custom elements can only be dropped on empty fields */
14354   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14355     return FALSE;
14356
14357   if (old_element != EL_EMPTY)
14358     Back[dropx][dropy] = old_element;   /* store old element on this field */
14359
14360   ResetGfxAnimation(dropx, dropy);
14361   ResetRandomAnimationValue(dropx, dropy);
14362
14363   if (player->inventory_size > 0 ||
14364       player->inventory_infinite_element != EL_UNDEFINED)
14365   {
14366     if (player->inventory_size > 0)
14367     {
14368       player->inventory_size--;
14369
14370       DrawGameDoorValues();
14371
14372       if (new_element == EL_DYNAMITE)
14373         new_element = EL_DYNAMITE_ACTIVE;
14374       else if (new_element == EL_EM_DYNAMITE)
14375         new_element = EL_EM_DYNAMITE_ACTIVE;
14376       else if (new_element == EL_SP_DISK_RED)
14377         new_element = EL_SP_DISK_RED_ACTIVE;
14378     }
14379
14380     Feld[dropx][dropy] = new_element;
14381
14382     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14383       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14384                           el2img(Feld[dropx][dropy]), 0);
14385
14386     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14387
14388     /* needed if previous element just changed to "empty" in the last frame */
14389     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14390
14391     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14392                                player->index_bit, drop_side);
14393     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14394                                         CE_PLAYER_DROPS_X,
14395                                         player->index_bit, drop_side);
14396
14397     TestIfElementTouchesCustomElement(dropx, dropy);
14398   }
14399   else          /* player is dropping a dyna bomb */
14400   {
14401     player->dynabombs_left--;
14402
14403     Feld[dropx][dropy] = new_element;
14404
14405     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14406       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14407                           el2img(Feld[dropx][dropy]), 0);
14408
14409     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14410   }
14411
14412   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14413     InitField_WithBug1(dropx, dropy, FALSE);
14414
14415   new_element = Feld[dropx][dropy];     /* element might have changed */
14416
14417   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14418       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14419   {
14420     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14421       MovDir[dropx][dropy] = drop_direction;
14422
14423     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14424
14425     /* do not cause impact style collision by dropping elements that can fall */
14426     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14427   }
14428
14429   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14430   player->is_dropping = TRUE;
14431
14432   player->drop_pressed_delay = 0;
14433   player->is_dropping_pressed = FALSE;
14434
14435   player->drop_x = dropx;
14436   player->drop_y = dropy;
14437
14438   return TRUE;
14439 }
14440
14441 /* ------------------------------------------------------------------------- */
14442 /* game sound playing functions                                              */
14443 /* ------------------------------------------------------------------------- */
14444
14445 static int *loop_sound_frame = NULL;
14446 static int *loop_sound_volume = NULL;
14447
14448 void InitPlayLevelSound(void)
14449 {
14450   int num_sounds = getSoundListSize();
14451
14452   checked_free(loop_sound_frame);
14453   checked_free(loop_sound_volume);
14454
14455   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14456   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14457 }
14458
14459 static void PlayLevelSound(int x, int y, int nr)
14460 {
14461   int sx = SCREENX(x), sy = SCREENY(y);
14462   int volume, stereo_position;
14463   int max_distance = 8;
14464   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14465
14466   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14467       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14468     return;
14469
14470   if (!IN_LEV_FIELD(x, y) ||
14471       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14472       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14473     return;
14474
14475   volume = SOUND_MAX_VOLUME;
14476
14477   if (!IN_SCR_FIELD(sx, sy))
14478   {
14479     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14480     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14481
14482     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14483   }
14484
14485   stereo_position = (SOUND_MAX_LEFT +
14486                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14487                      (SCR_FIELDX + 2 * max_distance));
14488
14489   if (IS_LOOP_SOUND(nr))
14490   {
14491     /* This assures that quieter loop sounds do not overwrite louder ones,
14492        while restarting sound volume comparison with each new game frame. */
14493
14494     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14495       return;
14496
14497     loop_sound_volume[nr] = volume;
14498     loop_sound_frame[nr] = FrameCounter;
14499   }
14500
14501   PlaySoundExt(nr, volume, stereo_position, type);
14502 }
14503
14504 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14505 {
14506   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14507                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14508                  y < LEVELY(BY1) ? LEVELY(BY1) :
14509                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14510                  sound_action);
14511 }
14512
14513 static void PlayLevelSoundAction(int x, int y, int action)
14514 {
14515   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14516 }
14517
14518 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14519 {
14520   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14521
14522   if (sound_effect != SND_UNDEFINED)
14523     PlayLevelSound(x, y, sound_effect);
14524 }
14525
14526 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14527                                               int action)
14528 {
14529   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14530
14531   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14532     PlayLevelSound(x, y, sound_effect);
14533 }
14534
14535 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14536 {
14537   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14538
14539   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14540     PlayLevelSound(x, y, sound_effect);
14541 }
14542
14543 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14544 {
14545   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14546
14547   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14548     StopSound(sound_effect);
14549 }
14550
14551 static int getLevelMusicNr(void)
14552 {
14553   if (levelset.music[level_nr] != MUS_UNDEFINED)
14554     return levelset.music[level_nr];            /* from config file */
14555   else
14556     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14557 }
14558
14559 static void FadeLevelSounds(void)
14560 {
14561   FadeSounds();
14562 }
14563
14564 static void FadeLevelMusic(void)
14565 {
14566   int music_nr = getLevelMusicNr();
14567   char *curr_music = getCurrentlyPlayingMusicFilename();
14568   char *next_music = getMusicInfoEntryFilename(music_nr);
14569
14570   if (!strEqual(curr_music, next_music))
14571     FadeMusic();
14572 }
14573
14574 void FadeLevelSoundsAndMusic(void)
14575 {
14576   FadeLevelSounds();
14577   FadeLevelMusic();
14578 }
14579
14580 static void PlayLevelMusic(void)
14581 {
14582   int music_nr = getLevelMusicNr();
14583   char *curr_music = getCurrentlyPlayingMusicFilename();
14584   char *next_music = getMusicInfoEntryFilename(music_nr);
14585
14586   if (!strEqual(curr_music, next_music))
14587     PlayMusicLoop(music_nr);
14588 }
14589
14590 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14591 {
14592   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14593   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14594   int x = xx - 1 - offset;
14595   int y = yy - 1 - offset;
14596
14597   switch (sample)
14598   {
14599     case SAMPLE_blank:
14600       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14601       break;
14602
14603     case SAMPLE_roll:
14604       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14605       break;
14606
14607     case SAMPLE_stone:
14608       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14609       break;
14610
14611     case SAMPLE_nut:
14612       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14613       break;
14614
14615     case SAMPLE_crack:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14617       break;
14618
14619     case SAMPLE_bug:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14621       break;
14622
14623     case SAMPLE_tank:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14625       break;
14626
14627     case SAMPLE_android_clone:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14629       break;
14630
14631     case SAMPLE_android_move:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14633       break;
14634
14635     case SAMPLE_spring:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14637       break;
14638
14639     case SAMPLE_slurp:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14641       break;
14642
14643     case SAMPLE_eater:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14645       break;
14646
14647     case SAMPLE_eater_eat:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14649       break;
14650
14651     case SAMPLE_alien:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14653       break;
14654
14655     case SAMPLE_collect:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14657       break;
14658
14659     case SAMPLE_diamond:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14661       break;
14662
14663     case SAMPLE_squash:
14664       /* !!! CHECK THIS !!! */
14665 #if 1
14666       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14667 #else
14668       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14669 #endif
14670       break;
14671
14672     case SAMPLE_wonderfall:
14673       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14674       break;
14675
14676     case SAMPLE_drip:
14677       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14678       break;
14679
14680     case SAMPLE_push:
14681       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14682       break;
14683
14684     case SAMPLE_dirt:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14686       break;
14687
14688     case SAMPLE_acid:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14690       break;
14691
14692     case SAMPLE_ball:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14694       break;
14695
14696     case SAMPLE_grow:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14698       break;
14699
14700     case SAMPLE_wonder:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14702       break;
14703
14704     case SAMPLE_door:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14706       break;
14707
14708     case SAMPLE_exit_open:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14710       break;
14711
14712     case SAMPLE_exit_leave:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14714       break;
14715
14716     case SAMPLE_dynamite:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14718       break;
14719
14720     case SAMPLE_tick:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14722       break;
14723
14724     case SAMPLE_press:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14726       break;
14727
14728     case SAMPLE_wheel:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14730       break;
14731
14732     case SAMPLE_boom:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14734       break;
14735
14736     case SAMPLE_die:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14738       break;
14739
14740     case SAMPLE_time:
14741       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14742       break;
14743
14744     default:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14746       break;
14747   }
14748 }
14749
14750 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14751 {
14752   int element = map_element_SP_to_RND(element_sp);
14753   int action = map_action_SP_to_RND(action_sp);
14754   int offset = (setup.sp_show_border_elements ? 0 : 1);
14755   int x = xx - offset;
14756   int y = yy - offset;
14757
14758   PlayLevelSoundElementAction(x, y, element, action);
14759 }
14760
14761 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14762 {
14763   int element = map_element_MM_to_RND(element_mm);
14764   int action = map_action_MM_to_RND(action_mm);
14765   int offset = 0;
14766   int x = xx - offset;
14767   int y = yy - offset;
14768
14769   if (!IS_MM_ELEMENT(element))
14770     element = EL_MM_DEFAULT;
14771
14772   PlayLevelSoundElementAction(x, y, element, action);
14773 }
14774
14775 void PlaySound_MM(int sound_mm)
14776 {
14777   int sound = map_sound_MM_to_RND(sound_mm);
14778
14779   if (sound == SND_UNDEFINED)
14780     return;
14781
14782   PlaySound(sound);
14783 }
14784
14785 void PlaySoundLoop_MM(int sound_mm)
14786 {
14787   int sound = map_sound_MM_to_RND(sound_mm);
14788
14789   if (sound == SND_UNDEFINED)
14790     return;
14791
14792   PlaySoundLoop(sound);
14793 }
14794
14795 void StopSound_MM(int sound_mm)
14796 {
14797   int sound = map_sound_MM_to_RND(sound_mm);
14798
14799   if (sound == SND_UNDEFINED)
14800     return;
14801
14802   StopSound(sound);
14803 }
14804
14805 void RaiseScore(int value)
14806 {
14807   local_player->score += value;
14808
14809   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14810
14811   DisplayGameControlValues();
14812 }
14813
14814 void RaiseScoreElement(int element)
14815 {
14816   switch (element)
14817   {
14818     case EL_EMERALD:
14819     case EL_BD_DIAMOND:
14820     case EL_EMERALD_YELLOW:
14821     case EL_EMERALD_RED:
14822     case EL_EMERALD_PURPLE:
14823     case EL_SP_INFOTRON:
14824       RaiseScore(level.score[SC_EMERALD]);
14825       break;
14826     case EL_DIAMOND:
14827       RaiseScore(level.score[SC_DIAMOND]);
14828       break;
14829     case EL_CRYSTAL:
14830       RaiseScore(level.score[SC_CRYSTAL]);
14831       break;
14832     case EL_PEARL:
14833       RaiseScore(level.score[SC_PEARL]);
14834       break;
14835     case EL_BUG:
14836     case EL_BD_BUTTERFLY:
14837     case EL_SP_ELECTRON:
14838       RaiseScore(level.score[SC_BUG]);
14839       break;
14840     case EL_SPACESHIP:
14841     case EL_BD_FIREFLY:
14842     case EL_SP_SNIKSNAK:
14843       RaiseScore(level.score[SC_SPACESHIP]);
14844       break;
14845     case EL_YAMYAM:
14846     case EL_DARK_YAMYAM:
14847       RaiseScore(level.score[SC_YAMYAM]);
14848       break;
14849     case EL_ROBOT:
14850       RaiseScore(level.score[SC_ROBOT]);
14851       break;
14852     case EL_PACMAN:
14853       RaiseScore(level.score[SC_PACMAN]);
14854       break;
14855     case EL_NUT:
14856       RaiseScore(level.score[SC_NUT]);
14857       break;
14858     case EL_DYNAMITE:
14859     case EL_EM_DYNAMITE:
14860     case EL_SP_DISK_RED:
14861     case EL_DYNABOMB_INCREASE_NUMBER:
14862     case EL_DYNABOMB_INCREASE_SIZE:
14863     case EL_DYNABOMB_INCREASE_POWER:
14864       RaiseScore(level.score[SC_DYNAMITE]);
14865       break;
14866     case EL_SHIELD_NORMAL:
14867     case EL_SHIELD_DEADLY:
14868       RaiseScore(level.score[SC_SHIELD]);
14869       break;
14870     case EL_EXTRA_TIME:
14871       RaiseScore(level.extra_time_score);
14872       break;
14873     case EL_KEY_1:
14874     case EL_KEY_2:
14875     case EL_KEY_3:
14876     case EL_KEY_4:
14877     case EL_EM_KEY_1:
14878     case EL_EM_KEY_2:
14879     case EL_EM_KEY_3:
14880     case EL_EM_KEY_4:
14881     case EL_EMC_KEY_5:
14882     case EL_EMC_KEY_6:
14883     case EL_EMC_KEY_7:
14884     case EL_EMC_KEY_8:
14885     case EL_DC_KEY_WHITE:
14886       RaiseScore(level.score[SC_KEY]);
14887       break;
14888     default:
14889       RaiseScore(element_info[element].collect_score);
14890       break;
14891   }
14892 }
14893
14894 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14895 {
14896   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14897   {
14898     /* closing door required in case of envelope style request dialogs */
14899     if (!skip_request)
14900       CloseDoor(DOOR_CLOSE_1);
14901
14902     if (network.enabled)
14903       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14904     else
14905     {
14906       if (quick_quit)
14907         FadeSkipNextFadeIn();
14908
14909       SetGameStatus(GAME_MODE_MAIN);
14910
14911       DrawMainMenu();
14912     }
14913   }
14914   else          /* continue playing the game */
14915   {
14916     if (tape.playing && tape.deactivate_display)
14917       TapeDeactivateDisplayOff(TRUE);
14918
14919     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14920
14921     if (tape.playing && tape.deactivate_display)
14922       TapeDeactivateDisplayOn();
14923   }
14924 }
14925
14926 void RequestQuitGame(boolean ask_if_really_quit)
14927 {
14928   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14929   boolean skip_request = AllPlayersGone || quick_quit;
14930
14931   RequestQuitGameExt(skip_request, quick_quit,
14932                      "Do you really want to quit the game?");
14933 }
14934
14935 void RequestRestartGame(char *message)
14936 {
14937   game.restart_game_message = NULL;
14938
14939   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14940   {
14941     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14942   }
14943   else
14944   {
14945     SetGameStatus(GAME_MODE_MAIN);
14946
14947     DrawMainMenu();
14948   }
14949 }
14950
14951
14952 /* ------------------------------------------------------------------------- */
14953 /* random generator functions                                                */
14954 /* ------------------------------------------------------------------------- */
14955
14956 unsigned int InitEngineRandom_RND(int seed)
14957 {
14958   game.num_random_calls = 0;
14959
14960   return InitEngineRandom(seed);
14961 }
14962
14963 unsigned int RND(int max)
14964 {
14965   if (max > 0)
14966   {
14967     game.num_random_calls++;
14968
14969     return GetEngineRandom(max);
14970   }
14971
14972   return 0;
14973 }
14974
14975
14976 /* ------------------------------------------------------------------------- */
14977 /* game engine snapshot handling functions                                   */
14978 /* ------------------------------------------------------------------------- */
14979
14980 struct EngineSnapshotInfo
14981 {
14982   /* runtime values for custom element collect score */
14983   int collect_score[NUM_CUSTOM_ELEMENTS];
14984
14985   /* runtime values for group element choice position */
14986   int choice_pos[NUM_GROUP_ELEMENTS];
14987
14988   /* runtime values for belt position animations */
14989   int belt_graphic[4][NUM_BELT_PARTS];
14990   int belt_anim_mode[4][NUM_BELT_PARTS];
14991 };
14992
14993 static struct EngineSnapshotInfo engine_snapshot_rnd;
14994 static char *snapshot_level_identifier = NULL;
14995 static int snapshot_level_nr = -1;
14996
14997 static void SaveEngineSnapshotValues_RND(void)
14998 {
14999   static int belt_base_active_element[4] =
15000   {
15001     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15002     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15003     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15004     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15005   };
15006   int i, j;
15007
15008   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15009   {
15010     int element = EL_CUSTOM_START + i;
15011
15012     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15013   }
15014
15015   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15016   {
15017     int element = EL_GROUP_START + i;
15018
15019     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15020   }
15021
15022   for (i = 0; i < 4; i++)
15023   {
15024     for (j = 0; j < NUM_BELT_PARTS; j++)
15025     {
15026       int element = belt_base_active_element[i] + j;
15027       int graphic = el2img(element);
15028       int anim_mode = graphic_info[graphic].anim_mode;
15029
15030       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15031       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15032     }
15033   }
15034 }
15035
15036 static void LoadEngineSnapshotValues_RND(void)
15037 {
15038   unsigned int num_random_calls = game.num_random_calls;
15039   int i, j;
15040
15041   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15042   {
15043     int element = EL_CUSTOM_START + i;
15044
15045     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15046   }
15047
15048   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15049   {
15050     int element = EL_GROUP_START + i;
15051
15052     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15053   }
15054
15055   for (i = 0; i < 4; i++)
15056   {
15057     for (j = 0; j < NUM_BELT_PARTS; j++)
15058     {
15059       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15060       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15061
15062       graphic_info[graphic].anim_mode = anim_mode;
15063     }
15064   }
15065
15066   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15067   {
15068     InitRND(tape.random_seed);
15069     for (i = 0; i < num_random_calls; i++)
15070       RND(1);
15071   }
15072
15073   if (game.num_random_calls != num_random_calls)
15074   {
15075     Error(ERR_INFO, "number of random calls out of sync");
15076     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15077     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15078     Error(ERR_EXIT, "this should not happen -- please debug");
15079   }
15080 }
15081
15082 void FreeEngineSnapshotSingle(void)
15083 {
15084   FreeSnapshotSingle();
15085
15086   setString(&snapshot_level_identifier, NULL);
15087   snapshot_level_nr = -1;
15088 }
15089
15090 void FreeEngineSnapshotList(void)
15091 {
15092   FreeSnapshotList();
15093 }
15094
15095 static ListNode *SaveEngineSnapshotBuffers(void)
15096 {
15097   ListNode *buffers = NULL;
15098
15099   /* copy some special values to a structure better suited for the snapshot */
15100
15101   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15102     SaveEngineSnapshotValues_RND();
15103   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15104     SaveEngineSnapshotValues_EM();
15105   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15106     SaveEngineSnapshotValues_SP(&buffers);
15107   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15108     SaveEngineSnapshotValues_MM(&buffers);
15109
15110   /* save values stored in special snapshot structure */
15111
15112   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15113     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15114   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15115     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15116   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15117     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15118   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15119     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15120
15121   /* save further RND engine values */
15122
15123   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15125   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15126
15127   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15128   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15131
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15137
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15141
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15143
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15145
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15148
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15167
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15170
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15174
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15177
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15183
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15186
15187 #if 0
15188   ListNode *node = engine_snapshot_list_rnd;
15189   int num_bytes = 0;
15190
15191   while (node != NULL)
15192   {
15193     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15194
15195     node = node->next;
15196   }
15197
15198   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15199 #endif
15200
15201   return buffers;
15202 }
15203
15204 void SaveEngineSnapshotSingle(void)
15205 {
15206   ListNode *buffers = SaveEngineSnapshotBuffers();
15207
15208   /* finally save all snapshot buffers to single snapshot */
15209   SaveSnapshotSingle(buffers);
15210
15211   /* save level identification information */
15212   setString(&snapshot_level_identifier, leveldir_current->identifier);
15213   snapshot_level_nr = level_nr;
15214 }
15215
15216 boolean CheckSaveEngineSnapshotToList(void)
15217 {
15218   boolean save_snapshot =
15219     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15220      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15221       game.snapshot.changed_action) ||
15222      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15223       game.snapshot.collected_item));
15224
15225   game.snapshot.changed_action = FALSE;
15226   game.snapshot.collected_item = FALSE;
15227   game.snapshot.save_snapshot = save_snapshot;
15228
15229   return save_snapshot;
15230 }
15231
15232 void SaveEngineSnapshotToList(void)
15233 {
15234   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15235       tape.quick_resume)
15236     return;
15237
15238   ListNode *buffers = SaveEngineSnapshotBuffers();
15239
15240   /* finally save all snapshot buffers to snapshot list */
15241   SaveSnapshotToList(buffers);
15242 }
15243
15244 void SaveEngineSnapshotToListInitial(void)
15245 {
15246   FreeEngineSnapshotList();
15247
15248   SaveEngineSnapshotToList();
15249 }
15250
15251 static void LoadEngineSnapshotValues(void)
15252 {
15253   /* restore special values from snapshot structure */
15254
15255   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15256     LoadEngineSnapshotValues_RND();
15257   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15258     LoadEngineSnapshotValues_EM();
15259   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15260     LoadEngineSnapshotValues_SP();
15261   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15262     LoadEngineSnapshotValues_MM();
15263 }
15264
15265 void LoadEngineSnapshotSingle(void)
15266 {
15267   LoadSnapshotSingle();
15268
15269   LoadEngineSnapshotValues();
15270 }
15271
15272 static void LoadEngineSnapshot_Undo(int steps)
15273 {
15274   LoadSnapshotFromList_Older(steps);
15275
15276   LoadEngineSnapshotValues();
15277 }
15278
15279 static void LoadEngineSnapshot_Redo(int steps)
15280 {
15281   LoadSnapshotFromList_Newer(steps);
15282
15283   LoadEngineSnapshotValues();
15284 }
15285
15286 boolean CheckEngineSnapshotSingle(void)
15287 {
15288   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15289           snapshot_level_nr == level_nr);
15290 }
15291
15292 boolean CheckEngineSnapshotList(void)
15293 {
15294   return CheckSnapshotList();
15295 }
15296
15297
15298 /* ---------- new game button stuff ---------------------------------------- */
15299
15300 static struct
15301 {
15302   int graphic;
15303   struct XY *pos;
15304   int gadget_id;
15305   boolean *setup_value;
15306   boolean allowed_on_tape;
15307   char *infotext;
15308 } gamebutton_info[NUM_GAME_BUTTONS] =
15309 {
15310   {
15311     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15312     GAME_CTRL_ID_STOP,                          NULL,
15313     TRUE,                                       "stop game"
15314   },
15315   {
15316     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15317     GAME_CTRL_ID_PAUSE,                         NULL,
15318     TRUE,                                       "pause game"
15319   },
15320   {
15321     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15322     GAME_CTRL_ID_PLAY,                          NULL,
15323     TRUE,                                       "play game"
15324   },
15325   {
15326     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15327     GAME_CTRL_ID_UNDO,                          NULL,
15328     TRUE,                                       "undo step"
15329   },
15330   {
15331     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15332     GAME_CTRL_ID_REDO,                          NULL,
15333     TRUE,                                       "redo step"
15334   },
15335   {
15336     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15337     GAME_CTRL_ID_SAVE,                          NULL,
15338     TRUE,                                       "save game"
15339   },
15340   {
15341     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15342     GAME_CTRL_ID_PAUSE2,                        NULL,
15343     TRUE,                                       "pause game"
15344   },
15345   {
15346     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15347     GAME_CTRL_ID_LOAD,                          NULL,
15348     TRUE,                                       "load game"
15349   },
15350   {
15351     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15352     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15353     FALSE,                                      "stop game"
15354   },
15355   {
15356     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15357     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15358     FALSE,                                      "pause game"
15359   },
15360   {
15361     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15362     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15363     FALSE,                                      "play game"
15364   },
15365   {
15366     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15367     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15368     TRUE,                                       "background music on/off"
15369   },
15370   {
15371     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15372     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15373     TRUE,                                       "sound loops on/off"
15374   },
15375   {
15376     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15377     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15378     TRUE,                                       "normal sounds on/off"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15382     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15383     FALSE,                                      "background music on/off"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15387     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15388     FALSE,                                      "sound loops on/off"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15392     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15393     FALSE,                                      "normal sounds on/off"
15394   }
15395 };
15396
15397 void CreateGameButtons(void)
15398 {
15399   int i;
15400
15401   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15402   {
15403     int graphic = gamebutton_info[i].graphic;
15404     struct GraphicInfo *gfx = &graphic_info[graphic];
15405     struct XY *pos = gamebutton_info[i].pos;
15406     struct GadgetInfo *gi;
15407     int button_type;
15408     boolean checked;
15409     unsigned int event_mask;
15410     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15411     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15412     int base_x = (on_tape ? VX : DX);
15413     int base_y = (on_tape ? VY : DY);
15414     int gd_x   = gfx->src_x;
15415     int gd_y   = gfx->src_y;
15416     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15417     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15418     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15419     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15420     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15421     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15422     int id = i;
15423
15424     if (gfx->bitmap == NULL)
15425     {
15426       game_gadget[id] = NULL;
15427
15428       continue;
15429     }
15430
15431     if (id == GAME_CTRL_ID_STOP ||
15432         id == GAME_CTRL_ID_PANEL_STOP ||
15433         id == GAME_CTRL_ID_PLAY ||
15434         id == GAME_CTRL_ID_PANEL_PLAY ||
15435         id == GAME_CTRL_ID_SAVE ||
15436         id == GAME_CTRL_ID_LOAD)
15437     {
15438       button_type = GD_TYPE_NORMAL_BUTTON;
15439       checked = FALSE;
15440       event_mask = GD_EVENT_RELEASED;
15441     }
15442     else if (id == GAME_CTRL_ID_UNDO ||
15443              id == GAME_CTRL_ID_REDO)
15444     {
15445       button_type = GD_TYPE_NORMAL_BUTTON;
15446       checked = FALSE;
15447       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15448     }
15449     else
15450     {
15451       button_type = GD_TYPE_CHECK_BUTTON;
15452       checked = (gamebutton_info[i].setup_value != NULL ?
15453                  *gamebutton_info[i].setup_value : FALSE);
15454       event_mask = GD_EVENT_PRESSED;
15455     }
15456
15457     gi = CreateGadget(GDI_CUSTOM_ID, id,
15458                       GDI_IMAGE_ID, graphic,
15459                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15460                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15461                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15462                       GDI_WIDTH, gfx->width,
15463                       GDI_HEIGHT, gfx->height,
15464                       GDI_TYPE, button_type,
15465                       GDI_STATE, GD_BUTTON_UNPRESSED,
15466                       GDI_CHECKED, checked,
15467                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15468                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15469                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15470                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15471                       GDI_DIRECT_DRAW, FALSE,
15472                       GDI_EVENT_MASK, event_mask,
15473                       GDI_CALLBACK_ACTION, HandleGameButtons,
15474                       GDI_END);
15475
15476     if (gi == NULL)
15477       Error(ERR_EXIT, "cannot create gadget");
15478
15479     game_gadget[id] = gi;
15480   }
15481 }
15482
15483 void FreeGameButtons(void)
15484 {
15485   int i;
15486
15487   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15488     FreeGadget(game_gadget[i]);
15489 }
15490
15491 static void UnmapGameButtonsAtSamePosition(int id)
15492 {
15493   int i;
15494
15495   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15496     if (i != id &&
15497         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15498         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15499       UnmapGadget(game_gadget[i]);
15500 }
15501
15502 static void UnmapGameButtonsAtSamePosition_All(void)
15503 {
15504   if (setup.show_snapshot_buttons)
15505   {
15506     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15507     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15508     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15509   }
15510   else
15511   {
15512     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15513     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15514     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15515
15516     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15517     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15518     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15519   }
15520 }
15521
15522 static void MapGameButtonsAtSamePosition(int id)
15523 {
15524   int i;
15525
15526   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15527     if (i != id &&
15528         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15529         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15530       MapGadget(game_gadget[i]);
15531
15532   UnmapGameButtonsAtSamePosition_All();
15533 }
15534
15535 void MapUndoRedoButtons(void)
15536 {
15537   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15538   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15539
15540   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15541   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15542
15543   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15544 }
15545
15546 void UnmapUndoRedoButtons(void)
15547 {
15548   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15549   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15550
15551   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15552   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15553
15554   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15555 }
15556
15557 static void MapGameButtonsExt(boolean on_tape)
15558 {
15559   int i;
15560
15561   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15562     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15563         i != GAME_CTRL_ID_UNDO &&
15564         i != GAME_CTRL_ID_REDO)
15565       MapGadget(game_gadget[i]);
15566
15567   UnmapGameButtonsAtSamePosition_All();
15568
15569   RedrawGameButtons();
15570 }
15571
15572 static void UnmapGameButtonsExt(boolean on_tape)
15573 {
15574   int i;
15575
15576   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15577     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15578       UnmapGadget(game_gadget[i]);
15579 }
15580
15581 static void RedrawGameButtonsExt(boolean on_tape)
15582 {
15583   int i;
15584
15585   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15586     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15587       RedrawGadget(game_gadget[i]);
15588
15589   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15590   redraw_mask &= ~REDRAW_ALL;
15591 }
15592
15593 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15594 {
15595   if (gi == NULL)
15596     return;
15597
15598   gi->checked = state;
15599 }
15600
15601 static void RedrawSoundButtonGadget(int id)
15602 {
15603   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15604              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15605              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15606              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15607              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15608              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15609              id);
15610
15611   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15612   RedrawGadget(game_gadget[id2]);
15613 }
15614
15615 void MapGameButtons(void)
15616 {
15617   MapGameButtonsExt(FALSE);
15618 }
15619
15620 void UnmapGameButtons(void)
15621 {
15622   UnmapGameButtonsExt(FALSE);
15623 }
15624
15625 void RedrawGameButtons(void)
15626 {
15627   RedrawGameButtonsExt(FALSE);
15628 }
15629
15630 void MapGameButtonsOnTape(void)
15631 {
15632   MapGameButtonsExt(TRUE);
15633 }
15634
15635 void UnmapGameButtonsOnTape(void)
15636 {
15637   UnmapGameButtonsExt(TRUE);
15638 }
15639
15640 void RedrawGameButtonsOnTape(void)
15641 {
15642   RedrawGameButtonsExt(TRUE);
15643 }
15644
15645 static void GameUndoRedoExt(void)
15646 {
15647   ClearPlayerAction();
15648
15649   tape.pausing = TRUE;
15650
15651   RedrawPlayfield();
15652   UpdateAndDisplayGameControlValues();
15653
15654   DrawCompleteVideoDisplay();
15655   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15656   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15657   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15658
15659   BackToFront();
15660 }
15661
15662 static void GameUndo(int steps)
15663 {
15664   if (!CheckEngineSnapshotList())
15665     return;
15666
15667   LoadEngineSnapshot_Undo(steps);
15668
15669   GameUndoRedoExt();
15670 }
15671
15672 static void GameRedo(int steps)
15673 {
15674   if (!CheckEngineSnapshotList())
15675     return;
15676
15677   LoadEngineSnapshot_Redo(steps);
15678
15679   GameUndoRedoExt();
15680 }
15681
15682 static void HandleGameButtonsExt(int id, int button)
15683 {
15684   static boolean game_undo_executed = FALSE;
15685   int steps = BUTTON_STEPSIZE(button);
15686   boolean handle_game_buttons =
15687     (game_status == GAME_MODE_PLAYING ||
15688      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15689
15690   if (!handle_game_buttons)
15691     return;
15692
15693   switch (id)
15694   {
15695     case GAME_CTRL_ID_STOP:
15696     case GAME_CTRL_ID_PANEL_STOP:
15697       if (game_status == GAME_MODE_MAIN)
15698         break;
15699
15700       if (tape.playing)
15701         TapeStop();
15702       else
15703         RequestQuitGame(TRUE);
15704
15705       break;
15706
15707     case GAME_CTRL_ID_PAUSE:
15708     case GAME_CTRL_ID_PAUSE2:
15709     case GAME_CTRL_ID_PANEL_PAUSE:
15710       if (network.enabled && game_status == GAME_MODE_PLAYING)
15711       {
15712         if (tape.pausing)
15713           SendToServer_ContinuePlaying();
15714         else
15715           SendToServer_PausePlaying();
15716       }
15717       else
15718         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15719
15720       game_undo_executed = FALSE;
15721
15722       break;
15723
15724     case GAME_CTRL_ID_PLAY:
15725     case GAME_CTRL_ID_PANEL_PLAY:
15726       if (game_status == GAME_MODE_MAIN)
15727       {
15728         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15729       }
15730       else if (tape.pausing)
15731       {
15732         if (network.enabled)
15733           SendToServer_ContinuePlaying();
15734         else
15735           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15736       }
15737       break;
15738
15739     case GAME_CTRL_ID_UNDO:
15740       // Important: When using "save snapshot when collecting an item" mode,
15741       // load last (current) snapshot for first "undo" after pressing "pause"
15742       // (else the last-but-one snapshot would be loaded, because the snapshot
15743       // pointer already points to the last snapshot when pressing "pause",
15744       // which is fine for "every step/move" mode, but not for "every collect")
15745       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15746           !game_undo_executed)
15747         steps--;
15748
15749       game_undo_executed = TRUE;
15750
15751       GameUndo(steps);
15752       break;
15753
15754     case GAME_CTRL_ID_REDO:
15755       GameRedo(steps);
15756       break;
15757
15758     case GAME_CTRL_ID_SAVE:
15759       TapeQuickSave();
15760       break;
15761
15762     case GAME_CTRL_ID_LOAD:
15763       TapeQuickLoad();
15764       break;
15765
15766     case SOUND_CTRL_ID_MUSIC:
15767     case SOUND_CTRL_ID_PANEL_MUSIC:
15768       if (setup.sound_music)
15769       { 
15770         setup.sound_music = FALSE;
15771
15772         FadeMusic();
15773       }
15774       else if (audio.music_available)
15775       { 
15776         setup.sound = setup.sound_music = TRUE;
15777
15778         SetAudioMode(setup.sound);
15779
15780         if (game_status == GAME_MODE_PLAYING)
15781           PlayLevelMusic();
15782       }
15783
15784       RedrawSoundButtonGadget(id);
15785
15786       break;
15787
15788     case SOUND_CTRL_ID_LOOPS:
15789     case SOUND_CTRL_ID_PANEL_LOOPS:
15790       if (setup.sound_loops)
15791         setup.sound_loops = FALSE;
15792       else if (audio.loops_available)
15793       {
15794         setup.sound = setup.sound_loops = TRUE;
15795
15796         SetAudioMode(setup.sound);
15797       }
15798
15799       RedrawSoundButtonGadget(id);
15800
15801       break;
15802
15803     case SOUND_CTRL_ID_SIMPLE:
15804     case SOUND_CTRL_ID_PANEL_SIMPLE:
15805       if (setup.sound_simple)
15806         setup.sound_simple = FALSE;
15807       else if (audio.sound_available)
15808       {
15809         setup.sound = setup.sound_simple = TRUE;
15810
15811         SetAudioMode(setup.sound);
15812       }
15813
15814       RedrawSoundButtonGadget(id);
15815
15816       break;
15817
15818     default:
15819       break;
15820   }
15821 }
15822
15823 static void HandleGameButtons(struct GadgetInfo *gi)
15824 {
15825   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15826 }
15827
15828 void HandleSoundButtonKeys(Key key)
15829 {
15830   if (key == setup.shortcut.sound_simple)
15831     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15832   else if (key == setup.shortcut.sound_loops)
15833     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15834   else if (key == setup.shortcut.sound_music)
15835     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15836 }