removed checks for (unused) border element for EM engine
[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 GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843 #if 0
2844   printf("level %d: level.game_version  == %06d\n", level_nr,
2845          level.game_version);
2846   printf("          tape.file_version   == %06d\n",
2847          tape.file_version);
2848   printf("          tape.game_version   == %06d\n",
2849          tape.game_version);
2850   printf("          tape.engine_version == %06d\n",
2851          tape.engine_version);
2852   printf("       => game.engine_version == %06d [tape mode: %s]\n",
2853          game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2854 #endif
2855
2856   // --------------------------------------------------------------------------
2857   // set flags for bugs and changes according to active game engine version
2858   // --------------------------------------------------------------------------
2859
2860   /*
2861     Summary of bugfix:
2862     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2863
2864     Bug was introduced in version:
2865     2.0.1
2866
2867     Bug was fixed in version:
2868     4.1.4.2
2869
2870     Description:
2871     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2872     but the property "can fall" was missing, which caused some levels to be
2873     unsolvable. This was fixed in version 4.1.4.2.
2874
2875     Affected levels/tapes:
2876     An example for a tape that was fixed by this bugfix is tape 029 from the
2877     level set "rnd_sam_bateman".
2878     The wrong behaviour will still be used for all levels or tapes that were
2879     created/recorded with it. An example for this is tape 023 from the level
2880     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2881   */
2882
2883   boolean use_amoeba_dropping_cannot_fall_bug =
2884     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2885       game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
2886      (tape.playing &&
2887       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2888       tape.game_version <= VERSION_IDENT(4,1,4,1)));
2889
2890   /*
2891     Summary of bugfix/change:
2892     Fixed move speed of elements entering or leaving magic wall.
2893
2894     Fixed/changed in version:
2895     2.0.1
2896
2897     Description:
2898     Before 2.0.1, move speed of elements entering or leaving magic wall was
2899     twice as fast as it is now.
2900     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2901
2902     Affected levels/tapes:
2903     The first condition is generally needed for all levels/tapes before version
2904     2.0.1, which might use the old behaviour before it was changed; known tapes
2905     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2906     The second condition is an exception from the above case and is needed for
2907     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2908     above, but before it was known that this change would break tapes like the
2909     above and was fixed in 4.1.4.2, so that the changed behaviour was active
2910     although the engine version while recording maybe was before 2.0.1. There
2911     are a lot of tapes that are affected by this exception, like tape 006 from
2912     the level set "rnd_conor_mancone".
2913   */
2914
2915   boolean use_old_move_stepsize_for_magic_wall =
2916     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2917      !(tape.playing &&
2918        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2919        tape.game_version <  VERSION_IDENT(4,1,4,2)));
2920
2921   /*
2922     Summary of bugfix/change:
2923     Fixed handling for custom elements that change when pushed by the player.
2924
2925     Fixed/changed in version:
2926     3.1.0
2927
2928     Description:
2929     Before 3.1.0, custom elements that "change when pushing" changed directly
2930     after the player started pushing them (until then handled in "DigField()").
2931     Since 3.1.0, these custom elements are not changed until the "pushing"
2932     move of the element is finished (now handled in "ContinueMoving()").
2933
2934     Affected levels/tapes:
2935     The first condition is generally needed for all levels/tapes before version
2936     3.1.0, which might use the old behaviour before it was changed; known tapes
2937     that are affected are some tapes from the level set "Walpurgis Gardens" by
2938     Jamie Cullen.
2939     The second condition is an exception from the above case and is needed for
2940     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2941     above (including some development versions of 3.1.0), but before it was
2942     known that this change would break tapes like the above and was fixed in
2943     3.1.1, so that the changed behaviour was active although the engine version
2944     while recording maybe was before 3.1.0. There is at least one tape that is
2945     affected by this exception, which is the tape for the one-level set "Bug
2946     Machine" by Juergen Bonhagen.
2947   */
2948
2949   game.use_change_when_pushing_bug =
2950     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2951      !(tape.playing &&
2952        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2953        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2954
2955   /*
2956     Summary of bugfix/change:
2957     Fixed handling for blocking the field the player leaves when moving.
2958
2959     Fixed/changed in version:
2960     3.1.1
2961
2962     Description:
2963     Before 3.1.1, when "block last field when moving" was enabled, the field
2964     the player is leaving when moving was blocked for the time of the move,
2965     and was directly unblocked afterwards. This resulted in the last field
2966     being blocked for exactly one less than the number of frames of one player
2967     move. Additionally, even when blocking was disabled, the last field was
2968     blocked for exactly one frame.
2969     Since 3.1.1, due to changes in player movement handling, the last field
2970     is not blocked at all when blocking is disabled. When blocking is enabled,
2971     the last field is blocked for exactly the number of frames of one player
2972     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2973     last field is blocked for exactly one more than the number of frames of
2974     one player move.
2975
2976     Affected levels/tapes:
2977     (!!! yet to be determined -- probably many !!!)
2978   */
2979
2980   game.use_block_last_field_bug =
2981     (game.engine_version < VERSION_IDENT(3,1,1,0));
2982
2983   /* various special flags and settings for native Emerald Mine game engine */
2984
2985   game_em.use_single_button =
2986     (game.engine_version > VERSION_IDENT(4,0,0,2));
2987
2988   game_em.use_snap_key_bug =
2989     (game.engine_version < VERSION_IDENT(4,0,1,0));
2990
2991   game_em.use_old_explosions =
2992     (game.engine_version < VERSION_IDENT(4,1,4,2));
2993
2994   // --------------------------------------------------------------------------
2995
2996   // set maximal allowed number of custom element changes per game frame
2997   game.max_num_changes_per_frame = 1;
2998
2999   // default scan direction: scan playfield from top/left to bottom/right
3000   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3001
3002   // dynamically adjust element properties according to game engine version
3003   InitElementPropertiesEngine(game.engine_version);
3004
3005   // ---------- initialize special element properties -------------------------
3006
3007   // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
3008   if (use_amoeba_dropping_cannot_fall_bug)
3009     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3010
3011   // ---------- initialize player's initial move delay ------------------------
3012
3013   // dynamically adjust player properties according to level information
3014   for (i = 0; i < MAX_PLAYERS; i++)
3015     game.initial_move_delay_value[i] =
3016       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3017
3018   // dynamically adjust player properties according to game engine version
3019   for (i = 0; i < MAX_PLAYERS; i++)
3020     game.initial_move_delay[i] =
3021       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3022        game.initial_move_delay_value[i] : 0);
3023
3024   // ---------- initialize player's initial push delay ------------------------
3025
3026   // dynamically adjust player properties according to game engine version
3027   game.initial_push_delay_value =
3028     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3029
3030   // ---------- initialize changing elements ----------------------------------
3031
3032   // initialize changing elements information
3033   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3034   {
3035     struct ElementInfo *ei = &element_info[i];
3036
3037     // this pointer might have been changed in the level editor
3038     ei->change = &ei->change_page[0];
3039
3040     if (!IS_CUSTOM_ELEMENT(i))
3041     {
3042       ei->change->target_element = EL_EMPTY_SPACE;
3043       ei->change->delay_fixed = 0;
3044       ei->change->delay_random = 0;
3045       ei->change->delay_frames = 1;
3046     }
3047
3048     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3049     {
3050       ei->has_change_event[j] = FALSE;
3051
3052       ei->event_page_nr[j] = 0;
3053       ei->event_page[j] = &ei->change_page[0];
3054     }
3055   }
3056
3057   // add changing elements from pre-defined list
3058   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3059   {
3060     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3061     struct ElementInfo *ei = &element_info[ch_delay->element];
3062
3063     ei->change->target_element       = ch_delay->target_element;
3064     ei->change->delay_fixed          = ch_delay->change_delay;
3065
3066     ei->change->pre_change_function  = ch_delay->pre_change_function;
3067     ei->change->change_function      = ch_delay->change_function;
3068     ei->change->post_change_function = ch_delay->post_change_function;
3069
3070     ei->change->can_change = TRUE;
3071     ei->change->can_change_or_has_action = TRUE;
3072
3073     ei->has_change_event[CE_DELAY] = TRUE;
3074
3075     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3076     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3077   }
3078
3079   // ---------- initialize internal run-time variables ------------------------
3080
3081   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3082   {
3083     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3084
3085     for (j = 0; j < ei->num_change_pages; j++)
3086     {
3087       ei->change_page[j].can_change_or_has_action =
3088         (ei->change_page[j].can_change |
3089          ei->change_page[j].has_action);
3090     }
3091   }
3092
3093   // add change events from custom element configuration
3094   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3095   {
3096     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3097
3098     for (j = 0; j < ei->num_change_pages; j++)
3099     {
3100       if (!ei->change_page[j].can_change_or_has_action)
3101         continue;
3102
3103       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3104       {
3105         // only add event page for the first page found with this event
3106         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3107         {
3108           ei->has_change_event[k] = TRUE;
3109
3110           ei->event_page_nr[k] = j;
3111           ei->event_page[k] = &ei->change_page[j];
3112         }
3113       }
3114     }
3115   }
3116
3117   // ---------- initialize reference elements in change conditions ------------
3118
3119   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3120   {
3121     int element = EL_CUSTOM_START + i;
3122     struct ElementInfo *ei = &element_info[element];
3123
3124     for (j = 0; j < ei->num_change_pages; j++)
3125     {
3126       int trigger_element = ei->change_page[j].initial_trigger_element;
3127
3128       if (trigger_element >= EL_PREV_CE_8 &&
3129           trigger_element <= EL_NEXT_CE_8)
3130         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3131
3132       ei->change_page[j].trigger_element = trigger_element;
3133     }
3134   }
3135
3136   // ---------- initialize run-time trigger player and element ----------------
3137
3138   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3139   {
3140     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3141
3142     for (j = 0; j < ei->num_change_pages; j++)
3143     {
3144       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3145       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3146       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3147       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3148       ei->change_page[j].actual_trigger_ce_value = 0;
3149       ei->change_page[j].actual_trigger_ce_score = 0;
3150     }
3151   }
3152
3153   // ---------- initialize trigger events -------------------------------------
3154
3155   // initialize trigger events information
3156   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3157     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3158       trigger_events[i][j] = FALSE;
3159
3160   // add trigger events from element change event properties
3161   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3162   {
3163     struct ElementInfo *ei = &element_info[i];
3164
3165     for (j = 0; j < ei->num_change_pages; j++)
3166     {
3167       if (!ei->change_page[j].can_change_or_has_action)
3168         continue;
3169
3170       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3171       {
3172         int trigger_element = ei->change_page[j].trigger_element;
3173
3174         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3175         {
3176           if (ei->change_page[j].has_event[k])
3177           {
3178             if (IS_GROUP_ELEMENT(trigger_element))
3179             {
3180               struct ElementGroupInfo *group =
3181                 element_info[trigger_element].group;
3182
3183               for (l = 0; l < group->num_elements_resolved; l++)
3184                 trigger_events[group->element_resolved[l]][k] = TRUE;
3185             }
3186             else if (trigger_element == EL_ANY_ELEMENT)
3187               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3188                 trigger_events[l][k] = TRUE;
3189             else
3190               trigger_events[trigger_element][k] = TRUE;
3191           }
3192         }
3193       }
3194     }
3195   }
3196
3197   // ---------- initialize push delay -----------------------------------------
3198
3199   // initialize push delay values to default
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     if (!IS_CUSTOM_ELEMENT(i))
3203     {
3204       // set default push delay values (corrected since version 3.0.7-1)
3205       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3206       {
3207         element_info[i].push_delay_fixed = 2;
3208         element_info[i].push_delay_random = 8;
3209       }
3210       else
3211       {
3212         element_info[i].push_delay_fixed = 8;
3213         element_info[i].push_delay_random = 8;
3214       }
3215     }
3216   }
3217
3218   // set push delay value for certain elements from pre-defined list
3219   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3220   {
3221     int e = push_delay_list[i].element;
3222
3223     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3224     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3225   }
3226
3227   // set push delay value for Supaplex elements for newer engine versions
3228   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3229   {
3230     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3231     {
3232       if (IS_SP_ELEMENT(i))
3233       {
3234         // set SP push delay to just enough to push under a falling zonk
3235         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3236
3237         element_info[i].push_delay_fixed  = delay;
3238         element_info[i].push_delay_random = 0;
3239       }
3240     }
3241   }
3242
3243   // ---------- initialize move stepsize --------------------------------------
3244
3245   // initialize move stepsize values to default
3246   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3247     if (!IS_CUSTOM_ELEMENT(i))
3248       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3249
3250   // set move stepsize value for certain elements from pre-defined list
3251   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3252   {
3253     int e = move_stepsize_list[i].element;
3254
3255     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3256
3257     // set move stepsize value for certain elements for older engine versions
3258     if (use_old_move_stepsize_for_magic_wall)
3259     {
3260       if (e == EL_MAGIC_WALL_FILLING ||
3261           e == EL_MAGIC_WALL_EMPTYING ||
3262           e == EL_BD_MAGIC_WALL_FILLING ||
3263           e == EL_BD_MAGIC_WALL_EMPTYING)
3264         element_info[e].move_stepsize *= 2;
3265     }
3266   }
3267
3268   // ---------- initialize collect score --------------------------------------
3269
3270   // initialize collect score values for custom elements from initial value
3271   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272     if (IS_CUSTOM_ELEMENT(i))
3273       element_info[i].collect_score = element_info[i].collect_score_initial;
3274
3275   // ---------- initialize collect count --------------------------------------
3276
3277   // initialize collect count values for non-custom elements
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279     if (!IS_CUSTOM_ELEMENT(i))
3280       element_info[i].collect_count_initial = 0;
3281
3282   // add collect count values for all elements from pre-defined list
3283   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3284     element_info[collect_count_list[i].element].collect_count_initial =
3285       collect_count_list[i].count;
3286
3287   // ---------- initialize access direction -----------------------------------
3288
3289   // initialize access direction values to default (access from every side)
3290   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3291     if (!IS_CUSTOM_ELEMENT(i))
3292       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3293
3294   // set access direction value for certain elements from pre-defined list
3295   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3296     element_info[access_direction_list[i].element].access_direction =
3297       access_direction_list[i].direction;
3298
3299   // ---------- initialize explosion content ----------------------------------
3300   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3301   {
3302     if (IS_CUSTOM_ELEMENT(i))
3303       continue;
3304
3305     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3306     {
3307       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3308
3309       element_info[i].content.e[x][y] =
3310         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3311          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3312          i == EL_PLAYER_3 ? EL_EMERALD :
3313          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3314          i == EL_MOLE ? EL_EMERALD_RED :
3315          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3316          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3317          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3318          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3319          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3320          i == EL_WALL_EMERALD ? EL_EMERALD :
3321          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3322          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3323          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3324          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3325          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3326          i == EL_WALL_PEARL ? EL_PEARL :
3327          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3328          EL_EMPTY);
3329     }
3330   }
3331
3332   // ---------- initialize recursion detection --------------------------------
3333   recursion_loop_depth = 0;
3334   recursion_loop_detected = FALSE;
3335   recursion_loop_element = EL_UNDEFINED;
3336
3337   // ---------- initialize graphics engine ------------------------------------
3338   game.scroll_delay_value =
3339     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3340      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3341      !setup.forced_scroll_delay           ? 0 :
3342      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3343   game.scroll_delay_value =
3344     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3345
3346   // ---------- initialize game engine snapshots ------------------------------
3347   for (i = 0; i < MAX_PLAYERS; i++)
3348     game.snapshot.last_action[i] = 0;
3349   game.snapshot.changed_action = FALSE;
3350   game.snapshot.collected_item = FALSE;
3351   game.snapshot.mode =
3352     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3353      SNAPSHOT_MODE_EVERY_STEP :
3354      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3355      SNAPSHOT_MODE_EVERY_MOVE :
3356      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3357      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3358   game.snapshot.save_snapshot = FALSE;
3359
3360   // ---------- initialize level time for Supaplex engine ---------------------
3361   // Supaplex levels with time limit currently unsupported -- should be added
3362   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3363     level.time = 0;
3364
3365   // ---------- initialize flags for handling game actions --------------------
3366
3367   // set flags for game actions to default values
3368   game.use_key_actions = TRUE;
3369   game.use_mouse_actions = FALSE;
3370
3371   // when using Mirror Magic game engine, handle mouse events only
3372   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3373   {
3374     game.use_key_actions = FALSE;
3375     game.use_mouse_actions = TRUE;
3376   }
3377
3378   // check for custom elements with mouse click events
3379   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3380   {
3381     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3382     {
3383       int element = EL_CUSTOM_START + i;
3384
3385       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3386           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3387           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3388           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3389         game.use_mouse_actions = TRUE;
3390     }
3391   }
3392 }
3393
3394 static int get_num_special_action(int element, int action_first,
3395                                   int action_last)
3396 {
3397   int num_special_action = 0;
3398   int i, j;
3399
3400   for (i = action_first; i <= action_last; i++)
3401   {
3402     boolean found = FALSE;
3403
3404     for (j = 0; j < NUM_DIRECTIONS; j++)
3405       if (el_act_dir2img(element, i, j) !=
3406           el_act_dir2img(element, ACTION_DEFAULT, j))
3407         found = TRUE;
3408
3409     if (found)
3410       num_special_action++;
3411     else
3412       break;
3413   }
3414
3415   return num_special_action;
3416 }
3417
3418
3419 // ============================================================================
3420 // InitGame()
3421 // ----------------------------------------------------------------------------
3422 // initialize and start new game
3423 // ============================================================================
3424
3425 #if DEBUG_INIT_PLAYER
3426 static void DebugPrintPlayerStatus(char *message)
3427 {
3428   int i;
3429
3430   if (!options.debug)
3431     return;
3432
3433   printf("%s:\n", message);
3434
3435   for (i = 0; i < MAX_PLAYERS; i++)
3436   {
3437     struct PlayerInfo *player = &stored_player[i];
3438
3439     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3440            i + 1,
3441            player->present,
3442            player->connected,
3443            player->connected_locally,
3444            player->connected_network,
3445            player->active);
3446
3447     if (local_player == player)
3448       printf(" (local player)");
3449
3450     printf("\n");
3451   }
3452 }
3453 #endif
3454
3455 void InitGame(void)
3456 {
3457   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3458   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3459   int fade_mask = REDRAW_FIELD;
3460
3461   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3462   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3463   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3464   int initial_move_dir = MV_DOWN;
3465   int i, j, x, y;
3466
3467   // required here to update video display before fading (FIX THIS)
3468   DrawMaskedBorder(REDRAW_DOOR_2);
3469
3470   if (!game.restart_level)
3471     CloseDoor(DOOR_CLOSE_1);
3472
3473   SetGameStatus(GAME_MODE_PLAYING);
3474
3475   if (level_editor_test_game)
3476     FadeSkipNextFadeOut();
3477   else
3478     FadeSetEnterScreen();
3479
3480   if (CheckFadeAll())
3481     fade_mask = REDRAW_ALL;
3482
3483   FadeLevelSoundsAndMusic();
3484
3485   ExpireSoundLoops(TRUE);
3486
3487   FadeOut(fade_mask);
3488
3489   if (level_editor_test_game)
3490     FadeSkipNextFadeIn();
3491
3492   // needed if different viewport properties defined for playing
3493   ChangeViewportPropertiesIfNeeded();
3494
3495   ClearField();
3496
3497   DrawCompleteVideoDisplay();
3498
3499   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3500
3501   InitGameEngine();
3502   InitGameControlValues();
3503
3504   // initialize tape actions from game when recording tape
3505   if (tape.recording)
3506   {
3507     tape.use_key_actions   = game.use_key_actions;
3508     tape.use_mouse_actions = game.use_mouse_actions;
3509   }
3510
3511   // don't play tapes over network
3512   network_playing = (network.enabled && !tape.playing);
3513
3514   for (i = 0; i < MAX_PLAYERS; i++)
3515   {
3516     struct PlayerInfo *player = &stored_player[i];
3517
3518     player->index_nr = i;
3519     player->index_bit = (1 << i);
3520     player->element_nr = EL_PLAYER_1 + i;
3521
3522     player->present = FALSE;
3523     player->active = FALSE;
3524     player->mapped = FALSE;
3525
3526     player->killed = FALSE;
3527     player->reanimated = FALSE;
3528     player->buried = FALSE;
3529
3530     player->action = 0;
3531     player->effective_action = 0;
3532     player->programmed_action = 0;
3533     player->snap_action = 0;
3534
3535     player->mouse_action.lx = 0;
3536     player->mouse_action.ly = 0;
3537     player->mouse_action.button = 0;
3538     player->mouse_action.button_hint = 0;
3539
3540     player->effective_mouse_action.lx = 0;
3541     player->effective_mouse_action.ly = 0;
3542     player->effective_mouse_action.button = 0;
3543     player->effective_mouse_action.button_hint = 0;
3544
3545     for (j = 0; j < MAX_NUM_KEYS; j++)
3546       player->key[j] = FALSE;
3547
3548     player->num_white_keys = 0;
3549
3550     player->dynabomb_count = 0;
3551     player->dynabomb_size = 1;
3552     player->dynabombs_left = 0;
3553     player->dynabomb_xl = FALSE;
3554
3555     player->MovDir = initial_move_dir;
3556     player->MovPos = 0;
3557     player->GfxPos = 0;
3558     player->GfxDir = initial_move_dir;
3559     player->GfxAction = ACTION_DEFAULT;
3560     player->Frame = 0;
3561     player->StepFrame = 0;
3562
3563     player->initial_element = player->element_nr;
3564     player->artwork_element =
3565       (level.use_artwork_element[i] ? level.artwork_element[i] :
3566        player->element_nr);
3567     player->use_murphy = FALSE;
3568
3569     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3570     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3571
3572     player->gravity = level.initial_player_gravity[i];
3573
3574     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3575
3576     player->actual_frame_counter = 0;
3577
3578     player->step_counter = 0;
3579
3580     player->last_move_dir = initial_move_dir;
3581
3582     player->is_active = FALSE;
3583
3584     player->is_waiting = FALSE;
3585     player->is_moving = FALSE;
3586     player->is_auto_moving = FALSE;
3587     player->is_digging = FALSE;
3588     player->is_snapping = FALSE;
3589     player->is_collecting = FALSE;
3590     player->is_pushing = FALSE;
3591     player->is_switching = FALSE;
3592     player->is_dropping = FALSE;
3593     player->is_dropping_pressed = FALSE;
3594
3595     player->is_bored = FALSE;
3596     player->is_sleeping = FALSE;
3597
3598     player->was_waiting = TRUE;
3599     player->was_moving = FALSE;
3600     player->was_snapping = FALSE;
3601     player->was_dropping = FALSE;
3602
3603     player->force_dropping = FALSE;
3604
3605     player->frame_counter_bored = -1;
3606     player->frame_counter_sleeping = -1;
3607
3608     player->anim_delay_counter = 0;
3609     player->post_delay_counter = 0;
3610
3611     player->dir_waiting = initial_move_dir;
3612     player->action_waiting = ACTION_DEFAULT;
3613     player->last_action_waiting = ACTION_DEFAULT;
3614     player->special_action_bored = ACTION_DEFAULT;
3615     player->special_action_sleeping = ACTION_DEFAULT;
3616
3617     player->switch_x = -1;
3618     player->switch_y = -1;
3619
3620     player->drop_x = -1;
3621     player->drop_y = -1;
3622
3623     player->show_envelope = 0;
3624
3625     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3626
3627     player->push_delay       = -1;      // initialized when pushing starts
3628     player->push_delay_value = game.initial_push_delay_value;
3629
3630     player->drop_delay = 0;
3631     player->drop_pressed_delay = 0;
3632
3633     player->last_jx = -1;
3634     player->last_jy = -1;
3635     player->jx = -1;
3636     player->jy = -1;
3637
3638     player->shield_normal_time_left = 0;
3639     player->shield_deadly_time_left = 0;
3640
3641     player->inventory_infinite_element = EL_UNDEFINED;
3642     player->inventory_size = 0;
3643
3644     if (level.use_initial_inventory[i])
3645     {
3646       for (j = 0; j < level.initial_inventory_size[i]; j++)
3647       {
3648         int element = level.initial_inventory_content[i][j];
3649         int collect_count = element_info[element].collect_count_initial;
3650         int k;
3651
3652         if (!IS_CUSTOM_ELEMENT(element))
3653           collect_count = 1;
3654
3655         if (collect_count == 0)
3656           player->inventory_infinite_element = element;
3657         else
3658           for (k = 0; k < collect_count; k++)
3659             if (player->inventory_size < MAX_INVENTORY_SIZE)
3660               player->inventory_element[player->inventory_size++] = element;
3661       }
3662     }
3663
3664     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3665     SnapField(player, 0, 0);
3666
3667     map_player_action[i] = i;
3668   }
3669
3670   network_player_action_received = FALSE;
3671
3672   // initial null action
3673   if (network_playing)
3674     SendToServer_MovePlayer(MV_NONE);
3675
3676   FrameCounter = 0;
3677   TimeFrames = 0;
3678   TimePlayed = 0;
3679   TimeLeft = level.time;
3680   TapeTime = 0;
3681
3682   ScreenMovDir = MV_NONE;
3683   ScreenMovPos = 0;
3684   ScreenGfxPos = 0;
3685
3686   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3687
3688   game.robot_wheel_x = -1;
3689   game.robot_wheel_y = -1;
3690
3691   game.exit_x = -1;
3692   game.exit_y = -1;
3693
3694   game.all_players_gone = FALSE;
3695
3696   game.LevelSolved = FALSE;
3697   game.GameOver = FALSE;
3698
3699   game.GamePlayed = !tape.playing;
3700
3701   game.LevelSolved_GameWon = FALSE;
3702   game.LevelSolved_GameEnd = FALSE;
3703   game.LevelSolved_SaveTape = FALSE;
3704   game.LevelSolved_SaveScore = FALSE;
3705
3706   game.LevelSolved_CountingTime = 0;
3707   game.LevelSolved_CountingScore = 0;
3708   game.LevelSolved_CountingHealth = 0;
3709
3710   game.panel.active = TRUE;
3711
3712   game.no_time_limit = (level.time == 0);
3713
3714   game.yamyam_content_nr = 0;
3715   game.robot_wheel_active = FALSE;
3716   game.magic_wall_active = FALSE;
3717   game.magic_wall_time_left = 0;
3718   game.light_time_left = 0;
3719   game.timegate_time_left = 0;
3720   game.switchgate_pos = 0;
3721   game.wind_direction = level.wind_direction_initial;
3722
3723   game.score = 0;
3724   game.score_final = 0;
3725
3726   game.health = MAX_HEALTH;
3727   game.health_final = MAX_HEALTH;
3728
3729   game.gems_still_needed = level.gems_needed;
3730   game.sokoban_fields_still_needed = 0;
3731   game.sokoban_objects_still_needed = 0;
3732   game.lights_still_needed = 0;
3733   game.players_still_needed = 0;
3734   game.friends_still_needed = 0;
3735
3736   game.lenses_time_left = 0;
3737   game.magnify_time_left = 0;
3738
3739   game.ball_active = level.ball_active_initial;
3740   game.ball_content_nr = 0;
3741
3742   game.explosions_delayed = TRUE;
3743
3744   game.envelope_active = FALSE;
3745
3746   for (i = 0; i < NUM_BELTS; i++)
3747   {
3748     game.belt_dir[i] = MV_NONE;
3749     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3750   }
3751
3752   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3753     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3754
3755 #if DEBUG_INIT_PLAYER
3756   DebugPrintPlayerStatus("Player status at level initialization");
3757 #endif
3758
3759   SCAN_PLAYFIELD(x, y)
3760   {
3761     Feld[x][y] = Last[x][y] = level.field[x][y];
3762     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3763     ChangeDelay[x][y] = 0;
3764     ChangePage[x][y] = -1;
3765     CustomValue[x][y] = 0;              // initialized in InitField()
3766     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3767     AmoebaNr[x][y] = 0;
3768     WasJustMoving[x][y] = 0;
3769     WasJustFalling[x][y] = 0;
3770     CheckCollision[x][y] = 0;
3771     CheckImpact[x][y] = 0;
3772     Stop[x][y] = FALSE;
3773     Pushed[x][y] = FALSE;
3774
3775     ChangeCount[x][y] = 0;
3776     ChangeEvent[x][y] = -1;
3777
3778     ExplodePhase[x][y] = 0;
3779     ExplodeDelay[x][y] = 0;
3780     ExplodeField[x][y] = EX_TYPE_NONE;
3781
3782     RunnerVisit[x][y] = 0;
3783     PlayerVisit[x][y] = 0;
3784
3785     GfxFrame[x][y] = 0;
3786     GfxRandom[x][y] = INIT_GFX_RANDOM();
3787     GfxElement[x][y] = EL_UNDEFINED;
3788     GfxAction[x][y] = ACTION_DEFAULT;
3789     GfxDir[x][y] = MV_NONE;
3790     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3791   }
3792
3793   SCAN_PLAYFIELD(x, y)
3794   {
3795     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3796       emulate_bd = FALSE;
3797     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3798       emulate_sb = FALSE;
3799     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3800       emulate_sp = FALSE;
3801
3802     InitField(x, y, TRUE);
3803
3804     ResetGfxAnimation(x, y);
3805   }
3806
3807   InitBeltMovement();
3808
3809   for (i = 0; i < MAX_PLAYERS; i++)
3810   {
3811     struct PlayerInfo *player = &stored_player[i];
3812
3813     // set number of special actions for bored and sleeping animation
3814     player->num_special_action_bored =
3815       get_num_special_action(player->artwork_element,
3816                              ACTION_BORING_1, ACTION_BORING_LAST);
3817     player->num_special_action_sleeping =
3818       get_num_special_action(player->artwork_element,
3819                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3820   }
3821
3822   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3823                     emulate_sb ? EMU_SOKOBAN :
3824                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3825
3826   // initialize type of slippery elements
3827   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3828   {
3829     if (!IS_CUSTOM_ELEMENT(i))
3830     {
3831       // default: elements slip down either to the left or right randomly
3832       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3833
3834       // SP style elements prefer to slip down on the left side
3835       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3837
3838       // BD style elements prefer to slip down on the left side
3839       if (game.emulation == EMU_BOULDERDASH)
3840         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3841     }
3842   }
3843
3844   // initialize explosion and ignition delay
3845   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3846   {
3847     if (!IS_CUSTOM_ELEMENT(i))
3848     {
3849       int num_phase = 8;
3850       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3851                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3852                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3853       int last_phase = (num_phase + 1) * delay;
3854       int half_phase = (num_phase / 2) * delay;
3855
3856       element_info[i].explosion_delay = last_phase - 1;
3857       element_info[i].ignition_delay = half_phase;
3858
3859       if (i == EL_BLACK_ORB)
3860         element_info[i].ignition_delay = 1;
3861     }
3862   }
3863
3864   // correct non-moving belts to start moving left
3865   for (i = 0; i < NUM_BELTS; i++)
3866     if (game.belt_dir[i] == MV_NONE)
3867       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3868
3869 #if USE_NEW_PLAYER_ASSIGNMENTS
3870   // use preferred player also in local single-player mode
3871   if (!network.enabled && !game.team_mode)
3872   {
3873     int new_index_nr = setup.network_player_nr;
3874
3875     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3876     {
3877       for (i = 0; i < MAX_PLAYERS; i++)
3878         stored_player[i].connected_locally = FALSE;
3879
3880       stored_player[new_index_nr].connected_locally = TRUE;
3881     }
3882   }
3883
3884   for (i = 0; i < MAX_PLAYERS; i++)
3885   {
3886     stored_player[i].connected = FALSE;
3887
3888     // in network game mode, the local player might not be the first player
3889     if (stored_player[i].connected_locally)
3890       local_player = &stored_player[i];
3891   }
3892
3893   if (!network.enabled)
3894     local_player->connected = TRUE;
3895
3896   if (tape.playing)
3897   {
3898     for (i = 0; i < MAX_PLAYERS; i++)
3899       stored_player[i].connected = tape.player_participates[i];
3900   }
3901   else if (network.enabled)
3902   {
3903     // add team mode players connected over the network (needed for correct
3904     // assignment of player figures from level to locally playing players)
3905
3906     for (i = 0; i < MAX_PLAYERS; i++)
3907       if (stored_player[i].connected_network)
3908         stored_player[i].connected = TRUE;
3909   }
3910   else if (game.team_mode)
3911   {
3912     // try to guess locally connected team mode players (needed for correct
3913     // assignment of player figures from level to locally playing players)
3914
3915     for (i = 0; i < MAX_PLAYERS; i++)
3916       if (setup.input[i].use_joystick ||
3917           setup.input[i].key.left != KSYM_UNDEFINED)
3918         stored_player[i].connected = TRUE;
3919   }
3920
3921 #if DEBUG_INIT_PLAYER
3922   DebugPrintPlayerStatus("Player status after level initialization");
3923 #endif
3924
3925 #if DEBUG_INIT_PLAYER
3926   if (options.debug)
3927     printf("Reassigning players ...\n");
3928 #endif
3929
3930   // check if any connected player was not found in playfield
3931   for (i = 0; i < MAX_PLAYERS; i++)
3932   {
3933     struct PlayerInfo *player = &stored_player[i];
3934
3935     if (player->connected && !player->present)
3936     {
3937       struct PlayerInfo *field_player = NULL;
3938
3939 #if DEBUG_INIT_PLAYER
3940       if (options.debug)
3941         printf("- looking for field player for player %d ...\n", i + 1);
3942 #endif
3943
3944       // assign first free player found that is present in the playfield
3945
3946       // first try: look for unmapped playfield player that is not connected
3947       for (j = 0; j < MAX_PLAYERS; j++)
3948         if (field_player == NULL &&
3949             stored_player[j].present &&
3950             !stored_player[j].mapped &&
3951             !stored_player[j].connected)
3952           field_player = &stored_player[j];
3953
3954       // second try: look for *any* unmapped playfield player
3955       for (j = 0; j < MAX_PLAYERS; j++)
3956         if (field_player == NULL &&
3957             stored_player[j].present &&
3958             !stored_player[j].mapped)
3959           field_player = &stored_player[j];
3960
3961       if (field_player != NULL)
3962       {
3963         int jx = field_player->jx, jy = field_player->jy;
3964
3965 #if DEBUG_INIT_PLAYER
3966         if (options.debug)
3967           printf("- found player %d\n", field_player->index_nr + 1);
3968 #endif
3969
3970         player->present = FALSE;
3971         player->active = FALSE;
3972
3973         field_player->present = TRUE;
3974         field_player->active = TRUE;
3975
3976         /*
3977         player->initial_element = field_player->initial_element;
3978         player->artwork_element = field_player->artwork_element;
3979
3980         player->block_last_field       = field_player->block_last_field;
3981         player->block_delay_adjustment = field_player->block_delay_adjustment;
3982         */
3983
3984         StorePlayer[jx][jy] = field_player->element_nr;
3985
3986         field_player->jx = field_player->last_jx = jx;
3987         field_player->jy = field_player->last_jy = jy;
3988
3989         if (local_player == player)
3990           local_player = field_player;
3991
3992         map_player_action[field_player->index_nr] = i;
3993
3994         field_player->mapped = TRUE;
3995
3996 #if DEBUG_INIT_PLAYER
3997         if (options.debug)
3998           printf("- map_player_action[%d] == %d\n",
3999                  field_player->index_nr + 1, i + 1);
4000 #endif
4001       }
4002     }
4003
4004     if (player->connected && player->present)
4005       player->mapped = TRUE;
4006   }
4007
4008 #if DEBUG_INIT_PLAYER
4009   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4010 #endif
4011
4012 #else
4013
4014   // check if any connected player was not found in playfield
4015   for (i = 0; i < MAX_PLAYERS; i++)
4016   {
4017     struct PlayerInfo *player = &stored_player[i];
4018
4019     if (player->connected && !player->present)
4020     {
4021       for (j = 0; j < MAX_PLAYERS; j++)
4022       {
4023         struct PlayerInfo *field_player = &stored_player[j];
4024         int jx = field_player->jx, jy = field_player->jy;
4025
4026         // assign first free player found that is present in the playfield
4027         if (field_player->present && !field_player->connected)
4028         {
4029           player->present = TRUE;
4030           player->active = TRUE;
4031
4032           field_player->present = FALSE;
4033           field_player->active = FALSE;
4034
4035           player->initial_element = field_player->initial_element;
4036           player->artwork_element = field_player->artwork_element;
4037
4038           player->block_last_field       = field_player->block_last_field;
4039           player->block_delay_adjustment = field_player->block_delay_adjustment;
4040
4041           StorePlayer[jx][jy] = player->element_nr;
4042
4043           player->jx = player->last_jx = jx;
4044           player->jy = player->last_jy = jy;
4045
4046           break;
4047         }
4048       }
4049     }
4050   }
4051 #endif
4052
4053 #if 0
4054   printf("::: local_player->present == %d\n", local_player->present);
4055 #endif
4056
4057   // set focus to local player for network games, else to all players
4058   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4059   game.centered_player_nr_next = game.centered_player_nr;
4060   game.set_centered_player = FALSE;
4061   game.set_centered_player_wrap = FALSE;
4062
4063   if (network_playing && tape.recording)
4064   {
4065     // store client dependent player focus when recording network games
4066     tape.centered_player_nr_next = game.centered_player_nr_next;
4067     tape.set_centered_player = TRUE;
4068   }
4069
4070   if (tape.playing)
4071   {
4072     // when playing a tape, eliminate all players who do not participate
4073
4074 #if USE_NEW_PLAYER_ASSIGNMENTS
4075
4076     if (!game.team_mode)
4077     {
4078       for (i = 0; i < MAX_PLAYERS; i++)
4079       {
4080         if (stored_player[i].active &&
4081             !tape.player_participates[map_player_action[i]])
4082         {
4083           struct PlayerInfo *player = &stored_player[i];
4084           int jx = player->jx, jy = player->jy;
4085
4086 #if DEBUG_INIT_PLAYER
4087           if (options.debug)
4088             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4089 #endif
4090
4091           player->active = FALSE;
4092           StorePlayer[jx][jy] = 0;
4093           Feld[jx][jy] = EL_EMPTY;
4094         }
4095       }
4096     }
4097
4098 #else
4099
4100     for (i = 0; i < MAX_PLAYERS; i++)
4101     {
4102       if (stored_player[i].active &&
4103           !tape.player_participates[i])
4104       {
4105         struct PlayerInfo *player = &stored_player[i];
4106         int jx = player->jx, jy = player->jy;
4107
4108         player->active = FALSE;
4109         StorePlayer[jx][jy] = 0;
4110         Feld[jx][jy] = EL_EMPTY;
4111       }
4112     }
4113 #endif
4114   }
4115   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4116   {
4117     // when in single player mode, eliminate all but the local player
4118
4119     for (i = 0; i < MAX_PLAYERS; i++)
4120     {
4121       struct PlayerInfo *player = &stored_player[i];
4122
4123       if (player->active && player != local_player)
4124       {
4125         int jx = player->jx, jy = player->jy;
4126
4127         player->active = FALSE;
4128         player->present = FALSE;
4129
4130         StorePlayer[jx][jy] = 0;
4131         Feld[jx][jy] = EL_EMPTY;
4132       }
4133     }
4134   }
4135
4136   for (i = 0; i < MAX_PLAYERS; i++)
4137     if (stored_player[i].active)
4138       game.players_still_needed++;
4139
4140   if (level.solved_by_one_player)
4141     game.players_still_needed = 1;
4142
4143   // when recording the game, store which players take part in the game
4144   if (tape.recording)
4145   {
4146 #if USE_NEW_PLAYER_ASSIGNMENTS
4147     for (i = 0; i < MAX_PLAYERS; i++)
4148       if (stored_player[i].connected)
4149         tape.player_participates[i] = TRUE;
4150 #else
4151     for (i = 0; i < MAX_PLAYERS; i++)
4152       if (stored_player[i].active)
4153         tape.player_participates[i] = TRUE;
4154 #endif
4155   }
4156
4157 #if DEBUG_INIT_PLAYER
4158   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4159 #endif
4160
4161   if (BorderElement == EL_EMPTY)
4162   {
4163     SBX_Left = 0;
4164     SBX_Right = lev_fieldx - SCR_FIELDX;
4165     SBY_Upper = 0;
4166     SBY_Lower = lev_fieldy - SCR_FIELDY;
4167   }
4168   else
4169   {
4170     SBX_Left = -1;
4171     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4172     SBY_Upper = -1;
4173     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4174   }
4175
4176   if (full_lev_fieldx <= SCR_FIELDX)
4177     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4178   if (full_lev_fieldy <= SCR_FIELDY)
4179     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4180
4181   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4182     SBX_Left--;
4183   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4184     SBY_Upper--;
4185
4186   // if local player not found, look for custom element that might create
4187   // the player (make some assumptions about the right custom element)
4188   if (!local_player->present)
4189   {
4190     int start_x = 0, start_y = 0;
4191     int found_rating = 0;
4192     int found_element = EL_UNDEFINED;
4193     int player_nr = local_player->index_nr;
4194
4195     SCAN_PLAYFIELD(x, y)
4196     {
4197       int element = Feld[x][y];
4198       int content;
4199       int xx, yy;
4200       boolean is_player;
4201
4202       if (level.use_start_element[player_nr] &&
4203           level.start_element[player_nr] == element &&
4204           found_rating < 4)
4205       {
4206         start_x = x;
4207         start_y = y;
4208
4209         found_rating = 4;
4210         found_element = element;
4211       }
4212
4213       if (!IS_CUSTOM_ELEMENT(element))
4214         continue;
4215
4216       if (CAN_CHANGE(element))
4217       {
4218         for (i = 0; i < element_info[element].num_change_pages; i++)
4219         {
4220           // check for player created from custom element as single target
4221           content = element_info[element].change_page[i].target_element;
4222           is_player = ELEM_IS_PLAYER(content);
4223
4224           if (is_player && (found_rating < 3 ||
4225                             (found_rating == 3 && element < found_element)))
4226           {
4227             start_x = x;
4228             start_y = y;
4229
4230             found_rating = 3;
4231             found_element = element;
4232           }
4233         }
4234       }
4235
4236       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4237       {
4238         // check for player created from custom element as explosion content
4239         content = element_info[element].content.e[xx][yy];
4240         is_player = ELEM_IS_PLAYER(content);
4241
4242         if (is_player && (found_rating < 2 ||
4243                           (found_rating == 2 && element < found_element)))
4244         {
4245           start_x = x + xx - 1;
4246           start_y = y + yy - 1;
4247
4248           found_rating = 2;
4249           found_element = element;
4250         }
4251
4252         if (!CAN_CHANGE(element))
4253           continue;
4254
4255         for (i = 0; i < element_info[element].num_change_pages; i++)
4256         {
4257           // check for player created from custom element as extended target
4258           content =
4259             element_info[element].change_page[i].target_content.e[xx][yy];
4260
4261           is_player = ELEM_IS_PLAYER(content);
4262
4263           if (is_player && (found_rating < 1 ||
4264                             (found_rating == 1 && element < found_element)))
4265           {
4266             start_x = x + xx - 1;
4267             start_y = y + yy - 1;
4268
4269             found_rating = 1;
4270             found_element = element;
4271           }
4272         }
4273       }
4274     }
4275
4276     scroll_x = SCROLL_POSITION_X(start_x);
4277     scroll_y = SCROLL_POSITION_Y(start_y);
4278   }
4279   else
4280   {
4281     scroll_x = SCROLL_POSITION_X(local_player->jx);
4282     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4283   }
4284
4285   // !!! FIX THIS (START) !!!
4286   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4287   {
4288     InitGameEngine_EM();
4289   }
4290   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4291   {
4292     InitGameEngine_SP();
4293   }
4294   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4295   {
4296     InitGameEngine_MM();
4297   }
4298   else
4299   {
4300     DrawLevel(REDRAW_FIELD);
4301     DrawAllPlayers();
4302
4303     // after drawing the level, correct some elements
4304     if (game.timegate_time_left == 0)
4305       CloseAllOpenTimegates();
4306   }
4307
4308   // blit playfield from scroll buffer to normal back buffer for fading in
4309   BlitScreenToBitmap(backbuffer);
4310   // !!! FIX THIS (END) !!!
4311
4312   DrawMaskedBorder(fade_mask);
4313
4314   FadeIn(fade_mask);
4315
4316 #if 1
4317   // full screen redraw is required at this point in the following cases:
4318   // - special editor door undrawn when game was started from level editor
4319   // - drawing area (playfield) was changed and has to be removed completely
4320   redraw_mask = REDRAW_ALL;
4321   BackToFront();
4322 #endif
4323
4324   if (!game.restart_level)
4325   {
4326     // copy default game door content to main double buffer
4327
4328     // !!! CHECK AGAIN !!!
4329     SetPanelBackground();
4330     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4331     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4332   }
4333
4334   SetPanelBackground();
4335   SetDrawBackgroundMask(REDRAW_DOOR_1);
4336
4337   UpdateAndDisplayGameControlValues();
4338
4339   if (!game.restart_level)
4340   {
4341     UnmapGameButtons();
4342     UnmapTapeButtons();
4343
4344     FreeGameButtons();
4345     CreateGameButtons();
4346
4347     MapGameButtons();
4348     MapTapeButtons();
4349
4350     // copy actual game door content to door double buffer for OpenDoor()
4351     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4352
4353     OpenDoor(DOOR_OPEN_ALL);
4354
4355     KeyboardAutoRepeatOffUnlessAutoplay();
4356
4357 #if DEBUG_INIT_PLAYER
4358     DebugPrintPlayerStatus("Player status (final)");
4359 #endif
4360   }
4361
4362   UnmapAllGadgets();
4363
4364   MapGameButtons();
4365   MapTapeButtons();
4366
4367   if (!game.restart_level && !tape.playing)
4368   {
4369     LevelStats_incPlayed(level_nr);
4370
4371     SaveLevelSetup_SeriesInfo();
4372   }
4373
4374   game.restart_level = FALSE;
4375   game.restart_game_message = NULL;
4376   game.request_active = FALSE;
4377
4378   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4379     InitGameActions_MM();
4380
4381   SaveEngineSnapshotToListInitial();
4382
4383   if (!game.restart_level)
4384   {
4385     PlaySound(SND_GAME_STARTING);
4386
4387     if (setup.sound_music)
4388       PlayLevelMusic();
4389   }
4390 }
4391
4392 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4393                         int actual_player_x, int actual_player_y)
4394 {
4395   // this is used for non-R'n'D game engines to update certain engine values
4396
4397   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4398   {
4399     actual_player_x = correctLevelPosX_EM(actual_player_x);
4400     actual_player_y = correctLevelPosY_EM(actual_player_y);
4401   }
4402
4403   // needed to determine if sounds are played within the visible screen area
4404   scroll_x = actual_scroll_x;
4405   scroll_y = actual_scroll_y;
4406
4407   // needed to get player position for "follow finger" playing input method
4408   local_player->jx = actual_player_x;
4409   local_player->jy = actual_player_y;
4410 }
4411
4412 void InitMovDir(int x, int y)
4413 {
4414   int i, element = Feld[x][y];
4415   static int xy[4][2] =
4416   {
4417     {  0, +1 },
4418     { +1,  0 },
4419     {  0, -1 },
4420     { -1,  0 }
4421   };
4422   static int direction[3][4] =
4423   {
4424     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4425     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4426     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4427   };
4428
4429   switch (element)
4430   {
4431     case EL_BUG_RIGHT:
4432     case EL_BUG_UP:
4433     case EL_BUG_LEFT:
4434     case EL_BUG_DOWN:
4435       Feld[x][y] = EL_BUG;
4436       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4437       break;
4438
4439     case EL_SPACESHIP_RIGHT:
4440     case EL_SPACESHIP_UP:
4441     case EL_SPACESHIP_LEFT:
4442     case EL_SPACESHIP_DOWN:
4443       Feld[x][y] = EL_SPACESHIP;
4444       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4445       break;
4446
4447     case EL_BD_BUTTERFLY_RIGHT:
4448     case EL_BD_BUTTERFLY_UP:
4449     case EL_BD_BUTTERFLY_LEFT:
4450     case EL_BD_BUTTERFLY_DOWN:
4451       Feld[x][y] = EL_BD_BUTTERFLY;
4452       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4453       break;
4454
4455     case EL_BD_FIREFLY_RIGHT:
4456     case EL_BD_FIREFLY_UP:
4457     case EL_BD_FIREFLY_LEFT:
4458     case EL_BD_FIREFLY_DOWN:
4459       Feld[x][y] = EL_BD_FIREFLY;
4460       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4461       break;
4462
4463     case EL_PACMAN_RIGHT:
4464     case EL_PACMAN_UP:
4465     case EL_PACMAN_LEFT:
4466     case EL_PACMAN_DOWN:
4467       Feld[x][y] = EL_PACMAN;
4468       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4469       break;
4470
4471     case EL_YAMYAM_LEFT:
4472     case EL_YAMYAM_RIGHT:
4473     case EL_YAMYAM_UP:
4474     case EL_YAMYAM_DOWN:
4475       Feld[x][y] = EL_YAMYAM;
4476       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4477       break;
4478
4479     case EL_SP_SNIKSNAK:
4480       MovDir[x][y] = MV_UP;
4481       break;
4482
4483     case EL_SP_ELECTRON:
4484       MovDir[x][y] = MV_LEFT;
4485       break;
4486
4487     case EL_MOLE_LEFT:
4488     case EL_MOLE_RIGHT:
4489     case EL_MOLE_UP:
4490     case EL_MOLE_DOWN:
4491       Feld[x][y] = EL_MOLE;
4492       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4493       break;
4494
4495     default:
4496       if (IS_CUSTOM_ELEMENT(element))
4497       {
4498         struct ElementInfo *ei = &element_info[element];
4499         int move_direction_initial = ei->move_direction_initial;
4500         int move_pattern = ei->move_pattern;
4501
4502         if (move_direction_initial == MV_START_PREVIOUS)
4503         {
4504           if (MovDir[x][y] != MV_NONE)
4505             return;
4506
4507           move_direction_initial = MV_START_AUTOMATIC;
4508         }
4509
4510         if (move_direction_initial == MV_START_RANDOM)
4511           MovDir[x][y] = 1 << RND(4);
4512         else if (move_direction_initial & MV_ANY_DIRECTION)
4513           MovDir[x][y] = move_direction_initial;
4514         else if (move_pattern == MV_ALL_DIRECTIONS ||
4515                  move_pattern == MV_TURNING_LEFT ||
4516                  move_pattern == MV_TURNING_RIGHT ||
4517                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4518                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4519                  move_pattern == MV_TURNING_RANDOM)
4520           MovDir[x][y] = 1 << RND(4);
4521         else if (move_pattern == MV_HORIZONTAL)
4522           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4523         else if (move_pattern == MV_VERTICAL)
4524           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4525         else if (move_pattern & MV_ANY_DIRECTION)
4526           MovDir[x][y] = element_info[element].move_pattern;
4527         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4528                  move_pattern == MV_ALONG_RIGHT_SIDE)
4529         {
4530           // use random direction as default start direction
4531           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4532             MovDir[x][y] = 1 << RND(4);
4533
4534           for (i = 0; i < NUM_DIRECTIONS; i++)
4535           {
4536             int x1 = x + xy[i][0];
4537             int y1 = y + xy[i][1];
4538
4539             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4540             {
4541               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4542                 MovDir[x][y] = direction[0][i];
4543               else
4544                 MovDir[x][y] = direction[1][i];
4545
4546               break;
4547             }
4548           }
4549         }                
4550       }
4551       else
4552       {
4553         MovDir[x][y] = 1 << RND(4);
4554
4555         if (element != EL_BUG &&
4556             element != EL_SPACESHIP &&
4557             element != EL_BD_BUTTERFLY &&
4558             element != EL_BD_FIREFLY)
4559           break;
4560
4561         for (i = 0; i < NUM_DIRECTIONS; i++)
4562         {
4563           int x1 = x + xy[i][0];
4564           int y1 = y + xy[i][1];
4565
4566           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4567           {
4568             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4569             {
4570               MovDir[x][y] = direction[0][i];
4571               break;
4572             }
4573             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4574                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4575             {
4576               MovDir[x][y] = direction[1][i];
4577               break;
4578             }
4579           }
4580         }
4581       }
4582       break;
4583   }
4584
4585   GfxDir[x][y] = MovDir[x][y];
4586 }
4587
4588 void InitAmoebaNr(int x, int y)
4589 {
4590   int i;
4591   int group_nr = AmoebeNachbarNr(x, y);
4592
4593   if (group_nr == 0)
4594   {
4595     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4596     {
4597       if (AmoebaCnt[i] == 0)
4598       {
4599         group_nr = i;
4600         break;
4601       }
4602     }
4603   }
4604
4605   AmoebaNr[x][y] = group_nr;
4606   AmoebaCnt[group_nr]++;
4607   AmoebaCnt2[group_nr]++;
4608 }
4609
4610 static void LevelSolved(void)
4611 {
4612   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4613       game.players_still_needed > 0)
4614     return;
4615
4616   game.LevelSolved = TRUE;
4617   game.GameOver = TRUE;
4618
4619   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4620                       game_em.lev->score :
4621                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4622                       game_mm.score :
4623                       game.score);
4624   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4625                        MM_HEALTH(game_mm.laser_overload_value) :
4626                        game.health);
4627
4628   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4629   game.LevelSolved_CountingScore = game.score_final;
4630   game.LevelSolved_CountingHealth = game.health_final;
4631 }
4632
4633 void GameWon(void)
4634 {
4635   static int time_count_steps;
4636   static int time, time_final;
4637   static int score, score_final;
4638   static int health, health_final;
4639   static int game_over_delay_1 = 0;
4640   static int game_over_delay_2 = 0;
4641   static int game_over_delay_3 = 0;
4642   int game_over_delay_value_1 = 50;
4643   int game_over_delay_value_2 = 25;
4644   int game_over_delay_value_3 = 50;
4645
4646   if (!game.LevelSolved_GameWon)
4647   {
4648     int i;
4649
4650     // do not start end game actions before the player stops moving (to exit)
4651     if (local_player->active && local_player->MovPos)
4652       return;
4653
4654     game.LevelSolved_GameWon = TRUE;
4655     game.LevelSolved_SaveTape = tape.recording;
4656     game.LevelSolved_SaveScore = !tape.playing;
4657
4658     if (!tape.playing)
4659     {
4660       LevelStats_incSolved(level_nr);
4661
4662       SaveLevelSetup_SeriesInfo();
4663     }
4664
4665     if (tape.auto_play)         // tape might already be stopped here
4666       tape.auto_play_level_solved = TRUE;
4667
4668     TapeStop();
4669
4670     game_over_delay_1 = 0;
4671     game_over_delay_2 = 0;
4672     game_over_delay_3 = game_over_delay_value_3;
4673
4674     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4675     score = score_final = game.score_final;
4676     health = health_final = game.health_final;
4677
4678     if (level.score[SC_TIME_BONUS] > 0)
4679     {
4680       if (TimeLeft > 0)
4681       {
4682         time_final = 0;
4683         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4684       }
4685       else if (game.no_time_limit && TimePlayed < 999)
4686       {
4687         time_final = 999;
4688         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4689       }
4690
4691       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4692
4693       game_over_delay_1 = game_over_delay_value_1;
4694
4695       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4696       {
4697         health_final = 0;
4698         score_final += health * level.score[SC_TIME_BONUS];
4699
4700         game_over_delay_2 = game_over_delay_value_2;
4701       }
4702
4703       game.score_final = score_final;
4704       game.health_final = health_final;
4705     }
4706
4707     if (level_editor_test_game)
4708     {
4709       time = time_final;
4710       score = score_final;
4711
4712       game.LevelSolved_CountingTime = time;
4713       game.LevelSolved_CountingScore = score;
4714
4715       game_panel_controls[GAME_PANEL_TIME].value = time;
4716       game_panel_controls[GAME_PANEL_SCORE].value = score;
4717
4718       DisplayGameControlValues();
4719     }
4720
4721     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4722     {
4723       // check if last player has left the level
4724       if (game.exit_x >= 0 &&
4725           game.exit_y >= 0)
4726       {
4727         int x = game.exit_x;
4728         int y = game.exit_y;
4729         int element = Feld[x][y];
4730
4731         // close exit door after last player
4732         if ((game.all_players_gone &&
4733              (element == EL_EXIT_OPEN ||
4734               element == EL_SP_EXIT_OPEN ||
4735               element == EL_STEEL_EXIT_OPEN)) ||
4736             element == EL_EM_EXIT_OPEN ||
4737             element == EL_EM_STEEL_EXIT_OPEN)
4738         {
4739
4740           Feld[x][y] =
4741             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4742              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4743              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4744              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4745              EL_EM_STEEL_EXIT_CLOSING);
4746
4747           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4748         }
4749
4750         // player disappears
4751         DrawLevelField(x, y);
4752       }
4753
4754       for (i = 0; i < MAX_PLAYERS; i++)
4755       {
4756         struct PlayerInfo *player = &stored_player[i];
4757
4758         if (player->present)
4759         {
4760           RemovePlayer(player);
4761
4762           // player disappears
4763           DrawLevelField(player->jx, player->jy);
4764         }
4765       }
4766     }
4767
4768     PlaySound(SND_GAME_WINNING);
4769   }
4770
4771   if (game_over_delay_1 > 0)
4772   {
4773     game_over_delay_1--;
4774
4775     return;
4776   }
4777
4778   if (time != time_final)
4779   {
4780     int time_to_go = ABS(time_final - time);
4781     int time_count_dir = (time < time_final ? +1 : -1);
4782
4783     if (time_to_go < time_count_steps)
4784       time_count_steps = 1;
4785
4786     time  += time_count_steps * time_count_dir;
4787     score += time_count_steps * level.score[SC_TIME_BONUS];
4788
4789     game.LevelSolved_CountingTime = time;
4790     game.LevelSolved_CountingScore = score;
4791
4792     game_panel_controls[GAME_PANEL_TIME].value = time;
4793     game_panel_controls[GAME_PANEL_SCORE].value = score;
4794
4795     DisplayGameControlValues();
4796
4797     if (time == time_final)
4798       StopSound(SND_GAME_LEVELTIME_BONUS);
4799     else if (setup.sound_loops)
4800       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4801     else
4802       PlaySound(SND_GAME_LEVELTIME_BONUS);
4803
4804     return;
4805   }
4806
4807   if (game_over_delay_2 > 0)
4808   {
4809     game_over_delay_2--;
4810
4811     return;
4812   }
4813
4814   if (health != health_final)
4815   {
4816     int health_count_dir = (health < health_final ? +1 : -1);
4817
4818     health += health_count_dir;
4819     score  += level.score[SC_TIME_BONUS];
4820
4821     game.LevelSolved_CountingHealth = health;
4822     game.LevelSolved_CountingScore = score;
4823
4824     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4825     game_panel_controls[GAME_PANEL_SCORE].value = score;
4826
4827     DisplayGameControlValues();
4828
4829     if (health == health_final)
4830       StopSound(SND_GAME_LEVELTIME_BONUS);
4831     else if (setup.sound_loops)
4832       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4833     else
4834       PlaySound(SND_GAME_LEVELTIME_BONUS);
4835
4836     return;
4837   }
4838
4839   game.panel.active = FALSE;
4840
4841   if (game_over_delay_3 > 0)
4842   {
4843     game_over_delay_3--;
4844
4845     return;
4846   }
4847
4848   GameEnd();
4849 }
4850
4851 void GameEnd(void)
4852 {
4853   // used instead of "level_nr" (needed for network games)
4854   int last_level_nr = levelset.level_nr;
4855   int hi_pos;
4856
4857   game.LevelSolved_GameEnd = TRUE;
4858
4859   if (game.LevelSolved_SaveTape)
4860   {
4861     // make sure that request dialog to save tape does not open door again
4862     if (!global.use_envelope_request)
4863       CloseDoor(DOOR_CLOSE_1);
4864
4865     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4866   }
4867
4868   // if no tape is to be saved, close both doors simultaneously
4869   CloseDoor(DOOR_CLOSE_ALL);
4870
4871   if (level_editor_test_game)
4872   {
4873     SetGameStatus(GAME_MODE_MAIN);
4874
4875     DrawMainMenu();
4876
4877     return;
4878   }
4879
4880   if (!game.LevelSolved_SaveScore)
4881   {
4882     SetGameStatus(GAME_MODE_MAIN);
4883
4884     DrawMainMenu();
4885
4886     return;
4887   }
4888
4889   if (level_nr == leveldir_current->handicap_level)
4890   {
4891     leveldir_current->handicap_level++;
4892
4893     SaveLevelSetup_SeriesInfo();
4894   }
4895
4896   if (setup.increment_levels &&
4897       level_nr < leveldir_current->last_level &&
4898       !network_playing)
4899   {
4900     level_nr++;         // advance to next level
4901     TapeErase();        // start with empty tape
4902
4903     if (setup.auto_play_next_level)
4904     {
4905       LoadLevel(level_nr);
4906
4907       SaveLevelSetup_SeriesInfo();
4908     }
4909   }
4910
4911   hi_pos = NewHiScore(last_level_nr);
4912
4913   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4914   {
4915     SetGameStatus(GAME_MODE_SCORES);
4916
4917     DrawHallOfFame(last_level_nr, hi_pos);
4918   }
4919   else if (setup.auto_play_next_level && setup.increment_levels &&
4920            last_level_nr < leveldir_current->last_level &&
4921            !network_playing)
4922   {
4923     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4924   }
4925   else
4926   {
4927     SetGameStatus(GAME_MODE_MAIN);
4928
4929     DrawMainMenu();
4930   }
4931 }
4932
4933 int NewHiScore(int level_nr)
4934 {
4935   int k, l;
4936   int position = -1;
4937   boolean one_score_entry_per_name = !program.many_scores_per_name;
4938
4939   LoadScore(level_nr);
4940
4941   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4942       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4943     return -1;
4944
4945   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4946   {
4947     if (game.score_final > highscore[k].Score)
4948     {
4949       // player has made it to the hall of fame
4950
4951       if (k < MAX_SCORE_ENTRIES - 1)
4952       {
4953         int m = MAX_SCORE_ENTRIES - 1;
4954
4955         if (one_score_entry_per_name)
4956         {
4957           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4958             if (strEqual(setup.player_name, highscore[l].Name))
4959               m = l;
4960
4961           if (m == k)   // player's new highscore overwrites his old one
4962             goto put_into_list;
4963         }
4964
4965         for (l = m; l > k; l--)
4966         {
4967           strcpy(highscore[l].Name, highscore[l - 1].Name);
4968           highscore[l].Score = highscore[l - 1].Score;
4969         }
4970       }
4971
4972       put_into_list:
4973
4974       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4975       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4976       highscore[k].Score = game.score_final;
4977       position = k;
4978
4979       break;
4980     }
4981     else if (one_score_entry_per_name &&
4982              !strncmp(setup.player_name, highscore[k].Name,
4983                       MAX_PLAYER_NAME_LEN))
4984       break;    // player already there with a higher score
4985   }
4986
4987   if (position >= 0) 
4988     SaveScore(level_nr);
4989
4990   return position;
4991 }
4992
4993 static int getElementMoveStepsizeExt(int x, int y, int direction)
4994 {
4995   int element = Feld[x][y];
4996   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4997   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4998   int horiz_move = (dx != 0);
4999   int sign = (horiz_move ? dx : dy);
5000   int step = sign * element_info[element].move_stepsize;
5001
5002   // special values for move stepsize for spring and things on conveyor belt
5003   if (horiz_move)
5004   {
5005     if (CAN_FALL(element) &&
5006         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5007       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5008     else if (element == EL_SPRING)
5009       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5010   }
5011
5012   return step;
5013 }
5014
5015 static int getElementMoveStepsize(int x, int y)
5016 {
5017   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5018 }
5019
5020 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5021 {
5022   if (player->GfxAction != action || player->GfxDir != dir)
5023   {
5024     player->GfxAction = action;
5025     player->GfxDir = dir;
5026     player->Frame = 0;
5027     player->StepFrame = 0;
5028   }
5029 }
5030
5031 static void ResetGfxFrame(int x, int y)
5032 {
5033   // profiling showed that "autotest" spends 10~20% of its time in this function
5034   if (DrawingDeactivatedField())
5035     return;
5036
5037   int element = Feld[x][y];
5038   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5039
5040   if (graphic_info[graphic].anim_global_sync)
5041     GfxFrame[x][y] = FrameCounter;
5042   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5043     GfxFrame[x][y] = CustomValue[x][y];
5044   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5045     GfxFrame[x][y] = element_info[element].collect_score;
5046   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5047     GfxFrame[x][y] = ChangeDelay[x][y];
5048 }
5049
5050 static void ResetGfxAnimation(int x, int y)
5051 {
5052   GfxAction[x][y] = ACTION_DEFAULT;
5053   GfxDir[x][y] = MovDir[x][y];
5054   GfxFrame[x][y] = 0;
5055
5056   ResetGfxFrame(x, y);
5057 }
5058
5059 static void ResetRandomAnimationValue(int x, int y)
5060 {
5061   GfxRandom[x][y] = INIT_GFX_RANDOM();
5062 }
5063
5064 static void InitMovingField(int x, int y, int direction)
5065 {
5066   int element = Feld[x][y];
5067   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5068   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5069   int newx = x + dx;
5070   int newy = y + dy;
5071   boolean is_moving_before, is_moving_after;
5072
5073   // check if element was/is moving or being moved before/after mode change
5074   is_moving_before = (WasJustMoving[x][y] != 0);
5075   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5076
5077   // reset animation only for moving elements which change direction of moving
5078   // or which just started or stopped moving
5079   // (else CEs with property "can move" / "not moving" are reset each frame)
5080   if (is_moving_before != is_moving_after ||
5081       direction != MovDir[x][y])
5082     ResetGfxAnimation(x, y);
5083
5084   MovDir[x][y] = direction;
5085   GfxDir[x][y] = direction;
5086
5087   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5088                      direction == MV_DOWN && CAN_FALL(element) ?
5089                      ACTION_FALLING : ACTION_MOVING);
5090
5091   // this is needed for CEs with property "can move" / "not moving"
5092
5093   if (is_moving_after)
5094   {
5095     if (Feld[newx][newy] == EL_EMPTY)
5096       Feld[newx][newy] = EL_BLOCKED;
5097
5098     MovDir[newx][newy] = MovDir[x][y];
5099
5100     CustomValue[newx][newy] = CustomValue[x][y];
5101
5102     GfxFrame[newx][newy] = GfxFrame[x][y];
5103     GfxRandom[newx][newy] = GfxRandom[x][y];
5104     GfxAction[newx][newy] = GfxAction[x][y];
5105     GfxDir[newx][newy] = GfxDir[x][y];
5106   }
5107 }
5108
5109 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5110 {
5111   int direction = MovDir[x][y];
5112   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5113   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5114
5115   *goes_to_x = newx;
5116   *goes_to_y = newy;
5117 }
5118
5119 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5120 {
5121   int oldx = x, oldy = y;
5122   int direction = MovDir[x][y];
5123
5124   if (direction == MV_LEFT)
5125     oldx++;
5126   else if (direction == MV_RIGHT)
5127     oldx--;
5128   else if (direction == MV_UP)
5129     oldy++;
5130   else if (direction == MV_DOWN)
5131     oldy--;
5132
5133   *comes_from_x = oldx;
5134   *comes_from_y = oldy;
5135 }
5136
5137 static int MovingOrBlocked2Element(int x, int y)
5138 {
5139   int element = Feld[x][y];
5140
5141   if (element == EL_BLOCKED)
5142   {
5143     int oldx, oldy;
5144
5145     Blocked2Moving(x, y, &oldx, &oldy);
5146     return Feld[oldx][oldy];
5147   }
5148   else
5149     return element;
5150 }
5151
5152 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5153 {
5154   // like MovingOrBlocked2Element(), but if element is moving
5155   // and (x,y) is the field the moving element is just leaving,
5156   // return EL_BLOCKED instead of the element value
5157   int element = Feld[x][y];
5158
5159   if (IS_MOVING(x, y))
5160   {
5161     if (element == EL_BLOCKED)
5162     {
5163       int oldx, oldy;
5164
5165       Blocked2Moving(x, y, &oldx, &oldy);
5166       return Feld[oldx][oldy];
5167     }
5168     else
5169       return EL_BLOCKED;
5170   }
5171   else
5172     return element;
5173 }
5174
5175 static void RemoveField(int x, int y)
5176 {
5177   Feld[x][y] = EL_EMPTY;
5178
5179   MovPos[x][y] = 0;
5180   MovDir[x][y] = 0;
5181   MovDelay[x][y] = 0;
5182
5183   CustomValue[x][y] = 0;
5184
5185   AmoebaNr[x][y] = 0;
5186   ChangeDelay[x][y] = 0;
5187   ChangePage[x][y] = -1;
5188   Pushed[x][y] = FALSE;
5189
5190   GfxElement[x][y] = EL_UNDEFINED;
5191   GfxAction[x][y] = ACTION_DEFAULT;
5192   GfxDir[x][y] = MV_NONE;
5193 }
5194
5195 static void RemoveMovingField(int x, int y)
5196 {
5197   int oldx = x, oldy = y, newx = x, newy = y;
5198   int element = Feld[x][y];
5199   int next_element = EL_UNDEFINED;
5200
5201   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5202     return;
5203
5204   if (IS_MOVING(x, y))
5205   {
5206     Moving2Blocked(x, y, &newx, &newy);
5207
5208     if (Feld[newx][newy] != EL_BLOCKED)
5209     {
5210       // element is moving, but target field is not free (blocked), but
5211       // already occupied by something different (example: acid pool);
5212       // in this case, only remove the moving field, but not the target
5213
5214       RemoveField(oldx, oldy);
5215
5216       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5217
5218       TEST_DrawLevelField(oldx, oldy);
5219
5220       return;
5221     }
5222   }
5223   else if (element == EL_BLOCKED)
5224   {
5225     Blocked2Moving(x, y, &oldx, &oldy);
5226     if (!IS_MOVING(oldx, oldy))
5227       return;
5228   }
5229
5230   if (element == EL_BLOCKED &&
5231       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5232        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5233        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5234        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5235        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5236        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5237     next_element = get_next_element(Feld[oldx][oldy]);
5238
5239   RemoveField(oldx, oldy);
5240   RemoveField(newx, newy);
5241
5242   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5243
5244   if (next_element != EL_UNDEFINED)
5245     Feld[oldx][oldy] = next_element;
5246
5247   TEST_DrawLevelField(oldx, oldy);
5248   TEST_DrawLevelField(newx, newy);
5249 }
5250
5251 void DrawDynamite(int x, int y)
5252 {
5253   int sx = SCREENX(x), sy = SCREENY(y);
5254   int graphic = el2img(Feld[x][y]);
5255   int frame;
5256
5257   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5258     return;
5259
5260   if (IS_WALKABLE_INSIDE(Back[x][y]))
5261     return;
5262
5263   if (Back[x][y])
5264     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5265   else if (Store[x][y])
5266     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5267
5268   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5269
5270   if (Back[x][y] || Store[x][y])
5271     DrawGraphicThruMask(sx, sy, graphic, frame);
5272   else
5273     DrawGraphic(sx, sy, graphic, frame);
5274 }
5275
5276 static void CheckDynamite(int x, int y)
5277 {
5278   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5279   {
5280     MovDelay[x][y]--;
5281
5282     if (MovDelay[x][y] != 0)
5283     {
5284       DrawDynamite(x, y);
5285       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5286
5287       return;
5288     }
5289   }
5290
5291   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5292
5293   Bang(x, y);
5294 }
5295
5296 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5297 {
5298   boolean num_checked_players = 0;
5299   int i;
5300
5301   for (i = 0; i < MAX_PLAYERS; i++)
5302   {
5303     if (stored_player[i].active)
5304     {
5305       int sx = stored_player[i].jx;
5306       int sy = stored_player[i].jy;
5307
5308       if (num_checked_players == 0)
5309       {
5310         *sx1 = *sx2 = sx;
5311         *sy1 = *sy2 = sy;
5312       }
5313       else
5314       {
5315         *sx1 = MIN(*sx1, sx);
5316         *sy1 = MIN(*sy1, sy);
5317         *sx2 = MAX(*sx2, sx);
5318         *sy2 = MAX(*sy2, sy);
5319       }
5320
5321       num_checked_players++;
5322     }
5323   }
5324 }
5325
5326 static boolean checkIfAllPlayersFitToScreen_RND(void)
5327 {
5328   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5329
5330   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5331
5332   return (sx2 - sx1 < SCR_FIELDX &&
5333           sy2 - sy1 < SCR_FIELDY);
5334 }
5335
5336 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5337 {
5338   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5339
5340   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5341
5342   *sx = (sx1 + sx2) / 2;
5343   *sy = (sy1 + sy2) / 2;
5344 }
5345
5346 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5347                                boolean center_screen, boolean quick_relocation)
5348 {
5349   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5350   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5351   boolean no_delay = (tape.warp_forward);
5352   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5353   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5354   int new_scroll_x, new_scroll_y;
5355
5356   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5357   {
5358     // case 1: quick relocation inside visible screen (without scrolling)
5359
5360     RedrawPlayfield();
5361
5362     return;
5363   }
5364
5365   if (!level.shifted_relocation || center_screen)
5366   {
5367     // relocation _with_ centering of screen
5368
5369     new_scroll_x = SCROLL_POSITION_X(x);
5370     new_scroll_y = SCROLL_POSITION_Y(y);
5371   }
5372   else
5373   {
5374     // relocation _without_ centering of screen
5375
5376     int center_scroll_x = SCROLL_POSITION_X(old_x);
5377     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5378     int offset_x = x + (scroll_x - center_scroll_x);
5379     int offset_y = y + (scroll_y - center_scroll_y);
5380
5381     // for new screen position, apply previous offset to center position
5382     new_scroll_x = SCROLL_POSITION_X(offset_x);
5383     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5384   }
5385
5386   if (quick_relocation)
5387   {
5388     // case 2: quick relocation (redraw without visible scrolling)
5389
5390     scroll_x = new_scroll_x;
5391     scroll_y = new_scroll_y;
5392
5393     RedrawPlayfield();
5394
5395     return;
5396   }
5397
5398   // case 3: visible relocation (with scrolling to new position)
5399
5400   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5401
5402   SetVideoFrameDelay(wait_delay_value);
5403
5404   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5405   {
5406     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5407     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5408
5409     if (dx == 0 && dy == 0)             // no scrolling needed at all
5410       break;
5411
5412     scroll_x -= dx;
5413     scroll_y -= dy;
5414
5415     // set values for horizontal/vertical screen scrolling (half tile size)
5416     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5417     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5418     int pos_x = dx * TILEX / 2;
5419     int pos_y = dy * TILEY / 2;
5420     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5421     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5422
5423     ScrollLevel(dx, dy);
5424     DrawAllPlayers();
5425
5426     // scroll in two steps of half tile size to make things smoother
5427     BlitScreenToBitmapExt_RND(window, fx, fy);
5428
5429     // scroll second step to align at full tile size
5430     BlitScreenToBitmap(window);
5431   }
5432
5433   DrawAllPlayers();
5434   BackToFront();
5435
5436   SetVideoFrameDelay(frame_delay_value_old);
5437 }
5438
5439 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5440 {
5441   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5442   int player_nr = GET_PLAYER_NR(el_player);
5443   struct PlayerInfo *player = &stored_player[player_nr];
5444   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5445   boolean no_delay = (tape.warp_forward);
5446   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5447   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5448   int old_jx = player->jx;
5449   int old_jy = player->jy;
5450   int old_element = Feld[old_jx][old_jy];
5451   int element = Feld[jx][jy];
5452   boolean player_relocated = (old_jx != jx || old_jy != jy);
5453
5454   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5455   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5456   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5457   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5458   int leave_side_horiz = move_dir_horiz;
5459   int leave_side_vert  = move_dir_vert;
5460   int enter_side = enter_side_horiz | enter_side_vert;
5461   int leave_side = leave_side_horiz | leave_side_vert;
5462
5463   if (player->buried)           // do not reanimate dead player
5464     return;
5465
5466   if (!player_relocated)        // no need to relocate the player
5467     return;
5468
5469   if (IS_PLAYER(jx, jy))        // player already placed at new position
5470   {
5471     RemoveField(jx, jy);        // temporarily remove newly placed player
5472     DrawLevelField(jx, jy);
5473   }
5474
5475   if (player->present)
5476   {
5477     while (player->MovPos)
5478     {
5479       ScrollPlayer(player, SCROLL_GO_ON);
5480       ScrollScreen(NULL, SCROLL_GO_ON);
5481
5482       AdvanceFrameAndPlayerCounters(player->index_nr);
5483
5484       DrawPlayer(player);
5485
5486       BackToFront_WithFrameDelay(wait_delay_value);
5487     }
5488
5489     DrawPlayer(player);         // needed here only to cleanup last field
5490     DrawLevelField(player->jx, player->jy);     // remove player graphic
5491
5492     player->is_moving = FALSE;
5493   }
5494
5495   if (IS_CUSTOM_ELEMENT(old_element))
5496     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5497                                CE_LEFT_BY_PLAYER,
5498                                player->index_bit, leave_side);
5499
5500   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5501                                       CE_PLAYER_LEAVES_X,
5502                                       player->index_bit, leave_side);
5503
5504   Feld[jx][jy] = el_player;
5505   InitPlayerField(jx, jy, el_player, TRUE);
5506
5507   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5508      possible that the relocation target field did not contain a player element,
5509      but a walkable element, to which the new player was relocated -- in this
5510      case, restore that (already initialized!) element on the player field */
5511   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5512   {
5513     Feld[jx][jy] = element;     // restore previously existing element
5514   }
5515
5516   // only visually relocate centered player
5517   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5518                      FALSE, level.instant_relocation);
5519
5520   TestIfPlayerTouchesBadThing(jx, jy);
5521   TestIfPlayerTouchesCustomElement(jx, jy);
5522
5523   if (IS_CUSTOM_ELEMENT(element))
5524     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5525                                player->index_bit, enter_side);
5526
5527   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5528                                       player->index_bit, enter_side);
5529
5530   if (player->is_switching)
5531   {
5532     /* ensure that relocation while still switching an element does not cause
5533        a new element to be treated as also switched directly after relocation
5534        (this is important for teleporter switches that teleport the player to
5535        a place where another teleporter switch is in the same direction, which
5536        would then incorrectly be treated as immediately switched before the
5537        direction key that caused the switch was released) */
5538
5539     player->switch_x += jx - old_jx;
5540     player->switch_y += jy - old_jy;
5541   }
5542 }
5543
5544 static void Explode(int ex, int ey, int phase, int mode)
5545 {
5546   int x, y;
5547   int last_phase;
5548   int border_element;
5549
5550   // !!! eliminate this variable !!!
5551   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5552
5553   if (game.explosions_delayed)
5554   {
5555     ExplodeField[ex][ey] = mode;
5556     return;
5557   }
5558
5559   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5560   {
5561     int center_element = Feld[ex][ey];
5562     int artwork_element, explosion_element;     // set these values later
5563
5564     // remove things displayed in background while burning dynamite
5565     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5566       Back[ex][ey] = 0;
5567
5568     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5569     {
5570       // put moving element to center field (and let it explode there)
5571       center_element = MovingOrBlocked2Element(ex, ey);
5572       RemoveMovingField(ex, ey);
5573       Feld[ex][ey] = center_element;
5574     }
5575
5576     // now "center_element" is finally determined -- set related values now
5577     artwork_element = center_element;           // for custom player artwork
5578     explosion_element = center_element;         // for custom player artwork
5579
5580     if (IS_PLAYER(ex, ey))
5581     {
5582       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5583
5584       artwork_element = stored_player[player_nr].artwork_element;
5585
5586       if (level.use_explosion_element[player_nr])
5587       {
5588         explosion_element = level.explosion_element[player_nr];
5589         artwork_element = explosion_element;
5590       }
5591     }
5592
5593     if (mode == EX_TYPE_NORMAL ||
5594         mode == EX_TYPE_CENTER ||
5595         mode == EX_TYPE_CROSS)
5596       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5597
5598     last_phase = element_info[explosion_element].explosion_delay + 1;
5599
5600     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5601     {
5602       int xx = x - ex + 1;
5603       int yy = y - ey + 1;
5604       int element;
5605
5606       if (!IN_LEV_FIELD(x, y) ||
5607           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5608           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5609         continue;
5610
5611       element = Feld[x][y];
5612
5613       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5614       {
5615         element = MovingOrBlocked2Element(x, y);
5616
5617         if (!IS_EXPLOSION_PROOF(element))
5618           RemoveMovingField(x, y);
5619       }
5620
5621       // indestructible elements can only explode in center (but not flames)
5622       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5623                                            mode == EX_TYPE_BORDER)) ||
5624           element == EL_FLAMES)
5625         continue;
5626
5627       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5628          behaviour, for example when touching a yamyam that explodes to rocks
5629          with active deadly shield, a rock is created under the player !!! */
5630       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5631 #if 0
5632       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5633           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5634            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5635 #else
5636       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5637 #endif
5638       {
5639         if (IS_ACTIVE_BOMB(element))
5640         {
5641           // re-activate things under the bomb like gate or penguin
5642           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5643           Back[x][y] = 0;
5644         }
5645
5646         continue;
5647       }
5648
5649       // save walkable background elements while explosion on same tile
5650       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5651           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5652         Back[x][y] = element;
5653
5654       // ignite explodable elements reached by other explosion
5655       if (element == EL_EXPLOSION)
5656         element = Store2[x][y];
5657
5658       if (AmoebaNr[x][y] &&
5659           (element == EL_AMOEBA_FULL ||
5660            element == EL_BD_AMOEBA ||
5661            element == EL_AMOEBA_GROWING))
5662       {
5663         AmoebaCnt[AmoebaNr[x][y]]--;
5664         AmoebaCnt2[AmoebaNr[x][y]]--;
5665       }
5666
5667       RemoveField(x, y);
5668
5669       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5670       {
5671         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5672
5673         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5674
5675         if (PLAYERINFO(ex, ey)->use_murphy)
5676           Store[x][y] = EL_EMPTY;
5677       }
5678
5679       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5680       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5681       else if (ELEM_IS_PLAYER(center_element))
5682         Store[x][y] = EL_EMPTY;
5683       else if (center_element == EL_YAMYAM)
5684         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5685       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5686         Store[x][y] = element_info[center_element].content.e[xx][yy];
5687 #if 1
5688       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5689       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5690       // otherwise) -- FIX THIS !!!
5691       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5692         Store[x][y] = element_info[element].content.e[1][1];
5693 #else
5694       else if (!CAN_EXPLODE(element))
5695         Store[x][y] = element_info[element].content.e[1][1];
5696 #endif
5697       else
5698         Store[x][y] = EL_EMPTY;
5699
5700       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5701           center_element == EL_AMOEBA_TO_DIAMOND)
5702         Store2[x][y] = element;
5703
5704       Feld[x][y] = EL_EXPLOSION;
5705       GfxElement[x][y] = artwork_element;
5706
5707       ExplodePhase[x][y] = 1;
5708       ExplodeDelay[x][y] = last_phase;
5709
5710       Stop[x][y] = TRUE;
5711     }
5712
5713     if (center_element == EL_YAMYAM)
5714       game.yamyam_content_nr =
5715         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5716
5717     return;
5718   }
5719
5720   if (Stop[ex][ey])
5721     return;
5722
5723   x = ex;
5724   y = ey;
5725
5726   if (phase == 1)
5727     GfxFrame[x][y] = 0;         // restart explosion animation
5728
5729   last_phase = ExplodeDelay[x][y];
5730
5731   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5732
5733   // this can happen if the player leaves an explosion just in time
5734   if (GfxElement[x][y] == EL_UNDEFINED)
5735     GfxElement[x][y] = EL_EMPTY;
5736
5737   border_element = Store2[x][y];
5738   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5739     border_element = StorePlayer[x][y];
5740
5741   if (phase == element_info[border_element].ignition_delay ||
5742       phase == last_phase)
5743   {
5744     boolean border_explosion = FALSE;
5745
5746     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5747         !PLAYER_EXPLOSION_PROTECTED(x, y))
5748     {
5749       KillPlayerUnlessExplosionProtected(x, y);
5750       border_explosion = TRUE;
5751     }
5752     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5753     {
5754       Feld[x][y] = Store2[x][y];
5755       Store2[x][y] = 0;
5756       Bang(x, y);
5757       border_explosion = TRUE;
5758     }
5759     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5760     {
5761       AmoebeUmwandeln(x, y);
5762       Store2[x][y] = 0;
5763       border_explosion = TRUE;
5764     }
5765
5766     // if an element just explodes due to another explosion (chain-reaction),
5767     // do not immediately end the new explosion when it was the last frame of
5768     // the explosion (as it would be done in the following "if"-statement!)
5769     if (border_explosion && phase == last_phase)
5770       return;
5771   }
5772
5773   if (phase == last_phase)
5774   {
5775     int element;
5776
5777     element = Feld[x][y] = Store[x][y];
5778     Store[x][y] = Store2[x][y] = 0;
5779     GfxElement[x][y] = EL_UNDEFINED;
5780
5781     // player can escape from explosions and might therefore be still alive
5782     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5783         element <= EL_PLAYER_IS_EXPLODING_4)
5784     {
5785       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5786       int explosion_element = EL_PLAYER_1 + player_nr;
5787       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5788       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5789
5790       if (level.use_explosion_element[player_nr])
5791         explosion_element = level.explosion_element[player_nr];
5792
5793       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5794                     element_info[explosion_element].content.e[xx][yy]);
5795     }
5796
5797     // restore probably existing indestructible background element
5798     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5799       element = Feld[x][y] = Back[x][y];
5800     Back[x][y] = 0;
5801
5802     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5803     GfxDir[x][y] = MV_NONE;
5804     ChangeDelay[x][y] = 0;
5805     ChangePage[x][y] = -1;
5806
5807     CustomValue[x][y] = 0;
5808
5809     InitField_WithBug2(x, y, FALSE);
5810
5811     TEST_DrawLevelField(x, y);
5812
5813     TestIfElementTouchesCustomElement(x, y);
5814
5815     if (GFX_CRUMBLED(element))
5816       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5817
5818     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5819       StorePlayer[x][y] = 0;
5820
5821     if (ELEM_IS_PLAYER(element))
5822       RelocatePlayer(x, y, element);
5823   }
5824   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5825   {
5826     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5827     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5828
5829     if (phase == delay)
5830       TEST_DrawLevelFieldCrumbled(x, y);
5831
5832     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5833     {
5834       DrawLevelElement(x, y, Back[x][y]);
5835       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5836     }
5837     else if (IS_WALKABLE_UNDER(Back[x][y]))
5838     {
5839       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5840       DrawLevelElementThruMask(x, y, Back[x][y]);
5841     }
5842     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5843       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5844   }
5845 }
5846
5847 static void DynaExplode(int ex, int ey)
5848 {
5849   int i, j;
5850   int dynabomb_element = Feld[ex][ey];
5851   int dynabomb_size = 1;
5852   boolean dynabomb_xl = FALSE;
5853   struct PlayerInfo *player;
5854   static int xy[4][2] =
5855   {
5856     { 0, -1 },
5857     { -1, 0 },
5858     { +1, 0 },
5859     { 0, +1 }
5860   };
5861
5862   if (IS_ACTIVE_BOMB(dynabomb_element))
5863   {
5864     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5865     dynabomb_size = player->dynabomb_size;
5866     dynabomb_xl = player->dynabomb_xl;
5867     player->dynabombs_left++;
5868   }
5869
5870   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5871
5872   for (i = 0; i < NUM_DIRECTIONS; i++)
5873   {
5874     for (j = 1; j <= dynabomb_size; j++)
5875     {
5876       int x = ex + j * xy[i][0];
5877       int y = ey + j * xy[i][1];
5878       int element;
5879
5880       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5881         break;
5882
5883       element = Feld[x][y];
5884
5885       // do not restart explosions of fields with active bombs
5886       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5887         continue;
5888
5889       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5890
5891       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5892           !IS_DIGGABLE(element) && !dynabomb_xl)
5893         break;
5894     }
5895   }
5896 }
5897
5898 void Bang(int x, int y)
5899 {
5900   int element = MovingOrBlocked2Element(x, y);
5901   int explosion_type = EX_TYPE_NORMAL;
5902
5903   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5904   {
5905     struct PlayerInfo *player = PLAYERINFO(x, y);
5906
5907     element = Feld[x][y] = player->initial_element;
5908
5909     if (level.use_explosion_element[player->index_nr])
5910     {
5911       int explosion_element = level.explosion_element[player->index_nr];
5912
5913       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5914         explosion_type = EX_TYPE_CROSS;
5915       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5916         explosion_type = EX_TYPE_CENTER;
5917     }
5918   }
5919
5920   switch (element)
5921   {
5922     case EL_BUG:
5923     case EL_SPACESHIP:
5924     case EL_BD_BUTTERFLY:
5925     case EL_BD_FIREFLY:
5926     case EL_YAMYAM:
5927     case EL_DARK_YAMYAM:
5928     case EL_ROBOT:
5929     case EL_PACMAN:
5930     case EL_MOLE:
5931       RaiseScoreElement(element);
5932       break;
5933
5934     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5935     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5936     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5937     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5938     case EL_DYNABOMB_INCREASE_NUMBER:
5939     case EL_DYNABOMB_INCREASE_SIZE:
5940     case EL_DYNABOMB_INCREASE_POWER:
5941       explosion_type = EX_TYPE_DYNA;
5942       break;
5943
5944     case EL_DC_LANDMINE:
5945       explosion_type = EX_TYPE_CENTER;
5946       break;
5947
5948     case EL_PENGUIN:
5949     case EL_LAMP:
5950     case EL_LAMP_ACTIVE:
5951     case EL_AMOEBA_TO_DIAMOND:
5952       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5953         explosion_type = EX_TYPE_CENTER;
5954       break;
5955
5956     default:
5957       if (element_info[element].explosion_type == EXPLODES_CROSS)
5958         explosion_type = EX_TYPE_CROSS;
5959       else if (element_info[element].explosion_type == EXPLODES_1X1)
5960         explosion_type = EX_TYPE_CENTER;
5961       break;
5962   }
5963
5964   if (explosion_type == EX_TYPE_DYNA)
5965     DynaExplode(x, y);
5966   else
5967     Explode(x, y, EX_PHASE_START, explosion_type);
5968
5969   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5970 }
5971
5972 static void SplashAcid(int x, int y)
5973 {
5974   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5975       (!IN_LEV_FIELD(x - 1, y - 2) ||
5976        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5977     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5978
5979   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5980       (!IN_LEV_FIELD(x + 1, y - 2) ||
5981        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5982     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5983
5984   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5985 }
5986
5987 static void InitBeltMovement(void)
5988 {
5989   static int belt_base_element[4] =
5990   {
5991     EL_CONVEYOR_BELT_1_LEFT,
5992     EL_CONVEYOR_BELT_2_LEFT,
5993     EL_CONVEYOR_BELT_3_LEFT,
5994     EL_CONVEYOR_BELT_4_LEFT
5995   };
5996   static int belt_base_active_element[4] =
5997   {
5998     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5999     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6000     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6001     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6002   };
6003
6004   int x, y, i, j;
6005
6006   // set frame order for belt animation graphic according to belt direction
6007   for (i = 0; i < NUM_BELTS; i++)
6008   {
6009     int belt_nr = i;
6010
6011     for (j = 0; j < NUM_BELT_PARTS; j++)
6012     {
6013       int element = belt_base_active_element[belt_nr] + j;
6014       int graphic_1 = el2img(element);
6015       int graphic_2 = el2panelimg(element);
6016
6017       if (game.belt_dir[i] == MV_LEFT)
6018       {
6019         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6020         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6021       }
6022       else
6023       {
6024         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6025         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6026       }
6027     }
6028   }
6029
6030   SCAN_PLAYFIELD(x, y)
6031   {
6032     int element = Feld[x][y];
6033
6034     for (i = 0; i < NUM_BELTS; i++)
6035     {
6036       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6037       {
6038         int e_belt_nr = getBeltNrFromBeltElement(element);
6039         int belt_nr = i;
6040
6041         if (e_belt_nr == belt_nr)
6042         {
6043           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6044
6045           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6046         }
6047       }
6048     }
6049   }
6050 }
6051
6052 static void ToggleBeltSwitch(int x, int y)
6053 {
6054   static int belt_base_element[4] =
6055   {
6056     EL_CONVEYOR_BELT_1_LEFT,
6057     EL_CONVEYOR_BELT_2_LEFT,
6058     EL_CONVEYOR_BELT_3_LEFT,
6059     EL_CONVEYOR_BELT_4_LEFT
6060   };
6061   static int belt_base_active_element[4] =
6062   {
6063     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6064     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6065     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6066     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6067   };
6068   static int belt_base_switch_element[4] =
6069   {
6070     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6071     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6072     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6073     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6074   };
6075   static int belt_move_dir[4] =
6076   {
6077     MV_LEFT,
6078     MV_NONE,
6079     MV_RIGHT,
6080     MV_NONE,
6081   };
6082
6083   int element = Feld[x][y];
6084   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6085   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6086   int belt_dir = belt_move_dir[belt_dir_nr];
6087   int xx, yy, i;
6088
6089   if (!IS_BELT_SWITCH(element))
6090     return;
6091
6092   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6093   game.belt_dir[belt_nr] = belt_dir;
6094
6095   if (belt_dir_nr == 3)
6096     belt_dir_nr = 1;
6097
6098   // set frame order for belt animation graphic according to belt direction
6099   for (i = 0; i < NUM_BELT_PARTS; i++)
6100   {
6101     int element = belt_base_active_element[belt_nr] + i;
6102     int graphic_1 = el2img(element);
6103     int graphic_2 = el2panelimg(element);
6104
6105     if (belt_dir == MV_LEFT)
6106     {
6107       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6108       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6109     }
6110     else
6111     {
6112       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6113       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6114     }
6115   }
6116
6117   SCAN_PLAYFIELD(xx, yy)
6118   {
6119     int element = Feld[xx][yy];
6120
6121     if (IS_BELT_SWITCH(element))
6122     {
6123       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6124
6125       if (e_belt_nr == belt_nr)
6126       {
6127         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6128         TEST_DrawLevelField(xx, yy);
6129       }
6130     }
6131     else if (IS_BELT(element) && belt_dir != MV_NONE)
6132     {
6133       int e_belt_nr = getBeltNrFromBeltElement(element);
6134
6135       if (e_belt_nr == belt_nr)
6136       {
6137         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6138
6139         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6140         TEST_DrawLevelField(xx, yy);
6141       }
6142     }
6143     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6144     {
6145       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6146
6147       if (e_belt_nr == belt_nr)
6148       {
6149         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6150
6151         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6152         TEST_DrawLevelField(xx, yy);
6153       }
6154     }
6155   }
6156 }
6157
6158 static void ToggleSwitchgateSwitch(int x, int y)
6159 {
6160   int xx, yy;
6161
6162   game.switchgate_pos = !game.switchgate_pos;
6163
6164   SCAN_PLAYFIELD(xx, yy)
6165   {
6166     int element = Feld[xx][yy];
6167
6168     if (element == EL_SWITCHGATE_SWITCH_UP)
6169     {
6170       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6171       TEST_DrawLevelField(xx, yy);
6172     }
6173     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6174     {
6175       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6176       TEST_DrawLevelField(xx, yy);
6177     }
6178     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6179     {
6180       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6181       TEST_DrawLevelField(xx, yy);
6182     }
6183     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6184     {
6185       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6186       TEST_DrawLevelField(xx, yy);
6187     }
6188     else if (element == EL_SWITCHGATE_OPEN ||
6189              element == EL_SWITCHGATE_OPENING)
6190     {
6191       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6192
6193       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6194     }
6195     else if (element == EL_SWITCHGATE_CLOSED ||
6196              element == EL_SWITCHGATE_CLOSING)
6197     {
6198       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6199
6200       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6201     }
6202   }
6203 }
6204
6205 static int getInvisibleActiveFromInvisibleElement(int element)
6206 {
6207   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6208           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6209           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6210           element);
6211 }
6212
6213 static int getInvisibleFromInvisibleActiveElement(int element)
6214 {
6215   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6216           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6217           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6218           element);
6219 }
6220
6221 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6222 {
6223   int x, y;
6224
6225   SCAN_PLAYFIELD(x, y)
6226   {
6227     int element = Feld[x][y];
6228
6229     if (element == EL_LIGHT_SWITCH &&
6230         game.light_time_left > 0)
6231     {
6232       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6233       TEST_DrawLevelField(x, y);
6234     }
6235     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6236              game.light_time_left == 0)
6237     {
6238       Feld[x][y] = EL_LIGHT_SWITCH;
6239       TEST_DrawLevelField(x, y);
6240     }
6241     else if (element == EL_EMC_DRIPPER &&
6242              game.light_time_left > 0)
6243     {
6244       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6245       TEST_DrawLevelField(x, y);
6246     }
6247     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6248              game.light_time_left == 0)
6249     {
6250       Feld[x][y] = EL_EMC_DRIPPER;
6251       TEST_DrawLevelField(x, y);
6252     }
6253     else if (element == EL_INVISIBLE_STEELWALL ||
6254              element == EL_INVISIBLE_WALL ||
6255              element == EL_INVISIBLE_SAND)
6256     {
6257       if (game.light_time_left > 0)
6258         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6259
6260       TEST_DrawLevelField(x, y);
6261
6262       // uncrumble neighbour fields, if needed
6263       if (element == EL_INVISIBLE_SAND)
6264         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6265     }
6266     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6267              element == EL_INVISIBLE_WALL_ACTIVE ||
6268              element == EL_INVISIBLE_SAND_ACTIVE)
6269     {
6270       if (game.light_time_left == 0)
6271         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6272
6273       TEST_DrawLevelField(x, y);
6274
6275       // re-crumble neighbour fields, if needed
6276       if (element == EL_INVISIBLE_SAND)
6277         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6278     }
6279   }
6280 }
6281
6282 static void RedrawAllInvisibleElementsForLenses(void)
6283 {
6284   int x, y;
6285
6286   SCAN_PLAYFIELD(x, y)
6287   {
6288     int element = Feld[x][y];
6289
6290     if (element == EL_EMC_DRIPPER &&
6291         game.lenses_time_left > 0)
6292     {
6293       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6294       TEST_DrawLevelField(x, y);
6295     }
6296     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6297              game.lenses_time_left == 0)
6298     {
6299       Feld[x][y] = EL_EMC_DRIPPER;
6300       TEST_DrawLevelField(x, y);
6301     }
6302     else if (element == EL_INVISIBLE_STEELWALL ||
6303              element == EL_INVISIBLE_WALL ||
6304              element == EL_INVISIBLE_SAND)
6305     {
6306       if (game.lenses_time_left > 0)
6307         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6308
6309       TEST_DrawLevelField(x, y);
6310
6311       // uncrumble neighbour fields, if needed
6312       if (element == EL_INVISIBLE_SAND)
6313         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6314     }
6315     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6316              element == EL_INVISIBLE_WALL_ACTIVE ||
6317              element == EL_INVISIBLE_SAND_ACTIVE)
6318     {
6319       if (game.lenses_time_left == 0)
6320         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6321
6322       TEST_DrawLevelField(x, y);
6323
6324       // re-crumble neighbour fields, if needed
6325       if (element == EL_INVISIBLE_SAND)
6326         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6327     }
6328   }
6329 }
6330
6331 static void RedrawAllInvisibleElementsForMagnifier(void)
6332 {
6333   int x, y;
6334
6335   SCAN_PLAYFIELD(x, y)
6336   {
6337     int element = Feld[x][y];
6338
6339     if (element == EL_EMC_FAKE_GRASS &&
6340         game.magnify_time_left > 0)
6341     {
6342       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6343       TEST_DrawLevelField(x, y);
6344     }
6345     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6346              game.magnify_time_left == 0)
6347     {
6348       Feld[x][y] = EL_EMC_FAKE_GRASS;
6349       TEST_DrawLevelField(x, y);
6350     }
6351     else if (IS_GATE_GRAY(element) &&
6352              game.magnify_time_left > 0)
6353     {
6354       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6355                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6356                     IS_EM_GATE_GRAY(element) ?
6357                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6358                     IS_EMC_GATE_GRAY(element) ?
6359                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6360                     IS_DC_GATE_GRAY(element) ?
6361                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6362                     element);
6363       TEST_DrawLevelField(x, y);
6364     }
6365     else if (IS_GATE_GRAY_ACTIVE(element) &&
6366              game.magnify_time_left == 0)
6367     {
6368       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6369                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6370                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6371                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6372                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6373                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6374                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6375                     EL_DC_GATE_WHITE_GRAY :
6376                     element);
6377       TEST_DrawLevelField(x, y);
6378     }
6379   }
6380 }
6381
6382 static void ToggleLightSwitch(int x, int y)
6383 {
6384   int element = Feld[x][y];
6385
6386   game.light_time_left =
6387     (element == EL_LIGHT_SWITCH ?
6388      level.time_light * FRAMES_PER_SECOND : 0);
6389
6390   RedrawAllLightSwitchesAndInvisibleElements();
6391 }
6392
6393 static void ActivateTimegateSwitch(int x, int y)
6394 {
6395   int xx, yy;
6396
6397   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6398
6399   SCAN_PLAYFIELD(xx, yy)
6400   {
6401     int element = Feld[xx][yy];
6402
6403     if (element == EL_TIMEGATE_CLOSED ||
6404         element == EL_TIMEGATE_CLOSING)
6405     {
6406       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6407       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6408     }
6409
6410     /*
6411     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6412     {
6413       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6414       TEST_DrawLevelField(xx, yy);
6415     }
6416     */
6417
6418   }
6419
6420   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6421                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6422 }
6423
6424 static void Impact(int x, int y)
6425 {
6426   boolean last_line = (y == lev_fieldy - 1);
6427   boolean object_hit = FALSE;
6428   boolean impact = (last_line || object_hit);
6429   int element = Feld[x][y];
6430   int smashed = EL_STEELWALL;
6431
6432   if (!last_line)       // check if element below was hit
6433   {
6434     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6435       return;
6436
6437     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6438                                          MovDir[x][y + 1] != MV_DOWN ||
6439                                          MovPos[x][y + 1] <= TILEY / 2));
6440
6441     // do not smash moving elements that left the smashed field in time
6442     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6443         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6444       object_hit = FALSE;
6445
6446 #if USE_QUICKSAND_IMPACT_BUGFIX
6447     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6448     {
6449       RemoveMovingField(x, y + 1);
6450       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6451       Feld[x][y + 2] = EL_ROCK;
6452       TEST_DrawLevelField(x, y + 2);
6453
6454       object_hit = TRUE;
6455     }
6456
6457     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6458     {
6459       RemoveMovingField(x, y + 1);
6460       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6461       Feld[x][y + 2] = EL_ROCK;
6462       TEST_DrawLevelField(x, y + 2);
6463
6464       object_hit = TRUE;
6465     }
6466 #endif
6467
6468     if (object_hit)
6469       smashed = MovingOrBlocked2Element(x, y + 1);
6470
6471     impact = (last_line || object_hit);
6472   }
6473
6474   if (!last_line && smashed == EL_ACID) // element falls into acid
6475   {
6476     SplashAcid(x, y + 1);
6477     return;
6478   }
6479
6480   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6481   // only reset graphic animation if graphic really changes after impact
6482   if (impact &&
6483       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6484   {
6485     ResetGfxAnimation(x, y);
6486     TEST_DrawLevelField(x, y);
6487   }
6488
6489   if (impact && CAN_EXPLODE_IMPACT(element))
6490   {
6491     Bang(x, y);
6492     return;
6493   }
6494   else if (impact && element == EL_PEARL &&
6495            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6496   {
6497     ResetGfxAnimation(x, y);
6498
6499     Feld[x][y] = EL_PEARL_BREAKING;
6500     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6501     return;
6502   }
6503   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6504   {
6505     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6506
6507     return;
6508   }
6509
6510   if (impact && element == EL_AMOEBA_DROP)
6511   {
6512     if (object_hit && IS_PLAYER(x, y + 1))
6513       KillPlayerUnlessEnemyProtected(x, y + 1);
6514     else if (object_hit && smashed == EL_PENGUIN)
6515       Bang(x, y + 1);
6516     else
6517     {
6518       Feld[x][y] = EL_AMOEBA_GROWING;
6519       Store[x][y] = EL_AMOEBA_WET;
6520
6521       ResetRandomAnimationValue(x, y);
6522     }
6523     return;
6524   }
6525
6526   if (object_hit)               // check which object was hit
6527   {
6528     if ((CAN_PASS_MAGIC_WALL(element) && 
6529          (smashed == EL_MAGIC_WALL ||
6530           smashed == EL_BD_MAGIC_WALL)) ||
6531         (CAN_PASS_DC_MAGIC_WALL(element) &&
6532          smashed == EL_DC_MAGIC_WALL))
6533     {
6534       int xx, yy;
6535       int activated_magic_wall =
6536         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6537          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6538          EL_DC_MAGIC_WALL_ACTIVE);
6539
6540       // activate magic wall / mill
6541       SCAN_PLAYFIELD(xx, yy)
6542       {
6543         if (Feld[xx][yy] == smashed)
6544           Feld[xx][yy] = activated_magic_wall;
6545       }
6546
6547       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6548       game.magic_wall_active = TRUE;
6549
6550       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6551                             SND_MAGIC_WALL_ACTIVATING :
6552                             smashed == EL_BD_MAGIC_WALL ?
6553                             SND_BD_MAGIC_WALL_ACTIVATING :
6554                             SND_DC_MAGIC_WALL_ACTIVATING));
6555     }
6556
6557     if (IS_PLAYER(x, y + 1))
6558     {
6559       if (CAN_SMASH_PLAYER(element))
6560       {
6561         KillPlayerUnlessEnemyProtected(x, y + 1);
6562         return;
6563       }
6564     }
6565     else if (smashed == EL_PENGUIN)
6566     {
6567       if (CAN_SMASH_PLAYER(element))
6568       {
6569         Bang(x, y + 1);
6570         return;
6571       }
6572     }
6573     else if (element == EL_BD_DIAMOND)
6574     {
6575       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6576       {
6577         Bang(x, y + 1);
6578         return;
6579       }
6580     }
6581     else if (((element == EL_SP_INFOTRON ||
6582                element == EL_SP_ZONK) &&
6583               (smashed == EL_SP_SNIKSNAK ||
6584                smashed == EL_SP_ELECTRON ||
6585                smashed == EL_SP_DISK_ORANGE)) ||
6586              (element == EL_SP_INFOTRON &&
6587               smashed == EL_SP_DISK_YELLOW))
6588     {
6589       Bang(x, y + 1);
6590       return;
6591     }
6592     else if (CAN_SMASH_EVERYTHING(element))
6593     {
6594       if (IS_CLASSIC_ENEMY(smashed) ||
6595           CAN_EXPLODE_SMASHED(smashed))
6596       {
6597         Bang(x, y + 1);
6598         return;
6599       }
6600       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6601       {
6602         if (smashed == EL_LAMP ||
6603             smashed == EL_LAMP_ACTIVE)
6604         {
6605           Bang(x, y + 1);
6606           return;
6607         }
6608         else if (smashed == EL_NUT)
6609         {
6610           Feld[x][y + 1] = EL_NUT_BREAKING;
6611           PlayLevelSound(x, y, SND_NUT_BREAKING);
6612           RaiseScoreElement(EL_NUT);
6613           return;
6614         }
6615         else if (smashed == EL_PEARL)
6616         {
6617           ResetGfxAnimation(x, y);
6618
6619           Feld[x][y + 1] = EL_PEARL_BREAKING;
6620           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6621           return;
6622         }
6623         else if (smashed == EL_DIAMOND)
6624         {
6625           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6626           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6627           return;
6628         }
6629         else if (IS_BELT_SWITCH(smashed))
6630         {
6631           ToggleBeltSwitch(x, y + 1);
6632         }
6633         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6634                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6635                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6636                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6637         {
6638           ToggleSwitchgateSwitch(x, y + 1);
6639         }
6640         else if (smashed == EL_LIGHT_SWITCH ||
6641                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6642         {
6643           ToggleLightSwitch(x, y + 1);
6644         }
6645         else
6646         {
6647           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6648
6649           CheckElementChangeBySide(x, y + 1, smashed, element,
6650                                    CE_SWITCHED, CH_SIDE_TOP);
6651           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6652                                             CH_SIDE_TOP);
6653         }
6654       }
6655       else
6656       {
6657         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6658       }
6659     }
6660   }
6661
6662   // play sound of magic wall / mill
6663   if (!last_line &&
6664       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6665        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6666        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6667   {
6668     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6669       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6670     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6671       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6672     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6673       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6674
6675     return;
6676   }
6677
6678   // play sound of object that hits the ground
6679   if (last_line || object_hit)
6680     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6681 }
6682
6683 static void TurnRoundExt(int x, int y)
6684 {
6685   static struct
6686   {
6687     int dx, dy;
6688   } move_xy[] =
6689   {
6690     {  0,  0 },
6691     { -1,  0 },
6692     { +1,  0 },
6693     {  0,  0 },
6694     {  0, -1 },
6695     {  0,  0 }, { 0, 0 }, { 0, 0 },
6696     {  0, +1 }
6697   };
6698   static struct
6699   {
6700     int left, right, back;
6701   } turn[] =
6702   {
6703     { 0,        0,              0        },
6704     { MV_DOWN,  MV_UP,          MV_RIGHT },
6705     { MV_UP,    MV_DOWN,        MV_LEFT  },
6706     { 0,        0,              0        },
6707     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6708     { 0,        0,              0        },
6709     { 0,        0,              0        },
6710     { 0,        0,              0        },
6711     { MV_RIGHT, MV_LEFT,        MV_UP    }
6712   };
6713
6714   int element = Feld[x][y];
6715   int move_pattern = element_info[element].move_pattern;
6716
6717   int old_move_dir = MovDir[x][y];
6718   int left_dir  = turn[old_move_dir].left;
6719   int right_dir = turn[old_move_dir].right;
6720   int back_dir  = turn[old_move_dir].back;
6721
6722   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6723   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6724   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6725   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6726
6727   int left_x  = x + left_dx,  left_y  = y + left_dy;
6728   int right_x = x + right_dx, right_y = y + right_dy;
6729   int move_x  = x + move_dx,  move_y  = y + move_dy;
6730
6731   int xx, yy;
6732
6733   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6734   {
6735     TestIfBadThingTouchesOtherBadThing(x, y);
6736
6737     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6738       MovDir[x][y] = right_dir;
6739     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6740       MovDir[x][y] = left_dir;
6741
6742     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6743       MovDelay[x][y] = 9;
6744     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6745       MovDelay[x][y] = 1;
6746   }
6747   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6748   {
6749     TestIfBadThingTouchesOtherBadThing(x, y);
6750
6751     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6752       MovDir[x][y] = left_dir;
6753     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6754       MovDir[x][y] = right_dir;
6755
6756     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6757       MovDelay[x][y] = 9;
6758     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6759       MovDelay[x][y] = 1;
6760   }
6761   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6762   {
6763     TestIfBadThingTouchesOtherBadThing(x, y);
6764
6765     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6766       MovDir[x][y] = left_dir;
6767     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6768       MovDir[x][y] = right_dir;
6769
6770     if (MovDir[x][y] != old_move_dir)
6771       MovDelay[x][y] = 9;
6772   }
6773   else if (element == EL_YAMYAM)
6774   {
6775     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6776     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6777
6778     if (can_turn_left && can_turn_right)
6779       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6780     else if (can_turn_left)
6781       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6782     else if (can_turn_right)
6783       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6784     else
6785       MovDir[x][y] = back_dir;
6786
6787     MovDelay[x][y] = 16 + 16 * RND(3);
6788   }
6789   else if (element == EL_DARK_YAMYAM)
6790   {
6791     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6792                                                          left_x, left_y);
6793     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6794                                                          right_x, right_y);
6795
6796     if (can_turn_left && can_turn_right)
6797       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6798     else if (can_turn_left)
6799       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6800     else if (can_turn_right)
6801       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6802     else
6803       MovDir[x][y] = back_dir;
6804
6805     MovDelay[x][y] = 16 + 16 * RND(3);
6806   }
6807   else if (element == EL_PACMAN)
6808   {
6809     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6810     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6811
6812     if (can_turn_left && can_turn_right)
6813       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6814     else if (can_turn_left)
6815       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6816     else if (can_turn_right)
6817       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6818     else
6819       MovDir[x][y] = back_dir;
6820
6821     MovDelay[x][y] = 6 + RND(40);
6822   }
6823   else if (element == EL_PIG)
6824   {
6825     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6826     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6827     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6828     boolean should_turn_left, should_turn_right, should_move_on;
6829     int rnd_value = 24;
6830     int rnd = RND(rnd_value);
6831
6832     should_turn_left = (can_turn_left &&
6833                         (!can_move_on ||
6834                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6835                                                    y + back_dy + left_dy)));
6836     should_turn_right = (can_turn_right &&
6837                          (!can_move_on ||
6838                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6839                                                     y + back_dy + right_dy)));
6840     should_move_on = (can_move_on &&
6841                       (!can_turn_left ||
6842                        !can_turn_right ||
6843                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6844                                                  y + move_dy + left_dy) ||
6845                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6846                                                  y + move_dy + right_dy)));
6847
6848     if (should_turn_left || should_turn_right || should_move_on)
6849     {
6850       if (should_turn_left && should_turn_right && should_move_on)
6851         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6852                         rnd < 2 * rnd_value / 3 ? right_dir :
6853                         old_move_dir);
6854       else if (should_turn_left && should_turn_right)
6855         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6856       else if (should_turn_left && should_move_on)
6857         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6858       else if (should_turn_right && should_move_on)
6859         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6860       else if (should_turn_left)
6861         MovDir[x][y] = left_dir;
6862       else if (should_turn_right)
6863         MovDir[x][y] = right_dir;
6864       else if (should_move_on)
6865         MovDir[x][y] = old_move_dir;
6866     }
6867     else if (can_move_on && rnd > rnd_value / 8)
6868       MovDir[x][y] = old_move_dir;
6869     else if (can_turn_left && can_turn_right)
6870       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6871     else if (can_turn_left && rnd > rnd_value / 8)
6872       MovDir[x][y] = left_dir;
6873     else if (can_turn_right && rnd > rnd_value/8)
6874       MovDir[x][y] = right_dir;
6875     else
6876       MovDir[x][y] = back_dir;
6877
6878     xx = x + move_xy[MovDir[x][y]].dx;
6879     yy = y + move_xy[MovDir[x][y]].dy;
6880
6881     if (!IN_LEV_FIELD(xx, yy) ||
6882         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6883       MovDir[x][y] = old_move_dir;
6884
6885     MovDelay[x][y] = 0;
6886   }
6887   else if (element == EL_DRAGON)
6888   {
6889     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6890     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6891     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6892     int rnd_value = 24;
6893     int rnd = RND(rnd_value);
6894
6895     if (can_move_on && rnd > rnd_value / 8)
6896       MovDir[x][y] = old_move_dir;
6897     else if (can_turn_left && can_turn_right)
6898       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6899     else if (can_turn_left && rnd > rnd_value / 8)
6900       MovDir[x][y] = left_dir;
6901     else if (can_turn_right && rnd > rnd_value / 8)
6902       MovDir[x][y] = right_dir;
6903     else
6904       MovDir[x][y] = back_dir;
6905
6906     xx = x + move_xy[MovDir[x][y]].dx;
6907     yy = y + move_xy[MovDir[x][y]].dy;
6908
6909     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6910       MovDir[x][y] = old_move_dir;
6911
6912     MovDelay[x][y] = 0;
6913   }
6914   else if (element == EL_MOLE)
6915   {
6916     boolean can_move_on =
6917       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6918                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6919                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6920     if (!can_move_on)
6921     {
6922       boolean can_turn_left =
6923         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6924                               IS_AMOEBOID(Feld[left_x][left_y])));
6925
6926       boolean can_turn_right =
6927         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6928                               IS_AMOEBOID(Feld[right_x][right_y])));
6929
6930       if (can_turn_left && can_turn_right)
6931         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6932       else if (can_turn_left)
6933         MovDir[x][y] = left_dir;
6934       else
6935         MovDir[x][y] = right_dir;
6936     }
6937
6938     if (MovDir[x][y] != old_move_dir)
6939       MovDelay[x][y] = 9;
6940   }
6941   else if (element == EL_BALLOON)
6942   {
6943     MovDir[x][y] = game.wind_direction;
6944     MovDelay[x][y] = 0;
6945   }
6946   else if (element == EL_SPRING)
6947   {
6948     if (MovDir[x][y] & MV_HORIZONTAL)
6949     {
6950       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6951           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6952       {
6953         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6954         ResetGfxAnimation(move_x, move_y);
6955         TEST_DrawLevelField(move_x, move_y);
6956
6957         MovDir[x][y] = back_dir;
6958       }
6959       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6960                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6961         MovDir[x][y] = MV_NONE;
6962     }
6963
6964     MovDelay[x][y] = 0;
6965   }
6966   else if (element == EL_ROBOT ||
6967            element == EL_SATELLITE ||
6968            element == EL_PENGUIN ||
6969            element == EL_EMC_ANDROID)
6970   {
6971     int attr_x = -1, attr_y = -1;
6972
6973     if (game.all_players_gone)
6974     {
6975       attr_x = game.exit_x;
6976       attr_y = game.exit_y;
6977     }
6978     else
6979     {
6980       int i;
6981
6982       for (i = 0; i < MAX_PLAYERS; i++)
6983       {
6984         struct PlayerInfo *player = &stored_player[i];
6985         int jx = player->jx, jy = player->jy;
6986
6987         if (!player->active)
6988           continue;
6989
6990         if (attr_x == -1 ||
6991             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6992         {
6993           attr_x = jx;
6994           attr_y = jy;
6995         }
6996       }
6997     }
6998
6999     if (element == EL_ROBOT &&
7000         game.robot_wheel_x >= 0 &&
7001         game.robot_wheel_y >= 0 &&
7002         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7003          game.engine_version < VERSION_IDENT(3,1,0,0)))
7004     {
7005       attr_x = game.robot_wheel_x;
7006       attr_y = game.robot_wheel_y;
7007     }
7008
7009     if (element == EL_PENGUIN)
7010     {
7011       int i;
7012       static int xy[4][2] =
7013       {
7014         { 0, -1 },
7015         { -1, 0 },
7016         { +1, 0 },
7017         { 0, +1 }
7018       };
7019
7020       for (i = 0; i < NUM_DIRECTIONS; i++)
7021       {
7022         int ex = x + xy[i][0];
7023         int ey = y + xy[i][1];
7024
7025         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7026                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7027                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7028                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7029         {
7030           attr_x = ex;
7031           attr_y = ey;
7032           break;
7033         }
7034       }
7035     }
7036
7037     MovDir[x][y] = MV_NONE;
7038     if (attr_x < x)
7039       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7040     else if (attr_x > x)
7041       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7042     if (attr_y < y)
7043       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7044     else if (attr_y > y)
7045       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7046
7047     if (element == EL_ROBOT)
7048     {
7049       int newx, newy;
7050
7051       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7052         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7053       Moving2Blocked(x, y, &newx, &newy);
7054
7055       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7056         MovDelay[x][y] = 8 + 8 * !RND(3);
7057       else
7058         MovDelay[x][y] = 16;
7059     }
7060     else if (element == EL_PENGUIN)
7061     {
7062       int newx, newy;
7063
7064       MovDelay[x][y] = 1;
7065
7066       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7067       {
7068         boolean first_horiz = RND(2);
7069         int new_move_dir = MovDir[x][y];
7070
7071         MovDir[x][y] =
7072           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073         Moving2Blocked(x, y, &newx, &newy);
7074
7075         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7076           return;
7077
7078         MovDir[x][y] =
7079           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7080         Moving2Blocked(x, y, &newx, &newy);
7081
7082         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7083           return;
7084
7085         MovDir[x][y] = old_move_dir;
7086         return;
7087       }
7088     }
7089     else if (element == EL_SATELLITE)
7090     {
7091       int newx, newy;
7092
7093       MovDelay[x][y] = 1;
7094
7095       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7096       {
7097         boolean first_horiz = RND(2);
7098         int new_move_dir = MovDir[x][y];
7099
7100         MovDir[x][y] =
7101           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7102         Moving2Blocked(x, y, &newx, &newy);
7103
7104         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7105           return;
7106
7107         MovDir[x][y] =
7108           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7109         Moving2Blocked(x, y, &newx, &newy);
7110
7111         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7112           return;
7113
7114         MovDir[x][y] = old_move_dir;
7115         return;
7116       }
7117     }
7118     else if (element == EL_EMC_ANDROID)
7119     {
7120       static int check_pos[16] =
7121       {
7122         -1,             //  0 => (invalid)
7123         7,              //  1 => MV_LEFT
7124         3,              //  2 => MV_RIGHT
7125         -1,             //  3 => (invalid)
7126         1,              //  4 =>            MV_UP
7127         0,              //  5 => MV_LEFT  | MV_UP
7128         2,              //  6 => MV_RIGHT | MV_UP
7129         -1,             //  7 => (invalid)
7130         5,              //  8 =>            MV_DOWN
7131         6,              //  9 => MV_LEFT  | MV_DOWN
7132         4,              // 10 => MV_RIGHT | MV_DOWN
7133         -1,             // 11 => (invalid)
7134         -1,             // 12 => (invalid)
7135         -1,             // 13 => (invalid)
7136         -1,             // 14 => (invalid)
7137         -1,             // 15 => (invalid)
7138       };
7139       static struct
7140       {
7141         int dx, dy;
7142         int dir;
7143       } check_xy[8] =
7144       {
7145         { -1, -1,       MV_LEFT  | MV_UP   },
7146         {  0, -1,                  MV_UP   },
7147         { +1, -1,       MV_RIGHT | MV_UP   },
7148         { +1,  0,       MV_RIGHT           },
7149         { +1, +1,       MV_RIGHT | MV_DOWN },
7150         {  0, +1,                  MV_DOWN },
7151         { -1, +1,       MV_LEFT  | MV_DOWN },
7152         { -1,  0,       MV_LEFT            },
7153       };
7154       int start_pos, check_order;
7155       boolean can_clone = FALSE;
7156       int i;
7157
7158       // check if there is any free field around current position
7159       for (i = 0; i < 8; i++)
7160       {
7161         int newx = x + check_xy[i].dx;
7162         int newy = y + check_xy[i].dy;
7163
7164         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7165         {
7166           can_clone = TRUE;
7167
7168           break;
7169         }
7170       }
7171
7172       if (can_clone)            // randomly find an element to clone
7173       {
7174         can_clone = FALSE;
7175
7176         start_pos = check_pos[RND(8)];
7177         check_order = (RND(2) ? -1 : +1);
7178
7179         for (i = 0; i < 8; i++)
7180         {
7181           int pos_raw = start_pos + i * check_order;
7182           int pos = (pos_raw + 8) % 8;
7183           int newx = x + check_xy[pos].dx;
7184           int newy = y + check_xy[pos].dy;
7185
7186           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7187           {
7188             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7189             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7190
7191             Store[x][y] = Feld[newx][newy];
7192
7193             can_clone = TRUE;
7194
7195             break;
7196           }
7197         }
7198       }
7199
7200       if (can_clone)            // randomly find a direction to move
7201       {
7202         can_clone = FALSE;
7203
7204         start_pos = check_pos[RND(8)];
7205         check_order = (RND(2) ? -1 : +1);
7206
7207         for (i = 0; i < 8; i++)
7208         {
7209           int pos_raw = start_pos + i * check_order;
7210           int pos = (pos_raw + 8) % 8;
7211           int newx = x + check_xy[pos].dx;
7212           int newy = y + check_xy[pos].dy;
7213           int new_move_dir = check_xy[pos].dir;
7214
7215           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7216           {
7217             MovDir[x][y] = new_move_dir;
7218             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7219
7220             can_clone = TRUE;
7221
7222             break;
7223           }
7224         }
7225       }
7226
7227       if (can_clone)            // cloning and moving successful
7228         return;
7229
7230       // cannot clone -- try to move towards player
7231
7232       start_pos = check_pos[MovDir[x][y] & 0x0f];
7233       check_order = (RND(2) ? -1 : +1);
7234
7235       for (i = 0; i < 3; i++)
7236       {
7237         // first check start_pos, then previous/next or (next/previous) pos
7238         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7239         int pos = (pos_raw + 8) % 8;
7240         int newx = x + check_xy[pos].dx;
7241         int newy = y + check_xy[pos].dy;
7242         int new_move_dir = check_xy[pos].dir;
7243
7244         if (IS_PLAYER(newx, newy))
7245           break;
7246
7247         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7248         {
7249           MovDir[x][y] = new_move_dir;
7250           MovDelay[x][y] = level.android_move_time * 8 + 1;
7251
7252           break;
7253         }
7254       }
7255     }
7256   }
7257   else if (move_pattern == MV_TURNING_LEFT ||
7258            move_pattern == MV_TURNING_RIGHT ||
7259            move_pattern == MV_TURNING_LEFT_RIGHT ||
7260            move_pattern == MV_TURNING_RIGHT_LEFT ||
7261            move_pattern == MV_TURNING_RANDOM ||
7262            move_pattern == MV_ALL_DIRECTIONS)
7263   {
7264     boolean can_turn_left =
7265       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7266     boolean can_turn_right =
7267       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7268
7269     if (element_info[element].move_stepsize == 0)       // "not moving"
7270       return;
7271
7272     if (move_pattern == MV_TURNING_LEFT)
7273       MovDir[x][y] = left_dir;
7274     else if (move_pattern == MV_TURNING_RIGHT)
7275       MovDir[x][y] = right_dir;
7276     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7277       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7278     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7279       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7280     else if (move_pattern == MV_TURNING_RANDOM)
7281       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7282                       can_turn_right && !can_turn_left ? right_dir :
7283                       RND(2) ? left_dir : right_dir);
7284     else if (can_turn_left && can_turn_right)
7285       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7286     else if (can_turn_left)
7287       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7288     else if (can_turn_right)
7289       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7290     else
7291       MovDir[x][y] = back_dir;
7292
7293     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7294   }
7295   else if (move_pattern == MV_HORIZONTAL ||
7296            move_pattern == MV_VERTICAL)
7297   {
7298     if (move_pattern & old_move_dir)
7299       MovDir[x][y] = back_dir;
7300     else if (move_pattern == MV_HORIZONTAL)
7301       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7302     else if (move_pattern == MV_VERTICAL)
7303       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7304
7305     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7306   }
7307   else if (move_pattern & MV_ANY_DIRECTION)
7308   {
7309     MovDir[x][y] = move_pattern;
7310     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7311   }
7312   else if (move_pattern & MV_WIND_DIRECTION)
7313   {
7314     MovDir[x][y] = game.wind_direction;
7315     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7318   {
7319     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7320       MovDir[x][y] = left_dir;
7321     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7322       MovDir[x][y] = right_dir;
7323
7324     if (MovDir[x][y] != old_move_dir)
7325       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7326   }
7327   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7328   {
7329     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7330       MovDir[x][y] = right_dir;
7331     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7332       MovDir[x][y] = left_dir;
7333
7334     if (MovDir[x][y] != old_move_dir)
7335       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7336   }
7337   else if (move_pattern == MV_TOWARDS_PLAYER ||
7338            move_pattern == MV_AWAY_FROM_PLAYER)
7339   {
7340     int attr_x = -1, attr_y = -1;
7341     int newx, newy;
7342     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7343
7344     if (game.all_players_gone)
7345     {
7346       attr_x = game.exit_x;
7347       attr_y = game.exit_y;
7348     }
7349     else
7350     {
7351       int i;
7352
7353       for (i = 0; i < MAX_PLAYERS; i++)
7354       {
7355         struct PlayerInfo *player = &stored_player[i];
7356         int jx = player->jx, jy = player->jy;
7357
7358         if (!player->active)
7359           continue;
7360
7361         if (attr_x == -1 ||
7362             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7363         {
7364           attr_x = jx;
7365           attr_y = jy;
7366         }
7367       }
7368     }
7369
7370     MovDir[x][y] = MV_NONE;
7371     if (attr_x < x)
7372       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7373     else if (attr_x > x)
7374       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7375     if (attr_y < y)
7376       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7377     else if (attr_y > y)
7378       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7379
7380     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7381
7382     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7383     {
7384       boolean first_horiz = RND(2);
7385       int new_move_dir = MovDir[x][y];
7386
7387       if (element_info[element].move_stepsize == 0)     // "not moving"
7388       {
7389         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7390         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7391
7392         return;
7393       }
7394
7395       MovDir[x][y] =
7396         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7397       Moving2Blocked(x, y, &newx, &newy);
7398
7399       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7400         return;
7401
7402       MovDir[x][y] =
7403         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7404       Moving2Blocked(x, y, &newx, &newy);
7405
7406       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7407         return;
7408
7409       MovDir[x][y] = old_move_dir;
7410     }
7411   }
7412   else if (move_pattern == MV_WHEN_PUSHED ||
7413            move_pattern == MV_WHEN_DROPPED)
7414   {
7415     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = MV_NONE;
7417
7418     MovDelay[x][y] = 0;
7419   }
7420   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7421   {
7422     static int test_xy[7][2] =
7423     {
7424       { 0, -1 },
7425       { -1, 0 },
7426       { +1, 0 },
7427       { 0, +1 },
7428       { 0, -1 },
7429       { -1, 0 },
7430       { +1, 0 },
7431     };
7432     static int test_dir[7] =
7433     {
7434       MV_UP,
7435       MV_LEFT,
7436       MV_RIGHT,
7437       MV_DOWN,
7438       MV_UP,
7439       MV_LEFT,
7440       MV_RIGHT,
7441     };
7442     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7443     int move_preference = -1000000;     // start with very low preference
7444     int new_move_dir = MV_NONE;
7445     int start_test = RND(4);
7446     int i;
7447
7448     for (i = 0; i < NUM_DIRECTIONS; i++)
7449     {
7450       int move_dir = test_dir[start_test + i];
7451       int move_dir_preference;
7452
7453       xx = x + test_xy[start_test + i][0];
7454       yy = y + test_xy[start_test + i][1];
7455
7456       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7457           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7458       {
7459         new_move_dir = move_dir;
7460
7461         break;
7462       }
7463
7464       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7465         continue;
7466
7467       move_dir_preference = -1 * RunnerVisit[xx][yy];
7468       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7469         move_dir_preference = PlayerVisit[xx][yy];
7470
7471       if (move_dir_preference > move_preference)
7472       {
7473         // prefer field that has not been visited for the longest time
7474         move_preference = move_dir_preference;
7475         new_move_dir = move_dir;
7476       }
7477       else if (move_dir_preference == move_preference &&
7478                move_dir == old_move_dir)
7479       {
7480         // prefer last direction when all directions are preferred equally
7481         move_preference = move_dir_preference;
7482         new_move_dir = move_dir;
7483       }
7484     }
7485
7486     MovDir[x][y] = new_move_dir;
7487     if (old_move_dir != new_move_dir)
7488       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7489   }
7490 }
7491
7492 static void TurnRound(int x, int y)
7493 {
7494   int direction = MovDir[x][y];
7495
7496   TurnRoundExt(x, y);
7497
7498   GfxDir[x][y] = MovDir[x][y];
7499
7500   if (direction != MovDir[x][y])
7501     GfxFrame[x][y] = 0;
7502
7503   if (MovDelay[x][y])
7504     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7505
7506   ResetGfxFrame(x, y);
7507 }
7508
7509 static boolean JustBeingPushed(int x, int y)
7510 {
7511   int i;
7512
7513   for (i = 0; i < MAX_PLAYERS; i++)
7514   {
7515     struct PlayerInfo *player = &stored_player[i];
7516
7517     if (player->active && player->is_pushing && player->MovPos)
7518     {
7519       int next_jx = player->jx + (player->jx - player->last_jx);
7520       int next_jy = player->jy + (player->jy - player->last_jy);
7521
7522       if (x == next_jx && y == next_jy)
7523         return TRUE;
7524     }
7525   }
7526
7527   return FALSE;
7528 }
7529
7530 static void StartMoving(int x, int y)
7531 {
7532   boolean started_moving = FALSE;       // some elements can fall _and_ move
7533   int element = Feld[x][y];
7534
7535   if (Stop[x][y])
7536     return;
7537
7538   if (MovDelay[x][y] == 0)
7539     GfxAction[x][y] = ACTION_DEFAULT;
7540
7541   if (CAN_FALL(element) && y < lev_fieldy - 1)
7542   {
7543     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7544         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7545       if (JustBeingPushed(x, y))
7546         return;
7547
7548     if (element == EL_QUICKSAND_FULL)
7549     {
7550       if (IS_FREE(x, y + 1))
7551       {
7552         InitMovingField(x, y, MV_DOWN);
7553         started_moving = TRUE;
7554
7555         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7556 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7557         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7558           Store[x][y] = EL_ROCK;
7559 #else
7560         Store[x][y] = EL_ROCK;
7561 #endif
7562
7563         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7564       }
7565       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7566       {
7567         if (!MovDelay[x][y])
7568         {
7569           MovDelay[x][y] = TILEY + 1;
7570
7571           ResetGfxAnimation(x, y);
7572           ResetGfxAnimation(x, y + 1);
7573         }
7574
7575         if (MovDelay[x][y])
7576         {
7577           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7578           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7579
7580           MovDelay[x][y]--;
7581           if (MovDelay[x][y])
7582             return;
7583         }
7584
7585         Feld[x][y] = EL_QUICKSAND_EMPTY;
7586         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7587         Store[x][y + 1] = Store[x][y];
7588         Store[x][y] = 0;
7589
7590         PlayLevelSoundAction(x, y, ACTION_FILLING);
7591       }
7592       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7593       {
7594         if (!MovDelay[x][y])
7595         {
7596           MovDelay[x][y] = TILEY + 1;
7597
7598           ResetGfxAnimation(x, y);
7599           ResetGfxAnimation(x, y + 1);
7600         }
7601
7602         if (MovDelay[x][y])
7603         {
7604           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7605           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7606
7607           MovDelay[x][y]--;
7608           if (MovDelay[x][y])
7609             return;
7610         }
7611
7612         Feld[x][y] = EL_QUICKSAND_EMPTY;
7613         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7614         Store[x][y + 1] = Store[x][y];
7615         Store[x][y] = 0;
7616
7617         PlayLevelSoundAction(x, y, ACTION_FILLING);
7618       }
7619     }
7620     else if (element == EL_QUICKSAND_FAST_FULL)
7621     {
7622       if (IS_FREE(x, y + 1))
7623       {
7624         InitMovingField(x, y, MV_DOWN);
7625         started_moving = TRUE;
7626
7627         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7628 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7629         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7630           Store[x][y] = EL_ROCK;
7631 #else
7632         Store[x][y] = EL_ROCK;
7633 #endif
7634
7635         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7636       }
7637       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7638       {
7639         if (!MovDelay[x][y])
7640         {
7641           MovDelay[x][y] = TILEY + 1;
7642
7643           ResetGfxAnimation(x, y);
7644           ResetGfxAnimation(x, y + 1);
7645         }
7646
7647         if (MovDelay[x][y])
7648         {
7649           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7650           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7651
7652           MovDelay[x][y]--;
7653           if (MovDelay[x][y])
7654             return;
7655         }
7656
7657         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7658         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7659         Store[x][y + 1] = Store[x][y];
7660         Store[x][y] = 0;
7661
7662         PlayLevelSoundAction(x, y, ACTION_FILLING);
7663       }
7664       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7665       {
7666         if (!MovDelay[x][y])
7667         {
7668           MovDelay[x][y] = TILEY + 1;
7669
7670           ResetGfxAnimation(x, y);
7671           ResetGfxAnimation(x, y + 1);
7672         }
7673
7674         if (MovDelay[x][y])
7675         {
7676           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7677           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7678
7679           MovDelay[x][y]--;
7680           if (MovDelay[x][y])
7681             return;
7682         }
7683
7684         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7685         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7686         Store[x][y + 1] = Store[x][y];
7687         Store[x][y] = 0;
7688
7689         PlayLevelSoundAction(x, y, ACTION_FILLING);
7690       }
7691     }
7692     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7693              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7694     {
7695       InitMovingField(x, y, MV_DOWN);
7696       started_moving = TRUE;
7697
7698       Feld[x][y] = EL_QUICKSAND_FILLING;
7699       Store[x][y] = element;
7700
7701       PlayLevelSoundAction(x, y, ACTION_FILLING);
7702     }
7703     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7704              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7705     {
7706       InitMovingField(x, y, MV_DOWN);
7707       started_moving = TRUE;
7708
7709       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7710       Store[x][y] = element;
7711
7712       PlayLevelSoundAction(x, y, ACTION_FILLING);
7713     }
7714     else if (element == EL_MAGIC_WALL_FULL)
7715     {
7716       if (IS_FREE(x, y + 1))
7717       {
7718         InitMovingField(x, y, MV_DOWN);
7719         started_moving = TRUE;
7720
7721         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7722         Store[x][y] = EL_CHANGED(Store[x][y]);
7723       }
7724       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7725       {
7726         if (!MovDelay[x][y])
7727           MovDelay[x][y] = TILEY / 4 + 1;
7728
7729         if (MovDelay[x][y])
7730         {
7731           MovDelay[x][y]--;
7732           if (MovDelay[x][y])
7733             return;
7734         }
7735
7736         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7737         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7738         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7739         Store[x][y] = 0;
7740       }
7741     }
7742     else if (element == EL_BD_MAGIC_WALL_FULL)
7743     {
7744       if (IS_FREE(x, y + 1))
7745       {
7746         InitMovingField(x, y, MV_DOWN);
7747         started_moving = TRUE;
7748
7749         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7750         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7751       }
7752       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7753       {
7754         if (!MovDelay[x][y])
7755           MovDelay[x][y] = TILEY / 4 + 1;
7756
7757         if (MovDelay[x][y])
7758         {
7759           MovDelay[x][y]--;
7760           if (MovDelay[x][y])
7761             return;
7762         }
7763
7764         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7765         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7766         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7767         Store[x][y] = 0;
7768       }
7769     }
7770     else if (element == EL_DC_MAGIC_WALL_FULL)
7771     {
7772       if (IS_FREE(x, y + 1))
7773       {
7774         InitMovingField(x, y, MV_DOWN);
7775         started_moving = TRUE;
7776
7777         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7778         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7779       }
7780       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7781       {
7782         if (!MovDelay[x][y])
7783           MovDelay[x][y] = TILEY / 4 + 1;
7784
7785         if (MovDelay[x][y])
7786         {
7787           MovDelay[x][y]--;
7788           if (MovDelay[x][y])
7789             return;
7790         }
7791
7792         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7793         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7794         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7795         Store[x][y] = 0;
7796       }
7797     }
7798     else if ((CAN_PASS_MAGIC_WALL(element) &&
7799               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7800                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7801              (CAN_PASS_DC_MAGIC_WALL(element) &&
7802               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7803
7804     {
7805       InitMovingField(x, y, MV_DOWN);
7806       started_moving = TRUE;
7807
7808       Feld[x][y] =
7809         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7810          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7811          EL_DC_MAGIC_WALL_FILLING);
7812       Store[x][y] = element;
7813     }
7814     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7815     {
7816       SplashAcid(x, y + 1);
7817
7818       InitMovingField(x, y, MV_DOWN);
7819       started_moving = TRUE;
7820
7821       Store[x][y] = EL_ACID;
7822     }
7823     else if (
7824              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7825               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7826              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7827               CAN_FALL(element) && WasJustFalling[x][y] &&
7828               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7829
7830              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7831               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7832               (Feld[x][y + 1] == EL_BLOCKED)))
7833     {
7834       /* this is needed for a special case not covered by calling "Impact()"
7835          from "ContinueMoving()": if an element moves to a tile directly below
7836          another element which was just falling on that tile (which was empty
7837          in the previous frame), the falling element above would just stop
7838          instead of smashing the element below (in previous version, the above
7839          element was just checked for "moving" instead of "falling", resulting
7840          in incorrect smashes caused by horizontal movement of the above
7841          element; also, the case of the player being the element to smash was
7842          simply not covered here... :-/ ) */
7843
7844       CheckCollision[x][y] = 0;
7845       CheckImpact[x][y] = 0;
7846
7847       Impact(x, y);
7848     }
7849     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7850     {
7851       if (MovDir[x][y] == MV_NONE)
7852       {
7853         InitMovingField(x, y, MV_DOWN);
7854         started_moving = TRUE;
7855       }
7856     }
7857     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7858     {
7859       if (WasJustFalling[x][y]) // prevent animation from being restarted
7860         MovDir[x][y] = MV_DOWN;
7861
7862       InitMovingField(x, y, MV_DOWN);
7863       started_moving = TRUE;
7864     }
7865     else if (element == EL_AMOEBA_DROP)
7866     {
7867       Feld[x][y] = EL_AMOEBA_GROWING;
7868       Store[x][y] = EL_AMOEBA_WET;
7869     }
7870     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7871               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7872              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7873              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7874     {
7875       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7876                                 (IS_FREE(x - 1, y + 1) ||
7877                                  Feld[x - 1][y + 1] == EL_ACID));
7878       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7879                                 (IS_FREE(x + 1, y + 1) ||
7880                                  Feld[x + 1][y + 1] == EL_ACID));
7881       boolean can_fall_any  = (can_fall_left || can_fall_right);
7882       boolean can_fall_both = (can_fall_left && can_fall_right);
7883       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7884
7885       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7886       {
7887         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7888           can_fall_right = FALSE;
7889         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7890           can_fall_left = FALSE;
7891         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7892           can_fall_right = FALSE;
7893         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7894           can_fall_left = FALSE;
7895
7896         can_fall_any  = (can_fall_left || can_fall_right);
7897         can_fall_both = FALSE;
7898       }
7899
7900       if (can_fall_both)
7901       {
7902         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7903           can_fall_right = FALSE;       // slip down on left side
7904         else
7905           can_fall_left = !(can_fall_right = RND(2));
7906
7907         can_fall_both = FALSE;
7908       }
7909
7910       if (can_fall_any)
7911       {
7912         // if not determined otherwise, prefer left side for slipping down
7913         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7914         started_moving = TRUE;
7915       }
7916     }
7917     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7918     {
7919       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7920       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7921       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7922       int belt_dir = game.belt_dir[belt_nr];
7923
7924       if ((belt_dir == MV_LEFT  && left_is_free) ||
7925           (belt_dir == MV_RIGHT && right_is_free))
7926       {
7927         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7928
7929         InitMovingField(x, y, belt_dir);
7930         started_moving = TRUE;
7931
7932         Pushed[x][y] = TRUE;
7933         Pushed[nextx][y] = TRUE;
7934
7935         GfxAction[x][y] = ACTION_DEFAULT;
7936       }
7937       else
7938       {
7939         MovDir[x][y] = 0;       // if element was moving, stop it
7940       }
7941     }
7942   }
7943
7944   // not "else if" because of elements that can fall and move (EL_SPRING)
7945   if (CAN_MOVE(element) && !started_moving)
7946   {
7947     int move_pattern = element_info[element].move_pattern;
7948     int newx, newy;
7949
7950     Moving2Blocked(x, y, &newx, &newy);
7951
7952     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7953       return;
7954
7955     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7956         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7957     {
7958       WasJustMoving[x][y] = 0;
7959       CheckCollision[x][y] = 0;
7960
7961       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7962
7963       if (Feld[x][y] != element)        // element has changed
7964         return;
7965     }
7966
7967     if (!MovDelay[x][y])        // start new movement phase
7968     {
7969       // all objects that can change their move direction after each step
7970       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7971
7972       if (element != EL_YAMYAM &&
7973           element != EL_DARK_YAMYAM &&
7974           element != EL_PACMAN &&
7975           !(move_pattern & MV_ANY_DIRECTION) &&
7976           move_pattern != MV_TURNING_LEFT &&
7977           move_pattern != MV_TURNING_RIGHT &&
7978           move_pattern != MV_TURNING_LEFT_RIGHT &&
7979           move_pattern != MV_TURNING_RIGHT_LEFT &&
7980           move_pattern != MV_TURNING_RANDOM)
7981       {
7982         TurnRound(x, y);
7983
7984         if (MovDelay[x][y] && (element == EL_BUG ||
7985                                element == EL_SPACESHIP ||
7986                                element == EL_SP_SNIKSNAK ||
7987                                element == EL_SP_ELECTRON ||
7988                                element == EL_MOLE))
7989           TEST_DrawLevelField(x, y);
7990       }
7991     }
7992
7993     if (MovDelay[x][y])         // wait some time before next movement
7994     {
7995       MovDelay[x][y]--;
7996
7997       if (element == EL_ROBOT ||
7998           element == EL_YAMYAM ||
7999           element == EL_DARK_YAMYAM)
8000       {
8001         DrawLevelElementAnimationIfNeeded(x, y, element);
8002         PlayLevelSoundAction(x, y, ACTION_WAITING);
8003       }
8004       else if (element == EL_SP_ELECTRON)
8005         DrawLevelElementAnimationIfNeeded(x, y, element);
8006       else if (element == EL_DRAGON)
8007       {
8008         int i;
8009         int dir = MovDir[x][y];
8010         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8011         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8012         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8013                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8014                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8015                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8016         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8017
8018         GfxAction[x][y] = ACTION_ATTACKING;
8019
8020         if (IS_PLAYER(x, y))
8021           DrawPlayerField(x, y);
8022         else
8023           TEST_DrawLevelField(x, y);
8024
8025         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8026
8027         for (i = 1; i <= 3; i++)
8028         {
8029           int xx = x + i * dx;
8030           int yy = y + i * dy;
8031           int sx = SCREENX(xx);
8032           int sy = SCREENY(yy);
8033           int flame_graphic = graphic + (i - 1);
8034
8035           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8036             break;
8037
8038           if (MovDelay[x][y])
8039           {
8040             int flamed = MovingOrBlocked2Element(xx, yy);
8041
8042             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8043               Bang(xx, yy);
8044             else
8045               RemoveMovingField(xx, yy);
8046
8047             ChangeDelay[xx][yy] = 0;
8048
8049             Feld[xx][yy] = EL_FLAMES;
8050
8051             if (IN_SCR_FIELD(sx, sy))
8052             {
8053               TEST_DrawLevelFieldCrumbled(xx, yy);
8054               DrawGraphic(sx, sy, flame_graphic, frame);
8055             }
8056           }
8057           else
8058           {
8059             if (Feld[xx][yy] == EL_FLAMES)
8060               Feld[xx][yy] = EL_EMPTY;
8061             TEST_DrawLevelField(xx, yy);
8062           }
8063         }
8064       }
8065
8066       if (MovDelay[x][y])       // element still has to wait some time
8067       {
8068         PlayLevelSoundAction(x, y, ACTION_WAITING);
8069
8070         return;
8071       }
8072     }
8073
8074     // now make next step
8075
8076     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8077
8078     if (DONT_COLLIDE_WITH(element) &&
8079         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8080         !PLAYER_ENEMY_PROTECTED(newx, newy))
8081     {
8082       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8083
8084       return;
8085     }
8086
8087     else if (CAN_MOVE_INTO_ACID(element) &&
8088              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8089              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8090              (MovDir[x][y] == MV_DOWN ||
8091               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8092     {
8093       SplashAcid(newx, newy);
8094       Store[x][y] = EL_ACID;
8095     }
8096     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8097     {
8098       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8099           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8100           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8101           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8102       {
8103         RemoveField(x, y);
8104         TEST_DrawLevelField(x, y);
8105
8106         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8107         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8108           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8109
8110         game.friends_still_needed--;
8111         if (!game.friends_still_needed &&
8112             !game.GameOver &&
8113             game.all_players_gone)
8114           LevelSolved();
8115
8116         return;
8117       }
8118       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8119       {
8120         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8121           TEST_DrawLevelField(newx, newy);
8122         else
8123           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8124       }
8125       else if (!IS_FREE(newx, newy))
8126       {
8127         GfxAction[x][y] = ACTION_WAITING;
8128
8129         if (IS_PLAYER(x, y))
8130           DrawPlayerField(x, y);
8131         else
8132           TEST_DrawLevelField(x, y);
8133
8134         return;
8135       }
8136     }
8137     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8138     {
8139       if (IS_FOOD_PIG(Feld[newx][newy]))
8140       {
8141         if (IS_MOVING(newx, newy))
8142           RemoveMovingField(newx, newy);
8143         else
8144         {
8145           Feld[newx][newy] = EL_EMPTY;
8146           TEST_DrawLevelField(newx, newy);
8147         }
8148
8149         PlayLevelSound(x, y, SND_PIG_DIGGING);
8150       }
8151       else if (!IS_FREE(newx, newy))
8152       {
8153         if (IS_PLAYER(x, y))
8154           DrawPlayerField(x, y);
8155         else
8156           TEST_DrawLevelField(x, y);
8157
8158         return;
8159       }
8160     }
8161     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8162     {
8163       if (Store[x][y] != EL_EMPTY)
8164       {
8165         boolean can_clone = FALSE;
8166         int xx, yy;
8167
8168         // check if element to clone is still there
8169         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8170         {
8171           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8172           {
8173             can_clone = TRUE;
8174
8175             break;
8176           }
8177         }
8178
8179         // cannot clone or target field not free anymore -- do not clone
8180         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8181           Store[x][y] = EL_EMPTY;
8182       }
8183
8184       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8185       {
8186         if (IS_MV_DIAGONAL(MovDir[x][y]))
8187         {
8188           int diagonal_move_dir = MovDir[x][y];
8189           int stored = Store[x][y];
8190           int change_delay = 8;
8191           int graphic;
8192
8193           // android is moving diagonally
8194
8195           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8196
8197           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8198           GfxElement[x][y] = EL_EMC_ANDROID;
8199           GfxAction[x][y] = ACTION_SHRINKING;
8200           GfxDir[x][y] = diagonal_move_dir;
8201           ChangeDelay[x][y] = change_delay;
8202
8203           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8204                                    GfxDir[x][y]);
8205
8206           DrawLevelGraphicAnimation(x, y, graphic);
8207           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8208
8209           if (Feld[newx][newy] == EL_ACID)
8210           {
8211             SplashAcid(newx, newy);
8212
8213             return;
8214           }
8215
8216           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8217
8218           Store[newx][newy] = EL_EMC_ANDROID;
8219           GfxElement[newx][newy] = EL_EMC_ANDROID;
8220           GfxAction[newx][newy] = ACTION_GROWING;
8221           GfxDir[newx][newy] = diagonal_move_dir;
8222           ChangeDelay[newx][newy] = change_delay;
8223
8224           graphic = el_act_dir2img(GfxElement[newx][newy],
8225                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8226
8227           DrawLevelGraphicAnimation(newx, newy, graphic);
8228           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8229
8230           return;
8231         }
8232         else
8233         {
8234           Feld[newx][newy] = EL_EMPTY;
8235           TEST_DrawLevelField(newx, newy);
8236
8237           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8238         }
8239       }
8240       else if (!IS_FREE(newx, newy))
8241       {
8242         return;
8243       }
8244     }
8245     else if (IS_CUSTOM_ELEMENT(element) &&
8246              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8247     {
8248       if (!DigFieldByCE(newx, newy, element))
8249         return;
8250
8251       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8252       {
8253         RunnerVisit[x][y] = FrameCounter;
8254         PlayerVisit[x][y] /= 8;         // expire player visit path
8255       }
8256     }
8257     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8258     {
8259       if (!IS_FREE(newx, newy))
8260       {
8261         if (IS_PLAYER(x, y))
8262           DrawPlayerField(x, y);
8263         else
8264           TEST_DrawLevelField(x, y);
8265
8266         return;
8267       }
8268       else
8269       {
8270         boolean wanna_flame = !RND(10);
8271         int dx = newx - x, dy = newy - y;
8272         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8273         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8274         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8275                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8276         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8277                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8278
8279         if ((wanna_flame ||
8280              IS_CLASSIC_ENEMY(element1) ||
8281              IS_CLASSIC_ENEMY(element2)) &&
8282             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8283             element1 != EL_FLAMES && element2 != EL_FLAMES)
8284         {
8285           ResetGfxAnimation(x, y);
8286           GfxAction[x][y] = ACTION_ATTACKING;
8287
8288           if (IS_PLAYER(x, y))
8289             DrawPlayerField(x, y);
8290           else
8291             TEST_DrawLevelField(x, y);
8292
8293           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8294
8295           MovDelay[x][y] = 50;
8296
8297           Feld[newx][newy] = EL_FLAMES;
8298           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8299             Feld[newx1][newy1] = EL_FLAMES;
8300           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8301             Feld[newx2][newy2] = EL_FLAMES;
8302
8303           return;
8304         }
8305       }
8306     }
8307     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8308              Feld[newx][newy] == EL_DIAMOND)
8309     {
8310       if (IS_MOVING(newx, newy))
8311         RemoveMovingField(newx, newy);
8312       else
8313       {
8314         Feld[newx][newy] = EL_EMPTY;
8315         TEST_DrawLevelField(newx, newy);
8316       }
8317
8318       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8319     }
8320     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8321              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8322     {
8323       if (AmoebaNr[newx][newy])
8324       {
8325         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8326         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8327             Feld[newx][newy] == EL_BD_AMOEBA)
8328           AmoebaCnt[AmoebaNr[newx][newy]]--;
8329       }
8330
8331       if (IS_MOVING(newx, newy))
8332       {
8333         RemoveMovingField(newx, newy);
8334       }
8335       else
8336       {
8337         Feld[newx][newy] = EL_EMPTY;
8338         TEST_DrawLevelField(newx, newy);
8339       }
8340
8341       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8342     }
8343     else if ((element == EL_PACMAN || element == EL_MOLE)
8344              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8345     {
8346       if (AmoebaNr[newx][newy])
8347       {
8348         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8349         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8350             Feld[newx][newy] == EL_BD_AMOEBA)
8351           AmoebaCnt[AmoebaNr[newx][newy]]--;
8352       }
8353
8354       if (element == EL_MOLE)
8355       {
8356         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8357         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8358
8359         ResetGfxAnimation(x, y);
8360         GfxAction[x][y] = ACTION_DIGGING;
8361         TEST_DrawLevelField(x, y);
8362
8363         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8364
8365         return;                         // wait for shrinking amoeba
8366       }
8367       else      // element == EL_PACMAN
8368       {
8369         Feld[newx][newy] = EL_EMPTY;
8370         TEST_DrawLevelField(newx, newy);
8371         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8372       }
8373     }
8374     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8375              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8376               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8377     {
8378       // wait for shrinking amoeba to completely disappear
8379       return;
8380     }
8381     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8382     {
8383       // object was running against a wall
8384
8385       TurnRound(x, y);
8386
8387       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8388         DrawLevelElementAnimation(x, y, element);
8389
8390       if (DONT_TOUCH(element))
8391         TestIfBadThingTouchesPlayer(x, y);
8392
8393       return;
8394     }
8395
8396     InitMovingField(x, y, MovDir[x][y]);
8397
8398     PlayLevelSoundAction(x, y, ACTION_MOVING);
8399   }
8400
8401   if (MovDir[x][y])
8402     ContinueMoving(x, y);
8403 }
8404
8405 void ContinueMoving(int x, int y)
8406 {
8407   int element = Feld[x][y];
8408   struct ElementInfo *ei = &element_info[element];
8409   int direction = MovDir[x][y];
8410   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8411   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8412   int newx = x + dx, newy = y + dy;
8413   int stored = Store[x][y];
8414   int stored_new = Store[newx][newy];
8415   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8416   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8417   boolean last_line = (newy == lev_fieldy - 1);
8418
8419   MovPos[x][y] += getElementMoveStepsize(x, y);
8420
8421   if (pushed_by_player) // special case: moving object pushed by player
8422     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8423
8424   if (ABS(MovPos[x][y]) < TILEX)
8425   {
8426     TEST_DrawLevelField(x, y);
8427
8428     return;     // element is still moving
8429   }
8430
8431   // element reached destination field
8432
8433   Feld[x][y] = EL_EMPTY;
8434   Feld[newx][newy] = element;
8435   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8436
8437   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8438   {
8439     element = Feld[newx][newy] = EL_ACID;
8440   }
8441   else if (element == EL_MOLE)
8442   {
8443     Feld[x][y] = EL_SAND;
8444
8445     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8446   }
8447   else if (element == EL_QUICKSAND_FILLING)
8448   {
8449     element = Feld[newx][newy] = get_next_element(element);
8450     Store[newx][newy] = Store[x][y];
8451   }
8452   else if (element == EL_QUICKSAND_EMPTYING)
8453   {
8454     Feld[x][y] = get_next_element(element);
8455     element = Feld[newx][newy] = Store[x][y];
8456   }
8457   else if (element == EL_QUICKSAND_FAST_FILLING)
8458   {
8459     element = Feld[newx][newy] = get_next_element(element);
8460     Store[newx][newy] = Store[x][y];
8461   }
8462   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8463   {
8464     Feld[x][y] = get_next_element(element);
8465     element = Feld[newx][newy] = Store[x][y];
8466   }
8467   else if (element == EL_MAGIC_WALL_FILLING)
8468   {
8469     element = Feld[newx][newy] = get_next_element(element);
8470     if (!game.magic_wall_active)
8471       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8472     Store[newx][newy] = Store[x][y];
8473   }
8474   else if (element == EL_MAGIC_WALL_EMPTYING)
8475   {
8476     Feld[x][y] = get_next_element(element);
8477     if (!game.magic_wall_active)
8478       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8479     element = Feld[newx][newy] = Store[x][y];
8480
8481     InitField(newx, newy, FALSE);
8482   }
8483   else if (element == EL_BD_MAGIC_WALL_FILLING)
8484   {
8485     element = Feld[newx][newy] = get_next_element(element);
8486     if (!game.magic_wall_active)
8487       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8488     Store[newx][newy] = Store[x][y];
8489   }
8490   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8491   {
8492     Feld[x][y] = get_next_element(element);
8493     if (!game.magic_wall_active)
8494       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8495     element = Feld[newx][newy] = Store[x][y];
8496
8497     InitField(newx, newy, FALSE);
8498   }
8499   else if (element == EL_DC_MAGIC_WALL_FILLING)
8500   {
8501     element = Feld[newx][newy] = get_next_element(element);
8502     if (!game.magic_wall_active)
8503       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8504     Store[newx][newy] = Store[x][y];
8505   }
8506   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8507   {
8508     Feld[x][y] = get_next_element(element);
8509     if (!game.magic_wall_active)
8510       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8511     element = Feld[newx][newy] = Store[x][y];
8512
8513     InitField(newx, newy, FALSE);
8514   }
8515   else if (element == EL_AMOEBA_DROPPING)
8516   {
8517     Feld[x][y] = get_next_element(element);
8518     element = Feld[newx][newy] = Store[x][y];
8519   }
8520   else if (element == EL_SOKOBAN_OBJECT)
8521   {
8522     if (Back[x][y])
8523       Feld[x][y] = Back[x][y];
8524
8525     if (Back[newx][newy])
8526       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8527
8528     Back[x][y] = Back[newx][newy] = 0;
8529   }
8530
8531   Store[x][y] = EL_EMPTY;
8532   MovPos[x][y] = 0;
8533   MovDir[x][y] = 0;
8534   MovDelay[x][y] = 0;
8535
8536   MovDelay[newx][newy] = 0;
8537
8538   if (CAN_CHANGE_OR_HAS_ACTION(element))
8539   {
8540     // copy element change control values to new field
8541     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8542     ChangePage[newx][newy]  = ChangePage[x][y];
8543     ChangeCount[newx][newy] = ChangeCount[x][y];
8544     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8545   }
8546
8547   CustomValue[newx][newy] = CustomValue[x][y];
8548
8549   ChangeDelay[x][y] = 0;
8550   ChangePage[x][y] = -1;
8551   ChangeCount[x][y] = 0;
8552   ChangeEvent[x][y] = -1;
8553
8554   CustomValue[x][y] = 0;
8555
8556   // copy animation control values to new field
8557   GfxFrame[newx][newy]  = GfxFrame[x][y];
8558   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8559   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8560   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8561
8562   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8563
8564   // some elements can leave other elements behind after moving
8565   if (ei->move_leave_element != EL_EMPTY &&
8566       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8567       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8568   {
8569     int move_leave_element = ei->move_leave_element;
8570
8571     // this makes it possible to leave the removed element again
8572     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8573       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8574
8575     Feld[x][y] = move_leave_element;
8576
8577     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8578       MovDir[x][y] = direction;
8579
8580     InitField(x, y, FALSE);
8581
8582     if (GFX_CRUMBLED(Feld[x][y]))
8583       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8584
8585     if (ELEM_IS_PLAYER(move_leave_element))
8586       RelocatePlayer(x, y, move_leave_element);
8587   }
8588
8589   // do this after checking for left-behind element
8590   ResetGfxAnimation(x, y);      // reset animation values for old field
8591
8592   if (!CAN_MOVE(element) ||
8593       (CAN_FALL(element) && direction == MV_DOWN &&
8594        (element == EL_SPRING ||
8595         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8596         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8597     GfxDir[x][y] = MovDir[newx][newy] = 0;
8598
8599   TEST_DrawLevelField(x, y);
8600   TEST_DrawLevelField(newx, newy);
8601
8602   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8603
8604   // prevent pushed element from moving on in pushed direction
8605   if (pushed_by_player && CAN_MOVE(element) &&
8606       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8607       !(element_info[element].move_pattern & direction))
8608     TurnRound(newx, newy);
8609
8610   // prevent elements on conveyor belt from moving on in last direction
8611   if (pushed_by_conveyor && CAN_FALL(element) &&
8612       direction & MV_HORIZONTAL)
8613     MovDir[newx][newy] = 0;
8614
8615   if (!pushed_by_player)
8616   {
8617     int nextx = newx + dx, nexty = newy + dy;
8618     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8619
8620     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8621
8622     if (CAN_FALL(element) && direction == MV_DOWN)
8623       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8624
8625     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8626       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8627
8628     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8629       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8630   }
8631
8632   if (DONT_TOUCH(element))      // object may be nasty to player or others
8633   {
8634     TestIfBadThingTouchesPlayer(newx, newy);
8635     TestIfBadThingTouchesFriend(newx, newy);
8636
8637     if (!IS_CUSTOM_ELEMENT(element))
8638       TestIfBadThingTouchesOtherBadThing(newx, newy);
8639   }
8640   else if (element == EL_PENGUIN)
8641     TestIfFriendTouchesBadThing(newx, newy);
8642
8643   if (DONT_GET_HIT_BY(element))
8644   {
8645     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8646   }
8647
8648   // give the player one last chance (one more frame) to move away
8649   if (CAN_FALL(element) && direction == MV_DOWN &&
8650       (last_line || (!IS_FREE(x, newy + 1) &&
8651                      (!IS_PLAYER(x, newy + 1) ||
8652                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8653     Impact(x, newy);
8654
8655   if (pushed_by_player && !game.use_change_when_pushing_bug)
8656   {
8657     int push_side = MV_DIR_OPPOSITE(direction);
8658     struct PlayerInfo *player = PLAYERINFO(x, y);
8659
8660     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8661                                player->index_bit, push_side);
8662     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8663                                         player->index_bit, push_side);
8664   }
8665
8666   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8667     MovDelay[newx][newy] = 1;
8668
8669   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8670
8671   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8672   TestIfElementHitsCustomElement(newx, newy, direction);
8673   TestIfPlayerTouchesCustomElement(newx, newy);
8674   TestIfElementTouchesCustomElement(newx, newy);
8675
8676   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8677       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8678     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8679                              MV_DIR_OPPOSITE(direction));
8680 }
8681
8682 int AmoebeNachbarNr(int ax, int ay)
8683 {
8684   int i;
8685   int element = Feld[ax][ay];
8686   int group_nr = 0;
8687   static int xy[4][2] =
8688   {
8689     { 0, -1 },
8690     { -1, 0 },
8691     { +1, 0 },
8692     { 0, +1 }
8693   };
8694
8695   for (i = 0; i < NUM_DIRECTIONS; i++)
8696   {
8697     int x = ax + xy[i][0];
8698     int y = ay + xy[i][1];
8699
8700     if (!IN_LEV_FIELD(x, y))
8701       continue;
8702
8703     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8704       group_nr = AmoebaNr[x][y];
8705   }
8706
8707   return group_nr;
8708 }
8709
8710 static void AmoebenVereinigen(int ax, int ay)
8711 {
8712   int i, x, y, xx, yy;
8713   int new_group_nr = AmoebaNr[ax][ay];
8714   static int xy[4][2] =
8715   {
8716     { 0, -1 },
8717     { -1, 0 },
8718     { +1, 0 },
8719     { 0, +1 }
8720   };
8721
8722   if (new_group_nr == 0)
8723     return;
8724
8725   for (i = 0; i < NUM_DIRECTIONS; i++)
8726   {
8727     x = ax + xy[i][0];
8728     y = ay + xy[i][1];
8729
8730     if (!IN_LEV_FIELD(x, y))
8731       continue;
8732
8733     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8734          Feld[x][y] == EL_BD_AMOEBA ||
8735          Feld[x][y] == EL_AMOEBA_DEAD) &&
8736         AmoebaNr[x][y] != new_group_nr)
8737     {
8738       int old_group_nr = AmoebaNr[x][y];
8739
8740       if (old_group_nr == 0)
8741         return;
8742
8743       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8744       AmoebaCnt[old_group_nr] = 0;
8745       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8746       AmoebaCnt2[old_group_nr] = 0;
8747
8748       SCAN_PLAYFIELD(xx, yy)
8749       {
8750         if (AmoebaNr[xx][yy] == old_group_nr)
8751           AmoebaNr[xx][yy] = new_group_nr;
8752       }
8753     }
8754   }
8755 }
8756
8757 void AmoebeUmwandeln(int ax, int ay)
8758 {
8759   int i, x, y;
8760
8761   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8762   {
8763     int group_nr = AmoebaNr[ax][ay];
8764
8765 #ifdef DEBUG
8766     if (group_nr == 0)
8767     {
8768       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8769       printf("AmoebeUmwandeln(): This should never happen!\n");
8770       return;
8771     }
8772 #endif
8773
8774     SCAN_PLAYFIELD(x, y)
8775     {
8776       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8777       {
8778         AmoebaNr[x][y] = 0;
8779         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8780       }
8781     }
8782
8783     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8784                             SND_AMOEBA_TURNING_TO_GEM :
8785                             SND_AMOEBA_TURNING_TO_ROCK));
8786     Bang(ax, ay);
8787   }
8788   else
8789   {
8790     static int xy[4][2] =
8791     {
8792       { 0, -1 },
8793       { -1, 0 },
8794       { +1, 0 },
8795       { 0, +1 }
8796     };
8797
8798     for (i = 0; i < NUM_DIRECTIONS; i++)
8799     {
8800       x = ax + xy[i][0];
8801       y = ay + xy[i][1];
8802
8803       if (!IN_LEV_FIELD(x, y))
8804         continue;
8805
8806       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8807       {
8808         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8809                               SND_AMOEBA_TURNING_TO_GEM :
8810                               SND_AMOEBA_TURNING_TO_ROCK));
8811         Bang(x, y);
8812       }
8813     }
8814   }
8815 }
8816
8817 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8818 {
8819   int x, y;
8820   int group_nr = AmoebaNr[ax][ay];
8821   boolean done = FALSE;
8822
8823 #ifdef DEBUG
8824   if (group_nr == 0)
8825   {
8826     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8827     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8828     return;
8829   }
8830 #endif
8831
8832   SCAN_PLAYFIELD(x, y)
8833   {
8834     if (AmoebaNr[x][y] == group_nr &&
8835         (Feld[x][y] == EL_AMOEBA_DEAD ||
8836          Feld[x][y] == EL_BD_AMOEBA ||
8837          Feld[x][y] == EL_AMOEBA_GROWING))
8838     {
8839       AmoebaNr[x][y] = 0;
8840       Feld[x][y] = new_element;
8841       InitField(x, y, FALSE);
8842       TEST_DrawLevelField(x, y);
8843       done = TRUE;
8844     }
8845   }
8846
8847   if (done)
8848     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8849                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8850                             SND_BD_AMOEBA_TURNING_TO_GEM));
8851 }
8852
8853 static void AmoebeWaechst(int x, int y)
8854 {
8855   static unsigned int sound_delay = 0;
8856   static unsigned int sound_delay_value = 0;
8857
8858   if (!MovDelay[x][y])          // start new growing cycle
8859   {
8860     MovDelay[x][y] = 7;
8861
8862     if (DelayReached(&sound_delay, sound_delay_value))
8863     {
8864       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8865       sound_delay_value = 30;
8866     }
8867   }
8868
8869   if (MovDelay[x][y])           // wait some time before growing bigger
8870   {
8871     MovDelay[x][y]--;
8872     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8873     {
8874       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8875                                            6 - MovDelay[x][y]);
8876
8877       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8878     }
8879
8880     if (!MovDelay[x][y])
8881     {
8882       Feld[x][y] = Store[x][y];
8883       Store[x][y] = 0;
8884       TEST_DrawLevelField(x, y);
8885     }
8886   }
8887 }
8888
8889 static void AmoebaDisappearing(int x, int y)
8890 {
8891   static unsigned int sound_delay = 0;
8892   static unsigned int sound_delay_value = 0;
8893
8894   if (!MovDelay[x][y])          // start new shrinking cycle
8895   {
8896     MovDelay[x][y] = 7;
8897
8898     if (DelayReached(&sound_delay, sound_delay_value))
8899       sound_delay_value = 30;
8900   }
8901
8902   if (MovDelay[x][y])           // wait some time before shrinking
8903   {
8904     MovDelay[x][y]--;
8905     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8906     {
8907       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8908                                            6 - MovDelay[x][y]);
8909
8910       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8911     }
8912
8913     if (!MovDelay[x][y])
8914     {
8915       Feld[x][y] = EL_EMPTY;
8916       TEST_DrawLevelField(x, y);
8917
8918       // don't let mole enter this field in this cycle;
8919       // (give priority to objects falling to this field from above)
8920       Stop[x][y] = TRUE;
8921     }
8922   }
8923 }
8924
8925 static void AmoebeAbleger(int ax, int ay)
8926 {
8927   int i;
8928   int element = Feld[ax][ay];
8929   int graphic = el2img(element);
8930   int newax = ax, neway = ay;
8931   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8932   static int xy[4][2] =
8933   {
8934     { 0, -1 },
8935     { -1, 0 },
8936     { +1, 0 },
8937     { 0, +1 }
8938   };
8939
8940   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8941   {
8942     Feld[ax][ay] = EL_AMOEBA_DEAD;
8943     TEST_DrawLevelField(ax, ay);
8944     return;
8945   }
8946
8947   if (IS_ANIMATED(graphic))
8948     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8949
8950   if (!MovDelay[ax][ay])        // start making new amoeba field
8951     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8952
8953   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8954   {
8955     MovDelay[ax][ay]--;
8956     if (MovDelay[ax][ay])
8957       return;
8958   }
8959
8960   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8961   {
8962     int start = RND(4);
8963     int x = ax + xy[start][0];
8964     int y = ay + xy[start][1];
8965
8966     if (!IN_LEV_FIELD(x, y))
8967       return;
8968
8969     if (IS_FREE(x, y) ||
8970         CAN_GROW_INTO(Feld[x][y]) ||
8971         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8972         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8973     {
8974       newax = x;
8975       neway = y;
8976     }
8977
8978     if (newax == ax && neway == ay)
8979       return;
8980   }
8981   else                          // normal or "filled" (BD style) amoeba
8982   {
8983     int start = RND(4);
8984     boolean waiting_for_player = FALSE;
8985
8986     for (i = 0; i < NUM_DIRECTIONS; i++)
8987     {
8988       int j = (start + i) % 4;
8989       int x = ax + xy[j][0];
8990       int y = ay + xy[j][1];
8991
8992       if (!IN_LEV_FIELD(x, y))
8993         continue;
8994
8995       if (IS_FREE(x, y) ||
8996           CAN_GROW_INTO(Feld[x][y]) ||
8997           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8998           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8999       {
9000         newax = x;
9001         neway = y;
9002         break;
9003       }
9004       else if (IS_PLAYER(x, y))
9005         waiting_for_player = TRUE;
9006     }
9007
9008     if (newax == ax && neway == ay)             // amoeba cannot grow
9009     {
9010       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9011       {
9012         Feld[ax][ay] = EL_AMOEBA_DEAD;
9013         TEST_DrawLevelField(ax, ay);
9014         AmoebaCnt[AmoebaNr[ax][ay]]--;
9015
9016         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9017         {
9018           if (element == EL_AMOEBA_FULL)
9019             AmoebeUmwandeln(ax, ay);
9020           else if (element == EL_BD_AMOEBA)
9021             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9022         }
9023       }
9024       return;
9025     }
9026     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9027     {
9028       // amoeba gets larger by growing in some direction
9029
9030       int new_group_nr = AmoebaNr[ax][ay];
9031
9032 #ifdef DEBUG
9033   if (new_group_nr == 0)
9034   {
9035     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9036     printf("AmoebeAbleger(): This should never happen!\n");
9037     return;
9038   }
9039 #endif
9040
9041       AmoebaNr[newax][neway] = new_group_nr;
9042       AmoebaCnt[new_group_nr]++;
9043       AmoebaCnt2[new_group_nr]++;
9044
9045       // if amoeba touches other amoeba(s) after growing, unify them
9046       AmoebenVereinigen(newax, neway);
9047
9048       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9049       {
9050         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9051         return;
9052       }
9053     }
9054   }
9055
9056   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9057       (neway == lev_fieldy - 1 && newax != ax))
9058   {
9059     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9060     Store[newax][neway] = element;
9061   }
9062   else if (neway == ay || element == EL_EMC_DRIPPER)
9063   {
9064     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9065
9066     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9067   }
9068   else
9069   {
9070     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9071     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9072     Store[ax][ay] = EL_AMOEBA_DROP;
9073     ContinueMoving(ax, ay);
9074     return;
9075   }
9076
9077   TEST_DrawLevelField(newax, neway);
9078 }
9079
9080 static void Life(int ax, int ay)
9081 {
9082   int x1, y1, x2, y2;
9083   int life_time = 40;
9084   int element = Feld[ax][ay];
9085   int graphic = el2img(element);
9086   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9087                          level.biomaze);
9088   boolean changed = FALSE;
9089
9090   if (IS_ANIMATED(graphic))
9091     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9092
9093   if (Stop[ax][ay])
9094     return;
9095
9096   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9097     MovDelay[ax][ay] = life_time;
9098
9099   if (MovDelay[ax][ay])         // wait some time before next cycle
9100   {
9101     MovDelay[ax][ay]--;
9102     if (MovDelay[ax][ay])
9103       return;
9104   }
9105
9106   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9107   {
9108     int xx = ax+x1, yy = ay+y1;
9109     int old_element = Feld[xx][yy];
9110     int num_neighbours = 0;
9111
9112     if (!IN_LEV_FIELD(xx, yy))
9113       continue;
9114
9115     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9116     {
9117       int x = xx+x2, y = yy+y2;
9118
9119       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9120         continue;
9121
9122       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9123       boolean is_neighbour = FALSE;
9124
9125       if (level.use_life_bugs)
9126         is_neighbour =
9127           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9128            (IS_FREE(x, y)                             &&  Stop[x][y]));
9129       else
9130         is_neighbour =
9131           (Last[x][y] == element || is_player_cell);
9132
9133       if (is_neighbour)
9134         num_neighbours++;
9135     }
9136
9137     boolean is_free = FALSE;
9138
9139     if (level.use_life_bugs)
9140       is_free = (IS_FREE(xx, yy));
9141     else
9142       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9143
9144     if (xx == ax && yy == ay)           // field in the middle
9145     {
9146       if (num_neighbours < life_parameter[0] ||
9147           num_neighbours > life_parameter[1])
9148       {
9149         Feld[xx][yy] = EL_EMPTY;
9150         if (Feld[xx][yy] != old_element)
9151           TEST_DrawLevelField(xx, yy);
9152         Stop[xx][yy] = TRUE;
9153         changed = TRUE;
9154       }
9155     }
9156     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9157     {                                   // free border field
9158       if (num_neighbours >= life_parameter[2] &&
9159           num_neighbours <= life_parameter[3])
9160       {
9161         Feld[xx][yy] = element;
9162         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9163         if (Feld[xx][yy] != old_element)
9164           TEST_DrawLevelField(xx, yy);
9165         Stop[xx][yy] = TRUE;
9166         changed = TRUE;
9167       }
9168     }
9169   }
9170
9171   if (changed)
9172     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9173                    SND_GAME_OF_LIFE_GROWING);
9174 }
9175
9176 static void InitRobotWheel(int x, int y)
9177 {
9178   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9179 }
9180
9181 static void RunRobotWheel(int x, int y)
9182 {
9183   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9184 }
9185
9186 static void StopRobotWheel(int x, int y)
9187 {
9188   if (game.robot_wheel_x == x &&
9189       game.robot_wheel_y == y)
9190   {
9191     game.robot_wheel_x = -1;
9192     game.robot_wheel_y = -1;
9193     game.robot_wheel_active = FALSE;
9194   }
9195 }
9196
9197 static void InitTimegateWheel(int x, int y)
9198 {
9199   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9200 }
9201
9202 static void RunTimegateWheel(int x, int y)
9203 {
9204   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9205 }
9206
9207 static void InitMagicBallDelay(int x, int y)
9208 {
9209   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9210 }
9211
9212 static void ActivateMagicBall(int bx, int by)
9213 {
9214   int x, y;
9215
9216   if (level.ball_random)
9217   {
9218     int pos_border = RND(8);    // select one of the eight border elements
9219     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9220     int xx = pos_content % 3;
9221     int yy = pos_content / 3;
9222
9223     x = bx - 1 + xx;
9224     y = by - 1 + yy;
9225
9226     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9227       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9228   }
9229   else
9230   {
9231     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9232     {
9233       int xx = x - bx + 1;
9234       int yy = y - by + 1;
9235
9236       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9237         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9238     }
9239   }
9240
9241   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9242 }
9243
9244 static void CheckExit(int x, int y)
9245 {
9246   if (game.gems_still_needed > 0 ||
9247       game.sokoban_fields_still_needed > 0 ||
9248       game.sokoban_objects_still_needed > 0 ||
9249       game.lights_still_needed > 0)
9250   {
9251     int element = Feld[x][y];
9252     int graphic = el2img(element);
9253
9254     if (IS_ANIMATED(graphic))
9255       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9256
9257     return;
9258   }
9259
9260   // do not re-open exit door closed after last player
9261   if (game.all_players_gone)
9262     return;
9263
9264   Feld[x][y] = EL_EXIT_OPENING;
9265
9266   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9267 }
9268
9269 static void CheckExitEM(int x, int y)
9270 {
9271   if (game.gems_still_needed > 0 ||
9272       game.sokoban_fields_still_needed > 0 ||
9273       game.sokoban_objects_still_needed > 0 ||
9274       game.lights_still_needed > 0)
9275   {
9276     int element = Feld[x][y];
9277     int graphic = el2img(element);
9278
9279     if (IS_ANIMATED(graphic))
9280       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9281
9282     return;
9283   }
9284
9285   // do not re-open exit door closed after last player
9286   if (game.all_players_gone)
9287     return;
9288
9289   Feld[x][y] = EL_EM_EXIT_OPENING;
9290
9291   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9292 }
9293
9294 static void CheckExitSteel(int x, int y)
9295 {
9296   if (game.gems_still_needed > 0 ||
9297       game.sokoban_fields_still_needed > 0 ||
9298       game.sokoban_objects_still_needed > 0 ||
9299       game.lights_still_needed > 0)
9300   {
9301     int element = Feld[x][y];
9302     int graphic = el2img(element);
9303
9304     if (IS_ANIMATED(graphic))
9305       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9306
9307     return;
9308   }
9309
9310   // do not re-open exit door closed after last player
9311   if (game.all_players_gone)
9312     return;
9313
9314   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9315
9316   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9317 }
9318
9319 static void CheckExitSteelEM(int x, int y)
9320 {
9321   if (game.gems_still_needed > 0 ||
9322       game.sokoban_fields_still_needed > 0 ||
9323       game.sokoban_objects_still_needed > 0 ||
9324       game.lights_still_needed > 0)
9325   {
9326     int element = Feld[x][y];
9327     int graphic = el2img(element);
9328
9329     if (IS_ANIMATED(graphic))
9330       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9331
9332     return;
9333   }
9334
9335   // do not re-open exit door closed after last player
9336   if (game.all_players_gone)
9337     return;
9338
9339   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9340
9341   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9342 }
9343
9344 static void CheckExitSP(int x, int y)
9345 {
9346   if (game.gems_still_needed > 0)
9347   {
9348     int element = Feld[x][y];
9349     int graphic = el2img(element);
9350
9351     if (IS_ANIMATED(graphic))
9352       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9353
9354     return;
9355   }
9356
9357   // do not re-open exit door closed after last player
9358   if (game.all_players_gone)
9359     return;
9360
9361   Feld[x][y] = EL_SP_EXIT_OPENING;
9362
9363   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9364 }
9365
9366 static void CloseAllOpenTimegates(void)
9367 {
9368   int x, y;
9369
9370   SCAN_PLAYFIELD(x, y)
9371   {
9372     int element = Feld[x][y];
9373
9374     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9375     {
9376       Feld[x][y] = EL_TIMEGATE_CLOSING;
9377
9378       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9379     }
9380   }
9381 }
9382
9383 static void DrawTwinkleOnField(int x, int y)
9384 {
9385   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9386     return;
9387
9388   if (Feld[x][y] == EL_BD_DIAMOND)
9389     return;
9390
9391   if (MovDelay[x][y] == 0)      // next animation frame
9392     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9393
9394   if (MovDelay[x][y] != 0)      // wait some time before next frame
9395   {
9396     MovDelay[x][y]--;
9397
9398     DrawLevelElementAnimation(x, y, Feld[x][y]);
9399
9400     if (MovDelay[x][y] != 0)
9401     {
9402       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9403                                            10 - MovDelay[x][y]);
9404
9405       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9406     }
9407   }
9408 }
9409
9410 static void MauerWaechst(int x, int y)
9411 {
9412   int delay = 6;
9413
9414   if (!MovDelay[x][y])          // next animation frame
9415     MovDelay[x][y] = 3 * delay;
9416
9417   if (MovDelay[x][y])           // wait some time before next frame
9418   {
9419     MovDelay[x][y]--;
9420
9421     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9422     {
9423       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9424       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9425
9426       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9427     }
9428
9429     if (!MovDelay[x][y])
9430     {
9431       if (MovDir[x][y] == MV_LEFT)
9432       {
9433         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9434           TEST_DrawLevelField(x - 1, y);
9435       }
9436       else if (MovDir[x][y] == MV_RIGHT)
9437       {
9438         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9439           TEST_DrawLevelField(x + 1, y);
9440       }
9441       else if (MovDir[x][y] == MV_UP)
9442       {
9443         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9444           TEST_DrawLevelField(x, y - 1);
9445       }
9446       else
9447       {
9448         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9449           TEST_DrawLevelField(x, y + 1);
9450       }
9451
9452       Feld[x][y] = Store[x][y];
9453       Store[x][y] = 0;
9454       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9455       TEST_DrawLevelField(x, y);
9456     }
9457   }
9458 }
9459
9460 static void MauerAbleger(int ax, int ay)
9461 {
9462   int element = Feld[ax][ay];
9463   int graphic = el2img(element);
9464   boolean oben_frei = FALSE, unten_frei = FALSE;
9465   boolean links_frei = FALSE, rechts_frei = FALSE;
9466   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9467   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9468   boolean new_wall = FALSE;
9469
9470   if (IS_ANIMATED(graphic))
9471     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9472
9473   if (!MovDelay[ax][ay])        // start building new wall
9474     MovDelay[ax][ay] = 6;
9475
9476   if (MovDelay[ax][ay])         // wait some time before building new wall
9477   {
9478     MovDelay[ax][ay]--;
9479     if (MovDelay[ax][ay])
9480       return;
9481   }
9482
9483   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9484     oben_frei = TRUE;
9485   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9486     unten_frei = TRUE;
9487   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9488     links_frei = TRUE;
9489   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9490     rechts_frei = TRUE;
9491
9492   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9493       element == EL_EXPANDABLE_WALL_ANY)
9494   {
9495     if (oben_frei)
9496     {
9497       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9498       Store[ax][ay-1] = element;
9499       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9500       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9501         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9502                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9503       new_wall = TRUE;
9504     }
9505     if (unten_frei)
9506     {
9507       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9508       Store[ax][ay+1] = element;
9509       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9510       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9511         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9512                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9513       new_wall = TRUE;
9514     }
9515   }
9516
9517   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9518       element == EL_EXPANDABLE_WALL_ANY ||
9519       element == EL_EXPANDABLE_WALL ||
9520       element == EL_BD_EXPANDABLE_WALL)
9521   {
9522     if (links_frei)
9523     {
9524       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9525       Store[ax-1][ay] = element;
9526       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9527       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9528         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9529                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9530       new_wall = TRUE;
9531     }
9532
9533     if (rechts_frei)
9534     {
9535       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9536       Store[ax+1][ay] = element;
9537       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9538       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9539         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9540                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9541       new_wall = TRUE;
9542     }
9543   }
9544
9545   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9546     TEST_DrawLevelField(ax, ay);
9547
9548   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9549     oben_massiv = TRUE;
9550   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9551     unten_massiv = TRUE;
9552   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9553     links_massiv = TRUE;
9554   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9555     rechts_massiv = TRUE;
9556
9557   if (((oben_massiv && unten_massiv) ||
9558        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9559        element == EL_EXPANDABLE_WALL) &&
9560       ((links_massiv && rechts_massiv) ||
9561        element == EL_EXPANDABLE_WALL_VERTICAL))
9562     Feld[ax][ay] = EL_WALL;
9563
9564   if (new_wall)
9565     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9566 }
9567
9568 static void MauerAblegerStahl(int ax, int ay)
9569 {
9570   int element = Feld[ax][ay];
9571   int graphic = el2img(element);
9572   boolean oben_frei = FALSE, unten_frei = FALSE;
9573   boolean links_frei = FALSE, rechts_frei = FALSE;
9574   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9575   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9576   boolean new_wall = FALSE;
9577
9578   if (IS_ANIMATED(graphic))
9579     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9580
9581   if (!MovDelay[ax][ay])        // start building new wall
9582     MovDelay[ax][ay] = 6;
9583
9584   if (MovDelay[ax][ay])         // wait some time before building new wall
9585   {
9586     MovDelay[ax][ay]--;
9587     if (MovDelay[ax][ay])
9588       return;
9589   }
9590
9591   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9592     oben_frei = TRUE;
9593   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9594     unten_frei = TRUE;
9595   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9596     links_frei = TRUE;
9597   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9598     rechts_frei = TRUE;
9599
9600   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9601       element == EL_EXPANDABLE_STEELWALL_ANY)
9602   {
9603     if (oben_frei)
9604     {
9605       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9606       Store[ax][ay-1] = element;
9607       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9608       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9609         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9610                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9611       new_wall = TRUE;
9612     }
9613     if (unten_frei)
9614     {
9615       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9616       Store[ax][ay+1] = element;
9617       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9618       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9619         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9620                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9621       new_wall = TRUE;
9622     }
9623   }
9624
9625   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9626       element == EL_EXPANDABLE_STEELWALL_ANY)
9627   {
9628     if (links_frei)
9629     {
9630       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9631       Store[ax-1][ay] = element;
9632       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9633       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9634         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9635                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9636       new_wall = TRUE;
9637     }
9638
9639     if (rechts_frei)
9640     {
9641       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9642       Store[ax+1][ay] = element;
9643       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9644       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9645         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9646                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9647       new_wall = TRUE;
9648     }
9649   }
9650
9651   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9652     oben_massiv = TRUE;
9653   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9654     unten_massiv = TRUE;
9655   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9656     links_massiv = TRUE;
9657   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9658     rechts_massiv = TRUE;
9659
9660   if (((oben_massiv && unten_massiv) ||
9661        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9662       ((links_massiv && rechts_massiv) ||
9663        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9664     Feld[ax][ay] = EL_STEELWALL;
9665
9666   if (new_wall)
9667     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9668 }
9669
9670 static void CheckForDragon(int x, int y)
9671 {
9672   int i, j;
9673   boolean dragon_found = FALSE;
9674   static int xy[4][2] =
9675   {
9676     { 0, -1 },
9677     { -1, 0 },
9678     { +1, 0 },
9679     { 0, +1 }
9680   };
9681
9682   for (i = 0; i < NUM_DIRECTIONS; i++)
9683   {
9684     for (j = 0; j < 4; j++)
9685     {
9686       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9687
9688       if (IN_LEV_FIELD(xx, yy) &&
9689           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9690       {
9691         if (Feld[xx][yy] == EL_DRAGON)
9692           dragon_found = TRUE;
9693       }
9694       else
9695         break;
9696     }
9697   }
9698
9699   if (!dragon_found)
9700   {
9701     for (i = 0; i < NUM_DIRECTIONS; i++)
9702     {
9703       for (j = 0; j < 3; j++)
9704       {
9705         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9706   
9707         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9708         {
9709           Feld[xx][yy] = EL_EMPTY;
9710           TEST_DrawLevelField(xx, yy);
9711         }
9712         else
9713           break;
9714       }
9715     }
9716   }
9717 }
9718
9719 static void InitBuggyBase(int x, int y)
9720 {
9721   int element = Feld[x][y];
9722   int activating_delay = FRAMES_PER_SECOND / 4;
9723
9724   ChangeDelay[x][y] =
9725     (element == EL_SP_BUGGY_BASE ?
9726      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9727      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9728      activating_delay :
9729      element == EL_SP_BUGGY_BASE_ACTIVE ?
9730      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9731 }
9732
9733 static void WarnBuggyBase(int x, int y)
9734 {
9735   int i;
9736   static int xy[4][2] =
9737   {
9738     { 0, -1 },
9739     { -1, 0 },
9740     { +1, 0 },
9741     { 0, +1 }
9742   };
9743
9744   for (i = 0; i < NUM_DIRECTIONS; i++)
9745   {
9746     int xx = x + xy[i][0];
9747     int yy = y + xy[i][1];
9748
9749     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9750     {
9751       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9752
9753       break;
9754     }
9755   }
9756 }
9757
9758 static void InitTrap(int x, int y)
9759 {
9760   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9761 }
9762
9763 static void ActivateTrap(int x, int y)
9764 {
9765   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9766 }
9767
9768 static void ChangeActiveTrap(int x, int y)
9769 {
9770   int graphic = IMG_TRAP_ACTIVE;
9771
9772   // if new animation frame was drawn, correct crumbled sand border
9773   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9774     TEST_DrawLevelFieldCrumbled(x, y);
9775 }
9776
9777 static int getSpecialActionElement(int element, int number, int base_element)
9778 {
9779   return (element != EL_EMPTY ? element :
9780           number != -1 ? base_element + number - 1 :
9781           EL_EMPTY);
9782 }
9783
9784 static int getModifiedActionNumber(int value_old, int operator, int operand,
9785                                    int value_min, int value_max)
9786 {
9787   int value_new = (operator == CA_MODE_SET      ? operand :
9788                    operator == CA_MODE_ADD      ? value_old + operand :
9789                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9790                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9791                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9792                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9793                    value_old);
9794
9795   return (value_new < value_min ? value_min :
9796           value_new > value_max ? value_max :
9797           value_new);
9798 }
9799
9800 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9801 {
9802   struct ElementInfo *ei = &element_info[element];
9803   struct ElementChangeInfo *change = &ei->change_page[page];
9804   int target_element = change->target_element;
9805   int action_type = change->action_type;
9806   int action_mode = change->action_mode;
9807   int action_arg = change->action_arg;
9808   int action_element = change->action_element;
9809   int i;
9810
9811   if (!change->has_action)
9812     return;
9813
9814   // ---------- determine action paramater values -----------------------------
9815
9816   int level_time_value =
9817     (level.time > 0 ? TimeLeft :
9818      TimePlayed);
9819
9820   int action_arg_element_raw =
9821     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9822      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9823      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9824      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9825      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9826      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9827      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9828      EL_EMPTY);
9829   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9830
9831   int action_arg_direction =
9832     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9833      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9834      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9835      change->actual_trigger_side :
9836      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9837      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9838      MV_NONE);
9839
9840   int action_arg_number_min =
9841     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9842      CA_ARG_MIN);
9843
9844   int action_arg_number_max =
9845     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9846      action_type == CA_SET_LEVEL_GEMS ? 999 :
9847      action_type == CA_SET_LEVEL_TIME ? 9999 :
9848      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9849      action_type == CA_SET_CE_VALUE ? 9999 :
9850      action_type == CA_SET_CE_SCORE ? 9999 :
9851      CA_ARG_MAX);
9852
9853   int action_arg_number_reset =
9854     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9855      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9856      action_type == CA_SET_LEVEL_TIME ? level.time :
9857      action_type == CA_SET_LEVEL_SCORE ? 0 :
9858      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9859      action_type == CA_SET_CE_SCORE ? 0 :
9860      0);
9861
9862   int action_arg_number =
9863     (action_arg <= CA_ARG_MAX ? action_arg :
9864      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9865      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9866      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9867      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9868      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9869      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9870      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9871      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9872      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9873      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9874      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9875      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9876      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9877      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9878      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9879      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9880      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9881      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9882      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9883      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9884      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9885      -1);
9886
9887   int action_arg_number_old =
9888     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9889      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9890      action_type == CA_SET_LEVEL_SCORE ? game.score :
9891      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9892      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9893      0);
9894
9895   int action_arg_number_new =
9896     getModifiedActionNumber(action_arg_number_old,
9897                             action_mode, action_arg_number,
9898                             action_arg_number_min, action_arg_number_max);
9899
9900   int trigger_player_bits =
9901     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9902      change->actual_trigger_player_bits : change->trigger_player);
9903
9904   int action_arg_player_bits =
9905     (action_arg >= CA_ARG_PLAYER_1 &&
9906      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9907      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9908      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9909      PLAYER_BITS_ANY);
9910
9911   // ---------- execute action  -----------------------------------------------
9912
9913   switch (action_type)
9914   {
9915     case CA_NO_ACTION:
9916     {
9917       return;
9918     }
9919
9920     // ---------- level actions  ----------------------------------------------
9921
9922     case CA_RESTART_LEVEL:
9923     {
9924       game.restart_level = TRUE;
9925
9926       break;
9927     }
9928
9929     case CA_SHOW_ENVELOPE:
9930     {
9931       int element = getSpecialActionElement(action_arg_element,
9932                                             action_arg_number, EL_ENVELOPE_1);
9933
9934       if (IS_ENVELOPE(element))
9935         local_player->show_envelope = element;
9936
9937       break;
9938     }
9939
9940     case CA_SET_LEVEL_TIME:
9941     {
9942       if (level.time > 0)       // only modify limited time value
9943       {
9944         TimeLeft = action_arg_number_new;
9945
9946         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9947
9948         DisplayGameControlValues();
9949
9950         if (!TimeLeft && setup.time_limit)
9951           for (i = 0; i < MAX_PLAYERS; i++)
9952             KillPlayer(&stored_player[i]);
9953       }
9954
9955       break;
9956     }
9957
9958     case CA_SET_LEVEL_SCORE:
9959     {
9960       game.score = action_arg_number_new;
9961
9962       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9963
9964       DisplayGameControlValues();
9965
9966       break;
9967     }
9968
9969     case CA_SET_LEVEL_GEMS:
9970     {
9971       game.gems_still_needed = action_arg_number_new;
9972
9973       game.snapshot.collected_item = TRUE;
9974
9975       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9976
9977       DisplayGameControlValues();
9978
9979       break;
9980     }
9981
9982     case CA_SET_LEVEL_WIND:
9983     {
9984       game.wind_direction = action_arg_direction;
9985
9986       break;
9987     }
9988
9989     case CA_SET_LEVEL_RANDOM_SEED:
9990     {
9991       // ensure that setting a new random seed while playing is predictable
9992       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9993
9994       break;
9995     }
9996
9997     // ---------- player actions  ---------------------------------------------
9998
9999     case CA_MOVE_PLAYER:
10000     case CA_MOVE_PLAYER_NEW:
10001     {
10002       // automatically move to the next field in specified direction
10003       for (i = 0; i < MAX_PLAYERS; i++)
10004         if (trigger_player_bits & (1 << i))
10005           if (action_type == CA_MOVE_PLAYER ||
10006               stored_player[i].MovPos == 0)
10007             stored_player[i].programmed_action = action_arg_direction;
10008
10009       break;
10010     }
10011
10012     case CA_EXIT_PLAYER:
10013     {
10014       for (i = 0; i < MAX_PLAYERS; i++)
10015         if (action_arg_player_bits & (1 << i))
10016           ExitPlayer(&stored_player[i]);
10017
10018       if (game.players_still_needed == 0)
10019         LevelSolved();
10020
10021       break;
10022     }
10023
10024     case CA_KILL_PLAYER:
10025     {
10026       for (i = 0; i < MAX_PLAYERS; i++)
10027         if (action_arg_player_bits & (1 << i))
10028           KillPlayer(&stored_player[i]);
10029
10030       break;
10031     }
10032
10033     case CA_SET_PLAYER_KEYS:
10034     {
10035       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10036       int element = getSpecialActionElement(action_arg_element,
10037                                             action_arg_number, EL_KEY_1);
10038
10039       if (IS_KEY(element))
10040       {
10041         for (i = 0; i < MAX_PLAYERS; i++)
10042         {
10043           if (trigger_player_bits & (1 << i))
10044           {
10045             stored_player[i].key[KEY_NR(element)] = key_state;
10046
10047             DrawGameDoorValues();
10048           }
10049         }
10050       }
10051
10052       break;
10053     }
10054
10055     case CA_SET_PLAYER_SPEED:
10056     {
10057       for (i = 0; i < MAX_PLAYERS; i++)
10058       {
10059         if (trigger_player_bits & (1 << i))
10060         {
10061           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10062
10063           if (action_arg == CA_ARG_SPEED_FASTER &&
10064               stored_player[i].cannot_move)
10065           {
10066             action_arg_number = STEPSIZE_VERY_SLOW;
10067           }
10068           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10069                    action_arg == CA_ARG_SPEED_FASTER)
10070           {
10071             action_arg_number = 2;
10072             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10073                            CA_MODE_MULTIPLY);
10074           }
10075           else if (action_arg == CA_ARG_NUMBER_RESET)
10076           {
10077             action_arg_number = level.initial_player_stepsize[i];
10078           }
10079
10080           move_stepsize =
10081             getModifiedActionNumber(move_stepsize,
10082                                     action_mode,
10083                                     action_arg_number,
10084                                     action_arg_number_min,
10085                                     action_arg_number_max);
10086
10087           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10088         }
10089       }
10090
10091       break;
10092     }
10093
10094     case CA_SET_PLAYER_SHIELD:
10095     {
10096       for (i = 0; i < MAX_PLAYERS; i++)
10097       {
10098         if (trigger_player_bits & (1 << i))
10099         {
10100           if (action_arg == CA_ARG_SHIELD_OFF)
10101           {
10102             stored_player[i].shield_normal_time_left = 0;
10103             stored_player[i].shield_deadly_time_left = 0;
10104           }
10105           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10106           {
10107             stored_player[i].shield_normal_time_left = 999999;
10108           }
10109           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10110           {
10111             stored_player[i].shield_normal_time_left = 999999;
10112             stored_player[i].shield_deadly_time_left = 999999;
10113           }
10114         }
10115       }
10116
10117       break;
10118     }
10119
10120     case CA_SET_PLAYER_GRAVITY:
10121     {
10122       for (i = 0; i < MAX_PLAYERS; i++)
10123       {
10124         if (trigger_player_bits & (1 << i))
10125         {
10126           stored_player[i].gravity =
10127             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10128              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10129              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10130              stored_player[i].gravity);
10131         }
10132       }
10133
10134       break;
10135     }
10136
10137     case CA_SET_PLAYER_ARTWORK:
10138     {
10139       for (i = 0; i < MAX_PLAYERS; i++)
10140       {
10141         if (trigger_player_bits & (1 << i))
10142         {
10143           int artwork_element = action_arg_element;
10144
10145           if (action_arg == CA_ARG_ELEMENT_RESET)
10146             artwork_element =
10147               (level.use_artwork_element[i] ? level.artwork_element[i] :
10148                stored_player[i].element_nr);
10149
10150           if (stored_player[i].artwork_element != artwork_element)
10151             stored_player[i].Frame = 0;
10152
10153           stored_player[i].artwork_element = artwork_element;
10154
10155           SetPlayerWaiting(&stored_player[i], FALSE);
10156
10157           // set number of special actions for bored and sleeping animation
10158           stored_player[i].num_special_action_bored =
10159             get_num_special_action(artwork_element,
10160                                    ACTION_BORING_1, ACTION_BORING_LAST);
10161           stored_player[i].num_special_action_sleeping =
10162             get_num_special_action(artwork_element,
10163                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10164         }
10165       }
10166
10167       break;
10168     }
10169
10170     case CA_SET_PLAYER_INVENTORY:
10171     {
10172       for (i = 0; i < MAX_PLAYERS; i++)
10173       {
10174         struct PlayerInfo *player = &stored_player[i];
10175         int j, k;
10176
10177         if (trigger_player_bits & (1 << i))
10178         {
10179           int inventory_element = action_arg_element;
10180
10181           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10182               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10183               action_arg == CA_ARG_ELEMENT_ACTION)
10184           {
10185             int element = inventory_element;
10186             int collect_count = element_info[element].collect_count_initial;
10187
10188             if (!IS_CUSTOM_ELEMENT(element))
10189               collect_count = 1;
10190
10191             if (collect_count == 0)
10192               player->inventory_infinite_element = element;
10193             else
10194               for (k = 0; k < collect_count; k++)
10195                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10196                   player->inventory_element[player->inventory_size++] =
10197                     element;
10198           }
10199           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10200                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10201                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10202           {
10203             if (player->inventory_infinite_element != EL_UNDEFINED &&
10204                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10205                                      action_arg_element_raw))
10206               player->inventory_infinite_element = EL_UNDEFINED;
10207
10208             for (k = 0, j = 0; j < player->inventory_size; j++)
10209             {
10210               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10211                                         action_arg_element_raw))
10212                 player->inventory_element[k++] = player->inventory_element[j];
10213             }
10214
10215             player->inventory_size = k;
10216           }
10217           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10218           {
10219             if (player->inventory_size > 0)
10220             {
10221               for (j = 0; j < player->inventory_size - 1; j++)
10222                 player->inventory_element[j] = player->inventory_element[j + 1];
10223
10224               player->inventory_size--;
10225             }
10226           }
10227           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10228           {
10229             if (player->inventory_size > 0)
10230               player->inventory_size--;
10231           }
10232           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10233           {
10234             player->inventory_infinite_element = EL_UNDEFINED;
10235             player->inventory_size = 0;
10236           }
10237           else if (action_arg == CA_ARG_INVENTORY_RESET)
10238           {
10239             player->inventory_infinite_element = EL_UNDEFINED;
10240             player->inventory_size = 0;
10241
10242             if (level.use_initial_inventory[i])
10243             {
10244               for (j = 0; j < level.initial_inventory_size[i]; j++)
10245               {
10246                 int element = level.initial_inventory_content[i][j];
10247                 int collect_count = element_info[element].collect_count_initial;
10248
10249                 if (!IS_CUSTOM_ELEMENT(element))
10250                   collect_count = 1;
10251
10252                 if (collect_count == 0)
10253                   player->inventory_infinite_element = element;
10254                 else
10255                   for (k = 0; k < collect_count; k++)
10256                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10257                       player->inventory_element[player->inventory_size++] =
10258                         element;
10259               }
10260             }
10261           }
10262         }
10263       }
10264
10265       break;
10266     }
10267
10268     // ---------- CE actions  -------------------------------------------------
10269
10270     case CA_SET_CE_VALUE:
10271     {
10272       int last_ce_value = CustomValue[x][y];
10273
10274       CustomValue[x][y] = action_arg_number_new;
10275
10276       if (CustomValue[x][y] != last_ce_value)
10277       {
10278         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10279         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10280
10281         if (CustomValue[x][y] == 0)
10282         {
10283           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10284           ChangeCount[x][y] = 0;        // allow at least one more change
10285
10286           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10287           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10288         }
10289       }
10290
10291       break;
10292     }
10293
10294     case CA_SET_CE_SCORE:
10295     {
10296       int last_ce_score = ei->collect_score;
10297
10298       ei->collect_score = action_arg_number_new;
10299
10300       if (ei->collect_score != last_ce_score)
10301       {
10302         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10303         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10304
10305         if (ei->collect_score == 0)
10306         {
10307           int xx, yy;
10308
10309           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10310           ChangeCount[x][y] = 0;        // allow at least one more change
10311
10312           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10313           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10314
10315           /*
10316             This is a very special case that seems to be a mixture between
10317             CheckElementChange() and CheckTriggeredElementChange(): while
10318             the first one only affects single elements that are triggered
10319             directly, the second one affects multiple elements in the playfield
10320             that are triggered indirectly by another element. This is a third
10321             case: Changing the CE score always affects multiple identical CEs,
10322             so every affected CE must be checked, not only the single CE for
10323             which the CE score was changed in the first place (as every instance
10324             of that CE shares the same CE score, and therefore also can change)!
10325           */
10326           SCAN_PLAYFIELD(xx, yy)
10327           {
10328             if (Feld[xx][yy] == element)
10329               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10330                                  CE_SCORE_GETS_ZERO);
10331           }
10332         }
10333       }
10334
10335       break;
10336     }
10337
10338     case CA_SET_CE_ARTWORK:
10339     {
10340       int artwork_element = action_arg_element;
10341       boolean reset_frame = FALSE;
10342       int xx, yy;
10343
10344       if (action_arg == CA_ARG_ELEMENT_RESET)
10345         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10346                            element);
10347
10348       if (ei->gfx_element != artwork_element)
10349         reset_frame = TRUE;
10350
10351       ei->gfx_element = artwork_element;
10352
10353       SCAN_PLAYFIELD(xx, yy)
10354       {
10355         if (Feld[xx][yy] == element)
10356         {
10357           if (reset_frame)
10358           {
10359             ResetGfxAnimation(xx, yy);
10360             ResetRandomAnimationValue(xx, yy);
10361           }
10362
10363           TEST_DrawLevelField(xx, yy);
10364         }
10365       }
10366
10367       break;
10368     }
10369
10370     // ---------- engine actions  ---------------------------------------------
10371
10372     case CA_SET_ENGINE_SCAN_MODE:
10373     {
10374       InitPlayfieldScanMode(action_arg);
10375
10376       break;
10377     }
10378
10379     default:
10380       break;
10381   }
10382 }
10383
10384 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10385 {
10386   int old_element = Feld[x][y];
10387   int new_element = GetElementFromGroupElement(element);
10388   int previous_move_direction = MovDir[x][y];
10389   int last_ce_value = CustomValue[x][y];
10390   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10391   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10392   boolean add_player_onto_element = (new_element_is_player &&
10393                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10394                                      IS_WALKABLE(old_element));
10395
10396   if (!add_player_onto_element)
10397   {
10398     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10399       RemoveMovingField(x, y);
10400     else
10401       RemoveField(x, y);
10402
10403     Feld[x][y] = new_element;
10404
10405     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10406       MovDir[x][y] = previous_move_direction;
10407
10408     if (element_info[new_element].use_last_ce_value)
10409       CustomValue[x][y] = last_ce_value;
10410
10411     InitField_WithBug1(x, y, FALSE);
10412
10413     new_element = Feld[x][y];   // element may have changed
10414
10415     ResetGfxAnimation(x, y);
10416     ResetRandomAnimationValue(x, y);
10417
10418     TEST_DrawLevelField(x, y);
10419
10420     if (GFX_CRUMBLED(new_element))
10421       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10422   }
10423
10424   // check if element under the player changes from accessible to unaccessible
10425   // (needed for special case of dropping element which then changes)
10426   // (must be checked after creating new element for walkable group elements)
10427   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10428       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10429   {
10430     Bang(x, y);
10431
10432     return;
10433   }
10434
10435   // "ChangeCount" not set yet to allow "entered by player" change one time
10436   if (new_element_is_player)
10437     RelocatePlayer(x, y, new_element);
10438
10439   if (is_change)
10440     ChangeCount[x][y]++;        // count number of changes in the same frame
10441
10442   TestIfBadThingTouchesPlayer(x, y);
10443   TestIfPlayerTouchesCustomElement(x, y);
10444   TestIfElementTouchesCustomElement(x, y);
10445 }
10446
10447 static void CreateField(int x, int y, int element)
10448 {
10449   CreateFieldExt(x, y, element, FALSE);
10450 }
10451
10452 static void CreateElementFromChange(int x, int y, int element)
10453 {
10454   element = GET_VALID_RUNTIME_ELEMENT(element);
10455
10456   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10457   {
10458     int old_element = Feld[x][y];
10459
10460     // prevent changed element from moving in same engine frame
10461     // unless both old and new element can either fall or move
10462     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10463         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10464       Stop[x][y] = TRUE;
10465   }
10466
10467   CreateFieldExt(x, y, element, TRUE);
10468 }
10469
10470 static boolean ChangeElement(int x, int y, int element, int page)
10471 {
10472   struct ElementInfo *ei = &element_info[element];
10473   struct ElementChangeInfo *change = &ei->change_page[page];
10474   int ce_value = CustomValue[x][y];
10475   int ce_score = ei->collect_score;
10476   int target_element;
10477   int old_element = Feld[x][y];
10478
10479   // always use default change event to prevent running into a loop
10480   if (ChangeEvent[x][y] == -1)
10481     ChangeEvent[x][y] = CE_DELAY;
10482
10483   if (ChangeEvent[x][y] == CE_DELAY)
10484   {
10485     // reset actual trigger element, trigger player and action element
10486     change->actual_trigger_element = EL_EMPTY;
10487     change->actual_trigger_player = EL_EMPTY;
10488     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10489     change->actual_trigger_side = CH_SIDE_NONE;
10490     change->actual_trigger_ce_value = 0;
10491     change->actual_trigger_ce_score = 0;
10492   }
10493
10494   // do not change elements more than a specified maximum number of changes
10495   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10496     return FALSE;
10497
10498   ChangeCount[x][y]++;          // count number of changes in the same frame
10499
10500   if (change->explode)
10501   {
10502     Bang(x, y);
10503
10504     return TRUE;
10505   }
10506
10507   if (change->use_target_content)
10508   {
10509     boolean complete_replace = TRUE;
10510     boolean can_replace[3][3];
10511     int xx, yy;
10512
10513     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10514     {
10515       boolean is_empty;
10516       boolean is_walkable;
10517       boolean is_diggable;
10518       boolean is_collectible;
10519       boolean is_removable;
10520       boolean is_destructible;
10521       int ex = x + xx - 1;
10522       int ey = y + yy - 1;
10523       int content_element = change->target_content.e[xx][yy];
10524       int e;
10525
10526       can_replace[xx][yy] = TRUE;
10527
10528       if (ex == x && ey == y)   // do not check changing element itself
10529         continue;
10530
10531       if (content_element == EL_EMPTY_SPACE)
10532       {
10533         can_replace[xx][yy] = FALSE;    // do not replace border with space
10534
10535         continue;
10536       }
10537
10538       if (!IN_LEV_FIELD(ex, ey))
10539       {
10540         can_replace[xx][yy] = FALSE;
10541         complete_replace = FALSE;
10542
10543         continue;
10544       }
10545
10546       e = Feld[ex][ey];
10547
10548       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10549         e = MovingOrBlocked2Element(ex, ey);
10550
10551       is_empty = (IS_FREE(ex, ey) ||
10552                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10553
10554       is_walkable     = (is_empty || IS_WALKABLE(e));
10555       is_diggable     = (is_empty || IS_DIGGABLE(e));
10556       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10557       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10558       is_removable    = (is_diggable || is_collectible);
10559
10560       can_replace[xx][yy] =
10561         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10562           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10563           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10564           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10565           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10566           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10567          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10568
10569       if (!can_replace[xx][yy])
10570         complete_replace = FALSE;
10571     }
10572
10573     if (!change->only_if_complete || complete_replace)
10574     {
10575       boolean something_has_changed = FALSE;
10576
10577       if (change->only_if_complete && change->use_random_replace &&
10578           RND(100) < change->random_percentage)
10579         return FALSE;
10580
10581       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10582       {
10583         int ex = x + xx - 1;
10584         int ey = y + yy - 1;
10585         int content_element;
10586
10587         if (can_replace[xx][yy] && (!change->use_random_replace ||
10588                                     RND(100) < change->random_percentage))
10589         {
10590           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10591             RemoveMovingField(ex, ey);
10592
10593           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10594
10595           content_element = change->target_content.e[xx][yy];
10596           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10597                                               ce_value, ce_score);
10598
10599           CreateElementFromChange(ex, ey, target_element);
10600
10601           something_has_changed = TRUE;
10602
10603           // for symmetry reasons, freeze newly created border elements
10604           if (ex != x || ey != y)
10605             Stop[ex][ey] = TRUE;        // no more moving in this frame
10606         }
10607       }
10608
10609       if (something_has_changed)
10610       {
10611         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10612         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10613       }
10614     }
10615   }
10616   else
10617   {
10618     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10619                                         ce_value, ce_score);
10620
10621     if (element == EL_DIAGONAL_GROWING ||
10622         element == EL_DIAGONAL_SHRINKING)
10623     {
10624       target_element = Store[x][y];
10625
10626       Store[x][y] = EL_EMPTY;
10627     }
10628
10629     CreateElementFromChange(x, y, target_element);
10630
10631     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10632     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10633   }
10634
10635   // this uses direct change before indirect change
10636   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10637
10638   return TRUE;
10639 }
10640
10641 static void HandleElementChange(int x, int y, int page)
10642 {
10643   int element = MovingOrBlocked2Element(x, y);
10644   struct ElementInfo *ei = &element_info[element];
10645   struct ElementChangeInfo *change = &ei->change_page[page];
10646   boolean handle_action_before_change = FALSE;
10647
10648 #ifdef DEBUG
10649   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10650       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10651   {
10652     printf("\n\n");
10653     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10654            x, y, element, element_info[element].token_name);
10655     printf("HandleElementChange(): This should never happen!\n");
10656     printf("\n\n");
10657   }
10658 #endif
10659
10660   // this can happen with classic bombs on walkable, changing elements
10661   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10662   {
10663     return;
10664   }
10665
10666   if (ChangeDelay[x][y] == 0)           // initialize element change
10667   {
10668     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10669
10670     if (change->can_change)
10671     {
10672       // !!! not clear why graphic animation should be reset at all here !!!
10673       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10674       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10675
10676       /*
10677         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10678
10679         When using an animation frame delay of 1 (this only happens with
10680         "sp_zonk.moving.left/right" in the classic graphics), the default
10681         (non-moving) animation shows wrong animation frames (while the
10682         moving animation, like "sp_zonk.moving.left/right", is correct,
10683         so this graphical bug never shows up with the classic graphics).
10684         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10685         be drawn instead of the correct frames 0,1,2,3. This is caused by
10686         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10687         an element change: First when the change delay ("ChangeDelay[][]")
10688         counter has reached zero after decrementing, then a second time in
10689         the next frame (after "GfxFrame[][]" was already incremented) when
10690         "ChangeDelay[][]" is reset to the initial delay value again.
10691
10692         This causes frame 0 to be drawn twice, while the last frame won't
10693         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10694
10695         As some animations may already be cleverly designed around this bug
10696         (at least the "Snake Bite" snake tail animation does this), it cannot
10697         simply be fixed here without breaking such existing animations.
10698         Unfortunately, it cannot easily be detected if a graphics set was
10699         designed "before" or "after" the bug was fixed. As a workaround,
10700         a new graphics set option "game.graphics_engine_version" was added
10701         to be able to specify the game's major release version for which the
10702         graphics set was designed, which can then be used to decide if the
10703         bugfix should be used (version 4 and above) or not (version 3 or
10704         below, or if no version was specified at all, as with old sets).
10705
10706         (The wrong/fixed animation frames can be tested with the test level set
10707         "test_gfxframe" and level "000", which contains a specially prepared
10708         custom element at level position (x/y) == (11/9) which uses the zonk
10709         animation mentioned above. Using "game.graphics_engine_version: 4"
10710         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10711         This can also be seen from the debug output for this test element.)
10712       */
10713
10714       // when a custom element is about to change (for example by change delay),
10715       // do not reset graphic animation when the custom element is moving
10716       if (game.graphics_engine_version < 4 &&
10717           !IS_MOVING(x, y))
10718       {
10719         ResetGfxAnimation(x, y);
10720         ResetRandomAnimationValue(x, y);
10721       }
10722
10723       if (change->pre_change_function)
10724         change->pre_change_function(x, y);
10725     }
10726   }
10727
10728   ChangeDelay[x][y]--;
10729
10730   if (ChangeDelay[x][y] != 0)           // continue element change
10731   {
10732     if (change->can_change)
10733     {
10734       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10735
10736       if (IS_ANIMATED(graphic))
10737         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10738
10739       if (change->change_function)
10740         change->change_function(x, y);
10741     }
10742   }
10743   else                                  // finish element change
10744   {
10745     if (ChangePage[x][y] != -1)         // remember page from delayed change
10746     {
10747       page = ChangePage[x][y];
10748       ChangePage[x][y] = -1;
10749
10750       change = &ei->change_page[page];
10751     }
10752
10753     if (IS_MOVING(x, y))                // never change a running system ;-)
10754     {
10755       ChangeDelay[x][y] = 1;            // try change after next move step
10756       ChangePage[x][y] = page;          // remember page to use for change
10757
10758       return;
10759     }
10760
10761     // special case: set new level random seed before changing element
10762     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10763       handle_action_before_change = TRUE;
10764
10765     if (change->has_action && handle_action_before_change)
10766       ExecuteCustomElementAction(x, y, element, page);
10767
10768     if (change->can_change)
10769     {
10770       if (ChangeElement(x, y, element, page))
10771       {
10772         if (change->post_change_function)
10773           change->post_change_function(x, y);
10774       }
10775     }
10776
10777     if (change->has_action && !handle_action_before_change)
10778       ExecuteCustomElementAction(x, y, element, page);
10779   }
10780 }
10781
10782 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10783                                               int trigger_element,
10784                                               int trigger_event,
10785                                               int trigger_player,
10786                                               int trigger_side,
10787                                               int trigger_page)
10788 {
10789   boolean change_done_any = FALSE;
10790   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10791   int i;
10792
10793   if (!(trigger_events[trigger_element][trigger_event]))
10794     return FALSE;
10795
10796   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10797
10798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10799   {
10800     int element = EL_CUSTOM_START + i;
10801     boolean change_done = FALSE;
10802     int p;
10803
10804     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10805         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10806       continue;
10807
10808     for (p = 0; p < element_info[element].num_change_pages; p++)
10809     {
10810       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10811
10812       if (change->can_change_or_has_action &&
10813           change->has_event[trigger_event] &&
10814           change->trigger_side & trigger_side &&
10815           change->trigger_player & trigger_player &&
10816           change->trigger_page & trigger_page_bits &&
10817           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10818       {
10819         change->actual_trigger_element = trigger_element;
10820         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10821         change->actual_trigger_player_bits = trigger_player;
10822         change->actual_trigger_side = trigger_side;
10823         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10824         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10825
10826         if ((change->can_change && !change_done) || change->has_action)
10827         {
10828           int x, y;
10829
10830           SCAN_PLAYFIELD(x, y)
10831           {
10832             if (Feld[x][y] == element)
10833             {
10834               if (change->can_change && !change_done)
10835               {
10836                 // if element already changed in this frame, not only prevent
10837                 // another element change (checked in ChangeElement()), but
10838                 // also prevent additional element actions for this element
10839
10840                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10841                     !level.use_action_after_change_bug)
10842                   continue;
10843
10844                 ChangeDelay[x][y] = 1;
10845                 ChangeEvent[x][y] = trigger_event;
10846
10847                 HandleElementChange(x, y, p);
10848               }
10849               else if (change->has_action)
10850               {
10851                 // if element already changed in this frame, not only prevent
10852                 // another element change (checked in ChangeElement()), but
10853                 // also prevent additional element actions for this element
10854
10855                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10856                     !level.use_action_after_change_bug)
10857                   continue;
10858
10859                 ExecuteCustomElementAction(x, y, element, p);
10860                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10861               }
10862             }
10863           }
10864
10865           if (change->can_change)
10866           {
10867             change_done = TRUE;
10868             change_done_any = TRUE;
10869           }
10870         }
10871       }
10872     }
10873   }
10874
10875   RECURSION_LOOP_DETECTION_END();
10876
10877   return change_done_any;
10878 }
10879
10880 static boolean CheckElementChangeExt(int x, int y,
10881                                      int element,
10882                                      int trigger_element,
10883                                      int trigger_event,
10884                                      int trigger_player,
10885                                      int trigger_side)
10886 {
10887   boolean change_done = FALSE;
10888   int p;
10889
10890   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10891       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10892     return FALSE;
10893
10894   if (Feld[x][y] == EL_BLOCKED)
10895   {
10896     Blocked2Moving(x, y, &x, &y);
10897     element = Feld[x][y];
10898   }
10899
10900   // check if element has already changed or is about to change after moving
10901   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10902        Feld[x][y] != element) ||
10903
10904       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10905        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10906         ChangePage[x][y] != -1)))
10907     return FALSE;
10908
10909   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10910
10911   for (p = 0; p < element_info[element].num_change_pages; p++)
10912   {
10913     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10914
10915     /* check trigger element for all events where the element that is checked
10916        for changing interacts with a directly adjacent element -- this is
10917        different to element changes that affect other elements to change on the
10918        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10919     boolean check_trigger_element =
10920       (trigger_event == CE_TOUCHING_X ||
10921        trigger_event == CE_HITTING_X ||
10922        trigger_event == CE_HIT_BY_X ||
10923        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10924
10925     if (change->can_change_or_has_action &&
10926         change->has_event[trigger_event] &&
10927         change->trigger_side & trigger_side &&
10928         change->trigger_player & trigger_player &&
10929         (!check_trigger_element ||
10930          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10931     {
10932       change->actual_trigger_element = trigger_element;
10933       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10934       change->actual_trigger_player_bits = trigger_player;
10935       change->actual_trigger_side = trigger_side;
10936       change->actual_trigger_ce_value = CustomValue[x][y];
10937       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10938
10939       // special case: trigger element not at (x,y) position for some events
10940       if (check_trigger_element)
10941       {
10942         static struct
10943         {
10944           int dx, dy;
10945         } move_xy[] =
10946           {
10947             {  0,  0 },
10948             { -1,  0 },
10949             { +1,  0 },
10950             {  0,  0 },
10951             {  0, -1 },
10952             {  0,  0 }, { 0, 0 }, { 0, 0 },
10953             {  0, +1 }
10954           };
10955
10956         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10957         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10958
10959         change->actual_trigger_ce_value = CustomValue[xx][yy];
10960         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10961       }
10962
10963       if (change->can_change && !change_done)
10964       {
10965         ChangeDelay[x][y] = 1;
10966         ChangeEvent[x][y] = trigger_event;
10967
10968         HandleElementChange(x, y, p);
10969
10970         change_done = TRUE;
10971       }
10972       else if (change->has_action)
10973       {
10974         ExecuteCustomElementAction(x, y, element, p);
10975         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10976       }
10977     }
10978   }
10979
10980   RECURSION_LOOP_DETECTION_END();
10981
10982   return change_done;
10983 }
10984
10985 static void PlayPlayerSound(struct PlayerInfo *player)
10986 {
10987   int jx = player->jx, jy = player->jy;
10988   int sound_element = player->artwork_element;
10989   int last_action = player->last_action_waiting;
10990   int action = player->action_waiting;
10991
10992   if (player->is_waiting)
10993   {
10994     if (action != last_action)
10995       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10996     else
10997       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10998   }
10999   else
11000   {
11001     if (action != last_action)
11002       StopSound(element_info[sound_element].sound[last_action]);
11003
11004     if (last_action == ACTION_SLEEPING)
11005       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11006   }
11007 }
11008
11009 static void PlayAllPlayersSound(void)
11010 {
11011   int i;
11012
11013   for (i = 0; i < MAX_PLAYERS; i++)
11014     if (stored_player[i].active)
11015       PlayPlayerSound(&stored_player[i]);
11016 }
11017
11018 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11019 {
11020   boolean last_waiting = player->is_waiting;
11021   int move_dir = player->MovDir;
11022
11023   player->dir_waiting = move_dir;
11024   player->last_action_waiting = player->action_waiting;
11025
11026   if (is_waiting)
11027   {
11028     if (!last_waiting)          // not waiting -> waiting
11029     {
11030       player->is_waiting = TRUE;
11031
11032       player->frame_counter_bored =
11033         FrameCounter +
11034         game.player_boring_delay_fixed +
11035         GetSimpleRandom(game.player_boring_delay_random);
11036       player->frame_counter_sleeping =
11037         FrameCounter +
11038         game.player_sleeping_delay_fixed +
11039         GetSimpleRandom(game.player_sleeping_delay_random);
11040
11041       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11042     }
11043
11044     if (game.player_sleeping_delay_fixed +
11045         game.player_sleeping_delay_random > 0 &&
11046         player->anim_delay_counter == 0 &&
11047         player->post_delay_counter == 0 &&
11048         FrameCounter >= player->frame_counter_sleeping)
11049       player->is_sleeping = TRUE;
11050     else if (game.player_boring_delay_fixed +
11051              game.player_boring_delay_random > 0 &&
11052              FrameCounter >= player->frame_counter_bored)
11053       player->is_bored = TRUE;
11054
11055     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11056                               player->is_bored ? ACTION_BORING :
11057                               ACTION_WAITING);
11058
11059     if (player->is_sleeping && player->use_murphy)
11060     {
11061       // special case for sleeping Murphy when leaning against non-free tile
11062
11063       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11064           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11065            !IS_MOVING(player->jx - 1, player->jy)))
11066         move_dir = MV_LEFT;
11067       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11068                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11069                 !IS_MOVING(player->jx + 1, player->jy)))
11070         move_dir = MV_RIGHT;
11071       else
11072         player->is_sleeping = FALSE;
11073
11074       player->dir_waiting = move_dir;
11075     }
11076
11077     if (player->is_sleeping)
11078     {
11079       if (player->num_special_action_sleeping > 0)
11080       {
11081         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11082         {
11083           int last_special_action = player->special_action_sleeping;
11084           int num_special_action = player->num_special_action_sleeping;
11085           int special_action =
11086             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11087              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11088              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11089              last_special_action + 1 : ACTION_SLEEPING);
11090           int special_graphic =
11091             el_act_dir2img(player->artwork_element, special_action, move_dir);
11092
11093           player->anim_delay_counter =
11094             graphic_info[special_graphic].anim_delay_fixed +
11095             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11096           player->post_delay_counter =
11097             graphic_info[special_graphic].post_delay_fixed +
11098             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11099
11100           player->special_action_sleeping = special_action;
11101         }
11102
11103         if (player->anim_delay_counter > 0)
11104         {
11105           player->action_waiting = player->special_action_sleeping;
11106           player->anim_delay_counter--;
11107         }
11108         else if (player->post_delay_counter > 0)
11109         {
11110           player->post_delay_counter--;
11111         }
11112       }
11113     }
11114     else if (player->is_bored)
11115     {
11116       if (player->num_special_action_bored > 0)
11117       {
11118         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11119         {
11120           int special_action =
11121             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11122           int special_graphic =
11123             el_act_dir2img(player->artwork_element, special_action, move_dir);
11124
11125           player->anim_delay_counter =
11126             graphic_info[special_graphic].anim_delay_fixed +
11127             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11128           player->post_delay_counter =
11129             graphic_info[special_graphic].post_delay_fixed +
11130             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11131
11132           player->special_action_bored = special_action;
11133         }
11134
11135         if (player->anim_delay_counter > 0)
11136         {
11137           player->action_waiting = player->special_action_bored;
11138           player->anim_delay_counter--;
11139         }
11140         else if (player->post_delay_counter > 0)
11141         {
11142           player->post_delay_counter--;
11143         }
11144       }
11145     }
11146   }
11147   else if (last_waiting)        // waiting -> not waiting
11148   {
11149     player->is_waiting = FALSE;
11150     player->is_bored = FALSE;
11151     player->is_sleeping = FALSE;
11152
11153     player->frame_counter_bored = -1;
11154     player->frame_counter_sleeping = -1;
11155
11156     player->anim_delay_counter = 0;
11157     player->post_delay_counter = 0;
11158
11159     player->dir_waiting = player->MovDir;
11160     player->action_waiting = ACTION_DEFAULT;
11161
11162     player->special_action_bored = ACTION_DEFAULT;
11163     player->special_action_sleeping = ACTION_DEFAULT;
11164   }
11165 }
11166
11167 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11168 {
11169   if ((!player->is_moving  && player->was_moving) ||
11170       (player->MovPos == 0 && player->was_moving) ||
11171       (player->is_snapping && !player->was_snapping) ||
11172       (player->is_dropping && !player->was_dropping))
11173   {
11174     if (!CheckSaveEngineSnapshotToList())
11175       return;
11176
11177     player->was_moving = FALSE;
11178     player->was_snapping = TRUE;
11179     player->was_dropping = TRUE;
11180   }
11181   else
11182   {
11183     if (player->is_moving)
11184       player->was_moving = TRUE;
11185
11186     if (!player->is_snapping)
11187       player->was_snapping = FALSE;
11188
11189     if (!player->is_dropping)
11190       player->was_dropping = FALSE;
11191   }
11192 }
11193
11194 static void CheckSingleStepMode(struct PlayerInfo *player)
11195 {
11196   if (tape.single_step && tape.recording && !tape.pausing)
11197   {
11198     /* as it is called "single step mode", just return to pause mode when the
11199        player stopped moving after one tile (or never starts moving at all) */
11200     if (!player->is_moving &&
11201         !player->is_pushing &&
11202         !player->is_dropping_pressed)
11203       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11204   }
11205
11206   CheckSaveEngineSnapshot(player);
11207 }
11208
11209 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11210 {
11211   int left      = player_action & JOY_LEFT;
11212   int right     = player_action & JOY_RIGHT;
11213   int up        = player_action & JOY_UP;
11214   int down      = player_action & JOY_DOWN;
11215   int button1   = player_action & JOY_BUTTON_1;
11216   int button2   = player_action & JOY_BUTTON_2;
11217   int dx        = (left ? -1 : right ? 1 : 0);
11218   int dy        = (up   ? -1 : down  ? 1 : 0);
11219
11220   if (!player->active || tape.pausing)
11221     return 0;
11222
11223   if (player_action)
11224   {
11225     if (button1)
11226       SnapField(player, dx, dy);
11227     else
11228     {
11229       if (button2)
11230         DropElement(player);
11231
11232       MovePlayer(player, dx, dy);
11233     }
11234
11235     CheckSingleStepMode(player);
11236
11237     SetPlayerWaiting(player, FALSE);
11238
11239     return player_action;
11240   }
11241   else
11242   {
11243     // no actions for this player (no input at player's configured device)
11244
11245     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11246     SnapField(player, 0, 0);
11247     CheckGravityMovementWhenNotMoving(player);
11248
11249     if (player->MovPos == 0)
11250       SetPlayerWaiting(player, TRUE);
11251
11252     if (player->MovPos == 0)    // needed for tape.playing
11253       player->is_moving = FALSE;
11254
11255     player->is_dropping = FALSE;
11256     player->is_dropping_pressed = FALSE;
11257     player->drop_pressed_delay = 0;
11258
11259     CheckSingleStepMode(player);
11260
11261     return 0;
11262   }
11263 }
11264
11265 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11266                                          byte *tape_action)
11267 {
11268   if (!tape.use_mouse_actions)
11269     return;
11270
11271   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11272   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11273   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11274 }
11275
11276 static void SetTapeActionFromMouseAction(byte *tape_action,
11277                                          struct MouseActionInfo *mouse_action)
11278 {
11279   if (!tape.use_mouse_actions)
11280     return;
11281
11282   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11283   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11284   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11285 }
11286
11287 static void CheckLevelSolved(void)
11288 {
11289   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11290   {
11291     if (game_em.level_solved &&
11292         !game_em.game_over)                             // game won
11293     {
11294       LevelSolved();
11295
11296       game_em.game_over = TRUE;
11297
11298       game.all_players_gone = TRUE;
11299     }
11300
11301     if (game_em.game_over)                              // game lost
11302       game.all_players_gone = TRUE;
11303   }
11304   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11305   {
11306     if (game_sp.level_solved &&
11307         !game_sp.game_over)                             // game won
11308     {
11309       LevelSolved();
11310
11311       game_sp.game_over = TRUE;
11312
11313       game.all_players_gone = TRUE;
11314     }
11315
11316     if (game_sp.game_over)                              // game lost
11317       game.all_players_gone = TRUE;
11318   }
11319   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11320   {
11321     if (game_mm.level_solved &&
11322         !game_mm.game_over)                             // game won
11323     {
11324       LevelSolved();
11325
11326       game_mm.game_over = TRUE;
11327
11328       game.all_players_gone = TRUE;
11329     }
11330
11331     if (game_mm.game_over)                              // game lost
11332       game.all_players_gone = TRUE;
11333   }
11334 }
11335
11336 static void CheckLevelTime(void)
11337 {
11338   int i;
11339
11340   if (TimeFrames >= FRAMES_PER_SECOND)
11341   {
11342     TimeFrames = 0;
11343     TapeTime++;
11344
11345     for (i = 0; i < MAX_PLAYERS; i++)
11346     {
11347       struct PlayerInfo *player = &stored_player[i];
11348
11349       if (SHIELD_ON(player))
11350       {
11351         player->shield_normal_time_left--;
11352
11353         if (player->shield_deadly_time_left > 0)
11354           player->shield_deadly_time_left--;
11355       }
11356     }
11357
11358     if (!game.LevelSolved && !level.use_step_counter)
11359     {
11360       TimePlayed++;
11361
11362       if (TimeLeft > 0)
11363       {
11364         TimeLeft--;
11365
11366         if (TimeLeft <= 10 && setup.time_limit)
11367           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11368
11369         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11370            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11371
11372         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11373
11374         if (!TimeLeft && setup.time_limit)
11375         {
11376           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11377             game_em.lev->killed_out_of_time = TRUE;
11378           else
11379             for (i = 0; i < MAX_PLAYERS; i++)
11380               KillPlayer(&stored_player[i]);
11381         }
11382       }
11383       else if (game.no_time_limit && !game.all_players_gone)
11384       {
11385         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11386       }
11387
11388       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11389     }
11390
11391     if (tape.recording || tape.playing)
11392       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11393   }
11394
11395   if (tape.recording || tape.playing)
11396     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11397
11398   UpdateAndDisplayGameControlValues();
11399 }
11400
11401 void AdvanceFrameAndPlayerCounters(int player_nr)
11402 {
11403   int i;
11404
11405   // advance frame counters (global frame counter and time frame counter)
11406   FrameCounter++;
11407   TimeFrames++;
11408
11409   // advance player counters (counters for move delay, move animation etc.)
11410   for (i = 0; i < MAX_PLAYERS; i++)
11411   {
11412     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11413     int move_delay_value = stored_player[i].move_delay_value;
11414     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11415
11416     if (!advance_player_counters)       // not all players may be affected
11417       continue;
11418
11419     if (move_frames == 0)       // less than one move per game frame
11420     {
11421       int stepsize = TILEX / move_delay_value;
11422       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11423       int count = (stored_player[i].is_moving ?
11424                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11425
11426       if (count % delay == 0)
11427         move_frames = 1;
11428     }
11429
11430     stored_player[i].Frame += move_frames;
11431
11432     if (stored_player[i].MovPos != 0)
11433       stored_player[i].StepFrame += move_frames;
11434
11435     if (stored_player[i].move_delay > 0)
11436       stored_player[i].move_delay--;
11437
11438     // due to bugs in previous versions, counter must count up, not down
11439     if (stored_player[i].push_delay != -1)
11440       stored_player[i].push_delay++;
11441
11442     if (stored_player[i].drop_delay > 0)
11443       stored_player[i].drop_delay--;
11444
11445     if (stored_player[i].is_dropping_pressed)
11446       stored_player[i].drop_pressed_delay++;
11447   }
11448 }
11449
11450 void StartGameActions(boolean init_network_game, boolean record_tape,
11451                       int random_seed)
11452 {
11453   unsigned int new_random_seed = InitRND(random_seed);
11454
11455   if (record_tape)
11456     TapeStartRecording(new_random_seed);
11457
11458   if (init_network_game)
11459   {
11460     SendToServer_LevelFile();
11461     SendToServer_StartPlaying();
11462
11463     return;
11464   }
11465
11466   InitGame();
11467 }
11468
11469 static void GameActionsExt(void)
11470 {
11471 #if 0
11472   static unsigned int game_frame_delay = 0;
11473 #endif
11474   unsigned int game_frame_delay_value;
11475   byte *recorded_player_action;
11476   byte summarized_player_action = 0;
11477   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11478   int i;
11479
11480   // detect endless loops, caused by custom element programming
11481   if (recursion_loop_detected && recursion_loop_depth == 0)
11482   {
11483     char *message = getStringCat3("Internal Error! Element ",
11484                                   EL_NAME(recursion_loop_element),
11485                                   " caused endless loop! Quit the game?");
11486
11487     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11488           EL_NAME(recursion_loop_element));
11489
11490     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11491
11492     recursion_loop_detected = FALSE;    // if game should be continued
11493
11494     free(message);
11495
11496     return;
11497   }
11498
11499   if (game.restart_level)
11500     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11501
11502   CheckLevelSolved();
11503
11504   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11505     GameWon();
11506
11507   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11508     TapeStop();
11509
11510   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11511     return;
11512
11513   game_frame_delay_value =
11514     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11515
11516   if (tape.playing && tape.warp_forward && !tape.pausing)
11517     game_frame_delay_value = 0;
11518
11519   SetVideoFrameDelay(game_frame_delay_value);
11520
11521   // (de)activate virtual buttons depending on current game status
11522   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11523   {
11524     if (game.all_players_gone)  // if no players there to be controlled anymore
11525       SetOverlayActive(FALSE);
11526     else if (!tape.playing)     // if game continues after tape stopped playing
11527       SetOverlayActive(TRUE);
11528   }
11529
11530 #if 0
11531 #if 0
11532   // ---------- main game synchronization point ----------
11533
11534   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11535
11536   printf("::: skip == %d\n", skip);
11537
11538 #else
11539   // ---------- main game synchronization point ----------
11540
11541   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11542 #endif
11543 #endif
11544
11545   if (network_playing && !network_player_action_received)
11546   {
11547     // try to get network player actions in time
11548
11549     // last chance to get network player actions without main loop delay
11550     HandleNetworking();
11551
11552     // game was quit by network peer
11553     if (game_status != GAME_MODE_PLAYING)
11554       return;
11555
11556     // check if network player actions still missing and game still running
11557     if (!network_player_action_received && !checkGameEnded())
11558       return;           // failed to get network player actions in time
11559
11560     // do not yet reset "network_player_action_received" (for tape.pausing)
11561   }
11562
11563   if (tape.pausing)
11564     return;
11565
11566   // at this point we know that we really continue executing the game
11567
11568   network_player_action_received = FALSE;
11569
11570   // when playing tape, read previously recorded player input from tape data
11571   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11572
11573   local_player->effective_mouse_action = local_player->mouse_action;
11574
11575   if (recorded_player_action != NULL)
11576     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11577                                  recorded_player_action);
11578
11579   // TapePlayAction() may return NULL when toggling to "pause before death"
11580   if (tape.pausing)
11581     return;
11582
11583   if (tape.set_centered_player)
11584   {
11585     game.centered_player_nr_next = tape.centered_player_nr_next;
11586     game.set_centered_player = TRUE;
11587   }
11588
11589   for (i = 0; i < MAX_PLAYERS; i++)
11590   {
11591     summarized_player_action |= stored_player[i].action;
11592
11593     if (!network_playing && (game.team_mode || tape.playing))
11594       stored_player[i].effective_action = stored_player[i].action;
11595   }
11596
11597   if (network_playing && !checkGameEnded())
11598     SendToServer_MovePlayer(summarized_player_action);
11599
11600   // summarize all actions at local players mapped input device position
11601   // (this allows using different input devices in single player mode)
11602   if (!network.enabled && !game.team_mode)
11603     stored_player[map_player_action[local_player->index_nr]].effective_action =
11604       summarized_player_action;
11605
11606   // summarize all actions at centered player in local team mode
11607   if (tape.recording &&
11608       setup.team_mode && !network.enabled &&
11609       setup.input_on_focus &&
11610       game.centered_player_nr != -1)
11611   {
11612     for (i = 0; i < MAX_PLAYERS; i++)
11613       stored_player[map_player_action[i]].effective_action =
11614         (i == game.centered_player_nr ? summarized_player_action : 0);
11615   }
11616
11617   if (recorded_player_action != NULL)
11618     for (i = 0; i < MAX_PLAYERS; i++)
11619       stored_player[i].effective_action = recorded_player_action[i];
11620
11621   for (i = 0; i < MAX_PLAYERS; i++)
11622   {
11623     tape_action[i] = stored_player[i].effective_action;
11624
11625     /* (this may happen in the RND game engine if a player was not present on
11626        the playfield on level start, but appeared later from a custom element */
11627     if (setup.team_mode &&
11628         tape.recording &&
11629         tape_action[i] &&
11630         !tape.player_participates[i])
11631       tape.player_participates[i] = TRUE;
11632   }
11633
11634   SetTapeActionFromMouseAction(tape_action,
11635                                &local_player->effective_mouse_action);
11636
11637   // only record actions from input devices, but not programmed actions
11638   if (tape.recording)
11639     TapeRecordAction(tape_action);
11640
11641   // remember if game was played (especially after tape stopped playing)
11642   if (!tape.playing && summarized_player_action)
11643     game.GamePlayed = TRUE;
11644
11645 #if USE_NEW_PLAYER_ASSIGNMENTS
11646   // !!! also map player actions in single player mode !!!
11647   // if (game.team_mode)
11648   if (1)
11649   {
11650     byte mapped_action[MAX_PLAYERS];
11651
11652 #if DEBUG_PLAYER_ACTIONS
11653     printf(":::");
11654     for (i = 0; i < MAX_PLAYERS; i++)
11655       printf(" %d, ", stored_player[i].effective_action);
11656 #endif
11657
11658     for (i = 0; i < MAX_PLAYERS; i++)
11659       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11660
11661     for (i = 0; i < MAX_PLAYERS; i++)
11662       stored_player[i].effective_action = mapped_action[i];
11663
11664 #if DEBUG_PLAYER_ACTIONS
11665     printf(" =>");
11666     for (i = 0; i < MAX_PLAYERS; i++)
11667       printf(" %d, ", stored_player[i].effective_action);
11668     printf("\n");
11669 #endif
11670   }
11671 #if DEBUG_PLAYER_ACTIONS
11672   else
11673   {
11674     printf(":::");
11675     for (i = 0; i < MAX_PLAYERS; i++)
11676       printf(" %d, ", stored_player[i].effective_action);
11677     printf("\n");
11678   }
11679 #endif
11680 #endif
11681
11682   for (i = 0; i < MAX_PLAYERS; i++)
11683   {
11684     // allow engine snapshot in case of changed movement attempt
11685     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11686         (stored_player[i].effective_action & KEY_MOTION))
11687       game.snapshot.changed_action = TRUE;
11688
11689     // allow engine snapshot in case of snapping/dropping attempt
11690     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11691         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11692       game.snapshot.changed_action = TRUE;
11693
11694     game.snapshot.last_action[i] = stored_player[i].effective_action;
11695   }
11696
11697   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11698   {
11699     GameActions_EM_Main();
11700   }
11701   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11702   {
11703     GameActions_SP_Main();
11704   }
11705   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11706   {
11707     GameActions_MM_Main();
11708   }
11709   else
11710   {
11711     GameActions_RND_Main();
11712   }
11713
11714   BlitScreenToBitmap(backbuffer);
11715
11716   CheckLevelSolved();
11717   CheckLevelTime();
11718
11719   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11720
11721   if (global.show_frames_per_second)
11722   {
11723     static unsigned int fps_counter = 0;
11724     static int fps_frames = 0;
11725     unsigned int fps_delay_ms = Counter() - fps_counter;
11726
11727     fps_frames++;
11728
11729     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11730     {
11731       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11732
11733       fps_frames = 0;
11734       fps_counter = Counter();
11735
11736       // always draw FPS to screen after FPS value was updated
11737       redraw_mask |= REDRAW_FPS;
11738     }
11739
11740     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11741     if (GetDrawDeactivationMask() == REDRAW_NONE)
11742       redraw_mask |= REDRAW_FPS;
11743   }
11744 }
11745
11746 static void GameActions_CheckSaveEngineSnapshot(void)
11747 {
11748   if (!game.snapshot.save_snapshot)
11749     return;
11750
11751   // clear flag for saving snapshot _before_ saving snapshot
11752   game.snapshot.save_snapshot = FALSE;
11753
11754   SaveEngineSnapshotToList();
11755 }
11756
11757 void GameActions(void)
11758 {
11759   GameActionsExt();
11760
11761   GameActions_CheckSaveEngineSnapshot();
11762 }
11763
11764 void GameActions_EM_Main(void)
11765 {
11766   byte effective_action[MAX_PLAYERS];
11767   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11768   int i;
11769
11770   for (i = 0; i < MAX_PLAYERS; i++)
11771     effective_action[i] = stored_player[i].effective_action;
11772
11773   GameActions_EM(effective_action, warp_mode);
11774 }
11775
11776 void GameActions_SP_Main(void)
11777 {
11778   byte effective_action[MAX_PLAYERS];
11779   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11780   int i;
11781
11782   for (i = 0; i < MAX_PLAYERS; i++)
11783     effective_action[i] = stored_player[i].effective_action;
11784
11785   GameActions_SP(effective_action, warp_mode);
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788   {
11789     if (stored_player[i].force_dropping)
11790       stored_player[i].action |= KEY_BUTTON_DROP;
11791
11792     stored_player[i].force_dropping = FALSE;
11793   }
11794 }
11795
11796 void GameActions_MM_Main(void)
11797 {
11798   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11799
11800   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11801 }
11802
11803 void GameActions_RND_Main(void)
11804 {
11805   GameActions_RND();
11806 }
11807
11808 void GameActions_RND(void)
11809 {
11810   static struct MouseActionInfo mouse_action_last = { 0 };
11811   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11812   int magic_wall_x = 0, magic_wall_y = 0;
11813   int i, x, y, element, graphic, last_gfx_frame;
11814
11815   InitPlayfieldScanModeVars();
11816
11817   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11818   {
11819     SCAN_PLAYFIELD(x, y)
11820     {
11821       ChangeCount[x][y] = 0;
11822       ChangeEvent[x][y] = -1;
11823     }
11824   }
11825
11826   if (game.set_centered_player)
11827   {
11828     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11829
11830     // switching to "all players" only possible if all players fit to screen
11831     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11832     {
11833       game.centered_player_nr_next = game.centered_player_nr;
11834       game.set_centered_player = FALSE;
11835     }
11836
11837     // do not switch focus to non-existing (or non-active) player
11838     if (game.centered_player_nr_next >= 0 &&
11839         !stored_player[game.centered_player_nr_next].active)
11840     {
11841       game.centered_player_nr_next = game.centered_player_nr;
11842       game.set_centered_player = FALSE;
11843     }
11844   }
11845
11846   if (game.set_centered_player &&
11847       ScreenMovPos == 0)        // screen currently aligned at tile position
11848   {
11849     int sx, sy;
11850
11851     if (game.centered_player_nr_next == -1)
11852     {
11853       setScreenCenteredToAllPlayers(&sx, &sy);
11854     }
11855     else
11856     {
11857       sx = stored_player[game.centered_player_nr_next].jx;
11858       sy = stored_player[game.centered_player_nr_next].jy;
11859     }
11860
11861     game.centered_player_nr = game.centered_player_nr_next;
11862     game.set_centered_player = FALSE;
11863
11864     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11865     DrawGameDoorValues();
11866   }
11867
11868   for (i = 0; i < MAX_PLAYERS; i++)
11869   {
11870     int actual_player_action = stored_player[i].effective_action;
11871
11872 #if 1
11873     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11874        - rnd_equinox_tetrachloride 048
11875        - rnd_equinox_tetrachloride_ii 096
11876        - rnd_emanuel_schmieg 002
11877        - doctor_sloan_ww 001, 020
11878     */
11879     if (stored_player[i].MovPos == 0)
11880       CheckGravityMovement(&stored_player[i]);
11881 #endif
11882
11883     // overwrite programmed action with tape action
11884     if (stored_player[i].programmed_action)
11885       actual_player_action = stored_player[i].programmed_action;
11886
11887     PlayerActions(&stored_player[i], actual_player_action);
11888
11889     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11890   }
11891
11892   ScrollScreen(NULL, SCROLL_GO_ON);
11893
11894   /* for backwards compatibility, the following code emulates a fixed bug that
11895      occured when pushing elements (causing elements that just made their last
11896      pushing step to already (if possible) make their first falling step in the
11897      same game frame, which is bad); this code is also needed to use the famous
11898      "spring push bug" which is used in older levels and might be wanted to be
11899      used also in newer levels, but in this case the buggy pushing code is only
11900      affecting the "spring" element and no other elements */
11901
11902   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11903   {
11904     for (i = 0; i < MAX_PLAYERS; i++)
11905     {
11906       struct PlayerInfo *player = &stored_player[i];
11907       int x = player->jx;
11908       int y = player->jy;
11909
11910       if (player->active && player->is_pushing && player->is_moving &&
11911           IS_MOVING(x, y) &&
11912           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11913            Feld[x][y] == EL_SPRING))
11914       {
11915         ContinueMoving(x, y);
11916
11917         // continue moving after pushing (this is actually a bug)
11918         if (!IS_MOVING(x, y))
11919           Stop[x][y] = FALSE;
11920       }
11921     }
11922   }
11923
11924   SCAN_PLAYFIELD(x, y)
11925   {
11926     Last[x][y] = Feld[x][y];
11927
11928     ChangeCount[x][y] = 0;
11929     ChangeEvent[x][y] = -1;
11930
11931     // this must be handled before main playfield loop
11932     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11933     {
11934       MovDelay[x][y]--;
11935       if (MovDelay[x][y] <= 0)
11936         RemoveField(x, y);
11937     }
11938
11939     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11940     {
11941       MovDelay[x][y]--;
11942       if (MovDelay[x][y] <= 0)
11943       {
11944         RemoveField(x, y);
11945         TEST_DrawLevelField(x, y);
11946
11947         TestIfElementTouchesCustomElement(x, y);        // for empty space
11948       }
11949     }
11950
11951 #if DEBUG
11952     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11953     {
11954       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11955       printf("GameActions(): This should never happen!\n");
11956
11957       ChangePage[x][y] = -1;
11958     }
11959 #endif
11960
11961     Stop[x][y] = FALSE;
11962     if (WasJustMoving[x][y] > 0)
11963       WasJustMoving[x][y]--;
11964     if (WasJustFalling[x][y] > 0)
11965       WasJustFalling[x][y]--;
11966     if (CheckCollision[x][y] > 0)
11967       CheckCollision[x][y]--;
11968     if (CheckImpact[x][y] > 0)
11969       CheckImpact[x][y]--;
11970
11971     GfxFrame[x][y]++;
11972
11973     /* reset finished pushing action (not done in ContinueMoving() to allow
11974        continuous pushing animation for elements with zero push delay) */
11975     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11976     {
11977       ResetGfxAnimation(x, y);
11978       TEST_DrawLevelField(x, y);
11979     }
11980
11981 #if DEBUG
11982     if (IS_BLOCKED(x, y))
11983     {
11984       int oldx, oldy;
11985
11986       Blocked2Moving(x, y, &oldx, &oldy);
11987       if (!IS_MOVING(oldx, oldy))
11988       {
11989         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11990         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11991         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11992         printf("GameActions(): This should never happen!\n");
11993       }
11994     }
11995 #endif
11996   }
11997
11998   if (mouse_action.button)
11999   {
12000     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12001
12002     x = mouse_action.lx;
12003     y = mouse_action.ly;
12004     element = Feld[x][y];
12005
12006     if (new_button)
12007     {
12008       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12009       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12010     }
12011
12012     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12013     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12014   }
12015
12016   SCAN_PLAYFIELD(x, y)
12017   {
12018     element = Feld[x][y];
12019     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12020     last_gfx_frame = GfxFrame[x][y];
12021
12022     ResetGfxFrame(x, y);
12023
12024     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12025       DrawLevelGraphicAnimation(x, y, graphic);
12026
12027     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12028         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12029       ResetRandomAnimationValue(x, y);
12030
12031     SetRandomAnimationValue(x, y);
12032
12033     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12034
12035     if (IS_INACTIVE(element))
12036     {
12037       if (IS_ANIMATED(graphic))
12038         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12039
12040       continue;
12041     }
12042
12043     // this may take place after moving, so 'element' may have changed
12044     if (IS_CHANGING(x, y) &&
12045         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12046     {
12047       int page = element_info[element].event_page_nr[CE_DELAY];
12048
12049       HandleElementChange(x, y, page);
12050
12051       element = Feld[x][y];
12052       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12053     }
12054
12055     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12056     {
12057       StartMoving(x, y);
12058
12059       element = Feld[x][y];
12060       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12061
12062       if (IS_ANIMATED(graphic) &&
12063           !IS_MOVING(x, y) &&
12064           !Stop[x][y])
12065         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12066
12067       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12068         TEST_DrawTwinkleOnField(x, y);
12069     }
12070     else if (element == EL_ACID)
12071     {
12072       if (!Stop[x][y])
12073         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12074     }
12075     else if ((element == EL_EXIT_OPEN ||
12076               element == EL_EM_EXIT_OPEN ||
12077               element == EL_SP_EXIT_OPEN ||
12078               element == EL_STEEL_EXIT_OPEN ||
12079               element == EL_EM_STEEL_EXIT_OPEN ||
12080               element == EL_SP_TERMINAL ||
12081               element == EL_SP_TERMINAL_ACTIVE ||
12082               element == EL_EXTRA_TIME ||
12083               element == EL_SHIELD_NORMAL ||
12084               element == EL_SHIELD_DEADLY) &&
12085              IS_ANIMATED(graphic))
12086       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12087     else if (IS_MOVING(x, y))
12088       ContinueMoving(x, y);
12089     else if (IS_ACTIVE_BOMB(element))
12090       CheckDynamite(x, y);
12091     else if (element == EL_AMOEBA_GROWING)
12092       AmoebeWaechst(x, y);
12093     else if (element == EL_AMOEBA_SHRINKING)
12094       AmoebaDisappearing(x, y);
12095
12096 #if !USE_NEW_AMOEBA_CODE
12097     else if (IS_AMOEBALIVE(element))
12098       AmoebeAbleger(x, y);
12099 #endif
12100
12101     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12102       Life(x, y);
12103     else if (element == EL_EXIT_CLOSED)
12104       CheckExit(x, y);
12105     else if (element == EL_EM_EXIT_CLOSED)
12106       CheckExitEM(x, y);
12107     else if (element == EL_STEEL_EXIT_CLOSED)
12108       CheckExitSteel(x, y);
12109     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12110       CheckExitSteelEM(x, y);
12111     else if (element == EL_SP_EXIT_CLOSED)
12112       CheckExitSP(x, y);
12113     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12114              element == EL_EXPANDABLE_STEELWALL_GROWING)
12115       MauerWaechst(x, y);
12116     else if (element == EL_EXPANDABLE_WALL ||
12117              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12118              element == EL_EXPANDABLE_WALL_VERTICAL ||
12119              element == EL_EXPANDABLE_WALL_ANY ||
12120              element == EL_BD_EXPANDABLE_WALL)
12121       MauerAbleger(x, y);
12122     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12123              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12124              element == EL_EXPANDABLE_STEELWALL_ANY)
12125       MauerAblegerStahl(x, y);
12126     else if (element == EL_FLAMES)
12127       CheckForDragon(x, y);
12128     else if (element == EL_EXPLOSION)
12129       ; // drawing of correct explosion animation is handled separately
12130     else if (element == EL_ELEMENT_SNAPPING ||
12131              element == EL_DIAGONAL_SHRINKING ||
12132              element == EL_DIAGONAL_GROWING)
12133     {
12134       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12135
12136       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12137     }
12138     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12139       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12140
12141     if (IS_BELT_ACTIVE(element))
12142       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12143
12144     if (game.magic_wall_active)
12145     {
12146       int jx = local_player->jx, jy = local_player->jy;
12147
12148       // play the element sound at the position nearest to the player
12149       if ((element == EL_MAGIC_WALL_FULL ||
12150            element == EL_MAGIC_WALL_ACTIVE ||
12151            element == EL_MAGIC_WALL_EMPTYING ||
12152            element == EL_BD_MAGIC_WALL_FULL ||
12153            element == EL_BD_MAGIC_WALL_ACTIVE ||
12154            element == EL_BD_MAGIC_WALL_EMPTYING ||
12155            element == EL_DC_MAGIC_WALL_FULL ||
12156            element == EL_DC_MAGIC_WALL_ACTIVE ||
12157            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12158           ABS(x - jx) + ABS(y - jy) <
12159           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12160       {
12161         magic_wall_x = x;
12162         magic_wall_y = y;
12163       }
12164     }
12165   }
12166
12167 #if USE_NEW_AMOEBA_CODE
12168   // new experimental amoeba growth stuff
12169   if (!(FrameCounter % 8))
12170   {
12171     static unsigned int random = 1684108901;
12172
12173     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12174     {
12175       x = RND(lev_fieldx);
12176       y = RND(lev_fieldy);
12177       element = Feld[x][y];
12178
12179       if (!IS_PLAYER(x,y) &&
12180           (element == EL_EMPTY ||
12181            CAN_GROW_INTO(element) ||
12182            element == EL_QUICKSAND_EMPTY ||
12183            element == EL_QUICKSAND_FAST_EMPTY ||
12184            element == EL_ACID_SPLASH_LEFT ||
12185            element == EL_ACID_SPLASH_RIGHT))
12186       {
12187         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12188             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12189             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12190             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12191           Feld[x][y] = EL_AMOEBA_DROP;
12192       }
12193
12194       random = random * 129 + 1;
12195     }
12196   }
12197 #endif
12198
12199   game.explosions_delayed = FALSE;
12200
12201   SCAN_PLAYFIELD(x, y)
12202   {
12203     element = Feld[x][y];
12204
12205     if (ExplodeField[x][y])
12206       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12207     else if (element == EL_EXPLOSION)
12208       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12209
12210     ExplodeField[x][y] = EX_TYPE_NONE;
12211   }
12212
12213   game.explosions_delayed = TRUE;
12214
12215   if (game.magic_wall_active)
12216   {
12217     if (!(game.magic_wall_time_left % 4))
12218     {
12219       int element = Feld[magic_wall_x][magic_wall_y];
12220
12221       if (element == EL_BD_MAGIC_WALL_FULL ||
12222           element == EL_BD_MAGIC_WALL_ACTIVE ||
12223           element == EL_BD_MAGIC_WALL_EMPTYING)
12224         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12225       else if (element == EL_DC_MAGIC_WALL_FULL ||
12226                element == EL_DC_MAGIC_WALL_ACTIVE ||
12227                element == EL_DC_MAGIC_WALL_EMPTYING)
12228         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12229       else
12230         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12231     }
12232
12233     if (game.magic_wall_time_left > 0)
12234     {
12235       game.magic_wall_time_left--;
12236
12237       if (!game.magic_wall_time_left)
12238       {
12239         SCAN_PLAYFIELD(x, y)
12240         {
12241           element = Feld[x][y];
12242
12243           if (element == EL_MAGIC_WALL_ACTIVE ||
12244               element == EL_MAGIC_WALL_FULL)
12245           {
12246             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12247             TEST_DrawLevelField(x, y);
12248           }
12249           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12250                    element == EL_BD_MAGIC_WALL_FULL)
12251           {
12252             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12253             TEST_DrawLevelField(x, y);
12254           }
12255           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12256                    element == EL_DC_MAGIC_WALL_FULL)
12257           {
12258             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12259             TEST_DrawLevelField(x, y);
12260           }
12261         }
12262
12263         game.magic_wall_active = FALSE;
12264       }
12265     }
12266   }
12267
12268   if (game.light_time_left > 0)
12269   {
12270     game.light_time_left--;
12271
12272     if (game.light_time_left == 0)
12273       RedrawAllLightSwitchesAndInvisibleElements();
12274   }
12275
12276   if (game.timegate_time_left > 0)
12277   {
12278     game.timegate_time_left--;
12279
12280     if (game.timegate_time_left == 0)
12281       CloseAllOpenTimegates();
12282   }
12283
12284   if (game.lenses_time_left > 0)
12285   {
12286     game.lenses_time_left--;
12287
12288     if (game.lenses_time_left == 0)
12289       RedrawAllInvisibleElementsForLenses();
12290   }
12291
12292   if (game.magnify_time_left > 0)
12293   {
12294     game.magnify_time_left--;
12295
12296     if (game.magnify_time_left == 0)
12297       RedrawAllInvisibleElementsForMagnifier();
12298   }
12299
12300   for (i = 0; i < MAX_PLAYERS; i++)
12301   {
12302     struct PlayerInfo *player = &stored_player[i];
12303
12304     if (SHIELD_ON(player))
12305     {
12306       if (player->shield_deadly_time_left)
12307         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12308       else if (player->shield_normal_time_left)
12309         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12310     }
12311   }
12312
12313 #if USE_DELAYED_GFX_REDRAW
12314   SCAN_PLAYFIELD(x, y)
12315   {
12316     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12317     {
12318       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12319          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12320
12321       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12322         DrawLevelField(x, y);
12323
12324       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12325         DrawLevelFieldCrumbled(x, y);
12326
12327       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12328         DrawLevelFieldCrumbledNeighbours(x, y);
12329
12330       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12331         DrawTwinkleOnField(x, y);
12332     }
12333
12334     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12335   }
12336 #endif
12337
12338   DrawAllPlayers();
12339   PlayAllPlayersSound();
12340
12341   for (i = 0; i < MAX_PLAYERS; i++)
12342   {
12343     struct PlayerInfo *player = &stored_player[i];
12344
12345     if (player->show_envelope != 0 && (!player->active ||
12346                                        player->MovPos == 0))
12347     {
12348       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12349
12350       player->show_envelope = 0;
12351     }
12352   }
12353
12354   // use random number generator in every frame to make it less predictable
12355   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12356     RND(1);
12357
12358   mouse_action_last = mouse_action;
12359 }
12360
12361 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12362 {
12363   int min_x = x, min_y = y, max_x = x, max_y = y;
12364   int i;
12365
12366   for (i = 0; i < MAX_PLAYERS; i++)
12367   {
12368     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12369
12370     if (!stored_player[i].active || &stored_player[i] == player)
12371       continue;
12372
12373     min_x = MIN(min_x, jx);
12374     min_y = MIN(min_y, jy);
12375     max_x = MAX(max_x, jx);
12376     max_y = MAX(max_y, jy);
12377   }
12378
12379   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12380 }
12381
12382 static boolean AllPlayersInVisibleScreen(void)
12383 {
12384   int i;
12385
12386   for (i = 0; i < MAX_PLAYERS; i++)
12387   {
12388     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12389
12390     if (!stored_player[i].active)
12391       continue;
12392
12393     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12394       return FALSE;
12395   }
12396
12397   return TRUE;
12398 }
12399
12400 void ScrollLevel(int dx, int dy)
12401 {
12402   int scroll_offset = 2 * TILEX_VAR;
12403   int x, y;
12404
12405   BlitBitmap(drawto_field, drawto_field,
12406              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12407              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12408              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12409              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12410              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12411              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12412
12413   if (dx != 0)
12414   {
12415     x = (dx == 1 ? BX1 : BX2);
12416     for (y = BY1; y <= BY2; y++)
12417       DrawScreenField(x, y);
12418   }
12419
12420   if (dy != 0)
12421   {
12422     y = (dy == 1 ? BY1 : BY2);
12423     for (x = BX1; x <= BX2; x++)
12424       DrawScreenField(x, y);
12425   }
12426
12427   redraw_mask |= REDRAW_FIELD;
12428 }
12429
12430 static boolean canFallDown(struct PlayerInfo *player)
12431 {
12432   int jx = player->jx, jy = player->jy;
12433
12434   return (IN_LEV_FIELD(jx, jy + 1) &&
12435           (IS_FREE(jx, jy + 1) ||
12436            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12437           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12438           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12439 }
12440
12441 static boolean canPassField(int x, int y, int move_dir)
12442 {
12443   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12444   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12445   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12446   int nextx = x + dx;
12447   int nexty = y + dy;
12448   int element = Feld[x][y];
12449
12450   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12451           !CAN_MOVE(element) &&
12452           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12453           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12454           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12455 }
12456
12457 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12458 {
12459   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12460   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12461   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12462   int newx = x + dx;
12463   int newy = y + dy;
12464
12465   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12466           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12467           (IS_DIGGABLE(Feld[newx][newy]) ||
12468            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12469            canPassField(newx, newy, move_dir)));
12470 }
12471
12472 static void CheckGravityMovement(struct PlayerInfo *player)
12473 {
12474   if (player->gravity && !player->programmed_action)
12475   {
12476     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12477     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12478     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12479     int jx = player->jx, jy = player->jy;
12480     boolean player_is_moving_to_valid_field =
12481       (!player_is_snapping &&
12482        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12483         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12484     boolean player_can_fall_down = canFallDown(player);
12485
12486     if (player_can_fall_down &&
12487         !player_is_moving_to_valid_field)
12488       player->programmed_action = MV_DOWN;
12489   }
12490 }
12491
12492 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12493 {
12494   return CheckGravityMovement(player);
12495
12496   if (player->gravity && !player->programmed_action)
12497   {
12498     int jx = player->jx, jy = player->jy;
12499     boolean field_under_player_is_free =
12500       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12501     boolean player_is_standing_on_valid_field =
12502       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12503        (IS_WALKABLE(Feld[jx][jy]) &&
12504         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12505
12506     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12507       player->programmed_action = MV_DOWN;
12508   }
12509 }
12510
12511 /*
12512   MovePlayerOneStep()
12513   -----------------------------------------------------------------------------
12514   dx, dy:               direction (non-diagonal) to try to move the player to
12515   real_dx, real_dy:     direction as read from input device (can be diagonal)
12516 */
12517
12518 boolean MovePlayerOneStep(struct PlayerInfo *player,
12519                           int dx, int dy, int real_dx, int real_dy)
12520 {
12521   int jx = player->jx, jy = player->jy;
12522   int new_jx = jx + dx, new_jy = jy + dy;
12523   int can_move;
12524   boolean player_can_move = !player->cannot_move;
12525
12526   if (!player->active || (!dx && !dy))
12527     return MP_NO_ACTION;
12528
12529   player->MovDir = (dx < 0 ? MV_LEFT :
12530                     dx > 0 ? MV_RIGHT :
12531                     dy < 0 ? MV_UP :
12532                     dy > 0 ? MV_DOWN :  MV_NONE);
12533
12534   if (!IN_LEV_FIELD(new_jx, new_jy))
12535     return MP_NO_ACTION;
12536
12537   if (!player_can_move)
12538   {
12539     if (player->MovPos == 0)
12540     {
12541       player->is_moving = FALSE;
12542       player->is_digging = FALSE;
12543       player->is_collecting = FALSE;
12544       player->is_snapping = FALSE;
12545       player->is_pushing = FALSE;
12546     }
12547   }
12548
12549   if (!network.enabled && game.centered_player_nr == -1 &&
12550       !AllPlayersInSight(player, new_jx, new_jy))
12551     return MP_NO_ACTION;
12552
12553   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12554   if (can_move != MP_MOVING)
12555     return can_move;
12556
12557   // check if DigField() has caused relocation of the player
12558   if (player->jx != jx || player->jy != jy)
12559     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12560
12561   StorePlayer[jx][jy] = 0;
12562   player->last_jx = jx;
12563   player->last_jy = jy;
12564   player->jx = new_jx;
12565   player->jy = new_jy;
12566   StorePlayer[new_jx][new_jy] = player->element_nr;
12567
12568   if (player->move_delay_value_next != -1)
12569   {
12570     player->move_delay_value = player->move_delay_value_next;
12571     player->move_delay_value_next = -1;
12572   }
12573
12574   player->MovPos =
12575     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12576
12577   player->step_counter++;
12578
12579   PlayerVisit[jx][jy] = FrameCounter;
12580
12581   player->is_moving = TRUE;
12582
12583 #if 1
12584   // should better be called in MovePlayer(), but this breaks some tapes
12585   ScrollPlayer(player, SCROLL_INIT);
12586 #endif
12587
12588   return MP_MOVING;
12589 }
12590
12591 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12592 {
12593   int jx = player->jx, jy = player->jy;
12594   int old_jx = jx, old_jy = jy;
12595   int moved = MP_NO_ACTION;
12596
12597   if (!player->active)
12598     return FALSE;
12599
12600   if (!dx && !dy)
12601   {
12602     if (player->MovPos == 0)
12603     {
12604       player->is_moving = FALSE;
12605       player->is_digging = FALSE;
12606       player->is_collecting = FALSE;
12607       player->is_snapping = FALSE;
12608       player->is_pushing = FALSE;
12609     }
12610
12611     return FALSE;
12612   }
12613
12614   if (player->move_delay > 0)
12615     return FALSE;
12616
12617   player->move_delay = -1;              // set to "uninitialized" value
12618
12619   // store if player is automatically moved to next field
12620   player->is_auto_moving = (player->programmed_action != MV_NONE);
12621
12622   // remove the last programmed player action
12623   player->programmed_action = 0;
12624
12625   if (player->MovPos)
12626   {
12627     // should only happen if pre-1.2 tape recordings are played
12628     // this is only for backward compatibility
12629
12630     int original_move_delay_value = player->move_delay_value;
12631
12632 #if DEBUG
12633     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12634            tape.counter);
12635 #endif
12636
12637     // scroll remaining steps with finest movement resolution
12638     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12639
12640     while (player->MovPos)
12641     {
12642       ScrollPlayer(player, SCROLL_GO_ON);
12643       ScrollScreen(NULL, SCROLL_GO_ON);
12644
12645       AdvanceFrameAndPlayerCounters(player->index_nr);
12646
12647       DrawAllPlayers();
12648       BackToFront_WithFrameDelay(0);
12649     }
12650
12651     player->move_delay_value = original_move_delay_value;
12652   }
12653
12654   player->is_active = FALSE;
12655
12656   if (player->last_move_dir & MV_HORIZONTAL)
12657   {
12658     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12659       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12660   }
12661   else
12662   {
12663     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12664       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12665   }
12666
12667   if (!moved && !player->is_active)
12668   {
12669     player->is_moving = FALSE;
12670     player->is_digging = FALSE;
12671     player->is_collecting = FALSE;
12672     player->is_snapping = FALSE;
12673     player->is_pushing = FALSE;
12674   }
12675
12676   jx = player->jx;
12677   jy = player->jy;
12678
12679   if (moved & MP_MOVING && !ScreenMovPos &&
12680       (player->index_nr == game.centered_player_nr ||
12681        game.centered_player_nr == -1))
12682   {
12683     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12684
12685     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12686     {
12687       // actual player has left the screen -- scroll in that direction
12688       if (jx != old_jx)         // player has moved horizontally
12689         scroll_x += (jx - old_jx);
12690       else                      // player has moved vertically
12691         scroll_y += (jy - old_jy);
12692     }
12693     else
12694     {
12695       int offset_raw = game.scroll_delay_value;
12696
12697       if (jx != old_jx)         // player has moved horizontally
12698       {
12699         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12700         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12701         int new_scroll_x = jx - MIDPOSX + offset_x;
12702
12703         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12704             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12705           scroll_x = new_scroll_x;
12706
12707         // don't scroll over playfield boundaries
12708         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12709
12710         // don't scroll more than one field at a time
12711         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12712
12713         // don't scroll against the player's moving direction
12714         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12715             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12716           scroll_x = old_scroll_x;
12717       }
12718       else                      // player has moved vertically
12719       {
12720         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12721         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12722         int new_scroll_y = jy - MIDPOSY + offset_y;
12723
12724         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12725             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12726           scroll_y = new_scroll_y;
12727
12728         // don't scroll over playfield boundaries
12729         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12730
12731         // don't scroll more than one field at a time
12732         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12733
12734         // don't scroll against the player's moving direction
12735         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12736             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12737           scroll_y = old_scroll_y;
12738       }
12739     }
12740
12741     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12742     {
12743       if (!network.enabled && game.centered_player_nr == -1 &&
12744           !AllPlayersInVisibleScreen())
12745       {
12746         scroll_x = old_scroll_x;
12747         scroll_y = old_scroll_y;
12748       }
12749       else
12750       {
12751         ScrollScreen(player, SCROLL_INIT);
12752         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12753       }
12754     }
12755   }
12756
12757   player->StepFrame = 0;
12758
12759   if (moved & MP_MOVING)
12760   {
12761     if (old_jx != jx && old_jy == jy)
12762       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12763     else if (old_jx == jx && old_jy != jy)
12764       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12765
12766     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12767
12768     player->last_move_dir = player->MovDir;
12769     player->is_moving = TRUE;
12770     player->is_snapping = FALSE;
12771     player->is_switching = FALSE;
12772     player->is_dropping = FALSE;
12773     player->is_dropping_pressed = FALSE;
12774     player->drop_pressed_delay = 0;
12775
12776 #if 0
12777     // should better be called here than above, but this breaks some tapes
12778     ScrollPlayer(player, SCROLL_INIT);
12779 #endif
12780   }
12781   else
12782   {
12783     CheckGravityMovementWhenNotMoving(player);
12784
12785     player->is_moving = FALSE;
12786
12787     /* at this point, the player is allowed to move, but cannot move right now
12788        (e.g. because of something blocking the way) -- ensure that the player
12789        is also allowed to move in the next frame (in old versions before 3.1.1,
12790        the player was forced to wait again for eight frames before next try) */
12791
12792     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12793       player->move_delay = 0;   // allow direct movement in the next frame
12794   }
12795
12796   if (player->move_delay == -1)         // not yet initialized by DigField()
12797     player->move_delay = player->move_delay_value;
12798
12799   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12800   {
12801     TestIfPlayerTouchesBadThing(jx, jy);
12802     TestIfPlayerTouchesCustomElement(jx, jy);
12803   }
12804
12805   if (!player->active)
12806     RemovePlayer(player);
12807
12808   return moved;
12809 }
12810
12811 void ScrollPlayer(struct PlayerInfo *player, int mode)
12812 {
12813   int jx = player->jx, jy = player->jy;
12814   int last_jx = player->last_jx, last_jy = player->last_jy;
12815   int move_stepsize = TILEX / player->move_delay_value;
12816
12817   if (!player->active)
12818     return;
12819
12820   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12821     return;
12822
12823   if (mode == SCROLL_INIT)
12824   {
12825     player->actual_frame_counter = FrameCounter;
12826     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12827
12828     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12829         Feld[last_jx][last_jy] == EL_EMPTY)
12830     {
12831       int last_field_block_delay = 0;   // start with no blocking at all
12832       int block_delay_adjustment = player->block_delay_adjustment;
12833
12834       // if player blocks last field, add delay for exactly one move
12835       if (player->block_last_field)
12836       {
12837         last_field_block_delay += player->move_delay_value;
12838
12839         // when blocking enabled, prevent moving up despite gravity
12840         if (player->gravity && player->MovDir == MV_UP)
12841           block_delay_adjustment = -1;
12842       }
12843
12844       // add block delay adjustment (also possible when not blocking)
12845       last_field_block_delay += block_delay_adjustment;
12846
12847       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12848       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12849     }
12850
12851     if (player->MovPos != 0)    // player has not yet reached destination
12852       return;
12853   }
12854   else if (!FrameReached(&player->actual_frame_counter, 1))
12855     return;
12856
12857   if (player->MovPos != 0)
12858   {
12859     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12860     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12861
12862     // before DrawPlayer() to draw correct player graphic for this case
12863     if (player->MovPos == 0)
12864       CheckGravityMovement(player);
12865   }
12866
12867   if (player->MovPos == 0)      // player reached destination field
12868   {
12869     if (player->move_delay_reset_counter > 0)
12870     {
12871       player->move_delay_reset_counter--;
12872
12873       if (player->move_delay_reset_counter == 0)
12874       {
12875         // continue with normal speed after quickly moving through gate
12876         HALVE_PLAYER_SPEED(player);
12877
12878         // be able to make the next move without delay
12879         player->move_delay = 0;
12880       }
12881     }
12882
12883     player->last_jx = jx;
12884     player->last_jy = jy;
12885
12886     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12887         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12888         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12889         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12890         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12891         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12892         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12893         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12894     {
12895       ExitPlayer(player);
12896
12897       if (game.players_still_needed == 0 &&
12898           (game.friends_still_needed == 0 ||
12899            IS_SP_ELEMENT(Feld[jx][jy])))
12900         LevelSolved();
12901     }
12902
12903     // this breaks one level: "machine", level 000
12904     {
12905       int move_direction = player->MovDir;
12906       int enter_side = MV_DIR_OPPOSITE(move_direction);
12907       int leave_side = move_direction;
12908       int old_jx = last_jx;
12909       int old_jy = last_jy;
12910       int old_element = Feld[old_jx][old_jy];
12911       int new_element = Feld[jx][jy];
12912
12913       if (IS_CUSTOM_ELEMENT(old_element))
12914         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12915                                    CE_LEFT_BY_PLAYER,
12916                                    player->index_bit, leave_side);
12917
12918       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12919                                           CE_PLAYER_LEAVES_X,
12920                                           player->index_bit, leave_side);
12921
12922       if (IS_CUSTOM_ELEMENT(new_element))
12923         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12924                                    player->index_bit, enter_side);
12925
12926       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12927                                           CE_PLAYER_ENTERS_X,
12928                                           player->index_bit, enter_side);
12929
12930       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12931                                         CE_MOVE_OF_X, move_direction);
12932     }
12933
12934     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12935     {
12936       TestIfPlayerTouchesBadThing(jx, jy);
12937       TestIfPlayerTouchesCustomElement(jx, jy);
12938
12939       /* needed because pushed element has not yet reached its destination,
12940          so it would trigger a change event at its previous field location */
12941       if (!player->is_pushing)
12942         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12943
12944       if (!player->active)
12945         RemovePlayer(player);
12946     }
12947
12948     if (!game.LevelSolved && level.use_step_counter)
12949     {
12950       int i;
12951
12952       TimePlayed++;
12953
12954       if (TimeLeft > 0)
12955       {
12956         TimeLeft--;
12957
12958         if (TimeLeft <= 10 && setup.time_limit)
12959           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12960
12961         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12962
12963         DisplayGameControlValues();
12964
12965         if (!TimeLeft && setup.time_limit)
12966           for (i = 0; i < MAX_PLAYERS; i++)
12967             KillPlayer(&stored_player[i]);
12968       }
12969       else if (game.no_time_limit && !game.all_players_gone)
12970       {
12971         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12972
12973         DisplayGameControlValues();
12974       }
12975     }
12976
12977     if (tape.single_step && tape.recording && !tape.pausing &&
12978         !player->programmed_action)
12979       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12980
12981     if (!player->programmed_action)
12982       CheckSaveEngineSnapshot(player);
12983   }
12984 }
12985
12986 void ScrollScreen(struct PlayerInfo *player, int mode)
12987 {
12988   static unsigned int screen_frame_counter = 0;
12989
12990   if (mode == SCROLL_INIT)
12991   {
12992     // set scrolling step size according to actual player's moving speed
12993     ScrollStepSize = TILEX / player->move_delay_value;
12994
12995     screen_frame_counter = FrameCounter;
12996     ScreenMovDir = player->MovDir;
12997     ScreenMovPos = player->MovPos;
12998     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12999     return;
13000   }
13001   else if (!FrameReached(&screen_frame_counter, 1))
13002     return;
13003
13004   if (ScreenMovPos)
13005   {
13006     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13007     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13008     redraw_mask |= REDRAW_FIELD;
13009   }
13010   else
13011     ScreenMovDir = MV_NONE;
13012 }
13013
13014 void TestIfPlayerTouchesCustomElement(int x, int y)
13015 {
13016   static int xy[4][2] =
13017   {
13018     { 0, -1 },
13019     { -1, 0 },
13020     { +1, 0 },
13021     { 0, +1 }
13022   };
13023   static int trigger_sides[4][2] =
13024   {
13025     // center side       border side
13026     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13027     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13028     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13029     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13030   };
13031   static int touch_dir[4] =
13032   {
13033     MV_LEFT | MV_RIGHT,
13034     MV_UP   | MV_DOWN,
13035     MV_UP   | MV_DOWN,
13036     MV_LEFT | MV_RIGHT
13037   };
13038   int center_element = Feld[x][y];      // should always be non-moving!
13039   int i;
13040
13041   for (i = 0; i < NUM_DIRECTIONS; i++)
13042   {
13043     int xx = x + xy[i][0];
13044     int yy = y + xy[i][1];
13045     int center_side = trigger_sides[i][0];
13046     int border_side = trigger_sides[i][1];
13047     int border_element;
13048
13049     if (!IN_LEV_FIELD(xx, yy))
13050       continue;
13051
13052     if (IS_PLAYER(x, y))                // player found at center element
13053     {
13054       struct PlayerInfo *player = PLAYERINFO(x, y);
13055
13056       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13057         border_element = Feld[xx][yy];          // may be moving!
13058       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13059         border_element = Feld[xx][yy];
13060       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13061         border_element = MovingOrBlocked2Element(xx, yy);
13062       else
13063         continue;               // center and border element do not touch
13064
13065       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13066                                  player->index_bit, border_side);
13067       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13068                                           CE_PLAYER_TOUCHES_X,
13069                                           player->index_bit, border_side);
13070
13071       {
13072         /* use player element that is initially defined in the level playfield,
13073            not the player element that corresponds to the runtime player number
13074            (example: a level that contains EL_PLAYER_3 as the only player would
13075            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13076         int player_element = PLAYERINFO(x, y)->initial_element;
13077
13078         CheckElementChangeBySide(xx, yy, border_element, player_element,
13079                                  CE_TOUCHING_X, border_side);
13080       }
13081     }
13082     else if (IS_PLAYER(xx, yy))         // player found at border element
13083     {
13084       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13085
13086       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13087       {
13088         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13089           continue;             // center and border element do not touch
13090       }
13091
13092       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13093                                  player->index_bit, center_side);
13094       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13095                                           CE_PLAYER_TOUCHES_X,
13096                                           player->index_bit, center_side);
13097
13098       {
13099         /* use player element that is initially defined in the level playfield,
13100            not the player element that corresponds to the runtime player number
13101            (example: a level that contains EL_PLAYER_3 as the only player would
13102            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13103         int player_element = PLAYERINFO(xx, yy)->initial_element;
13104
13105         CheckElementChangeBySide(x, y, center_element, player_element,
13106                                  CE_TOUCHING_X, center_side);
13107       }
13108
13109       break;
13110     }
13111   }
13112 }
13113
13114 void TestIfElementTouchesCustomElement(int x, int y)
13115 {
13116   static int xy[4][2] =
13117   {
13118     { 0, -1 },
13119     { -1, 0 },
13120     { +1, 0 },
13121     { 0, +1 }
13122   };
13123   static int trigger_sides[4][2] =
13124   {
13125     // center side      border side
13126     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13127     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13128     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13129     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13130   };
13131   static int touch_dir[4] =
13132   {
13133     MV_LEFT | MV_RIGHT,
13134     MV_UP   | MV_DOWN,
13135     MV_UP   | MV_DOWN,
13136     MV_LEFT | MV_RIGHT
13137   };
13138   boolean change_center_element = FALSE;
13139   int center_element = Feld[x][y];      // should always be non-moving!
13140   int border_element_old[NUM_DIRECTIONS];
13141   int i;
13142
13143   for (i = 0; i < NUM_DIRECTIONS; i++)
13144   {
13145     int xx = x + xy[i][0];
13146     int yy = y + xy[i][1];
13147     int border_element;
13148
13149     border_element_old[i] = -1;
13150
13151     if (!IN_LEV_FIELD(xx, yy))
13152       continue;
13153
13154     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13155       border_element = Feld[xx][yy];    // may be moving!
13156     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13157       border_element = Feld[xx][yy];
13158     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13159       border_element = MovingOrBlocked2Element(xx, yy);
13160     else
13161       continue;                 // center and border element do not touch
13162
13163     border_element_old[i] = border_element;
13164   }
13165
13166   for (i = 0; i < NUM_DIRECTIONS; i++)
13167   {
13168     int xx = x + xy[i][0];
13169     int yy = y + xy[i][1];
13170     int center_side = trigger_sides[i][0];
13171     int border_element = border_element_old[i];
13172
13173     if (border_element == -1)
13174       continue;
13175
13176     // check for change of border element
13177     CheckElementChangeBySide(xx, yy, border_element, center_element,
13178                              CE_TOUCHING_X, center_side);
13179
13180     // (center element cannot be player, so we dont have to check this here)
13181   }
13182
13183   for (i = 0; i < NUM_DIRECTIONS; i++)
13184   {
13185     int xx = x + xy[i][0];
13186     int yy = y + xy[i][1];
13187     int border_side = trigger_sides[i][1];
13188     int border_element = border_element_old[i];
13189
13190     if (border_element == -1)
13191       continue;
13192
13193     // check for change of center element (but change it only once)
13194     if (!change_center_element)
13195       change_center_element =
13196         CheckElementChangeBySide(x, y, center_element, border_element,
13197                                  CE_TOUCHING_X, border_side);
13198
13199     if (IS_PLAYER(xx, yy))
13200     {
13201       /* use player element that is initially defined in the level playfield,
13202          not the player element that corresponds to the runtime player number
13203          (example: a level that contains EL_PLAYER_3 as the only player would
13204          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13205       int player_element = PLAYERINFO(xx, yy)->initial_element;
13206
13207       CheckElementChangeBySide(x, y, center_element, player_element,
13208                                CE_TOUCHING_X, border_side);
13209     }
13210   }
13211 }
13212
13213 void TestIfElementHitsCustomElement(int x, int y, int direction)
13214 {
13215   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13216   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13217   int hitx = x + dx, hity = y + dy;
13218   int hitting_element = Feld[x][y];
13219   int touched_element;
13220
13221   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13222     return;
13223
13224   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13225                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13226
13227   if (IN_LEV_FIELD(hitx, hity))
13228   {
13229     int opposite_direction = MV_DIR_OPPOSITE(direction);
13230     int hitting_side = direction;
13231     int touched_side = opposite_direction;
13232     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13233                           MovDir[hitx][hity] != direction ||
13234                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13235
13236     object_hit = TRUE;
13237
13238     if (object_hit)
13239     {
13240       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13241                                CE_HITTING_X, touched_side);
13242
13243       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13244                                CE_HIT_BY_X, hitting_side);
13245
13246       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13247                                CE_HIT_BY_SOMETHING, opposite_direction);
13248
13249       if (IS_PLAYER(hitx, hity))
13250       {
13251         /* use player element that is initially defined in the level playfield,
13252            not the player element that corresponds to the runtime player number
13253            (example: a level that contains EL_PLAYER_3 as the only player would
13254            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13255         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13256
13257         CheckElementChangeBySide(x, y, hitting_element, player_element,
13258                                  CE_HITTING_X, touched_side);
13259       }
13260     }
13261   }
13262
13263   // "hitting something" is also true when hitting the playfield border
13264   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13265                            CE_HITTING_SOMETHING, direction);
13266 }
13267
13268 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13269 {
13270   int i, kill_x = -1, kill_y = -1;
13271
13272   int bad_element = -1;
13273   static int test_xy[4][2] =
13274   {
13275     { 0, -1 },
13276     { -1, 0 },
13277     { +1, 0 },
13278     { 0, +1 }
13279   };
13280   static int test_dir[4] =
13281   {
13282     MV_UP,
13283     MV_LEFT,
13284     MV_RIGHT,
13285     MV_DOWN
13286   };
13287
13288   for (i = 0; i < NUM_DIRECTIONS; i++)
13289   {
13290     int test_x, test_y, test_move_dir, test_element;
13291
13292     test_x = good_x + test_xy[i][0];
13293     test_y = good_y + test_xy[i][1];
13294
13295     if (!IN_LEV_FIELD(test_x, test_y))
13296       continue;
13297
13298     test_move_dir =
13299       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13300
13301     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13302
13303     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13304        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13305     */
13306     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13307         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13308     {
13309       kill_x = test_x;
13310       kill_y = test_y;
13311       bad_element = test_element;
13312
13313       break;
13314     }
13315   }
13316
13317   if (kill_x != -1 || kill_y != -1)
13318   {
13319     if (IS_PLAYER(good_x, good_y))
13320     {
13321       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13322
13323       if (player->shield_deadly_time_left > 0 &&
13324           !IS_INDESTRUCTIBLE(bad_element))
13325         Bang(kill_x, kill_y);
13326       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13327         KillPlayer(player);
13328     }
13329     else
13330       Bang(good_x, good_y);
13331   }
13332 }
13333
13334 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13335 {
13336   int i, kill_x = -1, kill_y = -1;
13337   int bad_element = Feld[bad_x][bad_y];
13338   static int test_xy[4][2] =
13339   {
13340     { 0, -1 },
13341     { -1, 0 },
13342     { +1, 0 },
13343     { 0, +1 }
13344   };
13345   static int touch_dir[4] =
13346   {
13347     MV_LEFT | MV_RIGHT,
13348     MV_UP   | MV_DOWN,
13349     MV_UP   | MV_DOWN,
13350     MV_LEFT | MV_RIGHT
13351   };
13352   static int test_dir[4] =
13353   {
13354     MV_UP,
13355     MV_LEFT,
13356     MV_RIGHT,
13357     MV_DOWN
13358   };
13359
13360   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13361     return;
13362
13363   for (i = 0; i < NUM_DIRECTIONS; i++)
13364   {
13365     int test_x, test_y, test_move_dir, test_element;
13366
13367     test_x = bad_x + test_xy[i][0];
13368     test_y = bad_y + test_xy[i][1];
13369
13370     if (!IN_LEV_FIELD(test_x, test_y))
13371       continue;
13372
13373     test_move_dir =
13374       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13375
13376     test_element = Feld[test_x][test_y];
13377
13378     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13379        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13380     */
13381     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13382         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13383     {
13384       // good thing is player or penguin that does not move away
13385       if (IS_PLAYER(test_x, test_y))
13386       {
13387         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13388
13389         if (bad_element == EL_ROBOT && player->is_moving)
13390           continue;     // robot does not kill player if he is moving
13391
13392         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13393         {
13394           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13395             continue;           // center and border element do not touch
13396         }
13397
13398         kill_x = test_x;
13399         kill_y = test_y;
13400
13401         break;
13402       }
13403       else if (test_element == EL_PENGUIN)
13404       {
13405         kill_x = test_x;
13406         kill_y = test_y;
13407
13408         break;
13409       }
13410     }
13411   }
13412
13413   if (kill_x != -1 || kill_y != -1)
13414   {
13415     if (IS_PLAYER(kill_x, kill_y))
13416     {
13417       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13418
13419       if (player->shield_deadly_time_left > 0 &&
13420           !IS_INDESTRUCTIBLE(bad_element))
13421         Bang(bad_x, bad_y);
13422       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13423         KillPlayer(player);
13424     }
13425     else
13426       Bang(kill_x, kill_y);
13427   }
13428 }
13429
13430 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13431 {
13432   int bad_element = Feld[bad_x][bad_y];
13433   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13434   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13435   int test_x = bad_x + dx, test_y = bad_y + dy;
13436   int test_move_dir, test_element;
13437   int kill_x = -1, kill_y = -1;
13438
13439   if (!IN_LEV_FIELD(test_x, test_y))
13440     return;
13441
13442   test_move_dir =
13443     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13444
13445   test_element = Feld[test_x][test_y];
13446
13447   if (test_move_dir != bad_move_dir)
13448   {
13449     // good thing can be player or penguin that does not move away
13450     if (IS_PLAYER(test_x, test_y))
13451     {
13452       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13453
13454       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13455          player as being hit when he is moving towards the bad thing, because
13456          the "get hit by" condition would be lost after the player stops) */
13457       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13458         return;         // player moves away from bad thing
13459
13460       kill_x = test_x;
13461       kill_y = test_y;
13462     }
13463     else if (test_element == EL_PENGUIN)
13464     {
13465       kill_x = test_x;
13466       kill_y = test_y;
13467     }
13468   }
13469
13470   if (kill_x != -1 || kill_y != -1)
13471   {
13472     if (IS_PLAYER(kill_x, kill_y))
13473     {
13474       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13475
13476       if (player->shield_deadly_time_left > 0 &&
13477           !IS_INDESTRUCTIBLE(bad_element))
13478         Bang(bad_x, bad_y);
13479       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13480         KillPlayer(player);
13481     }
13482     else
13483       Bang(kill_x, kill_y);
13484   }
13485 }
13486
13487 void TestIfPlayerTouchesBadThing(int x, int y)
13488 {
13489   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13490 }
13491
13492 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13493 {
13494   TestIfGoodThingHitsBadThing(x, y, move_dir);
13495 }
13496
13497 void TestIfBadThingTouchesPlayer(int x, int y)
13498 {
13499   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13500 }
13501
13502 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13503 {
13504   TestIfBadThingHitsGoodThing(x, y, move_dir);
13505 }
13506
13507 void TestIfFriendTouchesBadThing(int x, int y)
13508 {
13509   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13510 }
13511
13512 void TestIfBadThingTouchesFriend(int x, int y)
13513 {
13514   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13515 }
13516
13517 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13518 {
13519   int i, kill_x = bad_x, kill_y = bad_y;
13520   static int xy[4][2] =
13521   {
13522     { 0, -1 },
13523     { -1, 0 },
13524     { +1, 0 },
13525     { 0, +1 }
13526   };
13527
13528   for (i = 0; i < NUM_DIRECTIONS; i++)
13529   {
13530     int x, y, element;
13531
13532     x = bad_x + xy[i][0];
13533     y = bad_y + xy[i][1];
13534     if (!IN_LEV_FIELD(x, y))
13535       continue;
13536
13537     element = Feld[x][y];
13538     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13539         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13540     {
13541       kill_x = x;
13542       kill_y = y;
13543       break;
13544     }
13545   }
13546
13547   if (kill_x != bad_x || kill_y != bad_y)
13548     Bang(bad_x, bad_y);
13549 }
13550
13551 void KillPlayer(struct PlayerInfo *player)
13552 {
13553   int jx = player->jx, jy = player->jy;
13554
13555   if (!player->active)
13556     return;
13557
13558 #if 0
13559   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13560          player->killed, player->active, player->reanimated);
13561 #endif
13562
13563   /* the following code was introduced to prevent an infinite loop when calling
13564      -> Bang()
13565      -> CheckTriggeredElementChangeExt()
13566      -> ExecuteCustomElementAction()
13567      -> KillPlayer()
13568      -> (infinitely repeating the above sequence of function calls)
13569      which occurs when killing the player while having a CE with the setting
13570      "kill player X when explosion of <player X>"; the solution using a new
13571      field "player->killed" was chosen for backwards compatibility, although
13572      clever use of the fields "player->active" etc. would probably also work */
13573 #if 1
13574   if (player->killed)
13575     return;
13576 #endif
13577
13578   player->killed = TRUE;
13579
13580   // remove accessible field at the player's position
13581   Feld[jx][jy] = EL_EMPTY;
13582
13583   // deactivate shield (else Bang()/Explode() would not work right)
13584   player->shield_normal_time_left = 0;
13585   player->shield_deadly_time_left = 0;
13586
13587 #if 0
13588   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13589          player->killed, player->active, player->reanimated);
13590 #endif
13591
13592   Bang(jx, jy);
13593
13594 #if 0
13595   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13596          player->killed, player->active, player->reanimated);
13597 #endif
13598
13599   if (player->reanimated)       // killed player may have been reanimated
13600     player->killed = player->reanimated = FALSE;
13601   else
13602     BuryPlayer(player);
13603 }
13604
13605 static void KillPlayerUnlessEnemyProtected(int x, int y)
13606 {
13607   if (!PLAYER_ENEMY_PROTECTED(x, y))
13608     KillPlayer(PLAYERINFO(x, y));
13609 }
13610
13611 static void KillPlayerUnlessExplosionProtected(int x, int y)
13612 {
13613   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13614     KillPlayer(PLAYERINFO(x, y));
13615 }
13616
13617 void BuryPlayer(struct PlayerInfo *player)
13618 {
13619   int jx = player->jx, jy = player->jy;
13620
13621   if (!player->active)
13622     return;
13623
13624   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13625   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13626
13627   RemovePlayer(player);
13628
13629   player->buried = TRUE;
13630
13631   if (game.all_players_gone)
13632     game.GameOver = TRUE;
13633 }
13634
13635 void RemovePlayer(struct PlayerInfo *player)
13636 {
13637   int jx = player->jx, jy = player->jy;
13638   int i, found = FALSE;
13639
13640   player->present = FALSE;
13641   player->active = FALSE;
13642
13643   // required for some CE actions (even if the player is not active anymore)
13644   player->MovPos = 0;
13645
13646   if (!ExplodeField[jx][jy])
13647     StorePlayer[jx][jy] = 0;
13648
13649   if (player->is_moving)
13650     TEST_DrawLevelField(player->last_jx, player->last_jy);
13651
13652   for (i = 0; i < MAX_PLAYERS; i++)
13653     if (stored_player[i].active)
13654       found = TRUE;
13655
13656   if (!found)
13657   {
13658     game.all_players_gone = TRUE;
13659     game.GameOver = TRUE;
13660   }
13661
13662   game.exit_x = game.robot_wheel_x = jx;
13663   game.exit_y = game.robot_wheel_y = jy;
13664 }
13665
13666 void ExitPlayer(struct PlayerInfo *player)
13667 {
13668   DrawPlayer(player);   // needed here only to cleanup last field
13669   RemovePlayer(player);
13670
13671   if (game.players_still_needed > 0)
13672     game.players_still_needed--;
13673 }
13674
13675 static void setFieldForSnapping(int x, int y, int element, int direction)
13676 {
13677   struct ElementInfo *ei = &element_info[element];
13678   int direction_bit = MV_DIR_TO_BIT(direction);
13679   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13680   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13681                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13682
13683   Feld[x][y] = EL_ELEMENT_SNAPPING;
13684   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13685
13686   ResetGfxAnimation(x, y);
13687
13688   GfxElement[x][y] = element;
13689   GfxAction[x][y] = action;
13690   GfxDir[x][y] = direction;
13691   GfxFrame[x][y] = -1;
13692 }
13693
13694 /*
13695   =============================================================================
13696   checkDiagonalPushing()
13697   -----------------------------------------------------------------------------
13698   check if diagonal input device direction results in pushing of object
13699   (by checking if the alternative direction is walkable, diggable, ...)
13700   =============================================================================
13701 */
13702
13703 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13704                                     int x, int y, int real_dx, int real_dy)
13705 {
13706   int jx, jy, dx, dy, xx, yy;
13707
13708   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13709     return TRUE;
13710
13711   // diagonal direction: check alternative direction
13712   jx = player->jx;
13713   jy = player->jy;
13714   dx = x - jx;
13715   dy = y - jy;
13716   xx = jx + (dx == 0 ? real_dx : 0);
13717   yy = jy + (dy == 0 ? real_dy : 0);
13718
13719   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13720 }
13721
13722 /*
13723   =============================================================================
13724   DigField()
13725   -----------------------------------------------------------------------------
13726   x, y:                 field next to player (non-diagonal) to try to dig to
13727   real_dx, real_dy:     direction as read from input device (can be diagonal)
13728   =============================================================================
13729 */
13730
13731 static int DigField(struct PlayerInfo *player,
13732                     int oldx, int oldy, int x, int y,
13733                     int real_dx, int real_dy, int mode)
13734 {
13735   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13736   boolean player_was_pushing = player->is_pushing;
13737   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13738   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13739   int jx = oldx, jy = oldy;
13740   int dx = x - jx, dy = y - jy;
13741   int nextx = x + dx, nexty = y + dy;
13742   int move_direction = (dx == -1 ? MV_LEFT  :
13743                         dx == +1 ? MV_RIGHT :
13744                         dy == -1 ? MV_UP    :
13745                         dy == +1 ? MV_DOWN  : MV_NONE);
13746   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13747   int dig_side = MV_DIR_OPPOSITE(move_direction);
13748   int old_element = Feld[jx][jy];
13749   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13750   int collect_count;
13751
13752   if (is_player)                // function can also be called by EL_PENGUIN
13753   {
13754     if (player->MovPos == 0)
13755     {
13756       player->is_digging = FALSE;
13757       player->is_collecting = FALSE;
13758     }
13759
13760     if (player->MovPos == 0)    // last pushing move finished
13761       player->is_pushing = FALSE;
13762
13763     if (mode == DF_NO_PUSH)     // player just stopped pushing
13764     {
13765       player->is_switching = FALSE;
13766       player->push_delay = -1;
13767
13768       return MP_NO_ACTION;
13769     }
13770   }
13771
13772   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13773     old_element = Back[jx][jy];
13774
13775   // in case of element dropped at player position, check background
13776   else if (Back[jx][jy] != EL_EMPTY &&
13777            game.engine_version >= VERSION_IDENT(2,2,0,0))
13778     old_element = Back[jx][jy];
13779
13780   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13781     return MP_NO_ACTION;        // field has no opening in this direction
13782
13783   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13784     return MP_NO_ACTION;        // field has no opening in this direction
13785
13786   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13787   {
13788     SplashAcid(x, y);
13789
13790     Feld[jx][jy] = player->artwork_element;
13791     InitMovingField(jx, jy, MV_DOWN);
13792     Store[jx][jy] = EL_ACID;
13793     ContinueMoving(jx, jy);
13794     BuryPlayer(player);
13795
13796     return MP_DONT_RUN_INTO;
13797   }
13798
13799   if (player_can_move && DONT_RUN_INTO(element))
13800   {
13801     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13802
13803     return MP_DONT_RUN_INTO;
13804   }
13805
13806   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13807     return MP_NO_ACTION;
13808
13809   collect_count = element_info[element].collect_count_initial;
13810
13811   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13812     return MP_NO_ACTION;
13813
13814   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13815     player_can_move = player_can_move_or_snap;
13816
13817   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13818       game.engine_version >= VERSION_IDENT(2,2,0,0))
13819   {
13820     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13821                                player->index_bit, dig_side);
13822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13823                                         player->index_bit, dig_side);
13824
13825     if (element == EL_DC_LANDMINE)
13826       Bang(x, y);
13827
13828     if (Feld[x][y] != element)          // field changed by snapping
13829       return MP_ACTION;
13830
13831     return MP_NO_ACTION;
13832   }
13833
13834   if (player->gravity && is_player && !player->is_auto_moving &&
13835       canFallDown(player) && move_direction != MV_DOWN &&
13836       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13837     return MP_NO_ACTION;        // player cannot walk here due to gravity
13838
13839   if (player_can_move &&
13840       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13841   {
13842     int sound_element = SND_ELEMENT(element);
13843     int sound_action = ACTION_WALKING;
13844
13845     if (IS_RND_GATE(element))
13846     {
13847       if (!player->key[RND_GATE_NR(element)])
13848         return MP_NO_ACTION;
13849     }
13850     else if (IS_RND_GATE_GRAY(element))
13851     {
13852       if (!player->key[RND_GATE_GRAY_NR(element)])
13853         return MP_NO_ACTION;
13854     }
13855     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13856     {
13857       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13858         return MP_NO_ACTION;
13859     }
13860     else if (element == EL_EXIT_OPEN ||
13861              element == EL_EM_EXIT_OPEN ||
13862              element == EL_EM_EXIT_OPENING ||
13863              element == EL_STEEL_EXIT_OPEN ||
13864              element == EL_EM_STEEL_EXIT_OPEN ||
13865              element == EL_EM_STEEL_EXIT_OPENING ||
13866              element == EL_SP_EXIT_OPEN ||
13867              element == EL_SP_EXIT_OPENING)
13868     {
13869       sound_action = ACTION_PASSING;    // player is passing exit
13870     }
13871     else if (element == EL_EMPTY)
13872     {
13873       sound_action = ACTION_MOVING;             // nothing to walk on
13874     }
13875
13876     // play sound from background or player, whatever is available
13877     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13878       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13879     else
13880       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13881   }
13882   else if (player_can_move &&
13883            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13884   {
13885     if (!ACCESS_FROM(element, opposite_direction))
13886       return MP_NO_ACTION;      // field not accessible from this direction
13887
13888     if (CAN_MOVE(element))      // only fixed elements can be passed!
13889       return MP_NO_ACTION;
13890
13891     if (IS_EM_GATE(element))
13892     {
13893       if (!player->key[EM_GATE_NR(element)])
13894         return MP_NO_ACTION;
13895     }
13896     else if (IS_EM_GATE_GRAY(element))
13897     {
13898       if (!player->key[EM_GATE_GRAY_NR(element)])
13899         return MP_NO_ACTION;
13900     }
13901     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13902     {
13903       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13904         return MP_NO_ACTION;
13905     }
13906     else if (IS_EMC_GATE(element))
13907     {
13908       if (!player->key[EMC_GATE_NR(element)])
13909         return MP_NO_ACTION;
13910     }
13911     else if (IS_EMC_GATE_GRAY(element))
13912     {
13913       if (!player->key[EMC_GATE_GRAY_NR(element)])
13914         return MP_NO_ACTION;
13915     }
13916     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13917     {
13918       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13919         return MP_NO_ACTION;
13920     }
13921     else if (element == EL_DC_GATE_WHITE ||
13922              element == EL_DC_GATE_WHITE_GRAY ||
13923              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13924     {
13925       if (player->num_white_keys == 0)
13926         return MP_NO_ACTION;
13927
13928       player->num_white_keys--;
13929     }
13930     else if (IS_SP_PORT(element))
13931     {
13932       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13933           element == EL_SP_GRAVITY_PORT_RIGHT ||
13934           element == EL_SP_GRAVITY_PORT_UP ||
13935           element == EL_SP_GRAVITY_PORT_DOWN)
13936         player->gravity = !player->gravity;
13937       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13938                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13939                element == EL_SP_GRAVITY_ON_PORT_UP ||
13940                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13941         player->gravity = TRUE;
13942       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13943                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13944                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13945                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13946         player->gravity = FALSE;
13947     }
13948
13949     // automatically move to the next field with double speed
13950     player->programmed_action = move_direction;
13951
13952     if (player->move_delay_reset_counter == 0)
13953     {
13954       player->move_delay_reset_counter = 2;     // two double speed steps
13955
13956       DOUBLE_PLAYER_SPEED(player);
13957     }
13958
13959     PlayLevelSoundAction(x, y, ACTION_PASSING);
13960   }
13961   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13962   {
13963     RemoveField(x, y);
13964
13965     if (mode != DF_SNAP)
13966     {
13967       GfxElement[x][y] = GFX_ELEMENT(element);
13968       player->is_digging = TRUE;
13969     }
13970
13971     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13972
13973     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13974                                         player->index_bit, dig_side);
13975
13976     if (mode == DF_SNAP)
13977     {
13978       if (level.block_snap_field)
13979         setFieldForSnapping(x, y, element, move_direction);
13980       else
13981         TestIfElementTouchesCustomElement(x, y);        // for empty space
13982
13983       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13984                                           player->index_bit, dig_side);
13985     }
13986   }
13987   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13988   {
13989     RemoveField(x, y);
13990
13991     if (is_player && mode != DF_SNAP)
13992     {
13993       GfxElement[x][y] = element;
13994       player->is_collecting = TRUE;
13995     }
13996
13997     if (element == EL_SPEED_PILL)
13998     {
13999       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14000     }
14001     else if (element == EL_EXTRA_TIME && level.time > 0)
14002     {
14003       TimeLeft += level.extra_time;
14004
14005       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14006
14007       DisplayGameControlValues();
14008     }
14009     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14010     {
14011       player->shield_normal_time_left += level.shield_normal_time;
14012       if (element == EL_SHIELD_DEADLY)
14013         player->shield_deadly_time_left += level.shield_deadly_time;
14014     }
14015     else if (element == EL_DYNAMITE ||
14016              element == EL_EM_DYNAMITE ||
14017              element == EL_SP_DISK_RED)
14018     {
14019       if (player->inventory_size < MAX_INVENTORY_SIZE)
14020         player->inventory_element[player->inventory_size++] = element;
14021
14022       DrawGameDoorValues();
14023     }
14024     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14025     {
14026       player->dynabomb_count++;
14027       player->dynabombs_left++;
14028     }
14029     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14030     {
14031       player->dynabomb_size++;
14032     }
14033     else if (element == EL_DYNABOMB_INCREASE_POWER)
14034     {
14035       player->dynabomb_xl = TRUE;
14036     }
14037     else if (IS_KEY(element))
14038     {
14039       player->key[KEY_NR(element)] = TRUE;
14040
14041       DrawGameDoorValues();
14042     }
14043     else if (element == EL_DC_KEY_WHITE)
14044     {
14045       player->num_white_keys++;
14046
14047       // display white keys?
14048       // DrawGameDoorValues();
14049     }
14050     else if (IS_ENVELOPE(element))
14051     {
14052       player->show_envelope = element;
14053     }
14054     else if (element == EL_EMC_LENSES)
14055     {
14056       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14057
14058       RedrawAllInvisibleElementsForLenses();
14059     }
14060     else if (element == EL_EMC_MAGNIFIER)
14061     {
14062       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14063
14064       RedrawAllInvisibleElementsForMagnifier();
14065     }
14066     else if (IS_DROPPABLE(element) ||
14067              IS_THROWABLE(element))     // can be collected and dropped
14068     {
14069       int i;
14070
14071       if (collect_count == 0)
14072         player->inventory_infinite_element = element;
14073       else
14074         for (i = 0; i < collect_count; i++)
14075           if (player->inventory_size < MAX_INVENTORY_SIZE)
14076             player->inventory_element[player->inventory_size++] = element;
14077
14078       DrawGameDoorValues();
14079     }
14080     else if (collect_count > 0)
14081     {
14082       game.gems_still_needed -= collect_count;
14083       if (game.gems_still_needed < 0)
14084         game.gems_still_needed = 0;
14085
14086       game.snapshot.collected_item = TRUE;
14087
14088       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14089
14090       DisplayGameControlValues();
14091     }
14092
14093     RaiseScoreElement(element);
14094     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14095
14096     if (is_player)
14097       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14098                                           player->index_bit, dig_side);
14099
14100     if (mode == DF_SNAP)
14101     {
14102       if (level.block_snap_field)
14103         setFieldForSnapping(x, y, element, move_direction);
14104       else
14105         TestIfElementTouchesCustomElement(x, y);        // for empty space
14106
14107       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14108                                           player->index_bit, dig_side);
14109     }
14110   }
14111   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14112   {
14113     if (mode == DF_SNAP && element != EL_BD_ROCK)
14114       return MP_NO_ACTION;
14115
14116     if (CAN_FALL(element) && dy)
14117       return MP_NO_ACTION;
14118
14119     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14120         !(element == EL_SPRING && level.use_spring_bug))
14121       return MP_NO_ACTION;
14122
14123     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14124         ((move_direction & MV_VERTICAL &&
14125           ((element_info[element].move_pattern & MV_LEFT &&
14126             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14127            (element_info[element].move_pattern & MV_RIGHT &&
14128             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14129          (move_direction & MV_HORIZONTAL &&
14130           ((element_info[element].move_pattern & MV_UP &&
14131             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14132            (element_info[element].move_pattern & MV_DOWN &&
14133             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14134       return MP_NO_ACTION;
14135
14136     // do not push elements already moving away faster than player
14137     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14138         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14139       return MP_NO_ACTION;
14140
14141     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14142     {
14143       if (player->push_delay_value == -1 || !player_was_pushing)
14144         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14145     }
14146     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14147     {
14148       if (player->push_delay_value == -1)
14149         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14150     }
14151     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14152     {
14153       if (!player->is_pushing)
14154         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14155     }
14156
14157     player->is_pushing = TRUE;
14158     player->is_active = TRUE;
14159
14160     if (!(IN_LEV_FIELD(nextx, nexty) &&
14161           (IS_FREE(nextx, nexty) ||
14162            (IS_SB_ELEMENT(element) &&
14163             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14164            (IS_CUSTOM_ELEMENT(element) &&
14165             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14166       return MP_NO_ACTION;
14167
14168     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14169       return MP_NO_ACTION;
14170
14171     if (player->push_delay == -1)       // new pushing; restart delay
14172       player->push_delay = 0;
14173
14174     if (player->push_delay < player->push_delay_value &&
14175         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14176         element != EL_SPRING && element != EL_BALLOON)
14177     {
14178       // make sure that there is no move delay before next try to push
14179       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14180         player->move_delay = 0;
14181
14182       return MP_NO_ACTION;
14183     }
14184
14185     if (IS_CUSTOM_ELEMENT(element) &&
14186         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14187     {
14188       if (!DigFieldByCE(nextx, nexty, element))
14189         return MP_NO_ACTION;
14190     }
14191
14192     if (IS_SB_ELEMENT(element))
14193     {
14194       boolean sokoban_task_solved = FALSE;
14195
14196       if (element == EL_SOKOBAN_FIELD_FULL)
14197       {
14198         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14199
14200         IncrementSokobanFieldsNeeded();
14201         IncrementSokobanObjectsNeeded();
14202       }
14203
14204       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14205       {
14206         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14207
14208         DecrementSokobanFieldsNeeded();
14209         DecrementSokobanObjectsNeeded();
14210
14211         // sokoban object was pushed from empty field to sokoban field
14212         if (Back[x][y] == EL_EMPTY)
14213           sokoban_task_solved = TRUE;
14214       }
14215
14216       Feld[x][y] = EL_SOKOBAN_OBJECT;
14217
14218       if (Back[x][y] == Back[nextx][nexty])
14219         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14220       else if (Back[x][y] != 0)
14221         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14222                                     ACTION_EMPTYING);
14223       else
14224         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14225                                     ACTION_FILLING);
14226
14227       if (sokoban_task_solved &&
14228           game.sokoban_fields_still_needed == 0 &&
14229           game.sokoban_objects_still_needed == 0 &&
14230           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14231       {
14232         game.players_still_needed = 0;
14233
14234         LevelSolved();
14235
14236         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14237       }
14238     }
14239     else
14240       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14241
14242     InitMovingField(x, y, move_direction);
14243     GfxAction[x][y] = ACTION_PUSHING;
14244
14245     if (mode == DF_SNAP)
14246       ContinueMoving(x, y);
14247     else
14248       MovPos[x][y] = (dx != 0 ? dx : dy);
14249
14250     Pushed[x][y] = TRUE;
14251     Pushed[nextx][nexty] = TRUE;
14252
14253     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14254       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14255     else
14256       player->push_delay_value = -1;    // get new value later
14257
14258     // check for element change _after_ element has been pushed
14259     if (game.use_change_when_pushing_bug)
14260     {
14261       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14262                                  player->index_bit, dig_side);
14263       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14264                                           player->index_bit, dig_side);
14265     }
14266   }
14267   else if (IS_SWITCHABLE(element))
14268   {
14269     if (PLAYER_SWITCHING(player, x, y))
14270     {
14271       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14272                                           player->index_bit, dig_side);
14273
14274       return MP_ACTION;
14275     }
14276
14277     player->is_switching = TRUE;
14278     player->switch_x = x;
14279     player->switch_y = y;
14280
14281     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14282
14283     if (element == EL_ROBOT_WHEEL)
14284     {
14285       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14286
14287       game.robot_wheel_x = x;
14288       game.robot_wheel_y = y;
14289       game.robot_wheel_active = TRUE;
14290
14291       TEST_DrawLevelField(x, y);
14292     }
14293     else if (element == EL_SP_TERMINAL)
14294     {
14295       int xx, yy;
14296
14297       SCAN_PLAYFIELD(xx, yy)
14298       {
14299         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14300         {
14301           Bang(xx, yy);
14302         }
14303         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14304         {
14305           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14306
14307           ResetGfxAnimation(xx, yy);
14308           TEST_DrawLevelField(xx, yy);
14309         }
14310       }
14311     }
14312     else if (IS_BELT_SWITCH(element))
14313     {
14314       ToggleBeltSwitch(x, y);
14315     }
14316     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14317              element == EL_SWITCHGATE_SWITCH_DOWN ||
14318              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14319              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14320     {
14321       ToggleSwitchgateSwitch(x, y);
14322     }
14323     else if (element == EL_LIGHT_SWITCH ||
14324              element == EL_LIGHT_SWITCH_ACTIVE)
14325     {
14326       ToggleLightSwitch(x, y);
14327     }
14328     else if (element == EL_TIMEGATE_SWITCH ||
14329              element == EL_DC_TIMEGATE_SWITCH)
14330     {
14331       ActivateTimegateSwitch(x, y);
14332     }
14333     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14334              element == EL_BALLOON_SWITCH_RIGHT ||
14335              element == EL_BALLOON_SWITCH_UP    ||
14336              element == EL_BALLOON_SWITCH_DOWN  ||
14337              element == EL_BALLOON_SWITCH_NONE  ||
14338              element == EL_BALLOON_SWITCH_ANY)
14339     {
14340       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14341                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14342                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14343                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14344                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14345                              move_direction);
14346     }
14347     else if (element == EL_LAMP)
14348     {
14349       Feld[x][y] = EL_LAMP_ACTIVE;
14350       game.lights_still_needed--;
14351
14352       ResetGfxAnimation(x, y);
14353       TEST_DrawLevelField(x, y);
14354     }
14355     else if (element == EL_TIME_ORB_FULL)
14356     {
14357       Feld[x][y] = EL_TIME_ORB_EMPTY;
14358
14359       if (level.time > 0 || level.use_time_orb_bug)
14360       {
14361         TimeLeft += level.time_orb_time;
14362         game.no_time_limit = FALSE;
14363
14364         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14365
14366         DisplayGameControlValues();
14367       }
14368
14369       ResetGfxAnimation(x, y);
14370       TEST_DrawLevelField(x, y);
14371     }
14372     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14373              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14374     {
14375       int xx, yy;
14376
14377       game.ball_active = !game.ball_active;
14378
14379       SCAN_PLAYFIELD(xx, yy)
14380       {
14381         int e = Feld[xx][yy];
14382
14383         if (game.ball_active)
14384         {
14385           if (e == EL_EMC_MAGIC_BALL)
14386             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14387           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14388             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14389         }
14390         else
14391         {
14392           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14393             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14394           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14395             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14396         }
14397       }
14398     }
14399
14400     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14401                                         player->index_bit, dig_side);
14402
14403     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14404                                         player->index_bit, dig_side);
14405
14406     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14407                                         player->index_bit, dig_side);
14408
14409     return MP_ACTION;
14410   }
14411   else
14412   {
14413     if (!PLAYER_SWITCHING(player, x, y))
14414     {
14415       player->is_switching = TRUE;
14416       player->switch_x = x;
14417       player->switch_y = y;
14418
14419       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14420                                  player->index_bit, dig_side);
14421       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14422                                           player->index_bit, dig_side);
14423
14424       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14425                                  player->index_bit, dig_side);
14426       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14427                                           player->index_bit, dig_side);
14428     }
14429
14430     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14431                                player->index_bit, dig_side);
14432     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14433                                         player->index_bit, dig_side);
14434
14435     return MP_NO_ACTION;
14436   }
14437
14438   player->push_delay = -1;
14439
14440   if (is_player)                // function can also be called by EL_PENGUIN
14441   {
14442     if (Feld[x][y] != element)          // really digged/collected something
14443     {
14444       player->is_collecting = !player->is_digging;
14445       player->is_active = TRUE;
14446     }
14447   }
14448
14449   return MP_MOVING;
14450 }
14451
14452 static boolean DigFieldByCE(int x, int y, int digging_element)
14453 {
14454   int element = Feld[x][y];
14455
14456   if (!IS_FREE(x, y))
14457   {
14458     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14459                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14460                   ACTION_BREAKING);
14461
14462     // no element can dig solid indestructible elements
14463     if (IS_INDESTRUCTIBLE(element) &&
14464         !IS_DIGGABLE(element) &&
14465         !IS_COLLECTIBLE(element))
14466       return FALSE;
14467
14468     if (AmoebaNr[x][y] &&
14469         (element == EL_AMOEBA_FULL ||
14470          element == EL_BD_AMOEBA ||
14471          element == EL_AMOEBA_GROWING))
14472     {
14473       AmoebaCnt[AmoebaNr[x][y]]--;
14474       AmoebaCnt2[AmoebaNr[x][y]]--;
14475     }
14476
14477     if (IS_MOVING(x, y))
14478       RemoveMovingField(x, y);
14479     else
14480     {
14481       RemoveField(x, y);
14482       TEST_DrawLevelField(x, y);
14483     }
14484
14485     // if digged element was about to explode, prevent the explosion
14486     ExplodeField[x][y] = EX_TYPE_NONE;
14487
14488     PlayLevelSoundAction(x, y, action);
14489   }
14490
14491   Store[x][y] = EL_EMPTY;
14492
14493   // this makes it possible to leave the removed element again
14494   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14495     Store[x][y] = element;
14496
14497   return TRUE;
14498 }
14499
14500 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14501 {
14502   int jx = player->jx, jy = player->jy;
14503   int x = jx + dx, y = jy + dy;
14504   int snap_direction = (dx == -1 ? MV_LEFT  :
14505                         dx == +1 ? MV_RIGHT :
14506                         dy == -1 ? MV_UP    :
14507                         dy == +1 ? MV_DOWN  : MV_NONE);
14508   boolean can_continue_snapping = (level.continuous_snapping &&
14509                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14510
14511   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14512     return FALSE;
14513
14514   if (!player->active || !IN_LEV_FIELD(x, y))
14515     return FALSE;
14516
14517   if (dx && dy)
14518     return FALSE;
14519
14520   if (!dx && !dy)
14521   {
14522     if (player->MovPos == 0)
14523       player->is_pushing = FALSE;
14524
14525     player->is_snapping = FALSE;
14526
14527     if (player->MovPos == 0)
14528     {
14529       player->is_moving = FALSE;
14530       player->is_digging = FALSE;
14531       player->is_collecting = FALSE;
14532     }
14533
14534     return FALSE;
14535   }
14536
14537   // prevent snapping with already pressed snap key when not allowed
14538   if (player->is_snapping && !can_continue_snapping)
14539     return FALSE;
14540
14541   player->MovDir = snap_direction;
14542
14543   if (player->MovPos == 0)
14544   {
14545     player->is_moving = FALSE;
14546     player->is_digging = FALSE;
14547     player->is_collecting = FALSE;
14548   }
14549
14550   player->is_dropping = FALSE;
14551   player->is_dropping_pressed = FALSE;
14552   player->drop_pressed_delay = 0;
14553
14554   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14555     return FALSE;
14556
14557   player->is_snapping = TRUE;
14558   player->is_active = TRUE;
14559
14560   if (player->MovPos == 0)
14561   {
14562     player->is_moving = FALSE;
14563     player->is_digging = FALSE;
14564     player->is_collecting = FALSE;
14565   }
14566
14567   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14568     TEST_DrawLevelField(player->last_jx, player->last_jy);
14569
14570   TEST_DrawLevelField(x, y);
14571
14572   return TRUE;
14573 }
14574
14575 static boolean DropElement(struct PlayerInfo *player)
14576 {
14577   int old_element, new_element;
14578   int dropx = player->jx, dropy = player->jy;
14579   int drop_direction = player->MovDir;
14580   int drop_side = drop_direction;
14581   int drop_element = get_next_dropped_element(player);
14582
14583   /* do not drop an element on top of another element; when holding drop key
14584      pressed without moving, dropped element must move away before the next
14585      element can be dropped (this is especially important if the next element
14586      is dynamite, which can be placed on background for historical reasons) */
14587   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14588     return MP_ACTION;
14589
14590   if (IS_THROWABLE(drop_element))
14591   {
14592     dropx += GET_DX_FROM_DIR(drop_direction);
14593     dropy += GET_DY_FROM_DIR(drop_direction);
14594
14595     if (!IN_LEV_FIELD(dropx, dropy))
14596       return FALSE;
14597   }
14598
14599   old_element = Feld[dropx][dropy];     // old element at dropping position
14600   new_element = drop_element;           // default: no change when dropping
14601
14602   // check if player is active, not moving and ready to drop
14603   if (!player->active || player->MovPos || player->drop_delay > 0)
14604     return FALSE;
14605
14606   // check if player has anything that can be dropped
14607   if (new_element == EL_UNDEFINED)
14608     return FALSE;
14609
14610   // only set if player has anything that can be dropped
14611   player->is_dropping_pressed = TRUE;
14612
14613   // check if drop key was pressed long enough for EM style dynamite
14614   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14615     return FALSE;
14616
14617   // check if anything can be dropped at the current position
14618   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14619     return FALSE;
14620
14621   // collected custom elements can only be dropped on empty fields
14622   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14623     return FALSE;
14624
14625   if (old_element != EL_EMPTY)
14626     Back[dropx][dropy] = old_element;   // store old element on this field
14627
14628   ResetGfxAnimation(dropx, dropy);
14629   ResetRandomAnimationValue(dropx, dropy);
14630
14631   if (player->inventory_size > 0 ||
14632       player->inventory_infinite_element != EL_UNDEFINED)
14633   {
14634     if (player->inventory_size > 0)
14635     {
14636       player->inventory_size--;
14637
14638       DrawGameDoorValues();
14639
14640       if (new_element == EL_DYNAMITE)
14641         new_element = EL_DYNAMITE_ACTIVE;
14642       else if (new_element == EL_EM_DYNAMITE)
14643         new_element = EL_EM_DYNAMITE_ACTIVE;
14644       else if (new_element == EL_SP_DISK_RED)
14645         new_element = EL_SP_DISK_RED_ACTIVE;
14646     }
14647
14648     Feld[dropx][dropy] = new_element;
14649
14650     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14651       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14652                           el2img(Feld[dropx][dropy]), 0);
14653
14654     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14655
14656     // needed if previous element just changed to "empty" in the last frame
14657     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14658
14659     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14660                                player->index_bit, drop_side);
14661     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14662                                         CE_PLAYER_DROPS_X,
14663                                         player->index_bit, drop_side);
14664
14665     TestIfElementTouchesCustomElement(dropx, dropy);
14666   }
14667   else          // player is dropping a dyna bomb
14668   {
14669     player->dynabombs_left--;
14670
14671     Feld[dropx][dropy] = new_element;
14672
14673     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14674       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14675                           el2img(Feld[dropx][dropy]), 0);
14676
14677     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14678   }
14679
14680   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14681     InitField_WithBug1(dropx, dropy, FALSE);
14682
14683   new_element = Feld[dropx][dropy];     // element might have changed
14684
14685   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14686       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14687   {
14688     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14689       MovDir[dropx][dropy] = drop_direction;
14690
14691     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14692
14693     // do not cause impact style collision by dropping elements that can fall
14694     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14695   }
14696
14697   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14698   player->is_dropping = TRUE;
14699
14700   player->drop_pressed_delay = 0;
14701   player->is_dropping_pressed = FALSE;
14702
14703   player->drop_x = dropx;
14704   player->drop_y = dropy;
14705
14706   return TRUE;
14707 }
14708
14709 // ----------------------------------------------------------------------------
14710 // game sound playing functions
14711 // ----------------------------------------------------------------------------
14712
14713 static int *loop_sound_frame = NULL;
14714 static int *loop_sound_volume = NULL;
14715
14716 void InitPlayLevelSound(void)
14717 {
14718   int num_sounds = getSoundListSize();
14719
14720   checked_free(loop_sound_frame);
14721   checked_free(loop_sound_volume);
14722
14723   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14724   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14725 }
14726
14727 static void PlayLevelSound(int x, int y, int nr)
14728 {
14729   int sx = SCREENX(x), sy = SCREENY(y);
14730   int volume, stereo_position;
14731   int max_distance = 8;
14732   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14733
14734   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14735       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14736     return;
14737
14738   if (!IN_LEV_FIELD(x, y) ||
14739       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14740       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14741     return;
14742
14743   volume = SOUND_MAX_VOLUME;
14744
14745   if (!IN_SCR_FIELD(sx, sy))
14746   {
14747     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14748     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14749
14750     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14751   }
14752
14753   stereo_position = (SOUND_MAX_LEFT +
14754                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14755                      (SCR_FIELDX + 2 * max_distance));
14756
14757   if (IS_LOOP_SOUND(nr))
14758   {
14759     /* This assures that quieter loop sounds do not overwrite louder ones,
14760        while restarting sound volume comparison with each new game frame. */
14761
14762     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14763       return;
14764
14765     loop_sound_volume[nr] = volume;
14766     loop_sound_frame[nr] = FrameCounter;
14767   }
14768
14769   PlaySoundExt(nr, volume, stereo_position, type);
14770 }
14771
14772 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14773 {
14774   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14775                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14776                  y < LEVELY(BY1) ? LEVELY(BY1) :
14777                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14778                  sound_action);
14779 }
14780
14781 static void PlayLevelSoundAction(int x, int y, int action)
14782 {
14783   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14784 }
14785
14786 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14787 {
14788   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14789
14790   if (sound_effect != SND_UNDEFINED)
14791     PlayLevelSound(x, y, sound_effect);
14792 }
14793
14794 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14795                                               int action)
14796 {
14797   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14798
14799   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14800     PlayLevelSound(x, y, sound_effect);
14801 }
14802
14803 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14804 {
14805   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14806
14807   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14808     PlayLevelSound(x, y, sound_effect);
14809 }
14810
14811 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14812 {
14813   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14814
14815   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14816     StopSound(sound_effect);
14817 }
14818
14819 static int getLevelMusicNr(void)
14820 {
14821   if (levelset.music[level_nr] != MUS_UNDEFINED)
14822     return levelset.music[level_nr];            // from config file
14823   else
14824     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14825 }
14826
14827 static void FadeLevelSounds(void)
14828 {
14829   FadeSounds();
14830 }
14831
14832 static void FadeLevelMusic(void)
14833 {
14834   int music_nr = getLevelMusicNr();
14835   char *curr_music = getCurrentlyPlayingMusicFilename();
14836   char *next_music = getMusicInfoEntryFilename(music_nr);
14837
14838   if (!strEqual(curr_music, next_music))
14839     FadeMusic();
14840 }
14841
14842 void FadeLevelSoundsAndMusic(void)
14843 {
14844   FadeLevelSounds();
14845   FadeLevelMusic();
14846 }
14847
14848 static void PlayLevelMusic(void)
14849 {
14850   int music_nr = getLevelMusicNr();
14851   char *curr_music = getCurrentlyPlayingMusicFilename();
14852   char *next_music = getMusicInfoEntryFilename(music_nr);
14853
14854   if (!strEqual(curr_music, next_music))
14855     PlayMusicLoop(music_nr);
14856 }
14857
14858 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14859 {
14860   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14861   int offset = 0;
14862   int x = xx - 1 - offset;
14863   int y = yy - 1 - offset;
14864
14865   switch (sample)
14866   {
14867     case SOUND_blank:
14868       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14869       break;
14870
14871     case SOUND_roll:
14872       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14873       break;
14874
14875     case SOUND_stone:
14876       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14877       break;
14878
14879     case SOUND_nut:
14880       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14881       break;
14882
14883     case SOUND_crack:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14885       break;
14886
14887     case SOUND_bug:
14888       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14889       break;
14890
14891     case SOUND_tank:
14892       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14893       break;
14894
14895     case SOUND_android_clone:
14896       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14897       break;
14898
14899     case SOUND_android_move:
14900       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14901       break;
14902
14903     case SOUND_spring:
14904       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14905       break;
14906
14907     case SOUND_slurp:
14908       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14909       break;
14910
14911     case SOUND_eater:
14912       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14913       break;
14914
14915     case SOUND_eater_eat:
14916       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14917       break;
14918
14919     case SOUND_alien:
14920       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14921       break;
14922
14923     case SOUND_collect:
14924       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14925       break;
14926
14927     case SOUND_diamond:
14928       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14929       break;
14930
14931     case SOUND_squash:
14932       // !!! CHECK THIS !!!
14933 #if 1
14934       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14935 #else
14936       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14937 #endif
14938       break;
14939
14940     case SOUND_wonderfall:
14941       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14942       break;
14943
14944     case SOUND_drip:
14945       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14946       break;
14947
14948     case SOUND_push:
14949       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14950       break;
14951
14952     case SOUND_dirt:
14953       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14954       break;
14955
14956     case SOUND_acid:
14957       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14958       break;
14959
14960     case SOUND_ball:
14961       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14962       break;
14963
14964     case SOUND_slide:
14965       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14966       break;
14967
14968     case SOUND_wonder:
14969       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14970       break;
14971
14972     case SOUND_door:
14973       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14974       break;
14975
14976     case SOUND_exit_open:
14977       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14978       break;
14979
14980     case SOUND_exit_leave:
14981       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14982       break;
14983
14984     case SOUND_dynamite:
14985       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14986       break;
14987
14988     case SOUND_tick:
14989       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14990       break;
14991
14992     case SOUND_press:
14993       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14994       break;
14995
14996     case SOUND_wheel:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14998       break;
14999
15000     case SOUND_boom:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15002       break;
15003
15004     case SOUND_die:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15006       break;
15007
15008     case SOUND_time:
15009       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15010       break;
15011
15012     default:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15014       break;
15015   }
15016 }
15017
15018 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15019 {
15020   int element = map_element_SP_to_RND(element_sp);
15021   int action = map_action_SP_to_RND(action_sp);
15022   int offset = (setup.sp_show_border_elements ? 0 : 1);
15023   int x = xx - offset;
15024   int y = yy - offset;
15025
15026   PlayLevelSoundElementAction(x, y, element, action);
15027 }
15028
15029 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15030 {
15031   int element = map_element_MM_to_RND(element_mm);
15032   int action = map_action_MM_to_RND(action_mm);
15033   int offset = 0;
15034   int x = xx - offset;
15035   int y = yy - offset;
15036
15037   if (!IS_MM_ELEMENT(element))
15038     element = EL_MM_DEFAULT;
15039
15040   PlayLevelSoundElementAction(x, y, element, action);
15041 }
15042
15043 void PlaySound_MM(int sound_mm)
15044 {
15045   int sound = map_sound_MM_to_RND(sound_mm);
15046
15047   if (sound == SND_UNDEFINED)
15048     return;
15049
15050   PlaySound(sound);
15051 }
15052
15053 void PlaySoundLoop_MM(int sound_mm)
15054 {
15055   int sound = map_sound_MM_to_RND(sound_mm);
15056
15057   if (sound == SND_UNDEFINED)
15058     return;
15059
15060   PlaySoundLoop(sound);
15061 }
15062
15063 void StopSound_MM(int sound_mm)
15064 {
15065   int sound = map_sound_MM_to_RND(sound_mm);
15066
15067   if (sound == SND_UNDEFINED)
15068     return;
15069
15070   StopSound(sound);
15071 }
15072
15073 void RaiseScore(int value)
15074 {
15075   game.score += value;
15076
15077   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15078
15079   DisplayGameControlValues();
15080 }
15081
15082 void RaiseScoreElement(int element)
15083 {
15084   switch (element)
15085   {
15086     case EL_EMERALD:
15087     case EL_BD_DIAMOND:
15088     case EL_EMERALD_YELLOW:
15089     case EL_EMERALD_RED:
15090     case EL_EMERALD_PURPLE:
15091     case EL_SP_INFOTRON:
15092       RaiseScore(level.score[SC_EMERALD]);
15093       break;
15094     case EL_DIAMOND:
15095       RaiseScore(level.score[SC_DIAMOND]);
15096       break;
15097     case EL_CRYSTAL:
15098       RaiseScore(level.score[SC_CRYSTAL]);
15099       break;
15100     case EL_PEARL:
15101       RaiseScore(level.score[SC_PEARL]);
15102       break;
15103     case EL_BUG:
15104     case EL_BD_BUTTERFLY:
15105     case EL_SP_ELECTRON:
15106       RaiseScore(level.score[SC_BUG]);
15107       break;
15108     case EL_SPACESHIP:
15109     case EL_BD_FIREFLY:
15110     case EL_SP_SNIKSNAK:
15111       RaiseScore(level.score[SC_SPACESHIP]);
15112       break;
15113     case EL_YAMYAM:
15114     case EL_DARK_YAMYAM:
15115       RaiseScore(level.score[SC_YAMYAM]);
15116       break;
15117     case EL_ROBOT:
15118       RaiseScore(level.score[SC_ROBOT]);
15119       break;
15120     case EL_PACMAN:
15121       RaiseScore(level.score[SC_PACMAN]);
15122       break;
15123     case EL_NUT:
15124       RaiseScore(level.score[SC_NUT]);
15125       break;
15126     case EL_DYNAMITE:
15127     case EL_EM_DYNAMITE:
15128     case EL_SP_DISK_RED:
15129     case EL_DYNABOMB_INCREASE_NUMBER:
15130     case EL_DYNABOMB_INCREASE_SIZE:
15131     case EL_DYNABOMB_INCREASE_POWER:
15132       RaiseScore(level.score[SC_DYNAMITE]);
15133       break;
15134     case EL_SHIELD_NORMAL:
15135     case EL_SHIELD_DEADLY:
15136       RaiseScore(level.score[SC_SHIELD]);
15137       break;
15138     case EL_EXTRA_TIME:
15139       RaiseScore(level.extra_time_score);
15140       break;
15141     case EL_KEY_1:
15142     case EL_KEY_2:
15143     case EL_KEY_3:
15144     case EL_KEY_4:
15145     case EL_EM_KEY_1:
15146     case EL_EM_KEY_2:
15147     case EL_EM_KEY_3:
15148     case EL_EM_KEY_4:
15149     case EL_EMC_KEY_5:
15150     case EL_EMC_KEY_6:
15151     case EL_EMC_KEY_7:
15152     case EL_EMC_KEY_8:
15153     case EL_DC_KEY_WHITE:
15154       RaiseScore(level.score[SC_KEY]);
15155       break;
15156     default:
15157       RaiseScore(element_info[element].collect_score);
15158       break;
15159   }
15160 }
15161
15162 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15163 {
15164   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15165   {
15166     // closing door required in case of envelope style request dialogs
15167     if (!skip_request)
15168     {
15169       // prevent short reactivation of overlay buttons while closing door
15170       SetOverlayActive(FALSE);
15171
15172       CloseDoor(DOOR_CLOSE_1);
15173     }
15174
15175     if (network.enabled)
15176       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15177     else
15178     {
15179       if (quick_quit)
15180         FadeSkipNextFadeIn();
15181
15182       SetGameStatus(GAME_MODE_MAIN);
15183
15184       DrawMainMenu();
15185     }
15186   }
15187   else          // continue playing the game
15188   {
15189     if (tape.playing && tape.deactivate_display)
15190       TapeDeactivateDisplayOff(TRUE);
15191
15192     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15193
15194     if (tape.playing && tape.deactivate_display)
15195       TapeDeactivateDisplayOn();
15196   }
15197 }
15198
15199 void RequestQuitGame(boolean ask_if_really_quit)
15200 {
15201   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15202   boolean skip_request = game.all_players_gone || quick_quit;
15203
15204   RequestQuitGameExt(skip_request, quick_quit,
15205                      "Do you really want to quit the game?");
15206 }
15207
15208 void RequestRestartGame(char *message)
15209 {
15210   game.restart_game_message = NULL;
15211
15212   boolean has_started_game = hasStartedNetworkGame();
15213   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15214
15215   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15216   {
15217     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15218   }
15219   else
15220   {
15221     SetGameStatus(GAME_MODE_MAIN);
15222
15223     DrawMainMenu();
15224   }
15225 }
15226
15227 void CheckGameOver(void)
15228 {
15229   static boolean last_game_over = FALSE;
15230   static int game_over_delay = 0;
15231   int game_over_delay_value = 50;
15232   boolean game_over = checkGameFailed();
15233
15234   // do not handle game over if request dialog is already active
15235   if (game.request_active)
15236     return;
15237
15238   // do not ask to play again if game was never actually played
15239   if (!game.GamePlayed)
15240     return;
15241
15242   if (!game_over)
15243   {
15244     last_game_over = FALSE;
15245     game_over_delay = game_over_delay_value;
15246
15247     return;
15248   }
15249
15250   if (game_over_delay > 0)
15251   {
15252     game_over_delay--;
15253
15254     return;
15255   }
15256
15257   if (last_game_over != game_over)
15258     game.restart_game_message = (hasStartedNetworkGame() ?
15259                                  "Game over! Play it again?" :
15260                                  "Game over!");
15261
15262   last_game_over = game_over;
15263 }
15264
15265 boolean checkGameSolved(void)
15266 {
15267   // set for all game engines if level was solved
15268   return game.LevelSolved_GameEnd;
15269 }
15270
15271 boolean checkGameFailed(void)
15272 {
15273   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15274     return (game_em.game_over && !game_em.level_solved);
15275   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15276     return (game_sp.game_over && !game_sp.level_solved);
15277   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15278     return (game_mm.game_over && !game_mm.level_solved);
15279   else                          // GAME_ENGINE_TYPE_RND
15280     return (game.GameOver && !game.LevelSolved);
15281 }
15282
15283 boolean checkGameEnded(void)
15284 {
15285   return (checkGameSolved() || checkGameFailed());
15286 }
15287
15288
15289 // ----------------------------------------------------------------------------
15290 // random generator functions
15291 // ----------------------------------------------------------------------------
15292
15293 unsigned int InitEngineRandom_RND(int seed)
15294 {
15295   game.num_random_calls = 0;
15296
15297   return InitEngineRandom(seed);
15298 }
15299
15300 unsigned int RND(int max)
15301 {
15302   if (max > 0)
15303   {
15304     game.num_random_calls++;
15305
15306     return GetEngineRandom(max);
15307   }
15308
15309   return 0;
15310 }
15311
15312
15313 // ----------------------------------------------------------------------------
15314 // game engine snapshot handling functions
15315 // ----------------------------------------------------------------------------
15316
15317 struct EngineSnapshotInfo
15318 {
15319   // runtime values for custom element collect score
15320   int collect_score[NUM_CUSTOM_ELEMENTS];
15321
15322   // runtime values for group element choice position
15323   int choice_pos[NUM_GROUP_ELEMENTS];
15324
15325   // runtime values for belt position animations
15326   int belt_graphic[4][NUM_BELT_PARTS];
15327   int belt_anim_mode[4][NUM_BELT_PARTS];
15328 };
15329
15330 static struct EngineSnapshotInfo engine_snapshot_rnd;
15331 static char *snapshot_level_identifier = NULL;
15332 static int snapshot_level_nr = -1;
15333
15334 static void SaveEngineSnapshotValues_RND(void)
15335 {
15336   static int belt_base_active_element[4] =
15337   {
15338     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15339     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15340     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15341     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15342   };
15343   int i, j;
15344
15345   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15346   {
15347     int element = EL_CUSTOM_START + i;
15348
15349     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15350   }
15351
15352   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15353   {
15354     int element = EL_GROUP_START + i;
15355
15356     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15357   }
15358
15359   for (i = 0; i < 4; i++)
15360   {
15361     for (j = 0; j < NUM_BELT_PARTS; j++)
15362     {
15363       int element = belt_base_active_element[i] + j;
15364       int graphic = el2img(element);
15365       int anim_mode = graphic_info[graphic].anim_mode;
15366
15367       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15368       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15369     }
15370   }
15371 }
15372
15373 static void LoadEngineSnapshotValues_RND(void)
15374 {
15375   unsigned int num_random_calls = game.num_random_calls;
15376   int i, j;
15377
15378   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15379   {
15380     int element = EL_CUSTOM_START + i;
15381
15382     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15383   }
15384
15385   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15386   {
15387     int element = EL_GROUP_START + i;
15388
15389     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15390   }
15391
15392   for (i = 0; i < 4; i++)
15393   {
15394     for (j = 0; j < NUM_BELT_PARTS; j++)
15395     {
15396       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15397       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15398
15399       graphic_info[graphic].anim_mode = anim_mode;
15400     }
15401   }
15402
15403   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15404   {
15405     InitRND(tape.random_seed);
15406     for (i = 0; i < num_random_calls; i++)
15407       RND(1);
15408   }
15409
15410   if (game.num_random_calls != num_random_calls)
15411   {
15412     Error(ERR_INFO, "number of random calls out of sync");
15413     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15414     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15415     Error(ERR_EXIT, "this should not happen -- please debug");
15416   }
15417 }
15418
15419 void FreeEngineSnapshotSingle(void)
15420 {
15421   FreeSnapshotSingle();
15422
15423   setString(&snapshot_level_identifier, NULL);
15424   snapshot_level_nr = -1;
15425 }
15426
15427 void FreeEngineSnapshotList(void)
15428 {
15429   FreeSnapshotList();
15430 }
15431
15432 static ListNode *SaveEngineSnapshotBuffers(void)
15433 {
15434   ListNode *buffers = NULL;
15435
15436   // copy some special values to a structure better suited for the snapshot
15437
15438   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15439     SaveEngineSnapshotValues_RND();
15440   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15441     SaveEngineSnapshotValues_EM();
15442   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15443     SaveEngineSnapshotValues_SP(&buffers);
15444   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15445     SaveEngineSnapshotValues_MM(&buffers);
15446
15447   // save values stored in special snapshot structure
15448
15449   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15450     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15451   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15452     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15453   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15454     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15456     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15457
15458   // save further RND engine values
15459
15460   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15461   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15462   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15463
15464   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15465   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15466   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15467   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15468   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15469
15470   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15472   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15473
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15475
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15478
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15497
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15500
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15504
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15507
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15512   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15513
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15515   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15516
15517 #if 0
15518   ListNode *node = engine_snapshot_list_rnd;
15519   int num_bytes = 0;
15520
15521   while (node != NULL)
15522   {
15523     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15524
15525     node = node->next;
15526   }
15527
15528   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15529 #endif
15530
15531   return buffers;
15532 }
15533
15534 void SaveEngineSnapshotSingle(void)
15535 {
15536   ListNode *buffers = SaveEngineSnapshotBuffers();
15537
15538   // finally save all snapshot buffers to single snapshot
15539   SaveSnapshotSingle(buffers);
15540
15541   // save level identification information
15542   setString(&snapshot_level_identifier, leveldir_current->identifier);
15543   snapshot_level_nr = level_nr;
15544 }
15545
15546 boolean CheckSaveEngineSnapshotToList(void)
15547 {
15548   boolean save_snapshot =
15549     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15550      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15551       game.snapshot.changed_action) ||
15552      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15553       game.snapshot.collected_item));
15554
15555   game.snapshot.changed_action = FALSE;
15556   game.snapshot.collected_item = FALSE;
15557   game.snapshot.save_snapshot = save_snapshot;
15558
15559   return save_snapshot;
15560 }
15561
15562 void SaveEngineSnapshotToList(void)
15563 {
15564   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15565       tape.quick_resume)
15566     return;
15567
15568   ListNode *buffers = SaveEngineSnapshotBuffers();
15569
15570   // finally save all snapshot buffers to snapshot list
15571   SaveSnapshotToList(buffers);
15572 }
15573
15574 void SaveEngineSnapshotToListInitial(void)
15575 {
15576   FreeEngineSnapshotList();
15577
15578   SaveEngineSnapshotToList();
15579 }
15580
15581 static void LoadEngineSnapshotValues(void)
15582 {
15583   // restore special values from snapshot structure
15584
15585   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15586     LoadEngineSnapshotValues_RND();
15587   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15588     LoadEngineSnapshotValues_EM();
15589   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15590     LoadEngineSnapshotValues_SP();
15591   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15592     LoadEngineSnapshotValues_MM();
15593 }
15594
15595 void LoadEngineSnapshotSingle(void)
15596 {
15597   LoadSnapshotSingle();
15598
15599   LoadEngineSnapshotValues();
15600 }
15601
15602 static void LoadEngineSnapshot_Undo(int steps)
15603 {
15604   LoadSnapshotFromList_Older(steps);
15605
15606   LoadEngineSnapshotValues();
15607 }
15608
15609 static void LoadEngineSnapshot_Redo(int steps)
15610 {
15611   LoadSnapshotFromList_Newer(steps);
15612
15613   LoadEngineSnapshotValues();
15614 }
15615
15616 boolean CheckEngineSnapshotSingle(void)
15617 {
15618   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15619           snapshot_level_nr == level_nr);
15620 }
15621
15622 boolean CheckEngineSnapshotList(void)
15623 {
15624   return CheckSnapshotList();
15625 }
15626
15627
15628 // ---------- new game button stuff -------------------------------------------
15629
15630 static struct
15631 {
15632   int graphic;
15633   struct XY *pos;
15634   int gadget_id;
15635   boolean *setup_value;
15636   boolean allowed_on_tape;
15637   boolean is_touch_button;
15638   char *infotext;
15639 } gamebutton_info[NUM_GAME_BUTTONS] =
15640 {
15641   {
15642     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15643     GAME_CTRL_ID_STOP,                          NULL,
15644     TRUE, FALSE,                                "stop game"
15645   },
15646   {
15647     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15648     GAME_CTRL_ID_PAUSE,                         NULL,
15649     TRUE, FALSE,                                "pause game"
15650   },
15651   {
15652     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15653     GAME_CTRL_ID_PLAY,                          NULL,
15654     TRUE, FALSE,                                "play game"
15655   },
15656   {
15657     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15658     GAME_CTRL_ID_UNDO,                          NULL,
15659     TRUE, FALSE,                                "undo step"
15660   },
15661   {
15662     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15663     GAME_CTRL_ID_REDO,                          NULL,
15664     TRUE, FALSE,                                "redo step"
15665   },
15666   {
15667     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15668     GAME_CTRL_ID_SAVE,                          NULL,
15669     TRUE, FALSE,                                "save game"
15670   },
15671   {
15672     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15673     GAME_CTRL_ID_PAUSE2,                        NULL,
15674     TRUE, FALSE,                                "pause game"
15675   },
15676   {
15677     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15678     GAME_CTRL_ID_LOAD,                          NULL,
15679     TRUE, FALSE,                                "load game"
15680   },
15681   {
15682     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15683     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15684     FALSE, FALSE,                               "stop game"
15685   },
15686   {
15687     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15688     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15689     FALSE, FALSE,                               "pause game"
15690   },
15691   {
15692     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15693     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15694     FALSE, FALSE,                               "play game"
15695   },
15696   {
15697     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15698     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15699     FALSE, TRUE,                                "stop game"
15700   },
15701   {
15702     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15703     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15704     FALSE, TRUE,                                "pause game"
15705   },
15706   {
15707     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15708     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15709     TRUE, FALSE,                                "background music on/off"
15710   },
15711   {
15712     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15713     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15714     TRUE, FALSE,                                "sound loops on/off"
15715   },
15716   {
15717     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15718     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15719     TRUE, FALSE,                                "normal sounds on/off"
15720   },
15721   {
15722     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15723     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15724     FALSE, FALSE,                               "background music on/off"
15725   },
15726   {
15727     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15728     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15729     FALSE, FALSE,                               "sound loops on/off"
15730   },
15731   {
15732     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15733     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15734     FALSE, FALSE,                               "normal sounds on/off"
15735   }
15736 };
15737
15738 void CreateGameButtons(void)
15739 {
15740   int i;
15741
15742   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15743   {
15744     int graphic = gamebutton_info[i].graphic;
15745     struct GraphicInfo *gfx = &graphic_info[graphic];
15746     struct XY *pos = gamebutton_info[i].pos;
15747     struct GadgetInfo *gi;
15748     int button_type;
15749     boolean checked;
15750     unsigned int event_mask;
15751     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15752     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15753     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15754     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15755     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15756     int gd_x   = gfx->src_x;
15757     int gd_y   = gfx->src_y;
15758     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15759     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15760     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15761     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15762     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15763     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15764     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15765     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15766     int id = i;
15767
15768     if (gfx->bitmap == NULL)
15769     {
15770       game_gadget[id] = NULL;
15771
15772       continue;
15773     }
15774
15775     if (id == GAME_CTRL_ID_STOP ||
15776         id == GAME_CTRL_ID_PANEL_STOP ||
15777         id == GAME_CTRL_ID_TOUCH_STOP ||
15778         id == GAME_CTRL_ID_PLAY ||
15779         id == GAME_CTRL_ID_PANEL_PLAY ||
15780         id == GAME_CTRL_ID_SAVE ||
15781         id == GAME_CTRL_ID_LOAD)
15782     {
15783       button_type = GD_TYPE_NORMAL_BUTTON;
15784       checked = FALSE;
15785       event_mask = GD_EVENT_RELEASED;
15786     }
15787     else if (id == GAME_CTRL_ID_UNDO ||
15788              id == GAME_CTRL_ID_REDO)
15789     {
15790       button_type = GD_TYPE_NORMAL_BUTTON;
15791       checked = FALSE;
15792       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15793     }
15794     else
15795     {
15796       button_type = GD_TYPE_CHECK_BUTTON;
15797       checked = (gamebutton_info[i].setup_value != NULL ?
15798                  *gamebutton_info[i].setup_value : FALSE);
15799       event_mask = GD_EVENT_PRESSED;
15800     }
15801
15802     gi = CreateGadget(GDI_CUSTOM_ID, id,
15803                       GDI_IMAGE_ID, graphic,
15804                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15805                       GDI_X, base_x + x,
15806                       GDI_Y, base_y + y,
15807                       GDI_WIDTH, gfx->width,
15808                       GDI_HEIGHT, gfx->height,
15809                       GDI_TYPE, button_type,
15810                       GDI_STATE, GD_BUTTON_UNPRESSED,
15811                       GDI_CHECKED, checked,
15812                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15813                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15814                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15815                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15816                       GDI_DIRECT_DRAW, FALSE,
15817                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15818                       GDI_EVENT_MASK, event_mask,
15819                       GDI_CALLBACK_ACTION, HandleGameButtons,
15820                       GDI_END);
15821
15822     if (gi == NULL)
15823       Error(ERR_EXIT, "cannot create gadget");
15824
15825     game_gadget[id] = gi;
15826   }
15827 }
15828
15829 void FreeGameButtons(void)
15830 {
15831   int i;
15832
15833   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15834     FreeGadget(game_gadget[i]);
15835 }
15836
15837 static void UnmapGameButtonsAtSamePosition(int id)
15838 {
15839   int i;
15840
15841   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15842     if (i != id &&
15843         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15844         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15845       UnmapGadget(game_gadget[i]);
15846 }
15847
15848 static void UnmapGameButtonsAtSamePosition_All(void)
15849 {
15850   if (setup.show_snapshot_buttons)
15851   {
15852     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15853     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15854     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15855   }
15856   else
15857   {
15858     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15859     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15860     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15861
15862     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15863     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15864     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15865   }
15866 }
15867
15868 static void MapGameButtonsAtSamePosition(int id)
15869 {
15870   int i;
15871
15872   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15873     if (i != id &&
15874         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15875         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15876       MapGadget(game_gadget[i]);
15877
15878   UnmapGameButtonsAtSamePosition_All();
15879 }
15880
15881 void MapUndoRedoButtons(void)
15882 {
15883   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15884   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15885
15886   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15887   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15888 }
15889
15890 void UnmapUndoRedoButtons(void)
15891 {
15892   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15893   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15894
15895   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15896   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15897 }
15898
15899 void ModifyPauseButtons(void)
15900 {
15901   static int ids[] =
15902   {
15903     GAME_CTRL_ID_PAUSE,
15904     GAME_CTRL_ID_PAUSE2,
15905     GAME_CTRL_ID_PANEL_PAUSE,
15906     GAME_CTRL_ID_TOUCH_PAUSE,
15907     -1
15908   };
15909   int i;
15910
15911   for (i = 0; ids[i] > -1; i++)
15912     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15913 }
15914
15915 static void MapGameButtonsExt(boolean on_tape)
15916 {
15917   int i;
15918
15919   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15920     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15921         i != GAME_CTRL_ID_UNDO &&
15922         i != GAME_CTRL_ID_REDO)
15923       MapGadget(game_gadget[i]);
15924
15925   UnmapGameButtonsAtSamePosition_All();
15926
15927   RedrawGameButtons();
15928 }
15929
15930 static void UnmapGameButtonsExt(boolean on_tape)
15931 {
15932   int i;
15933
15934   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15935     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15936       UnmapGadget(game_gadget[i]);
15937 }
15938
15939 static void RedrawGameButtonsExt(boolean on_tape)
15940 {
15941   int i;
15942
15943   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15944     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15945       RedrawGadget(game_gadget[i]);
15946 }
15947
15948 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15949 {
15950   if (gi == NULL)
15951     return;
15952
15953   gi->checked = state;
15954 }
15955
15956 static void RedrawSoundButtonGadget(int id)
15957 {
15958   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15959              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15960              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15961              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15962              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15963              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15964              id);
15965
15966   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15967   RedrawGadget(game_gadget[id2]);
15968 }
15969
15970 void MapGameButtons(void)
15971 {
15972   MapGameButtonsExt(FALSE);
15973 }
15974
15975 void UnmapGameButtons(void)
15976 {
15977   UnmapGameButtonsExt(FALSE);
15978 }
15979
15980 void RedrawGameButtons(void)
15981 {
15982   RedrawGameButtonsExt(FALSE);
15983 }
15984
15985 void MapGameButtonsOnTape(void)
15986 {
15987   MapGameButtonsExt(TRUE);
15988 }
15989
15990 void UnmapGameButtonsOnTape(void)
15991 {
15992   UnmapGameButtonsExt(TRUE);
15993 }
15994
15995 void RedrawGameButtonsOnTape(void)
15996 {
15997   RedrawGameButtonsExt(TRUE);
15998 }
15999
16000 static void GameUndoRedoExt(void)
16001 {
16002   ClearPlayerAction();
16003
16004   tape.pausing = TRUE;
16005
16006   RedrawPlayfield();
16007   UpdateAndDisplayGameControlValues();
16008
16009   DrawCompleteVideoDisplay();
16010   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16011   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16012   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16013
16014   BackToFront();
16015 }
16016
16017 static void GameUndo(int steps)
16018 {
16019   if (!CheckEngineSnapshotList())
16020     return;
16021
16022   LoadEngineSnapshot_Undo(steps);
16023
16024   GameUndoRedoExt();
16025 }
16026
16027 static void GameRedo(int steps)
16028 {
16029   if (!CheckEngineSnapshotList())
16030     return;
16031
16032   LoadEngineSnapshot_Redo(steps);
16033
16034   GameUndoRedoExt();
16035 }
16036
16037 static void HandleGameButtonsExt(int id, int button)
16038 {
16039   static boolean game_undo_executed = FALSE;
16040   int steps = BUTTON_STEPSIZE(button);
16041   boolean handle_game_buttons =
16042     (game_status == GAME_MODE_PLAYING ||
16043      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16044
16045   if (!handle_game_buttons)
16046     return;
16047
16048   switch (id)
16049   {
16050     case GAME_CTRL_ID_STOP:
16051     case GAME_CTRL_ID_PANEL_STOP:
16052     case GAME_CTRL_ID_TOUCH_STOP:
16053       if (game_status == GAME_MODE_MAIN)
16054         break;
16055
16056       if (tape.playing)
16057         TapeStop();
16058       else
16059         RequestQuitGame(TRUE);
16060
16061       break;
16062
16063     case GAME_CTRL_ID_PAUSE:
16064     case GAME_CTRL_ID_PAUSE2:
16065     case GAME_CTRL_ID_PANEL_PAUSE:
16066     case GAME_CTRL_ID_TOUCH_PAUSE:
16067       if (network.enabled && game_status == GAME_MODE_PLAYING)
16068       {
16069         if (tape.pausing)
16070           SendToServer_ContinuePlaying();
16071         else
16072           SendToServer_PausePlaying();
16073       }
16074       else
16075         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16076
16077       game_undo_executed = FALSE;
16078
16079       break;
16080
16081     case GAME_CTRL_ID_PLAY:
16082     case GAME_CTRL_ID_PANEL_PLAY:
16083       if (game_status == GAME_MODE_MAIN)
16084       {
16085         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16086       }
16087       else if (tape.pausing)
16088       {
16089         if (network.enabled)
16090           SendToServer_ContinuePlaying();
16091         else
16092           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16093       }
16094       break;
16095
16096     case GAME_CTRL_ID_UNDO:
16097       // Important: When using "save snapshot when collecting an item" mode,
16098       // load last (current) snapshot for first "undo" after pressing "pause"
16099       // (else the last-but-one snapshot would be loaded, because the snapshot
16100       // pointer already points to the last snapshot when pressing "pause",
16101       // which is fine for "every step/move" mode, but not for "every collect")
16102       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16103           !game_undo_executed)
16104         steps--;
16105
16106       game_undo_executed = TRUE;
16107
16108       GameUndo(steps);
16109       break;
16110
16111     case GAME_CTRL_ID_REDO:
16112       GameRedo(steps);
16113       break;
16114
16115     case GAME_CTRL_ID_SAVE:
16116       TapeQuickSave();
16117       break;
16118
16119     case GAME_CTRL_ID_LOAD:
16120       TapeQuickLoad();
16121       break;
16122
16123     case SOUND_CTRL_ID_MUSIC:
16124     case SOUND_CTRL_ID_PANEL_MUSIC:
16125       if (setup.sound_music)
16126       { 
16127         setup.sound_music = FALSE;
16128
16129         FadeMusic();
16130       }
16131       else if (audio.music_available)
16132       { 
16133         setup.sound = setup.sound_music = TRUE;
16134
16135         SetAudioMode(setup.sound);
16136
16137         if (game_status == GAME_MODE_PLAYING)
16138           PlayLevelMusic();
16139       }
16140
16141       RedrawSoundButtonGadget(id);
16142
16143       break;
16144
16145     case SOUND_CTRL_ID_LOOPS:
16146     case SOUND_CTRL_ID_PANEL_LOOPS:
16147       if (setup.sound_loops)
16148         setup.sound_loops = FALSE;
16149       else if (audio.loops_available)
16150       {
16151         setup.sound = setup.sound_loops = TRUE;
16152
16153         SetAudioMode(setup.sound);
16154       }
16155
16156       RedrawSoundButtonGadget(id);
16157
16158       break;
16159
16160     case SOUND_CTRL_ID_SIMPLE:
16161     case SOUND_CTRL_ID_PANEL_SIMPLE:
16162       if (setup.sound_simple)
16163         setup.sound_simple = FALSE;
16164       else if (audio.sound_available)
16165       {
16166         setup.sound = setup.sound_simple = TRUE;
16167
16168         SetAudioMode(setup.sound);
16169       }
16170
16171       RedrawSoundButtonGadget(id);
16172
16173       break;
16174
16175     default:
16176       break;
16177   }
16178 }
16179
16180 static void HandleGameButtons(struct GadgetInfo *gi)
16181 {
16182   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16183 }
16184
16185 void HandleSoundButtonKeys(Key key)
16186 {
16187   if (key == setup.shortcut.sound_simple)
16188     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16189   else if (key == setup.shortcut.sound_loops)
16190     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16191   else if (key == setup.shortcut.sound_music)
16192     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16193 }