fixed bug with reanimating killed player by CE condition
[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   // always check if player was just killed and should be reanimated
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   // needed to determine if sounds are played within the visible screen area
4398   scroll_x = actual_scroll_x;
4399   scroll_y = actual_scroll_y;
4400
4401   // needed to get player position for "follow finger" playing input method
4402   local_player->jx = actual_player_x;
4403   local_player->jy = actual_player_y;
4404 }
4405
4406 void InitMovDir(int x, int y)
4407 {
4408   int i, element = Feld[x][y];
4409   static int xy[4][2] =
4410   {
4411     {  0, +1 },
4412     { +1,  0 },
4413     {  0, -1 },
4414     { -1,  0 }
4415   };
4416   static int direction[3][4] =
4417   {
4418     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4419     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4420     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4421   };
4422
4423   switch (element)
4424   {
4425     case EL_BUG_RIGHT:
4426     case EL_BUG_UP:
4427     case EL_BUG_LEFT:
4428     case EL_BUG_DOWN:
4429       Feld[x][y] = EL_BUG;
4430       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4431       break;
4432
4433     case EL_SPACESHIP_RIGHT:
4434     case EL_SPACESHIP_UP:
4435     case EL_SPACESHIP_LEFT:
4436     case EL_SPACESHIP_DOWN:
4437       Feld[x][y] = EL_SPACESHIP;
4438       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4439       break;
4440
4441     case EL_BD_BUTTERFLY_RIGHT:
4442     case EL_BD_BUTTERFLY_UP:
4443     case EL_BD_BUTTERFLY_LEFT:
4444     case EL_BD_BUTTERFLY_DOWN:
4445       Feld[x][y] = EL_BD_BUTTERFLY;
4446       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4447       break;
4448
4449     case EL_BD_FIREFLY_RIGHT:
4450     case EL_BD_FIREFLY_UP:
4451     case EL_BD_FIREFLY_LEFT:
4452     case EL_BD_FIREFLY_DOWN:
4453       Feld[x][y] = EL_BD_FIREFLY;
4454       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4455       break;
4456
4457     case EL_PACMAN_RIGHT:
4458     case EL_PACMAN_UP:
4459     case EL_PACMAN_LEFT:
4460     case EL_PACMAN_DOWN:
4461       Feld[x][y] = EL_PACMAN;
4462       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4463       break;
4464
4465     case EL_YAMYAM_LEFT:
4466     case EL_YAMYAM_RIGHT:
4467     case EL_YAMYAM_UP:
4468     case EL_YAMYAM_DOWN:
4469       Feld[x][y] = EL_YAMYAM;
4470       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4471       break;
4472
4473     case EL_SP_SNIKSNAK:
4474       MovDir[x][y] = MV_UP;
4475       break;
4476
4477     case EL_SP_ELECTRON:
4478       MovDir[x][y] = MV_LEFT;
4479       break;
4480
4481     case EL_MOLE_LEFT:
4482     case EL_MOLE_RIGHT:
4483     case EL_MOLE_UP:
4484     case EL_MOLE_DOWN:
4485       Feld[x][y] = EL_MOLE;
4486       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4487       break;
4488
4489     default:
4490       if (IS_CUSTOM_ELEMENT(element))
4491       {
4492         struct ElementInfo *ei = &element_info[element];
4493         int move_direction_initial = ei->move_direction_initial;
4494         int move_pattern = ei->move_pattern;
4495
4496         if (move_direction_initial == MV_START_PREVIOUS)
4497         {
4498           if (MovDir[x][y] != MV_NONE)
4499             return;
4500
4501           move_direction_initial = MV_START_AUTOMATIC;
4502         }
4503
4504         if (move_direction_initial == MV_START_RANDOM)
4505           MovDir[x][y] = 1 << RND(4);
4506         else if (move_direction_initial & MV_ANY_DIRECTION)
4507           MovDir[x][y] = move_direction_initial;
4508         else if (move_pattern == MV_ALL_DIRECTIONS ||
4509                  move_pattern == MV_TURNING_LEFT ||
4510                  move_pattern == MV_TURNING_RIGHT ||
4511                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4512                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4513                  move_pattern == MV_TURNING_RANDOM)
4514           MovDir[x][y] = 1 << RND(4);
4515         else if (move_pattern == MV_HORIZONTAL)
4516           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4517         else if (move_pattern == MV_VERTICAL)
4518           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4519         else if (move_pattern & MV_ANY_DIRECTION)
4520           MovDir[x][y] = element_info[element].move_pattern;
4521         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4522                  move_pattern == MV_ALONG_RIGHT_SIDE)
4523         {
4524           // use random direction as default start direction
4525           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4526             MovDir[x][y] = 1 << RND(4);
4527
4528           for (i = 0; i < NUM_DIRECTIONS; i++)
4529           {
4530             int x1 = x + xy[i][0];
4531             int y1 = y + xy[i][1];
4532
4533             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4534             {
4535               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4536                 MovDir[x][y] = direction[0][i];
4537               else
4538                 MovDir[x][y] = direction[1][i];
4539
4540               break;
4541             }
4542           }
4543         }                
4544       }
4545       else
4546       {
4547         MovDir[x][y] = 1 << RND(4);
4548
4549         if (element != EL_BUG &&
4550             element != EL_SPACESHIP &&
4551             element != EL_BD_BUTTERFLY &&
4552             element != EL_BD_FIREFLY)
4553           break;
4554
4555         for (i = 0; i < NUM_DIRECTIONS; i++)
4556         {
4557           int x1 = x + xy[i][0];
4558           int y1 = y + xy[i][1];
4559
4560           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4561           {
4562             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4563             {
4564               MovDir[x][y] = direction[0][i];
4565               break;
4566             }
4567             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4568                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4569             {
4570               MovDir[x][y] = direction[1][i];
4571               break;
4572             }
4573           }
4574         }
4575       }
4576       break;
4577   }
4578
4579   GfxDir[x][y] = MovDir[x][y];
4580 }
4581
4582 void InitAmoebaNr(int x, int y)
4583 {
4584   int i;
4585   int group_nr = AmoebeNachbarNr(x, y);
4586
4587   if (group_nr == 0)
4588   {
4589     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4590     {
4591       if (AmoebaCnt[i] == 0)
4592       {
4593         group_nr = i;
4594         break;
4595       }
4596     }
4597   }
4598
4599   AmoebaNr[x][y] = group_nr;
4600   AmoebaCnt[group_nr]++;
4601   AmoebaCnt2[group_nr]++;
4602 }
4603
4604 static void LevelSolved(void)
4605 {
4606   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4607       game.players_still_needed > 0)
4608     return;
4609
4610   game.LevelSolved = TRUE;
4611   game.GameOver = TRUE;
4612
4613   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4614                       game_em.lev->score :
4615                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4616                       game_mm.score :
4617                       game.score);
4618   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4619                        MM_HEALTH(game_mm.laser_overload_value) :
4620                        game.health);
4621
4622   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4623   game.LevelSolved_CountingScore = game.score_final;
4624   game.LevelSolved_CountingHealth = game.health_final;
4625 }
4626
4627 void GameWon(void)
4628 {
4629   static int time_count_steps;
4630   static int time, time_final;
4631   static int score, score_final;
4632   static int health, health_final;
4633   static int game_over_delay_1 = 0;
4634   static int game_over_delay_2 = 0;
4635   static int game_over_delay_3 = 0;
4636   int game_over_delay_value_1 = 50;
4637   int game_over_delay_value_2 = 25;
4638   int game_over_delay_value_3 = 50;
4639
4640   if (!game.LevelSolved_GameWon)
4641   {
4642     int i;
4643
4644     // do not start end game actions before the player stops moving (to exit)
4645     if (local_player->active && local_player->MovPos)
4646       return;
4647
4648     game.LevelSolved_GameWon = TRUE;
4649     game.LevelSolved_SaveTape = tape.recording;
4650     game.LevelSolved_SaveScore = !tape.playing;
4651
4652     if (!tape.playing)
4653     {
4654       LevelStats_incSolved(level_nr);
4655
4656       SaveLevelSetup_SeriesInfo();
4657     }
4658
4659     if (tape.auto_play)         // tape might already be stopped here
4660       tape.auto_play_level_solved = TRUE;
4661
4662     TapeStop();
4663
4664     game_over_delay_1 = 0;
4665     game_over_delay_2 = 0;
4666     game_over_delay_3 = game_over_delay_value_3;
4667
4668     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4669     score = score_final = game.score_final;
4670     health = health_final = game.health_final;
4671
4672     if (level.score[SC_TIME_BONUS] > 0)
4673     {
4674       if (TimeLeft > 0)
4675       {
4676         time_final = 0;
4677         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4678       }
4679       else if (game.no_time_limit && TimePlayed < 999)
4680       {
4681         time_final = 999;
4682         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4683       }
4684
4685       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4686
4687       game_over_delay_1 = game_over_delay_value_1;
4688
4689       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4690       {
4691         health_final = 0;
4692         score_final += health * level.score[SC_TIME_BONUS];
4693
4694         game_over_delay_2 = game_over_delay_value_2;
4695       }
4696
4697       game.score_final = score_final;
4698       game.health_final = health_final;
4699     }
4700
4701     if (level_editor_test_game)
4702     {
4703       time = time_final;
4704       score = score_final;
4705
4706       game.LevelSolved_CountingTime = time;
4707       game.LevelSolved_CountingScore = score;
4708
4709       game_panel_controls[GAME_PANEL_TIME].value = time;
4710       game_panel_controls[GAME_PANEL_SCORE].value = score;
4711
4712       DisplayGameControlValues();
4713     }
4714
4715     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4716     {
4717       // check if last player has left the level
4718       if (game.exit_x >= 0 &&
4719           game.exit_y >= 0)
4720       {
4721         int x = game.exit_x;
4722         int y = game.exit_y;
4723         int element = Feld[x][y];
4724
4725         // close exit door after last player
4726         if ((game.all_players_gone &&
4727              (element == EL_EXIT_OPEN ||
4728               element == EL_SP_EXIT_OPEN ||
4729               element == EL_STEEL_EXIT_OPEN)) ||
4730             element == EL_EM_EXIT_OPEN ||
4731             element == EL_EM_STEEL_EXIT_OPEN)
4732         {
4733
4734           Feld[x][y] =
4735             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4736              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4737              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4738              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4739              EL_EM_STEEL_EXIT_CLOSING);
4740
4741           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4742         }
4743
4744         // player disappears
4745         DrawLevelField(x, y);
4746       }
4747
4748       for (i = 0; i < MAX_PLAYERS; i++)
4749       {
4750         struct PlayerInfo *player = &stored_player[i];
4751
4752         if (player->present)
4753         {
4754           RemovePlayer(player);
4755
4756           // player disappears
4757           DrawLevelField(player->jx, player->jy);
4758         }
4759       }
4760     }
4761
4762     PlaySound(SND_GAME_WINNING);
4763   }
4764
4765   if (game_over_delay_1 > 0)
4766   {
4767     game_over_delay_1--;
4768
4769     return;
4770   }
4771
4772   if (time != time_final)
4773   {
4774     int time_to_go = ABS(time_final - time);
4775     int time_count_dir = (time < time_final ? +1 : -1);
4776
4777     if (time_to_go < time_count_steps)
4778       time_count_steps = 1;
4779
4780     time  += time_count_steps * time_count_dir;
4781     score += time_count_steps * level.score[SC_TIME_BONUS];
4782
4783     game.LevelSolved_CountingTime = time;
4784     game.LevelSolved_CountingScore = score;
4785
4786     game_panel_controls[GAME_PANEL_TIME].value = time;
4787     game_panel_controls[GAME_PANEL_SCORE].value = score;
4788
4789     DisplayGameControlValues();
4790
4791     if (time == time_final)
4792       StopSound(SND_GAME_LEVELTIME_BONUS);
4793     else if (setup.sound_loops)
4794       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4795     else
4796       PlaySound(SND_GAME_LEVELTIME_BONUS);
4797
4798     return;
4799   }
4800
4801   if (game_over_delay_2 > 0)
4802   {
4803     game_over_delay_2--;
4804
4805     return;
4806   }
4807
4808   if (health != health_final)
4809   {
4810     int health_count_dir = (health < health_final ? +1 : -1);
4811
4812     health += health_count_dir;
4813     score  += level.score[SC_TIME_BONUS];
4814
4815     game.LevelSolved_CountingHealth = health;
4816     game.LevelSolved_CountingScore = score;
4817
4818     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4819     game_panel_controls[GAME_PANEL_SCORE].value = score;
4820
4821     DisplayGameControlValues();
4822
4823     if (health == health_final)
4824       StopSound(SND_GAME_LEVELTIME_BONUS);
4825     else if (setup.sound_loops)
4826       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4827     else
4828       PlaySound(SND_GAME_LEVELTIME_BONUS);
4829
4830     return;
4831   }
4832
4833   game.panel.active = FALSE;
4834
4835   if (game_over_delay_3 > 0)
4836   {
4837     game_over_delay_3--;
4838
4839     return;
4840   }
4841
4842   GameEnd();
4843 }
4844
4845 void GameEnd(void)
4846 {
4847   // used instead of "level_nr" (needed for network games)
4848   int last_level_nr = levelset.level_nr;
4849   int hi_pos;
4850
4851   game.LevelSolved_GameEnd = TRUE;
4852
4853   if (game.LevelSolved_SaveTape)
4854   {
4855     // make sure that request dialog to save tape does not open door again
4856     if (!global.use_envelope_request)
4857       CloseDoor(DOOR_CLOSE_1);
4858
4859     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4860   }
4861
4862   // if no tape is to be saved, close both doors simultaneously
4863   CloseDoor(DOOR_CLOSE_ALL);
4864
4865   if (level_editor_test_game)
4866   {
4867     SetGameStatus(GAME_MODE_MAIN);
4868
4869     DrawMainMenu();
4870
4871     return;
4872   }
4873
4874   if (!game.LevelSolved_SaveScore)
4875   {
4876     SetGameStatus(GAME_MODE_MAIN);
4877
4878     DrawMainMenu();
4879
4880     return;
4881   }
4882
4883   if (level_nr == leveldir_current->handicap_level)
4884   {
4885     leveldir_current->handicap_level++;
4886
4887     SaveLevelSetup_SeriesInfo();
4888   }
4889
4890   if (setup.increment_levels &&
4891       level_nr < leveldir_current->last_level &&
4892       !network_playing)
4893   {
4894     level_nr++;         // advance to next level
4895     TapeErase();        // start with empty tape
4896
4897     if (setup.auto_play_next_level)
4898     {
4899       LoadLevel(level_nr);
4900
4901       SaveLevelSetup_SeriesInfo();
4902     }
4903   }
4904
4905   hi_pos = NewHiScore(last_level_nr);
4906
4907   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4908   {
4909     SetGameStatus(GAME_MODE_SCORES);
4910
4911     DrawHallOfFame(last_level_nr, hi_pos);
4912   }
4913   else if (setup.auto_play_next_level && setup.increment_levels &&
4914            last_level_nr < leveldir_current->last_level &&
4915            !network_playing)
4916   {
4917     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4918   }
4919   else
4920   {
4921     SetGameStatus(GAME_MODE_MAIN);
4922
4923     DrawMainMenu();
4924   }
4925 }
4926
4927 int NewHiScore(int level_nr)
4928 {
4929   int k, l;
4930   int position = -1;
4931   boolean one_score_entry_per_name = !program.many_scores_per_name;
4932
4933   LoadScore(level_nr);
4934
4935   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4936       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4937     return -1;
4938
4939   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4940   {
4941     if (game.score_final > highscore[k].Score)
4942     {
4943       // player has made it to the hall of fame
4944
4945       if (k < MAX_SCORE_ENTRIES - 1)
4946       {
4947         int m = MAX_SCORE_ENTRIES - 1;
4948
4949         if (one_score_entry_per_name)
4950         {
4951           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4952             if (strEqual(setup.player_name, highscore[l].Name))
4953               m = l;
4954
4955           if (m == k)   // player's new highscore overwrites his old one
4956             goto put_into_list;
4957         }
4958
4959         for (l = m; l > k; l--)
4960         {
4961           strcpy(highscore[l].Name, highscore[l - 1].Name);
4962           highscore[l].Score = highscore[l - 1].Score;
4963         }
4964       }
4965
4966       put_into_list:
4967
4968       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4969       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4970       highscore[k].Score = game.score_final;
4971       position = k;
4972
4973       break;
4974     }
4975     else if (one_score_entry_per_name &&
4976              !strncmp(setup.player_name, highscore[k].Name,
4977                       MAX_PLAYER_NAME_LEN))
4978       break;    // player already there with a higher score
4979   }
4980
4981   if (position >= 0) 
4982     SaveScore(level_nr);
4983
4984   return position;
4985 }
4986
4987 static int getElementMoveStepsizeExt(int x, int y, int direction)
4988 {
4989   int element = Feld[x][y];
4990   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4991   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4992   int horiz_move = (dx != 0);
4993   int sign = (horiz_move ? dx : dy);
4994   int step = sign * element_info[element].move_stepsize;
4995
4996   // special values for move stepsize for spring and things on conveyor belt
4997   if (horiz_move)
4998   {
4999     if (CAN_FALL(element) &&
5000         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5001       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5002     else if (element == EL_SPRING)
5003       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5004   }
5005
5006   return step;
5007 }
5008
5009 static int getElementMoveStepsize(int x, int y)
5010 {
5011   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5012 }
5013
5014 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5015 {
5016   if (player->GfxAction != action || player->GfxDir != dir)
5017   {
5018     player->GfxAction = action;
5019     player->GfxDir = dir;
5020     player->Frame = 0;
5021     player->StepFrame = 0;
5022   }
5023 }
5024
5025 static void ResetGfxFrame(int x, int y)
5026 {
5027   // profiling showed that "autotest" spends 10~20% of its time in this function
5028   if (DrawingDeactivatedField())
5029     return;
5030
5031   int element = Feld[x][y];
5032   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5033
5034   if (graphic_info[graphic].anim_global_sync)
5035     GfxFrame[x][y] = FrameCounter;
5036   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5037     GfxFrame[x][y] = CustomValue[x][y];
5038   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5039     GfxFrame[x][y] = element_info[element].collect_score;
5040   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5041     GfxFrame[x][y] = ChangeDelay[x][y];
5042 }
5043
5044 static void ResetGfxAnimation(int x, int y)
5045 {
5046   GfxAction[x][y] = ACTION_DEFAULT;
5047   GfxDir[x][y] = MovDir[x][y];
5048   GfxFrame[x][y] = 0;
5049
5050   ResetGfxFrame(x, y);
5051 }
5052
5053 static void ResetRandomAnimationValue(int x, int y)
5054 {
5055   GfxRandom[x][y] = INIT_GFX_RANDOM();
5056 }
5057
5058 static void InitMovingField(int x, int y, int direction)
5059 {
5060   int element = Feld[x][y];
5061   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5062   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5063   int newx = x + dx;
5064   int newy = y + dy;
5065   boolean is_moving_before, is_moving_after;
5066
5067   // check if element was/is moving or being moved before/after mode change
5068   is_moving_before = (WasJustMoving[x][y] != 0);
5069   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5070
5071   // reset animation only for moving elements which change direction of moving
5072   // or which just started or stopped moving
5073   // (else CEs with property "can move" / "not moving" are reset each frame)
5074   if (is_moving_before != is_moving_after ||
5075       direction != MovDir[x][y])
5076     ResetGfxAnimation(x, y);
5077
5078   MovDir[x][y] = direction;
5079   GfxDir[x][y] = direction;
5080
5081   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5082                      direction == MV_DOWN && CAN_FALL(element) ?
5083                      ACTION_FALLING : ACTION_MOVING);
5084
5085   // this is needed for CEs with property "can move" / "not moving"
5086
5087   if (is_moving_after)
5088   {
5089     if (Feld[newx][newy] == EL_EMPTY)
5090       Feld[newx][newy] = EL_BLOCKED;
5091
5092     MovDir[newx][newy] = MovDir[x][y];
5093
5094     CustomValue[newx][newy] = CustomValue[x][y];
5095
5096     GfxFrame[newx][newy] = GfxFrame[x][y];
5097     GfxRandom[newx][newy] = GfxRandom[x][y];
5098     GfxAction[newx][newy] = GfxAction[x][y];
5099     GfxDir[newx][newy] = GfxDir[x][y];
5100   }
5101 }
5102
5103 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5104 {
5105   int direction = MovDir[x][y];
5106   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5107   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5108
5109   *goes_to_x = newx;
5110   *goes_to_y = newy;
5111 }
5112
5113 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5114 {
5115   int oldx = x, oldy = y;
5116   int direction = MovDir[x][y];
5117
5118   if (direction == MV_LEFT)
5119     oldx++;
5120   else if (direction == MV_RIGHT)
5121     oldx--;
5122   else if (direction == MV_UP)
5123     oldy++;
5124   else if (direction == MV_DOWN)
5125     oldy--;
5126
5127   *comes_from_x = oldx;
5128   *comes_from_y = oldy;
5129 }
5130
5131 static int MovingOrBlocked2Element(int x, int y)
5132 {
5133   int element = Feld[x][y];
5134
5135   if (element == EL_BLOCKED)
5136   {
5137     int oldx, oldy;
5138
5139     Blocked2Moving(x, y, &oldx, &oldy);
5140     return Feld[oldx][oldy];
5141   }
5142   else
5143     return element;
5144 }
5145
5146 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5147 {
5148   // like MovingOrBlocked2Element(), but if element is moving
5149   // and (x,y) is the field the moving element is just leaving,
5150   // return EL_BLOCKED instead of the element value
5151   int element = Feld[x][y];
5152
5153   if (IS_MOVING(x, y))
5154   {
5155     if (element == EL_BLOCKED)
5156     {
5157       int oldx, oldy;
5158
5159       Blocked2Moving(x, y, &oldx, &oldy);
5160       return Feld[oldx][oldy];
5161     }
5162     else
5163       return EL_BLOCKED;
5164   }
5165   else
5166     return element;
5167 }
5168
5169 static void RemoveField(int x, int y)
5170 {
5171   Feld[x][y] = EL_EMPTY;
5172
5173   MovPos[x][y] = 0;
5174   MovDir[x][y] = 0;
5175   MovDelay[x][y] = 0;
5176
5177   CustomValue[x][y] = 0;
5178
5179   AmoebaNr[x][y] = 0;
5180   ChangeDelay[x][y] = 0;
5181   ChangePage[x][y] = -1;
5182   Pushed[x][y] = FALSE;
5183
5184   GfxElement[x][y] = EL_UNDEFINED;
5185   GfxAction[x][y] = ACTION_DEFAULT;
5186   GfxDir[x][y] = MV_NONE;
5187 }
5188
5189 static void RemoveMovingField(int x, int y)
5190 {
5191   int oldx = x, oldy = y, newx = x, newy = y;
5192   int element = Feld[x][y];
5193   int next_element = EL_UNDEFINED;
5194
5195   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5196     return;
5197
5198   if (IS_MOVING(x, y))
5199   {
5200     Moving2Blocked(x, y, &newx, &newy);
5201
5202     if (Feld[newx][newy] != EL_BLOCKED)
5203     {
5204       // element is moving, but target field is not free (blocked), but
5205       // already occupied by something different (example: acid pool);
5206       // in this case, only remove the moving field, but not the target
5207
5208       RemoveField(oldx, oldy);
5209
5210       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5211
5212       TEST_DrawLevelField(oldx, oldy);
5213
5214       return;
5215     }
5216   }
5217   else if (element == EL_BLOCKED)
5218   {
5219     Blocked2Moving(x, y, &oldx, &oldy);
5220     if (!IS_MOVING(oldx, oldy))
5221       return;
5222   }
5223
5224   if (element == EL_BLOCKED &&
5225       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5226        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5227        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5228        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5229        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5230        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5231     next_element = get_next_element(Feld[oldx][oldy]);
5232
5233   RemoveField(oldx, oldy);
5234   RemoveField(newx, newy);
5235
5236   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5237
5238   if (next_element != EL_UNDEFINED)
5239     Feld[oldx][oldy] = next_element;
5240
5241   TEST_DrawLevelField(oldx, oldy);
5242   TEST_DrawLevelField(newx, newy);
5243 }
5244
5245 void DrawDynamite(int x, int y)
5246 {
5247   int sx = SCREENX(x), sy = SCREENY(y);
5248   int graphic = el2img(Feld[x][y]);
5249   int frame;
5250
5251   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5252     return;
5253
5254   if (IS_WALKABLE_INSIDE(Back[x][y]))
5255     return;
5256
5257   if (Back[x][y])
5258     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5259   else if (Store[x][y])
5260     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5261
5262   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5263
5264   if (Back[x][y] || Store[x][y])
5265     DrawGraphicThruMask(sx, sy, graphic, frame);
5266   else
5267     DrawGraphic(sx, sy, graphic, frame);
5268 }
5269
5270 static void CheckDynamite(int x, int y)
5271 {
5272   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5273   {
5274     MovDelay[x][y]--;
5275
5276     if (MovDelay[x][y] != 0)
5277     {
5278       DrawDynamite(x, y);
5279       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5280
5281       return;
5282     }
5283   }
5284
5285   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5286
5287   Bang(x, y);
5288 }
5289
5290 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5291 {
5292   boolean num_checked_players = 0;
5293   int i;
5294
5295   for (i = 0; i < MAX_PLAYERS; i++)
5296   {
5297     if (stored_player[i].active)
5298     {
5299       int sx = stored_player[i].jx;
5300       int sy = stored_player[i].jy;
5301
5302       if (num_checked_players == 0)
5303       {
5304         *sx1 = *sx2 = sx;
5305         *sy1 = *sy2 = sy;
5306       }
5307       else
5308       {
5309         *sx1 = MIN(*sx1, sx);
5310         *sy1 = MIN(*sy1, sy);
5311         *sx2 = MAX(*sx2, sx);
5312         *sy2 = MAX(*sy2, sy);
5313       }
5314
5315       num_checked_players++;
5316     }
5317   }
5318 }
5319
5320 static boolean checkIfAllPlayersFitToScreen_RND(void)
5321 {
5322   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5323
5324   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5325
5326   return (sx2 - sx1 < SCR_FIELDX &&
5327           sy2 - sy1 < SCR_FIELDY);
5328 }
5329
5330 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5331 {
5332   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5333
5334   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5335
5336   *sx = (sx1 + sx2) / 2;
5337   *sy = (sy1 + sy2) / 2;
5338 }
5339
5340 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5341                                boolean center_screen, boolean quick_relocation)
5342 {
5343   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5344   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5345   boolean no_delay = (tape.warp_forward);
5346   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5347   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5348   int new_scroll_x, new_scroll_y;
5349
5350   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5351   {
5352     // case 1: quick relocation inside visible screen (without scrolling)
5353
5354     RedrawPlayfield();
5355
5356     return;
5357   }
5358
5359   if (!level.shifted_relocation || center_screen)
5360   {
5361     // relocation _with_ centering of screen
5362
5363     new_scroll_x = SCROLL_POSITION_X(x);
5364     new_scroll_y = SCROLL_POSITION_Y(y);
5365   }
5366   else
5367   {
5368     // relocation _without_ centering of screen
5369
5370     int center_scroll_x = SCROLL_POSITION_X(old_x);
5371     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5372     int offset_x = x + (scroll_x - center_scroll_x);
5373     int offset_y = y + (scroll_y - center_scroll_y);
5374
5375     // for new screen position, apply previous offset to center position
5376     new_scroll_x = SCROLL_POSITION_X(offset_x);
5377     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5378   }
5379
5380   if (quick_relocation)
5381   {
5382     // case 2: quick relocation (redraw without visible scrolling)
5383
5384     scroll_x = new_scroll_x;
5385     scroll_y = new_scroll_y;
5386
5387     RedrawPlayfield();
5388
5389     return;
5390   }
5391
5392   // case 3: visible relocation (with scrolling to new position)
5393
5394   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5395
5396   SetVideoFrameDelay(wait_delay_value);
5397
5398   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5399   {
5400     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5401     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5402
5403     if (dx == 0 && dy == 0)             // no scrolling needed at all
5404       break;
5405
5406     scroll_x -= dx;
5407     scroll_y -= dy;
5408
5409     // set values for horizontal/vertical screen scrolling (half tile size)
5410     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5411     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5412     int pos_x = dx * TILEX / 2;
5413     int pos_y = dy * TILEY / 2;
5414     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5415     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5416
5417     ScrollLevel(dx, dy);
5418     DrawAllPlayers();
5419
5420     // scroll in two steps of half tile size to make things smoother
5421     BlitScreenToBitmapExt_RND(window, fx, fy);
5422
5423     // scroll second step to align at full tile size
5424     BlitScreenToBitmap(window);
5425   }
5426
5427   DrawAllPlayers();
5428   BackToFront();
5429
5430   SetVideoFrameDelay(frame_delay_value_old);
5431 }
5432
5433 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5434 {
5435   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5436   int player_nr = GET_PLAYER_NR(el_player);
5437   struct PlayerInfo *player = &stored_player[player_nr];
5438   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5439   boolean no_delay = (tape.warp_forward);
5440   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5441   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5442   int old_jx = player->jx;
5443   int old_jy = player->jy;
5444   int old_element = Feld[old_jx][old_jy];
5445   int element = Feld[jx][jy];
5446   boolean player_relocated = (old_jx != jx || old_jy != jy);
5447
5448   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5449   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5450   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5451   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5452   int leave_side_horiz = move_dir_horiz;
5453   int leave_side_vert  = move_dir_vert;
5454   int enter_side = enter_side_horiz | enter_side_vert;
5455   int leave_side = leave_side_horiz | leave_side_vert;
5456
5457   if (player->buried)           // do not reanimate dead player
5458     return;
5459
5460   if (!player_relocated)        // no need to relocate the player
5461     return;
5462
5463   if (IS_PLAYER(jx, jy))        // player already placed at new position
5464   {
5465     RemoveField(jx, jy);        // temporarily remove newly placed player
5466     DrawLevelField(jx, jy);
5467   }
5468
5469   if (player->present)
5470   {
5471     while (player->MovPos)
5472     {
5473       ScrollPlayer(player, SCROLL_GO_ON);
5474       ScrollScreen(NULL, SCROLL_GO_ON);
5475
5476       AdvanceFrameAndPlayerCounters(player->index_nr);
5477
5478       DrawPlayer(player);
5479
5480       BackToFront_WithFrameDelay(wait_delay_value);
5481     }
5482
5483     DrawPlayer(player);         // needed here only to cleanup last field
5484     DrawLevelField(player->jx, player->jy);     // remove player graphic
5485
5486     player->is_moving = FALSE;
5487   }
5488
5489   if (IS_CUSTOM_ELEMENT(old_element))
5490     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5491                                CE_LEFT_BY_PLAYER,
5492                                player->index_bit, leave_side);
5493
5494   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5495                                       CE_PLAYER_LEAVES_X,
5496                                       player->index_bit, leave_side);
5497
5498   Feld[jx][jy] = el_player;
5499   InitPlayerField(jx, jy, el_player, TRUE);
5500
5501   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5502      possible that the relocation target field did not contain a player element,
5503      but a walkable element, to which the new player was relocated -- in this
5504      case, restore that (already initialized!) element on the player field */
5505   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5506   {
5507     Feld[jx][jy] = element;     // restore previously existing element
5508   }
5509
5510   // only visually relocate centered player
5511   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5512                      FALSE, level.instant_relocation);
5513
5514   TestIfPlayerTouchesBadThing(jx, jy);
5515   TestIfPlayerTouchesCustomElement(jx, jy);
5516
5517   if (IS_CUSTOM_ELEMENT(element))
5518     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5519                                player->index_bit, enter_side);
5520
5521   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5522                                       player->index_bit, enter_side);
5523
5524   if (player->is_switching)
5525   {
5526     /* ensure that relocation while still switching an element does not cause
5527        a new element to be treated as also switched directly after relocation
5528        (this is important for teleporter switches that teleport the player to
5529        a place where another teleporter switch is in the same direction, which
5530        would then incorrectly be treated as immediately switched before the
5531        direction key that caused the switch was released) */
5532
5533     player->switch_x += jx - old_jx;
5534     player->switch_y += jy - old_jy;
5535   }
5536 }
5537
5538 static void Explode(int ex, int ey, int phase, int mode)
5539 {
5540   int x, y;
5541   int last_phase;
5542   int border_element;
5543
5544   // !!! eliminate this variable !!!
5545   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5546
5547   if (game.explosions_delayed)
5548   {
5549     ExplodeField[ex][ey] = mode;
5550     return;
5551   }
5552
5553   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5554   {
5555     int center_element = Feld[ex][ey];
5556     int artwork_element, explosion_element;     // set these values later
5557
5558     // remove things displayed in background while burning dynamite
5559     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5560       Back[ex][ey] = 0;
5561
5562     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5563     {
5564       // put moving element to center field (and let it explode there)
5565       center_element = MovingOrBlocked2Element(ex, ey);
5566       RemoveMovingField(ex, ey);
5567       Feld[ex][ey] = center_element;
5568     }
5569
5570     // now "center_element" is finally determined -- set related values now
5571     artwork_element = center_element;           // for custom player artwork
5572     explosion_element = center_element;         // for custom player artwork
5573
5574     if (IS_PLAYER(ex, ey))
5575     {
5576       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5577
5578       artwork_element = stored_player[player_nr].artwork_element;
5579
5580       if (level.use_explosion_element[player_nr])
5581       {
5582         explosion_element = level.explosion_element[player_nr];
5583         artwork_element = explosion_element;
5584       }
5585     }
5586
5587     if (mode == EX_TYPE_NORMAL ||
5588         mode == EX_TYPE_CENTER ||
5589         mode == EX_TYPE_CROSS)
5590       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5591
5592     last_phase = element_info[explosion_element].explosion_delay + 1;
5593
5594     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5595     {
5596       int xx = x - ex + 1;
5597       int yy = y - ey + 1;
5598       int element;
5599
5600       if (!IN_LEV_FIELD(x, y) ||
5601           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5602           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5603         continue;
5604
5605       element = Feld[x][y];
5606
5607       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5608       {
5609         element = MovingOrBlocked2Element(x, y);
5610
5611         if (!IS_EXPLOSION_PROOF(element))
5612           RemoveMovingField(x, y);
5613       }
5614
5615       // indestructible elements can only explode in center (but not flames)
5616       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5617                                            mode == EX_TYPE_BORDER)) ||
5618           element == EL_FLAMES)
5619         continue;
5620
5621       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5622          behaviour, for example when touching a yamyam that explodes to rocks
5623          with active deadly shield, a rock is created under the player !!! */
5624       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5625 #if 0
5626       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5627           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5628            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5629 #else
5630       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5631 #endif
5632       {
5633         if (IS_ACTIVE_BOMB(element))
5634         {
5635           // re-activate things under the bomb like gate or penguin
5636           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5637           Back[x][y] = 0;
5638         }
5639
5640         continue;
5641       }
5642
5643       // save walkable background elements while explosion on same tile
5644       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5645           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5646         Back[x][y] = element;
5647
5648       // ignite explodable elements reached by other explosion
5649       if (element == EL_EXPLOSION)
5650         element = Store2[x][y];
5651
5652       if (AmoebaNr[x][y] &&
5653           (element == EL_AMOEBA_FULL ||
5654            element == EL_BD_AMOEBA ||
5655            element == EL_AMOEBA_GROWING))
5656       {
5657         AmoebaCnt[AmoebaNr[x][y]]--;
5658         AmoebaCnt2[AmoebaNr[x][y]]--;
5659       }
5660
5661       RemoveField(x, y);
5662
5663       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5664       {
5665         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5666
5667         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5668
5669         if (PLAYERINFO(ex, ey)->use_murphy)
5670           Store[x][y] = EL_EMPTY;
5671       }
5672
5673       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5674       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5675       else if (ELEM_IS_PLAYER(center_element))
5676         Store[x][y] = EL_EMPTY;
5677       else if (center_element == EL_YAMYAM)
5678         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5679       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5680         Store[x][y] = element_info[center_element].content.e[xx][yy];
5681 #if 1
5682       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5683       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5684       // otherwise) -- FIX THIS !!!
5685       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5686         Store[x][y] = element_info[element].content.e[1][1];
5687 #else
5688       else if (!CAN_EXPLODE(element))
5689         Store[x][y] = element_info[element].content.e[1][1];
5690 #endif
5691       else
5692         Store[x][y] = EL_EMPTY;
5693
5694       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5695           center_element == EL_AMOEBA_TO_DIAMOND)
5696         Store2[x][y] = element;
5697
5698       Feld[x][y] = EL_EXPLOSION;
5699       GfxElement[x][y] = artwork_element;
5700
5701       ExplodePhase[x][y] = 1;
5702       ExplodeDelay[x][y] = last_phase;
5703
5704       Stop[x][y] = TRUE;
5705     }
5706
5707     if (center_element == EL_YAMYAM)
5708       game.yamyam_content_nr =
5709         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5710
5711     return;
5712   }
5713
5714   if (Stop[ex][ey])
5715     return;
5716
5717   x = ex;
5718   y = ey;
5719
5720   if (phase == 1)
5721     GfxFrame[x][y] = 0;         // restart explosion animation
5722
5723   last_phase = ExplodeDelay[x][y];
5724
5725   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5726
5727   // this can happen if the player leaves an explosion just in time
5728   if (GfxElement[x][y] == EL_UNDEFINED)
5729     GfxElement[x][y] = EL_EMPTY;
5730
5731   border_element = Store2[x][y];
5732   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5733     border_element = StorePlayer[x][y];
5734
5735   if (phase == element_info[border_element].ignition_delay ||
5736       phase == last_phase)
5737   {
5738     boolean border_explosion = FALSE;
5739
5740     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5741         !PLAYER_EXPLOSION_PROTECTED(x, y))
5742     {
5743       KillPlayerUnlessExplosionProtected(x, y);
5744       border_explosion = TRUE;
5745     }
5746     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5747     {
5748       Feld[x][y] = Store2[x][y];
5749       Store2[x][y] = 0;
5750       Bang(x, y);
5751       border_explosion = TRUE;
5752     }
5753     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5754     {
5755       AmoebeUmwandeln(x, y);
5756       Store2[x][y] = 0;
5757       border_explosion = TRUE;
5758     }
5759
5760     // if an element just explodes due to another explosion (chain-reaction),
5761     // do not immediately end the new explosion when it was the last frame of
5762     // the explosion (as it would be done in the following "if"-statement!)
5763     if (border_explosion && phase == last_phase)
5764       return;
5765   }
5766
5767   if (phase == last_phase)
5768   {
5769     int element;
5770
5771     element = Feld[x][y] = Store[x][y];
5772     Store[x][y] = Store2[x][y] = 0;
5773     GfxElement[x][y] = EL_UNDEFINED;
5774
5775     // player can escape from explosions and might therefore be still alive
5776     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5777         element <= EL_PLAYER_IS_EXPLODING_4)
5778     {
5779       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5780       int explosion_element = EL_PLAYER_1 + player_nr;
5781       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5782       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5783
5784       if (level.use_explosion_element[player_nr])
5785         explosion_element = level.explosion_element[player_nr];
5786
5787       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5788                     element_info[explosion_element].content.e[xx][yy]);
5789     }
5790
5791     // restore probably existing indestructible background element
5792     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5793       element = Feld[x][y] = Back[x][y];
5794     Back[x][y] = 0;
5795
5796     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5797     GfxDir[x][y] = MV_NONE;
5798     ChangeDelay[x][y] = 0;
5799     ChangePage[x][y] = -1;
5800
5801     CustomValue[x][y] = 0;
5802
5803     InitField_WithBug2(x, y, FALSE);
5804
5805     TEST_DrawLevelField(x, y);
5806
5807     TestIfElementTouchesCustomElement(x, y);
5808
5809     if (GFX_CRUMBLED(element))
5810       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5811
5812     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5813       StorePlayer[x][y] = 0;
5814
5815     if (ELEM_IS_PLAYER(element))
5816       RelocatePlayer(x, y, element);
5817   }
5818   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5819   {
5820     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5821     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5822
5823     if (phase == delay)
5824       TEST_DrawLevelFieldCrumbled(x, y);
5825
5826     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5827     {
5828       DrawLevelElement(x, y, Back[x][y]);
5829       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5830     }
5831     else if (IS_WALKABLE_UNDER(Back[x][y]))
5832     {
5833       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5834       DrawLevelElementThruMask(x, y, Back[x][y]);
5835     }
5836     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5837       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5838   }
5839 }
5840
5841 static void DynaExplode(int ex, int ey)
5842 {
5843   int i, j;
5844   int dynabomb_element = Feld[ex][ey];
5845   int dynabomb_size = 1;
5846   boolean dynabomb_xl = FALSE;
5847   struct PlayerInfo *player;
5848   static int xy[4][2] =
5849   {
5850     { 0, -1 },
5851     { -1, 0 },
5852     { +1, 0 },
5853     { 0, +1 }
5854   };
5855
5856   if (IS_ACTIVE_BOMB(dynabomb_element))
5857   {
5858     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5859     dynabomb_size = player->dynabomb_size;
5860     dynabomb_xl = player->dynabomb_xl;
5861     player->dynabombs_left++;
5862   }
5863
5864   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5865
5866   for (i = 0; i < NUM_DIRECTIONS; i++)
5867   {
5868     for (j = 1; j <= dynabomb_size; j++)
5869     {
5870       int x = ex + j * xy[i][0];
5871       int y = ey + j * xy[i][1];
5872       int element;
5873
5874       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5875         break;
5876
5877       element = Feld[x][y];
5878
5879       // do not restart explosions of fields with active bombs
5880       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5881         continue;
5882
5883       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5884
5885       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5886           !IS_DIGGABLE(element) && !dynabomb_xl)
5887         break;
5888     }
5889   }
5890 }
5891
5892 void Bang(int x, int y)
5893 {
5894   int element = MovingOrBlocked2Element(x, y);
5895   int explosion_type = EX_TYPE_NORMAL;
5896
5897   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5898   {
5899     struct PlayerInfo *player = PLAYERINFO(x, y);
5900
5901     element = Feld[x][y] = player->initial_element;
5902
5903     if (level.use_explosion_element[player->index_nr])
5904     {
5905       int explosion_element = level.explosion_element[player->index_nr];
5906
5907       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5908         explosion_type = EX_TYPE_CROSS;
5909       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5910         explosion_type = EX_TYPE_CENTER;
5911     }
5912   }
5913
5914   switch (element)
5915   {
5916     case EL_BUG:
5917     case EL_SPACESHIP:
5918     case EL_BD_BUTTERFLY:
5919     case EL_BD_FIREFLY:
5920     case EL_YAMYAM:
5921     case EL_DARK_YAMYAM:
5922     case EL_ROBOT:
5923     case EL_PACMAN:
5924     case EL_MOLE:
5925       RaiseScoreElement(element);
5926       break;
5927
5928     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5929     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5930     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5931     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5932     case EL_DYNABOMB_INCREASE_NUMBER:
5933     case EL_DYNABOMB_INCREASE_SIZE:
5934     case EL_DYNABOMB_INCREASE_POWER:
5935       explosion_type = EX_TYPE_DYNA;
5936       break;
5937
5938     case EL_DC_LANDMINE:
5939       explosion_type = EX_TYPE_CENTER;
5940       break;
5941
5942     case EL_PENGUIN:
5943     case EL_LAMP:
5944     case EL_LAMP_ACTIVE:
5945     case EL_AMOEBA_TO_DIAMOND:
5946       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5947         explosion_type = EX_TYPE_CENTER;
5948       break;
5949
5950     default:
5951       if (element_info[element].explosion_type == EXPLODES_CROSS)
5952         explosion_type = EX_TYPE_CROSS;
5953       else if (element_info[element].explosion_type == EXPLODES_1X1)
5954         explosion_type = EX_TYPE_CENTER;
5955       break;
5956   }
5957
5958   if (explosion_type == EX_TYPE_DYNA)
5959     DynaExplode(x, y);
5960   else
5961     Explode(x, y, EX_PHASE_START, explosion_type);
5962
5963   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5964 }
5965
5966 static void SplashAcid(int x, int y)
5967 {
5968   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5969       (!IN_LEV_FIELD(x - 1, y - 2) ||
5970        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5971     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5972
5973   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5974       (!IN_LEV_FIELD(x + 1, y - 2) ||
5975        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5976     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5977
5978   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5979 }
5980
5981 static void InitBeltMovement(void)
5982 {
5983   static int belt_base_element[4] =
5984   {
5985     EL_CONVEYOR_BELT_1_LEFT,
5986     EL_CONVEYOR_BELT_2_LEFT,
5987     EL_CONVEYOR_BELT_3_LEFT,
5988     EL_CONVEYOR_BELT_4_LEFT
5989   };
5990   static int belt_base_active_element[4] =
5991   {
5992     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5993     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5994     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5995     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5996   };
5997
5998   int x, y, i, j;
5999
6000   // set frame order for belt animation graphic according to belt direction
6001   for (i = 0; i < NUM_BELTS; i++)
6002   {
6003     int belt_nr = i;
6004
6005     for (j = 0; j < NUM_BELT_PARTS; j++)
6006     {
6007       int element = belt_base_active_element[belt_nr] + j;
6008       int graphic_1 = el2img(element);
6009       int graphic_2 = el2panelimg(element);
6010
6011       if (game.belt_dir[i] == MV_LEFT)
6012       {
6013         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6014         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6015       }
6016       else
6017       {
6018         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6019         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6020       }
6021     }
6022   }
6023
6024   SCAN_PLAYFIELD(x, y)
6025   {
6026     int element = Feld[x][y];
6027
6028     for (i = 0; i < NUM_BELTS; i++)
6029     {
6030       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6031       {
6032         int e_belt_nr = getBeltNrFromBeltElement(element);
6033         int belt_nr = i;
6034
6035         if (e_belt_nr == belt_nr)
6036         {
6037           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6038
6039           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6040         }
6041       }
6042     }
6043   }
6044 }
6045
6046 static void ToggleBeltSwitch(int x, int y)
6047 {
6048   static int belt_base_element[4] =
6049   {
6050     EL_CONVEYOR_BELT_1_LEFT,
6051     EL_CONVEYOR_BELT_2_LEFT,
6052     EL_CONVEYOR_BELT_3_LEFT,
6053     EL_CONVEYOR_BELT_4_LEFT
6054   };
6055   static int belt_base_active_element[4] =
6056   {
6057     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6058     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6059     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6060     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6061   };
6062   static int belt_base_switch_element[4] =
6063   {
6064     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6065     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6066     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6067     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6068   };
6069   static int belt_move_dir[4] =
6070   {
6071     MV_LEFT,
6072     MV_NONE,
6073     MV_RIGHT,
6074     MV_NONE,
6075   };
6076
6077   int element = Feld[x][y];
6078   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6079   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6080   int belt_dir = belt_move_dir[belt_dir_nr];
6081   int xx, yy, i;
6082
6083   if (!IS_BELT_SWITCH(element))
6084     return;
6085
6086   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6087   game.belt_dir[belt_nr] = belt_dir;
6088
6089   if (belt_dir_nr == 3)
6090     belt_dir_nr = 1;
6091
6092   // set frame order for belt animation graphic according to belt direction
6093   for (i = 0; i < NUM_BELT_PARTS; i++)
6094   {
6095     int element = belt_base_active_element[belt_nr] + i;
6096     int graphic_1 = el2img(element);
6097     int graphic_2 = el2panelimg(element);
6098
6099     if (belt_dir == MV_LEFT)
6100     {
6101       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6102       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6103     }
6104     else
6105     {
6106       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6107       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6108     }
6109   }
6110
6111   SCAN_PLAYFIELD(xx, yy)
6112   {
6113     int element = Feld[xx][yy];
6114
6115     if (IS_BELT_SWITCH(element))
6116     {
6117       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6118
6119       if (e_belt_nr == belt_nr)
6120       {
6121         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6122         TEST_DrawLevelField(xx, yy);
6123       }
6124     }
6125     else if (IS_BELT(element) && belt_dir != MV_NONE)
6126     {
6127       int e_belt_nr = getBeltNrFromBeltElement(element);
6128
6129       if (e_belt_nr == belt_nr)
6130       {
6131         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6132
6133         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6134         TEST_DrawLevelField(xx, yy);
6135       }
6136     }
6137     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6138     {
6139       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6140
6141       if (e_belt_nr == belt_nr)
6142       {
6143         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6144
6145         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6146         TEST_DrawLevelField(xx, yy);
6147       }
6148     }
6149   }
6150 }
6151
6152 static void ToggleSwitchgateSwitch(int x, int y)
6153 {
6154   int xx, yy;
6155
6156   game.switchgate_pos = !game.switchgate_pos;
6157
6158   SCAN_PLAYFIELD(xx, yy)
6159   {
6160     int element = Feld[xx][yy];
6161
6162     if (element == EL_SWITCHGATE_SWITCH_UP)
6163     {
6164       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6165       TEST_DrawLevelField(xx, yy);
6166     }
6167     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6168     {
6169       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6170       TEST_DrawLevelField(xx, yy);
6171     }
6172     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6173     {
6174       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6175       TEST_DrawLevelField(xx, yy);
6176     }
6177     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6178     {
6179       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6180       TEST_DrawLevelField(xx, yy);
6181     }
6182     else if (element == EL_SWITCHGATE_OPEN ||
6183              element == EL_SWITCHGATE_OPENING)
6184     {
6185       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6186
6187       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6188     }
6189     else if (element == EL_SWITCHGATE_CLOSED ||
6190              element == EL_SWITCHGATE_CLOSING)
6191     {
6192       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6193
6194       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6195     }
6196   }
6197 }
6198
6199 static int getInvisibleActiveFromInvisibleElement(int element)
6200 {
6201   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6202           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6203           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6204           element);
6205 }
6206
6207 static int getInvisibleFromInvisibleActiveElement(int element)
6208 {
6209   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6210           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6211           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6212           element);
6213 }
6214
6215 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6216 {
6217   int x, y;
6218
6219   SCAN_PLAYFIELD(x, y)
6220   {
6221     int element = Feld[x][y];
6222
6223     if (element == EL_LIGHT_SWITCH &&
6224         game.light_time_left > 0)
6225     {
6226       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6227       TEST_DrawLevelField(x, y);
6228     }
6229     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6230              game.light_time_left == 0)
6231     {
6232       Feld[x][y] = EL_LIGHT_SWITCH;
6233       TEST_DrawLevelField(x, y);
6234     }
6235     else if (element == EL_EMC_DRIPPER &&
6236              game.light_time_left > 0)
6237     {
6238       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6239       TEST_DrawLevelField(x, y);
6240     }
6241     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6242              game.light_time_left == 0)
6243     {
6244       Feld[x][y] = EL_EMC_DRIPPER;
6245       TEST_DrawLevelField(x, y);
6246     }
6247     else if (element == EL_INVISIBLE_STEELWALL ||
6248              element == EL_INVISIBLE_WALL ||
6249              element == EL_INVISIBLE_SAND)
6250     {
6251       if (game.light_time_left > 0)
6252         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6253
6254       TEST_DrawLevelField(x, y);
6255
6256       // uncrumble neighbour fields, if needed
6257       if (element == EL_INVISIBLE_SAND)
6258         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6259     }
6260     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6261              element == EL_INVISIBLE_WALL_ACTIVE ||
6262              element == EL_INVISIBLE_SAND_ACTIVE)
6263     {
6264       if (game.light_time_left == 0)
6265         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6266
6267       TEST_DrawLevelField(x, y);
6268
6269       // re-crumble neighbour fields, if needed
6270       if (element == EL_INVISIBLE_SAND)
6271         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6272     }
6273   }
6274 }
6275
6276 static void RedrawAllInvisibleElementsForLenses(void)
6277 {
6278   int x, y;
6279
6280   SCAN_PLAYFIELD(x, y)
6281   {
6282     int element = Feld[x][y];
6283
6284     if (element == EL_EMC_DRIPPER &&
6285         game.lenses_time_left > 0)
6286     {
6287       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6288       TEST_DrawLevelField(x, y);
6289     }
6290     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6291              game.lenses_time_left == 0)
6292     {
6293       Feld[x][y] = EL_EMC_DRIPPER;
6294       TEST_DrawLevelField(x, y);
6295     }
6296     else if (element == EL_INVISIBLE_STEELWALL ||
6297              element == EL_INVISIBLE_WALL ||
6298              element == EL_INVISIBLE_SAND)
6299     {
6300       if (game.lenses_time_left > 0)
6301         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6302
6303       TEST_DrawLevelField(x, y);
6304
6305       // uncrumble neighbour fields, if needed
6306       if (element == EL_INVISIBLE_SAND)
6307         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6308     }
6309     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6310              element == EL_INVISIBLE_WALL_ACTIVE ||
6311              element == EL_INVISIBLE_SAND_ACTIVE)
6312     {
6313       if (game.lenses_time_left == 0)
6314         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6315
6316       TEST_DrawLevelField(x, y);
6317
6318       // re-crumble neighbour fields, if needed
6319       if (element == EL_INVISIBLE_SAND)
6320         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6321     }
6322   }
6323 }
6324
6325 static void RedrawAllInvisibleElementsForMagnifier(void)
6326 {
6327   int x, y;
6328
6329   SCAN_PLAYFIELD(x, y)
6330   {
6331     int element = Feld[x][y];
6332
6333     if (element == EL_EMC_FAKE_GRASS &&
6334         game.magnify_time_left > 0)
6335     {
6336       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6337       TEST_DrawLevelField(x, y);
6338     }
6339     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6340              game.magnify_time_left == 0)
6341     {
6342       Feld[x][y] = EL_EMC_FAKE_GRASS;
6343       TEST_DrawLevelField(x, y);
6344     }
6345     else if (IS_GATE_GRAY(element) &&
6346              game.magnify_time_left > 0)
6347     {
6348       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6349                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6350                     IS_EM_GATE_GRAY(element) ?
6351                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6352                     IS_EMC_GATE_GRAY(element) ?
6353                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6354                     IS_DC_GATE_GRAY(element) ?
6355                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6356                     element);
6357       TEST_DrawLevelField(x, y);
6358     }
6359     else if (IS_GATE_GRAY_ACTIVE(element) &&
6360              game.magnify_time_left == 0)
6361     {
6362       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6363                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6364                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6365                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6366                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6367                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6368                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6369                     EL_DC_GATE_WHITE_GRAY :
6370                     element);
6371       TEST_DrawLevelField(x, y);
6372     }
6373   }
6374 }
6375
6376 static void ToggleLightSwitch(int x, int y)
6377 {
6378   int element = Feld[x][y];
6379
6380   game.light_time_left =
6381     (element == EL_LIGHT_SWITCH ?
6382      level.time_light * FRAMES_PER_SECOND : 0);
6383
6384   RedrawAllLightSwitchesAndInvisibleElements();
6385 }
6386
6387 static void ActivateTimegateSwitch(int x, int y)
6388 {
6389   int xx, yy;
6390
6391   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6392
6393   SCAN_PLAYFIELD(xx, yy)
6394   {
6395     int element = Feld[xx][yy];
6396
6397     if (element == EL_TIMEGATE_CLOSED ||
6398         element == EL_TIMEGATE_CLOSING)
6399     {
6400       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6401       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6402     }
6403
6404     /*
6405     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6406     {
6407       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6408       TEST_DrawLevelField(xx, yy);
6409     }
6410     */
6411
6412   }
6413
6414   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6415                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6416 }
6417
6418 static void Impact(int x, int y)
6419 {
6420   boolean last_line = (y == lev_fieldy - 1);
6421   boolean object_hit = FALSE;
6422   boolean impact = (last_line || object_hit);
6423   int element = Feld[x][y];
6424   int smashed = EL_STEELWALL;
6425
6426   if (!last_line)       // check if element below was hit
6427   {
6428     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6429       return;
6430
6431     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6432                                          MovDir[x][y + 1] != MV_DOWN ||
6433                                          MovPos[x][y + 1] <= TILEY / 2));
6434
6435     // do not smash moving elements that left the smashed field in time
6436     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6437         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6438       object_hit = FALSE;
6439
6440 #if USE_QUICKSAND_IMPACT_BUGFIX
6441     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6442     {
6443       RemoveMovingField(x, y + 1);
6444       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6445       Feld[x][y + 2] = EL_ROCK;
6446       TEST_DrawLevelField(x, y + 2);
6447
6448       object_hit = TRUE;
6449     }
6450
6451     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6452     {
6453       RemoveMovingField(x, y + 1);
6454       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6455       Feld[x][y + 2] = EL_ROCK;
6456       TEST_DrawLevelField(x, y + 2);
6457
6458       object_hit = TRUE;
6459     }
6460 #endif
6461
6462     if (object_hit)
6463       smashed = MovingOrBlocked2Element(x, y + 1);
6464
6465     impact = (last_line || object_hit);
6466   }
6467
6468   if (!last_line && smashed == EL_ACID) // element falls into acid
6469   {
6470     SplashAcid(x, y + 1);
6471     return;
6472   }
6473
6474   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6475   // only reset graphic animation if graphic really changes after impact
6476   if (impact &&
6477       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6478   {
6479     ResetGfxAnimation(x, y);
6480     TEST_DrawLevelField(x, y);
6481   }
6482
6483   if (impact && CAN_EXPLODE_IMPACT(element))
6484   {
6485     Bang(x, y);
6486     return;
6487   }
6488   else if (impact && element == EL_PEARL &&
6489            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6490   {
6491     ResetGfxAnimation(x, y);
6492
6493     Feld[x][y] = EL_PEARL_BREAKING;
6494     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6495     return;
6496   }
6497   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6498   {
6499     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6500
6501     return;
6502   }
6503
6504   if (impact && element == EL_AMOEBA_DROP)
6505   {
6506     if (object_hit && IS_PLAYER(x, y + 1))
6507       KillPlayerUnlessEnemyProtected(x, y + 1);
6508     else if (object_hit && smashed == EL_PENGUIN)
6509       Bang(x, y + 1);
6510     else
6511     {
6512       Feld[x][y] = EL_AMOEBA_GROWING;
6513       Store[x][y] = EL_AMOEBA_WET;
6514
6515       ResetRandomAnimationValue(x, y);
6516     }
6517     return;
6518   }
6519
6520   if (object_hit)               // check which object was hit
6521   {
6522     if ((CAN_PASS_MAGIC_WALL(element) && 
6523          (smashed == EL_MAGIC_WALL ||
6524           smashed == EL_BD_MAGIC_WALL)) ||
6525         (CAN_PASS_DC_MAGIC_WALL(element) &&
6526          smashed == EL_DC_MAGIC_WALL))
6527     {
6528       int xx, yy;
6529       int activated_magic_wall =
6530         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6531          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6532          EL_DC_MAGIC_WALL_ACTIVE);
6533
6534       // activate magic wall / mill
6535       SCAN_PLAYFIELD(xx, yy)
6536       {
6537         if (Feld[xx][yy] == smashed)
6538           Feld[xx][yy] = activated_magic_wall;
6539       }
6540
6541       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6542       game.magic_wall_active = TRUE;
6543
6544       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6545                             SND_MAGIC_WALL_ACTIVATING :
6546                             smashed == EL_BD_MAGIC_WALL ?
6547                             SND_BD_MAGIC_WALL_ACTIVATING :
6548                             SND_DC_MAGIC_WALL_ACTIVATING));
6549     }
6550
6551     if (IS_PLAYER(x, y + 1))
6552     {
6553       if (CAN_SMASH_PLAYER(element))
6554       {
6555         KillPlayerUnlessEnemyProtected(x, y + 1);
6556         return;
6557       }
6558     }
6559     else if (smashed == EL_PENGUIN)
6560     {
6561       if (CAN_SMASH_PLAYER(element))
6562       {
6563         Bang(x, y + 1);
6564         return;
6565       }
6566     }
6567     else if (element == EL_BD_DIAMOND)
6568     {
6569       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6570       {
6571         Bang(x, y + 1);
6572         return;
6573       }
6574     }
6575     else if (((element == EL_SP_INFOTRON ||
6576                element == EL_SP_ZONK) &&
6577               (smashed == EL_SP_SNIKSNAK ||
6578                smashed == EL_SP_ELECTRON ||
6579                smashed == EL_SP_DISK_ORANGE)) ||
6580              (element == EL_SP_INFOTRON &&
6581               smashed == EL_SP_DISK_YELLOW))
6582     {
6583       Bang(x, y + 1);
6584       return;
6585     }
6586     else if (CAN_SMASH_EVERYTHING(element))
6587     {
6588       if (IS_CLASSIC_ENEMY(smashed) ||
6589           CAN_EXPLODE_SMASHED(smashed))
6590       {
6591         Bang(x, y + 1);
6592         return;
6593       }
6594       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6595       {
6596         if (smashed == EL_LAMP ||
6597             smashed == EL_LAMP_ACTIVE)
6598         {
6599           Bang(x, y + 1);
6600           return;
6601         }
6602         else if (smashed == EL_NUT)
6603         {
6604           Feld[x][y + 1] = EL_NUT_BREAKING;
6605           PlayLevelSound(x, y, SND_NUT_BREAKING);
6606           RaiseScoreElement(EL_NUT);
6607           return;
6608         }
6609         else if (smashed == EL_PEARL)
6610         {
6611           ResetGfxAnimation(x, y);
6612
6613           Feld[x][y + 1] = EL_PEARL_BREAKING;
6614           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6615           return;
6616         }
6617         else if (smashed == EL_DIAMOND)
6618         {
6619           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6620           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6621           return;
6622         }
6623         else if (IS_BELT_SWITCH(smashed))
6624         {
6625           ToggleBeltSwitch(x, y + 1);
6626         }
6627         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6628                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6629                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6630                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6631         {
6632           ToggleSwitchgateSwitch(x, y + 1);
6633         }
6634         else if (smashed == EL_LIGHT_SWITCH ||
6635                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6636         {
6637           ToggleLightSwitch(x, y + 1);
6638         }
6639         else
6640         {
6641           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6642
6643           CheckElementChangeBySide(x, y + 1, smashed, element,
6644                                    CE_SWITCHED, CH_SIDE_TOP);
6645           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6646                                             CH_SIDE_TOP);
6647         }
6648       }
6649       else
6650       {
6651         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6652       }
6653     }
6654   }
6655
6656   // play sound of magic wall / mill
6657   if (!last_line &&
6658       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6659        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6660        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6661   {
6662     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6663       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6664     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6665       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6666     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6667       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6668
6669     return;
6670   }
6671
6672   // play sound of object that hits the ground
6673   if (last_line || object_hit)
6674     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6675 }
6676
6677 static void TurnRoundExt(int x, int y)
6678 {
6679   static struct
6680   {
6681     int dx, dy;
6682   } move_xy[] =
6683   {
6684     {  0,  0 },
6685     { -1,  0 },
6686     { +1,  0 },
6687     {  0,  0 },
6688     {  0, -1 },
6689     {  0,  0 }, { 0, 0 }, { 0, 0 },
6690     {  0, +1 }
6691   };
6692   static struct
6693   {
6694     int left, right, back;
6695   } turn[] =
6696   {
6697     { 0,        0,              0        },
6698     { MV_DOWN,  MV_UP,          MV_RIGHT },
6699     { MV_UP,    MV_DOWN,        MV_LEFT  },
6700     { 0,        0,              0        },
6701     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6702     { 0,        0,              0        },
6703     { 0,        0,              0        },
6704     { 0,        0,              0        },
6705     { MV_RIGHT, MV_LEFT,        MV_UP    }
6706   };
6707
6708   int element = Feld[x][y];
6709   int move_pattern = element_info[element].move_pattern;
6710
6711   int old_move_dir = MovDir[x][y];
6712   int left_dir  = turn[old_move_dir].left;
6713   int right_dir = turn[old_move_dir].right;
6714   int back_dir  = turn[old_move_dir].back;
6715
6716   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6717   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6718   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6719   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6720
6721   int left_x  = x + left_dx,  left_y  = y + left_dy;
6722   int right_x = x + right_dx, right_y = y + right_dy;
6723   int move_x  = x + move_dx,  move_y  = y + move_dy;
6724
6725   int xx, yy;
6726
6727   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6728   {
6729     TestIfBadThingTouchesOtherBadThing(x, y);
6730
6731     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6732       MovDir[x][y] = right_dir;
6733     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6734       MovDir[x][y] = left_dir;
6735
6736     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6737       MovDelay[x][y] = 9;
6738     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6739       MovDelay[x][y] = 1;
6740   }
6741   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6742   {
6743     TestIfBadThingTouchesOtherBadThing(x, y);
6744
6745     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6746       MovDir[x][y] = left_dir;
6747     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6748       MovDir[x][y] = right_dir;
6749
6750     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6751       MovDelay[x][y] = 9;
6752     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6753       MovDelay[x][y] = 1;
6754   }
6755   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6756   {
6757     TestIfBadThingTouchesOtherBadThing(x, y);
6758
6759     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6760       MovDir[x][y] = left_dir;
6761     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6762       MovDir[x][y] = right_dir;
6763
6764     if (MovDir[x][y] != old_move_dir)
6765       MovDelay[x][y] = 9;
6766   }
6767   else if (element == EL_YAMYAM)
6768   {
6769     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6770     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6771
6772     if (can_turn_left && can_turn_right)
6773       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6774     else if (can_turn_left)
6775       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6776     else if (can_turn_right)
6777       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6778     else
6779       MovDir[x][y] = back_dir;
6780
6781     MovDelay[x][y] = 16 + 16 * RND(3);
6782   }
6783   else if (element == EL_DARK_YAMYAM)
6784   {
6785     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6786                                                          left_x, left_y);
6787     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6788                                                          right_x, right_y);
6789
6790     if (can_turn_left && can_turn_right)
6791       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6792     else if (can_turn_left)
6793       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6794     else if (can_turn_right)
6795       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6796     else
6797       MovDir[x][y] = back_dir;
6798
6799     MovDelay[x][y] = 16 + 16 * RND(3);
6800   }
6801   else if (element == EL_PACMAN)
6802   {
6803     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6804     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6805
6806     if (can_turn_left && can_turn_right)
6807       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6808     else if (can_turn_left)
6809       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6810     else if (can_turn_right)
6811       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6812     else
6813       MovDir[x][y] = back_dir;
6814
6815     MovDelay[x][y] = 6 + RND(40);
6816   }
6817   else if (element == EL_PIG)
6818   {
6819     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6820     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6821     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6822     boolean should_turn_left, should_turn_right, should_move_on;
6823     int rnd_value = 24;
6824     int rnd = RND(rnd_value);
6825
6826     should_turn_left = (can_turn_left &&
6827                         (!can_move_on ||
6828                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6829                                                    y + back_dy + left_dy)));
6830     should_turn_right = (can_turn_right &&
6831                          (!can_move_on ||
6832                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6833                                                     y + back_dy + right_dy)));
6834     should_move_on = (can_move_on &&
6835                       (!can_turn_left ||
6836                        !can_turn_right ||
6837                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6838                                                  y + move_dy + left_dy) ||
6839                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6840                                                  y + move_dy + right_dy)));
6841
6842     if (should_turn_left || should_turn_right || should_move_on)
6843     {
6844       if (should_turn_left && should_turn_right && should_move_on)
6845         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6846                         rnd < 2 * rnd_value / 3 ? right_dir :
6847                         old_move_dir);
6848       else if (should_turn_left && should_turn_right)
6849         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6850       else if (should_turn_left && should_move_on)
6851         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6852       else if (should_turn_right && should_move_on)
6853         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6854       else if (should_turn_left)
6855         MovDir[x][y] = left_dir;
6856       else if (should_turn_right)
6857         MovDir[x][y] = right_dir;
6858       else if (should_move_on)
6859         MovDir[x][y] = old_move_dir;
6860     }
6861     else if (can_move_on && rnd > rnd_value / 8)
6862       MovDir[x][y] = old_move_dir;
6863     else if (can_turn_left && can_turn_right)
6864       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6865     else if (can_turn_left && rnd > rnd_value / 8)
6866       MovDir[x][y] = left_dir;
6867     else if (can_turn_right && rnd > rnd_value/8)
6868       MovDir[x][y] = right_dir;
6869     else
6870       MovDir[x][y] = back_dir;
6871
6872     xx = x + move_xy[MovDir[x][y]].dx;
6873     yy = y + move_xy[MovDir[x][y]].dy;
6874
6875     if (!IN_LEV_FIELD(xx, yy) ||
6876         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6877       MovDir[x][y] = old_move_dir;
6878
6879     MovDelay[x][y] = 0;
6880   }
6881   else if (element == EL_DRAGON)
6882   {
6883     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6884     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6885     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6886     int rnd_value = 24;
6887     int rnd = RND(rnd_value);
6888
6889     if (can_move_on && rnd > rnd_value / 8)
6890       MovDir[x][y] = old_move_dir;
6891     else if (can_turn_left && can_turn_right)
6892       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6893     else if (can_turn_left && rnd > rnd_value / 8)
6894       MovDir[x][y] = left_dir;
6895     else if (can_turn_right && rnd > rnd_value / 8)
6896       MovDir[x][y] = right_dir;
6897     else
6898       MovDir[x][y] = back_dir;
6899
6900     xx = x + move_xy[MovDir[x][y]].dx;
6901     yy = y + move_xy[MovDir[x][y]].dy;
6902
6903     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6904       MovDir[x][y] = old_move_dir;
6905
6906     MovDelay[x][y] = 0;
6907   }
6908   else if (element == EL_MOLE)
6909   {
6910     boolean can_move_on =
6911       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6912                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6913                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6914     if (!can_move_on)
6915     {
6916       boolean can_turn_left =
6917         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6918                               IS_AMOEBOID(Feld[left_x][left_y])));
6919
6920       boolean can_turn_right =
6921         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6922                               IS_AMOEBOID(Feld[right_x][right_y])));
6923
6924       if (can_turn_left && can_turn_right)
6925         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6926       else if (can_turn_left)
6927         MovDir[x][y] = left_dir;
6928       else
6929         MovDir[x][y] = right_dir;
6930     }
6931
6932     if (MovDir[x][y] != old_move_dir)
6933       MovDelay[x][y] = 9;
6934   }
6935   else if (element == EL_BALLOON)
6936   {
6937     MovDir[x][y] = game.wind_direction;
6938     MovDelay[x][y] = 0;
6939   }
6940   else if (element == EL_SPRING)
6941   {
6942     if (MovDir[x][y] & MV_HORIZONTAL)
6943     {
6944       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6945           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6946       {
6947         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6948         ResetGfxAnimation(move_x, move_y);
6949         TEST_DrawLevelField(move_x, move_y);
6950
6951         MovDir[x][y] = back_dir;
6952       }
6953       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6954                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6955         MovDir[x][y] = MV_NONE;
6956     }
6957
6958     MovDelay[x][y] = 0;
6959   }
6960   else if (element == EL_ROBOT ||
6961            element == EL_SATELLITE ||
6962            element == EL_PENGUIN ||
6963            element == EL_EMC_ANDROID)
6964   {
6965     int attr_x = -1, attr_y = -1;
6966
6967     if (game.all_players_gone)
6968     {
6969       attr_x = game.exit_x;
6970       attr_y = game.exit_y;
6971     }
6972     else
6973     {
6974       int i;
6975
6976       for (i = 0; i < MAX_PLAYERS; i++)
6977       {
6978         struct PlayerInfo *player = &stored_player[i];
6979         int jx = player->jx, jy = player->jy;
6980
6981         if (!player->active)
6982           continue;
6983
6984         if (attr_x == -1 ||
6985             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6986         {
6987           attr_x = jx;
6988           attr_y = jy;
6989         }
6990       }
6991     }
6992
6993     if (element == EL_ROBOT &&
6994         game.robot_wheel_x >= 0 &&
6995         game.robot_wheel_y >= 0 &&
6996         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6997          game.engine_version < VERSION_IDENT(3,1,0,0)))
6998     {
6999       attr_x = game.robot_wheel_x;
7000       attr_y = game.robot_wheel_y;
7001     }
7002
7003     if (element == EL_PENGUIN)
7004     {
7005       int i;
7006       static int xy[4][2] =
7007       {
7008         { 0, -1 },
7009         { -1, 0 },
7010         { +1, 0 },
7011         { 0, +1 }
7012       };
7013
7014       for (i = 0; i < NUM_DIRECTIONS; i++)
7015       {
7016         int ex = x + xy[i][0];
7017         int ey = y + xy[i][1];
7018
7019         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7020                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7021                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7022                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7023         {
7024           attr_x = ex;
7025           attr_y = ey;
7026           break;
7027         }
7028       }
7029     }
7030
7031     MovDir[x][y] = MV_NONE;
7032     if (attr_x < x)
7033       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7034     else if (attr_x > x)
7035       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7036     if (attr_y < y)
7037       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7038     else if (attr_y > y)
7039       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7040
7041     if (element == EL_ROBOT)
7042     {
7043       int newx, newy;
7044
7045       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7046         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7047       Moving2Blocked(x, y, &newx, &newy);
7048
7049       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7050         MovDelay[x][y] = 8 + 8 * !RND(3);
7051       else
7052         MovDelay[x][y] = 16;
7053     }
7054     else if (element == EL_PENGUIN)
7055     {
7056       int newx, newy;
7057
7058       MovDelay[x][y] = 1;
7059
7060       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7061       {
7062         boolean first_horiz = RND(2);
7063         int new_move_dir = MovDir[x][y];
7064
7065         MovDir[x][y] =
7066           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7067         Moving2Blocked(x, y, &newx, &newy);
7068
7069         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7070           return;
7071
7072         MovDir[x][y] =
7073           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7074         Moving2Blocked(x, y, &newx, &newy);
7075
7076         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7077           return;
7078
7079         MovDir[x][y] = old_move_dir;
7080         return;
7081       }
7082     }
7083     else if (element == EL_SATELLITE)
7084     {
7085       int newx, newy;
7086
7087       MovDelay[x][y] = 1;
7088
7089       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7090       {
7091         boolean first_horiz = RND(2);
7092         int new_move_dir = MovDir[x][y];
7093
7094         MovDir[x][y] =
7095           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7096         Moving2Blocked(x, y, &newx, &newy);
7097
7098         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7099           return;
7100
7101         MovDir[x][y] =
7102           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7103         Moving2Blocked(x, y, &newx, &newy);
7104
7105         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7106           return;
7107
7108         MovDir[x][y] = old_move_dir;
7109         return;
7110       }
7111     }
7112     else if (element == EL_EMC_ANDROID)
7113     {
7114       static int check_pos[16] =
7115       {
7116         -1,             //  0 => (invalid)
7117         7,              //  1 => MV_LEFT
7118         3,              //  2 => MV_RIGHT
7119         -1,             //  3 => (invalid)
7120         1,              //  4 =>            MV_UP
7121         0,              //  5 => MV_LEFT  | MV_UP
7122         2,              //  6 => MV_RIGHT | MV_UP
7123         -1,             //  7 => (invalid)
7124         5,              //  8 =>            MV_DOWN
7125         6,              //  9 => MV_LEFT  | MV_DOWN
7126         4,              // 10 => MV_RIGHT | MV_DOWN
7127         -1,             // 11 => (invalid)
7128         -1,             // 12 => (invalid)
7129         -1,             // 13 => (invalid)
7130         -1,             // 14 => (invalid)
7131         -1,             // 15 => (invalid)
7132       };
7133       static struct
7134       {
7135         int dx, dy;
7136         int dir;
7137       } check_xy[8] =
7138       {
7139         { -1, -1,       MV_LEFT  | MV_UP   },
7140         {  0, -1,                  MV_UP   },
7141         { +1, -1,       MV_RIGHT | MV_UP   },
7142         { +1,  0,       MV_RIGHT           },
7143         { +1, +1,       MV_RIGHT | MV_DOWN },
7144         {  0, +1,                  MV_DOWN },
7145         { -1, +1,       MV_LEFT  | MV_DOWN },
7146         { -1,  0,       MV_LEFT            },
7147       };
7148       int start_pos, check_order;
7149       boolean can_clone = FALSE;
7150       int i;
7151
7152       // check if there is any free field around current position
7153       for (i = 0; i < 8; i++)
7154       {
7155         int newx = x + check_xy[i].dx;
7156         int newy = y + check_xy[i].dy;
7157
7158         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7159         {
7160           can_clone = TRUE;
7161
7162           break;
7163         }
7164       }
7165
7166       if (can_clone)            // randomly find an element to clone
7167       {
7168         can_clone = FALSE;
7169
7170         start_pos = check_pos[RND(8)];
7171         check_order = (RND(2) ? -1 : +1);
7172
7173         for (i = 0; i < 8; i++)
7174         {
7175           int pos_raw = start_pos + i * check_order;
7176           int pos = (pos_raw + 8) % 8;
7177           int newx = x + check_xy[pos].dx;
7178           int newy = y + check_xy[pos].dy;
7179
7180           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7181           {
7182             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7183             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7184
7185             Store[x][y] = Feld[newx][newy];
7186
7187             can_clone = TRUE;
7188
7189             break;
7190           }
7191         }
7192       }
7193
7194       if (can_clone)            // randomly find a direction to move
7195       {
7196         can_clone = FALSE;
7197
7198         start_pos = check_pos[RND(8)];
7199         check_order = (RND(2) ? -1 : +1);
7200
7201         for (i = 0; i < 8; i++)
7202         {
7203           int pos_raw = start_pos + i * check_order;
7204           int pos = (pos_raw + 8) % 8;
7205           int newx = x + check_xy[pos].dx;
7206           int newy = y + check_xy[pos].dy;
7207           int new_move_dir = check_xy[pos].dir;
7208
7209           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7210           {
7211             MovDir[x][y] = new_move_dir;
7212             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7213
7214             can_clone = TRUE;
7215
7216             break;
7217           }
7218         }
7219       }
7220
7221       if (can_clone)            // cloning and moving successful
7222         return;
7223
7224       // cannot clone -- try to move towards player
7225
7226       start_pos = check_pos[MovDir[x][y] & 0x0f];
7227       check_order = (RND(2) ? -1 : +1);
7228
7229       for (i = 0; i < 3; i++)
7230       {
7231         // first check start_pos, then previous/next or (next/previous) pos
7232         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7233         int pos = (pos_raw + 8) % 8;
7234         int newx = x + check_xy[pos].dx;
7235         int newy = y + check_xy[pos].dy;
7236         int new_move_dir = check_xy[pos].dir;
7237
7238         if (IS_PLAYER(newx, newy))
7239           break;
7240
7241         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7242         {
7243           MovDir[x][y] = new_move_dir;
7244           MovDelay[x][y] = level.android_move_time * 8 + 1;
7245
7246           break;
7247         }
7248       }
7249     }
7250   }
7251   else if (move_pattern == MV_TURNING_LEFT ||
7252            move_pattern == MV_TURNING_RIGHT ||
7253            move_pattern == MV_TURNING_LEFT_RIGHT ||
7254            move_pattern == MV_TURNING_RIGHT_LEFT ||
7255            move_pattern == MV_TURNING_RANDOM ||
7256            move_pattern == MV_ALL_DIRECTIONS)
7257   {
7258     boolean can_turn_left =
7259       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7260     boolean can_turn_right =
7261       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7262
7263     if (element_info[element].move_stepsize == 0)       // "not moving"
7264       return;
7265
7266     if (move_pattern == MV_TURNING_LEFT)
7267       MovDir[x][y] = left_dir;
7268     else if (move_pattern == MV_TURNING_RIGHT)
7269       MovDir[x][y] = right_dir;
7270     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7271       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7272     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7273       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7274     else if (move_pattern == MV_TURNING_RANDOM)
7275       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7276                       can_turn_right && !can_turn_left ? right_dir :
7277                       RND(2) ? left_dir : right_dir);
7278     else if (can_turn_left && can_turn_right)
7279       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7280     else if (can_turn_left)
7281       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7282     else if (can_turn_right)
7283       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7284     else
7285       MovDir[x][y] = back_dir;
7286
7287     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7288   }
7289   else if (move_pattern == MV_HORIZONTAL ||
7290            move_pattern == MV_VERTICAL)
7291   {
7292     if (move_pattern & old_move_dir)
7293       MovDir[x][y] = back_dir;
7294     else if (move_pattern == MV_HORIZONTAL)
7295       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7296     else if (move_pattern == MV_VERTICAL)
7297       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7298
7299     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7300   }
7301   else if (move_pattern & MV_ANY_DIRECTION)
7302   {
7303     MovDir[x][y] = move_pattern;
7304     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7305   }
7306   else if (move_pattern & MV_WIND_DIRECTION)
7307   {
7308     MovDir[x][y] = game.wind_direction;
7309     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7310   }
7311   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7312   {
7313     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7314       MovDir[x][y] = left_dir;
7315     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7316       MovDir[x][y] = right_dir;
7317
7318     if (MovDir[x][y] != old_move_dir)
7319       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7320   }
7321   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7322   {
7323     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7324       MovDir[x][y] = right_dir;
7325     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7326       MovDir[x][y] = left_dir;
7327
7328     if (MovDir[x][y] != old_move_dir)
7329       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7330   }
7331   else if (move_pattern == MV_TOWARDS_PLAYER ||
7332            move_pattern == MV_AWAY_FROM_PLAYER)
7333   {
7334     int attr_x = -1, attr_y = -1;
7335     int newx, newy;
7336     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7337
7338     if (game.all_players_gone)
7339     {
7340       attr_x = game.exit_x;
7341       attr_y = game.exit_y;
7342     }
7343     else
7344     {
7345       int i;
7346
7347       for (i = 0; i < MAX_PLAYERS; i++)
7348       {
7349         struct PlayerInfo *player = &stored_player[i];
7350         int jx = player->jx, jy = player->jy;
7351
7352         if (!player->active)
7353           continue;
7354
7355         if (attr_x == -1 ||
7356             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7357         {
7358           attr_x = jx;
7359           attr_y = jy;
7360         }
7361       }
7362     }
7363
7364     MovDir[x][y] = MV_NONE;
7365     if (attr_x < x)
7366       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7367     else if (attr_x > x)
7368       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7369     if (attr_y < y)
7370       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7371     else if (attr_y > y)
7372       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7373
7374     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7375
7376     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7377     {
7378       boolean first_horiz = RND(2);
7379       int new_move_dir = MovDir[x][y];
7380
7381       if (element_info[element].move_stepsize == 0)     // "not moving"
7382       {
7383         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7384         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7385
7386         return;
7387       }
7388
7389       MovDir[x][y] =
7390         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7391       Moving2Blocked(x, y, &newx, &newy);
7392
7393       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7394         return;
7395
7396       MovDir[x][y] =
7397         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7398       Moving2Blocked(x, y, &newx, &newy);
7399
7400       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7401         return;
7402
7403       MovDir[x][y] = old_move_dir;
7404     }
7405   }
7406   else if (move_pattern == MV_WHEN_PUSHED ||
7407            move_pattern == MV_WHEN_DROPPED)
7408   {
7409     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7410       MovDir[x][y] = MV_NONE;
7411
7412     MovDelay[x][y] = 0;
7413   }
7414   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7415   {
7416     static int test_xy[7][2] =
7417     {
7418       { 0, -1 },
7419       { -1, 0 },
7420       { +1, 0 },
7421       { 0, +1 },
7422       { 0, -1 },
7423       { -1, 0 },
7424       { +1, 0 },
7425     };
7426     static int test_dir[7] =
7427     {
7428       MV_UP,
7429       MV_LEFT,
7430       MV_RIGHT,
7431       MV_DOWN,
7432       MV_UP,
7433       MV_LEFT,
7434       MV_RIGHT,
7435     };
7436     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7437     int move_preference = -1000000;     // start with very low preference
7438     int new_move_dir = MV_NONE;
7439     int start_test = RND(4);
7440     int i;
7441
7442     for (i = 0; i < NUM_DIRECTIONS; i++)
7443     {
7444       int move_dir = test_dir[start_test + i];
7445       int move_dir_preference;
7446
7447       xx = x + test_xy[start_test + i][0];
7448       yy = y + test_xy[start_test + i][1];
7449
7450       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7451           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7452       {
7453         new_move_dir = move_dir;
7454
7455         break;
7456       }
7457
7458       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7459         continue;
7460
7461       move_dir_preference = -1 * RunnerVisit[xx][yy];
7462       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7463         move_dir_preference = PlayerVisit[xx][yy];
7464
7465       if (move_dir_preference > move_preference)
7466       {
7467         // prefer field that has not been visited for the longest time
7468         move_preference = move_dir_preference;
7469         new_move_dir = move_dir;
7470       }
7471       else if (move_dir_preference == move_preference &&
7472                move_dir == old_move_dir)
7473       {
7474         // prefer last direction when all directions are preferred equally
7475         move_preference = move_dir_preference;
7476         new_move_dir = move_dir;
7477       }
7478     }
7479
7480     MovDir[x][y] = new_move_dir;
7481     if (old_move_dir != new_move_dir)
7482       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7483   }
7484 }
7485
7486 static void TurnRound(int x, int y)
7487 {
7488   int direction = MovDir[x][y];
7489
7490   TurnRoundExt(x, y);
7491
7492   GfxDir[x][y] = MovDir[x][y];
7493
7494   if (direction != MovDir[x][y])
7495     GfxFrame[x][y] = 0;
7496
7497   if (MovDelay[x][y])
7498     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7499
7500   ResetGfxFrame(x, y);
7501 }
7502
7503 static boolean JustBeingPushed(int x, int y)
7504 {
7505   int i;
7506
7507   for (i = 0; i < MAX_PLAYERS; i++)
7508   {
7509     struct PlayerInfo *player = &stored_player[i];
7510
7511     if (player->active && player->is_pushing && player->MovPos)
7512     {
7513       int next_jx = player->jx + (player->jx - player->last_jx);
7514       int next_jy = player->jy + (player->jy - player->last_jy);
7515
7516       if (x == next_jx && y == next_jy)
7517         return TRUE;
7518     }
7519   }
7520
7521   return FALSE;
7522 }
7523
7524 static void StartMoving(int x, int y)
7525 {
7526   boolean started_moving = FALSE;       // some elements can fall _and_ move
7527   int element = Feld[x][y];
7528
7529   if (Stop[x][y])
7530     return;
7531
7532   if (MovDelay[x][y] == 0)
7533     GfxAction[x][y] = ACTION_DEFAULT;
7534
7535   if (CAN_FALL(element) && y < lev_fieldy - 1)
7536   {
7537     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7538         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7539       if (JustBeingPushed(x, y))
7540         return;
7541
7542     if (element == EL_QUICKSAND_FULL)
7543     {
7544       if (IS_FREE(x, y + 1))
7545       {
7546         InitMovingField(x, y, MV_DOWN);
7547         started_moving = TRUE;
7548
7549         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7550 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7551         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7552           Store[x][y] = EL_ROCK;
7553 #else
7554         Store[x][y] = EL_ROCK;
7555 #endif
7556
7557         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7558       }
7559       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7560       {
7561         if (!MovDelay[x][y])
7562         {
7563           MovDelay[x][y] = TILEY + 1;
7564
7565           ResetGfxAnimation(x, y);
7566           ResetGfxAnimation(x, y + 1);
7567         }
7568
7569         if (MovDelay[x][y])
7570         {
7571           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7572           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7573
7574           MovDelay[x][y]--;
7575           if (MovDelay[x][y])
7576             return;
7577         }
7578
7579         Feld[x][y] = EL_QUICKSAND_EMPTY;
7580         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7581         Store[x][y + 1] = Store[x][y];
7582         Store[x][y] = 0;
7583
7584         PlayLevelSoundAction(x, y, ACTION_FILLING);
7585       }
7586       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7587       {
7588         if (!MovDelay[x][y])
7589         {
7590           MovDelay[x][y] = TILEY + 1;
7591
7592           ResetGfxAnimation(x, y);
7593           ResetGfxAnimation(x, y + 1);
7594         }
7595
7596         if (MovDelay[x][y])
7597         {
7598           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7599           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7600
7601           MovDelay[x][y]--;
7602           if (MovDelay[x][y])
7603             return;
7604         }
7605
7606         Feld[x][y] = EL_QUICKSAND_EMPTY;
7607         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7608         Store[x][y + 1] = Store[x][y];
7609         Store[x][y] = 0;
7610
7611         PlayLevelSoundAction(x, y, ACTION_FILLING);
7612       }
7613     }
7614     else if (element == EL_QUICKSAND_FAST_FULL)
7615     {
7616       if (IS_FREE(x, y + 1))
7617       {
7618         InitMovingField(x, y, MV_DOWN);
7619         started_moving = TRUE;
7620
7621         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7622 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7623         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7624           Store[x][y] = EL_ROCK;
7625 #else
7626         Store[x][y] = EL_ROCK;
7627 #endif
7628
7629         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7630       }
7631       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7632       {
7633         if (!MovDelay[x][y])
7634         {
7635           MovDelay[x][y] = TILEY + 1;
7636
7637           ResetGfxAnimation(x, y);
7638           ResetGfxAnimation(x, y + 1);
7639         }
7640
7641         if (MovDelay[x][y])
7642         {
7643           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7644           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7645
7646           MovDelay[x][y]--;
7647           if (MovDelay[x][y])
7648             return;
7649         }
7650
7651         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7652         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7653         Store[x][y + 1] = Store[x][y];
7654         Store[x][y] = 0;
7655
7656         PlayLevelSoundAction(x, y, ACTION_FILLING);
7657       }
7658       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7659       {
7660         if (!MovDelay[x][y])
7661         {
7662           MovDelay[x][y] = TILEY + 1;
7663
7664           ResetGfxAnimation(x, y);
7665           ResetGfxAnimation(x, y + 1);
7666         }
7667
7668         if (MovDelay[x][y])
7669         {
7670           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7671           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7672
7673           MovDelay[x][y]--;
7674           if (MovDelay[x][y])
7675             return;
7676         }
7677
7678         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7679         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7680         Store[x][y + 1] = Store[x][y];
7681         Store[x][y] = 0;
7682
7683         PlayLevelSoundAction(x, y, ACTION_FILLING);
7684       }
7685     }
7686     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7687              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7688     {
7689       InitMovingField(x, y, MV_DOWN);
7690       started_moving = TRUE;
7691
7692       Feld[x][y] = EL_QUICKSAND_FILLING;
7693       Store[x][y] = element;
7694
7695       PlayLevelSoundAction(x, y, ACTION_FILLING);
7696     }
7697     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7698              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7699     {
7700       InitMovingField(x, y, MV_DOWN);
7701       started_moving = TRUE;
7702
7703       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7704       Store[x][y] = element;
7705
7706       PlayLevelSoundAction(x, y, ACTION_FILLING);
7707     }
7708     else if (element == EL_MAGIC_WALL_FULL)
7709     {
7710       if (IS_FREE(x, y + 1))
7711       {
7712         InitMovingField(x, y, MV_DOWN);
7713         started_moving = TRUE;
7714
7715         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7716         Store[x][y] = EL_CHANGED(Store[x][y]);
7717       }
7718       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7719       {
7720         if (!MovDelay[x][y])
7721           MovDelay[x][y] = TILEY / 4 + 1;
7722
7723         if (MovDelay[x][y])
7724         {
7725           MovDelay[x][y]--;
7726           if (MovDelay[x][y])
7727             return;
7728         }
7729
7730         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7731         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7732         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7733         Store[x][y] = 0;
7734       }
7735     }
7736     else if (element == EL_BD_MAGIC_WALL_FULL)
7737     {
7738       if (IS_FREE(x, y + 1))
7739       {
7740         InitMovingField(x, y, MV_DOWN);
7741         started_moving = TRUE;
7742
7743         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7744         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7745       }
7746       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7747       {
7748         if (!MovDelay[x][y])
7749           MovDelay[x][y] = TILEY / 4 + 1;
7750
7751         if (MovDelay[x][y])
7752         {
7753           MovDelay[x][y]--;
7754           if (MovDelay[x][y])
7755             return;
7756         }
7757
7758         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7759         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7760         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7761         Store[x][y] = 0;
7762       }
7763     }
7764     else if (element == EL_DC_MAGIC_WALL_FULL)
7765     {
7766       if (IS_FREE(x, y + 1))
7767       {
7768         InitMovingField(x, y, MV_DOWN);
7769         started_moving = TRUE;
7770
7771         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7772         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7773       }
7774       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7775       {
7776         if (!MovDelay[x][y])
7777           MovDelay[x][y] = TILEY / 4 + 1;
7778
7779         if (MovDelay[x][y])
7780         {
7781           MovDelay[x][y]--;
7782           if (MovDelay[x][y])
7783             return;
7784         }
7785
7786         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7787         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7788         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7789         Store[x][y] = 0;
7790       }
7791     }
7792     else if ((CAN_PASS_MAGIC_WALL(element) &&
7793               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7794                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7795              (CAN_PASS_DC_MAGIC_WALL(element) &&
7796               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7797
7798     {
7799       InitMovingField(x, y, MV_DOWN);
7800       started_moving = TRUE;
7801
7802       Feld[x][y] =
7803         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7804          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7805          EL_DC_MAGIC_WALL_FILLING);
7806       Store[x][y] = element;
7807     }
7808     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7809     {
7810       SplashAcid(x, y + 1);
7811
7812       InitMovingField(x, y, MV_DOWN);
7813       started_moving = TRUE;
7814
7815       Store[x][y] = EL_ACID;
7816     }
7817     else if (
7818              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7819               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7820              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7821               CAN_FALL(element) && WasJustFalling[x][y] &&
7822               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7823
7824              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7825               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7826               (Feld[x][y + 1] == EL_BLOCKED)))
7827     {
7828       /* this is needed for a special case not covered by calling "Impact()"
7829          from "ContinueMoving()": if an element moves to a tile directly below
7830          another element which was just falling on that tile (which was empty
7831          in the previous frame), the falling element above would just stop
7832          instead of smashing the element below (in previous version, the above
7833          element was just checked for "moving" instead of "falling", resulting
7834          in incorrect smashes caused by horizontal movement of the above
7835          element; also, the case of the player being the element to smash was
7836          simply not covered here... :-/ ) */
7837
7838       CheckCollision[x][y] = 0;
7839       CheckImpact[x][y] = 0;
7840
7841       Impact(x, y);
7842     }
7843     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7844     {
7845       if (MovDir[x][y] == MV_NONE)
7846       {
7847         InitMovingField(x, y, MV_DOWN);
7848         started_moving = TRUE;
7849       }
7850     }
7851     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7852     {
7853       if (WasJustFalling[x][y]) // prevent animation from being restarted
7854         MovDir[x][y] = MV_DOWN;
7855
7856       InitMovingField(x, y, MV_DOWN);
7857       started_moving = TRUE;
7858     }
7859     else if (element == EL_AMOEBA_DROP)
7860     {
7861       Feld[x][y] = EL_AMOEBA_GROWING;
7862       Store[x][y] = EL_AMOEBA_WET;
7863     }
7864     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7865               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7866              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7867              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7868     {
7869       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7870                                 (IS_FREE(x - 1, y + 1) ||
7871                                  Feld[x - 1][y + 1] == EL_ACID));
7872       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7873                                 (IS_FREE(x + 1, y + 1) ||
7874                                  Feld[x + 1][y + 1] == EL_ACID));
7875       boolean can_fall_any  = (can_fall_left || can_fall_right);
7876       boolean can_fall_both = (can_fall_left && can_fall_right);
7877       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7878
7879       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7880       {
7881         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7882           can_fall_right = FALSE;
7883         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7884           can_fall_left = FALSE;
7885         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7886           can_fall_right = FALSE;
7887         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7888           can_fall_left = FALSE;
7889
7890         can_fall_any  = (can_fall_left || can_fall_right);
7891         can_fall_both = FALSE;
7892       }
7893
7894       if (can_fall_both)
7895       {
7896         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7897           can_fall_right = FALSE;       // slip down on left side
7898         else
7899           can_fall_left = !(can_fall_right = RND(2));
7900
7901         can_fall_both = FALSE;
7902       }
7903
7904       if (can_fall_any)
7905       {
7906         // if not determined otherwise, prefer left side for slipping down
7907         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7908         started_moving = TRUE;
7909       }
7910     }
7911     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7912     {
7913       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7914       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7915       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7916       int belt_dir = game.belt_dir[belt_nr];
7917
7918       if ((belt_dir == MV_LEFT  && left_is_free) ||
7919           (belt_dir == MV_RIGHT && right_is_free))
7920       {
7921         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7922
7923         InitMovingField(x, y, belt_dir);
7924         started_moving = TRUE;
7925
7926         Pushed[x][y] = TRUE;
7927         Pushed[nextx][y] = TRUE;
7928
7929         GfxAction[x][y] = ACTION_DEFAULT;
7930       }
7931       else
7932       {
7933         MovDir[x][y] = 0;       // if element was moving, stop it
7934       }
7935     }
7936   }
7937
7938   // not "else if" because of elements that can fall and move (EL_SPRING)
7939   if (CAN_MOVE(element) && !started_moving)
7940   {
7941     int move_pattern = element_info[element].move_pattern;
7942     int newx, newy;
7943
7944     Moving2Blocked(x, y, &newx, &newy);
7945
7946     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7947       return;
7948
7949     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7950         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7951     {
7952       WasJustMoving[x][y] = 0;
7953       CheckCollision[x][y] = 0;
7954
7955       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7956
7957       if (Feld[x][y] != element)        // element has changed
7958         return;
7959     }
7960
7961     if (!MovDelay[x][y])        // start new movement phase
7962     {
7963       // all objects that can change their move direction after each step
7964       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7965
7966       if (element != EL_YAMYAM &&
7967           element != EL_DARK_YAMYAM &&
7968           element != EL_PACMAN &&
7969           !(move_pattern & MV_ANY_DIRECTION) &&
7970           move_pattern != MV_TURNING_LEFT &&
7971           move_pattern != MV_TURNING_RIGHT &&
7972           move_pattern != MV_TURNING_LEFT_RIGHT &&
7973           move_pattern != MV_TURNING_RIGHT_LEFT &&
7974           move_pattern != MV_TURNING_RANDOM)
7975       {
7976         TurnRound(x, y);
7977
7978         if (MovDelay[x][y] && (element == EL_BUG ||
7979                                element == EL_SPACESHIP ||
7980                                element == EL_SP_SNIKSNAK ||
7981                                element == EL_SP_ELECTRON ||
7982                                element == EL_MOLE))
7983           TEST_DrawLevelField(x, y);
7984       }
7985     }
7986
7987     if (MovDelay[x][y])         // wait some time before next movement
7988     {
7989       MovDelay[x][y]--;
7990
7991       if (element == EL_ROBOT ||
7992           element == EL_YAMYAM ||
7993           element == EL_DARK_YAMYAM)
7994       {
7995         DrawLevelElementAnimationIfNeeded(x, y, element);
7996         PlayLevelSoundAction(x, y, ACTION_WAITING);
7997       }
7998       else if (element == EL_SP_ELECTRON)
7999         DrawLevelElementAnimationIfNeeded(x, y, element);
8000       else if (element == EL_DRAGON)
8001       {
8002         int i;
8003         int dir = MovDir[x][y];
8004         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8005         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8006         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8007                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8008                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8009                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8010         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8011
8012         GfxAction[x][y] = ACTION_ATTACKING;
8013
8014         if (IS_PLAYER(x, y))
8015           DrawPlayerField(x, y);
8016         else
8017           TEST_DrawLevelField(x, y);
8018
8019         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8020
8021         for (i = 1; i <= 3; i++)
8022         {
8023           int xx = x + i * dx;
8024           int yy = y + i * dy;
8025           int sx = SCREENX(xx);
8026           int sy = SCREENY(yy);
8027           int flame_graphic = graphic + (i - 1);
8028
8029           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8030             break;
8031
8032           if (MovDelay[x][y])
8033           {
8034             int flamed = MovingOrBlocked2Element(xx, yy);
8035
8036             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8037               Bang(xx, yy);
8038             else
8039               RemoveMovingField(xx, yy);
8040
8041             ChangeDelay[xx][yy] = 0;
8042
8043             Feld[xx][yy] = EL_FLAMES;
8044
8045             if (IN_SCR_FIELD(sx, sy))
8046             {
8047               TEST_DrawLevelFieldCrumbled(xx, yy);
8048               DrawGraphic(sx, sy, flame_graphic, frame);
8049             }
8050           }
8051           else
8052           {
8053             if (Feld[xx][yy] == EL_FLAMES)
8054               Feld[xx][yy] = EL_EMPTY;
8055             TEST_DrawLevelField(xx, yy);
8056           }
8057         }
8058       }
8059
8060       if (MovDelay[x][y])       // element still has to wait some time
8061       {
8062         PlayLevelSoundAction(x, y, ACTION_WAITING);
8063
8064         return;
8065       }
8066     }
8067
8068     // now make next step
8069
8070     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8071
8072     if (DONT_COLLIDE_WITH(element) &&
8073         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8074         !PLAYER_ENEMY_PROTECTED(newx, newy))
8075     {
8076       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8077
8078       return;
8079     }
8080
8081     else if (CAN_MOVE_INTO_ACID(element) &&
8082              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8083              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8084              (MovDir[x][y] == MV_DOWN ||
8085               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8086     {
8087       SplashAcid(newx, newy);
8088       Store[x][y] = EL_ACID;
8089     }
8090     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8091     {
8092       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8093           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8094           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8095           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8096       {
8097         RemoveField(x, y);
8098         TEST_DrawLevelField(x, y);
8099
8100         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8101         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8102           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8103
8104         game.friends_still_needed--;
8105         if (!game.friends_still_needed &&
8106             !game.GameOver &&
8107             game.all_players_gone)
8108           LevelSolved();
8109
8110         return;
8111       }
8112       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8113       {
8114         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8115           TEST_DrawLevelField(newx, newy);
8116         else
8117           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8118       }
8119       else if (!IS_FREE(newx, newy))
8120       {
8121         GfxAction[x][y] = ACTION_WAITING;
8122
8123         if (IS_PLAYER(x, y))
8124           DrawPlayerField(x, y);
8125         else
8126           TEST_DrawLevelField(x, y);
8127
8128         return;
8129       }
8130     }
8131     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8132     {
8133       if (IS_FOOD_PIG(Feld[newx][newy]))
8134       {
8135         if (IS_MOVING(newx, newy))
8136           RemoveMovingField(newx, newy);
8137         else
8138         {
8139           Feld[newx][newy] = EL_EMPTY;
8140           TEST_DrawLevelField(newx, newy);
8141         }
8142
8143         PlayLevelSound(x, y, SND_PIG_DIGGING);
8144       }
8145       else if (!IS_FREE(newx, newy))
8146       {
8147         if (IS_PLAYER(x, y))
8148           DrawPlayerField(x, y);
8149         else
8150           TEST_DrawLevelField(x, y);
8151
8152         return;
8153       }
8154     }
8155     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8156     {
8157       if (Store[x][y] != EL_EMPTY)
8158       {
8159         boolean can_clone = FALSE;
8160         int xx, yy;
8161
8162         // check if element to clone is still there
8163         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8164         {
8165           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8166           {
8167             can_clone = TRUE;
8168
8169             break;
8170           }
8171         }
8172
8173         // cannot clone or target field not free anymore -- do not clone
8174         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8175           Store[x][y] = EL_EMPTY;
8176       }
8177
8178       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8179       {
8180         if (IS_MV_DIAGONAL(MovDir[x][y]))
8181         {
8182           int diagonal_move_dir = MovDir[x][y];
8183           int stored = Store[x][y];
8184           int change_delay = 8;
8185           int graphic;
8186
8187           // android is moving diagonally
8188
8189           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8190
8191           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8192           GfxElement[x][y] = EL_EMC_ANDROID;
8193           GfxAction[x][y] = ACTION_SHRINKING;
8194           GfxDir[x][y] = diagonal_move_dir;
8195           ChangeDelay[x][y] = change_delay;
8196
8197           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8198                                    GfxDir[x][y]);
8199
8200           DrawLevelGraphicAnimation(x, y, graphic);
8201           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8202
8203           if (Feld[newx][newy] == EL_ACID)
8204           {
8205             SplashAcid(newx, newy);
8206
8207             return;
8208           }
8209
8210           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8211
8212           Store[newx][newy] = EL_EMC_ANDROID;
8213           GfxElement[newx][newy] = EL_EMC_ANDROID;
8214           GfxAction[newx][newy] = ACTION_GROWING;
8215           GfxDir[newx][newy] = diagonal_move_dir;
8216           ChangeDelay[newx][newy] = change_delay;
8217
8218           graphic = el_act_dir2img(GfxElement[newx][newy],
8219                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8220
8221           DrawLevelGraphicAnimation(newx, newy, graphic);
8222           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8223
8224           return;
8225         }
8226         else
8227         {
8228           Feld[newx][newy] = EL_EMPTY;
8229           TEST_DrawLevelField(newx, newy);
8230
8231           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8232         }
8233       }
8234       else if (!IS_FREE(newx, newy))
8235       {
8236         return;
8237       }
8238     }
8239     else if (IS_CUSTOM_ELEMENT(element) &&
8240              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8241     {
8242       if (!DigFieldByCE(newx, newy, element))
8243         return;
8244
8245       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8246       {
8247         RunnerVisit[x][y] = FrameCounter;
8248         PlayerVisit[x][y] /= 8;         // expire player visit path
8249       }
8250     }
8251     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8252     {
8253       if (!IS_FREE(newx, newy))
8254       {
8255         if (IS_PLAYER(x, y))
8256           DrawPlayerField(x, y);
8257         else
8258           TEST_DrawLevelField(x, y);
8259
8260         return;
8261       }
8262       else
8263       {
8264         boolean wanna_flame = !RND(10);
8265         int dx = newx - x, dy = newy - y;
8266         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8267         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8268         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8269                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8270         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8271                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8272
8273         if ((wanna_flame ||
8274              IS_CLASSIC_ENEMY(element1) ||
8275              IS_CLASSIC_ENEMY(element2)) &&
8276             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8277             element1 != EL_FLAMES && element2 != EL_FLAMES)
8278         {
8279           ResetGfxAnimation(x, y);
8280           GfxAction[x][y] = ACTION_ATTACKING;
8281
8282           if (IS_PLAYER(x, y))
8283             DrawPlayerField(x, y);
8284           else
8285             TEST_DrawLevelField(x, y);
8286
8287           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8288
8289           MovDelay[x][y] = 50;
8290
8291           Feld[newx][newy] = EL_FLAMES;
8292           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8293             Feld[newx1][newy1] = EL_FLAMES;
8294           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8295             Feld[newx2][newy2] = EL_FLAMES;
8296
8297           return;
8298         }
8299       }
8300     }
8301     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8302              Feld[newx][newy] == EL_DIAMOND)
8303     {
8304       if (IS_MOVING(newx, newy))
8305         RemoveMovingField(newx, newy);
8306       else
8307       {
8308         Feld[newx][newy] = EL_EMPTY;
8309         TEST_DrawLevelField(newx, newy);
8310       }
8311
8312       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8313     }
8314     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8315              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8316     {
8317       if (AmoebaNr[newx][newy])
8318       {
8319         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8320         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8321             Feld[newx][newy] == EL_BD_AMOEBA)
8322           AmoebaCnt[AmoebaNr[newx][newy]]--;
8323       }
8324
8325       if (IS_MOVING(newx, newy))
8326       {
8327         RemoveMovingField(newx, newy);
8328       }
8329       else
8330       {
8331         Feld[newx][newy] = EL_EMPTY;
8332         TEST_DrawLevelField(newx, newy);
8333       }
8334
8335       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8336     }
8337     else if ((element == EL_PACMAN || element == EL_MOLE)
8338              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8339     {
8340       if (AmoebaNr[newx][newy])
8341       {
8342         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8343         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8344             Feld[newx][newy] == EL_BD_AMOEBA)
8345           AmoebaCnt[AmoebaNr[newx][newy]]--;
8346       }
8347
8348       if (element == EL_MOLE)
8349       {
8350         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8351         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8352
8353         ResetGfxAnimation(x, y);
8354         GfxAction[x][y] = ACTION_DIGGING;
8355         TEST_DrawLevelField(x, y);
8356
8357         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8358
8359         return;                         // wait for shrinking amoeba
8360       }
8361       else      // element == EL_PACMAN
8362       {
8363         Feld[newx][newy] = EL_EMPTY;
8364         TEST_DrawLevelField(newx, newy);
8365         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8366       }
8367     }
8368     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8369              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8370               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8371     {
8372       // wait for shrinking amoeba to completely disappear
8373       return;
8374     }
8375     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8376     {
8377       // object was running against a wall
8378
8379       TurnRound(x, y);
8380
8381       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8382         DrawLevelElementAnimation(x, y, element);
8383
8384       if (DONT_TOUCH(element))
8385         TestIfBadThingTouchesPlayer(x, y);
8386
8387       return;
8388     }
8389
8390     InitMovingField(x, y, MovDir[x][y]);
8391
8392     PlayLevelSoundAction(x, y, ACTION_MOVING);
8393   }
8394
8395   if (MovDir[x][y])
8396     ContinueMoving(x, y);
8397 }
8398
8399 void ContinueMoving(int x, int y)
8400 {
8401   int element = Feld[x][y];
8402   struct ElementInfo *ei = &element_info[element];
8403   int direction = MovDir[x][y];
8404   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8405   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8406   int newx = x + dx, newy = y + dy;
8407   int stored = Store[x][y];
8408   int stored_new = Store[newx][newy];
8409   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8410   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8411   boolean last_line = (newy == lev_fieldy - 1);
8412
8413   MovPos[x][y] += getElementMoveStepsize(x, y);
8414
8415   if (pushed_by_player) // special case: moving object pushed by player
8416     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8417
8418   if (ABS(MovPos[x][y]) < TILEX)
8419   {
8420     TEST_DrawLevelField(x, y);
8421
8422     return;     // element is still moving
8423   }
8424
8425   // element reached destination field
8426
8427   Feld[x][y] = EL_EMPTY;
8428   Feld[newx][newy] = element;
8429   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8430
8431   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8432   {
8433     element = Feld[newx][newy] = EL_ACID;
8434   }
8435   else if (element == EL_MOLE)
8436   {
8437     Feld[x][y] = EL_SAND;
8438
8439     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8440   }
8441   else if (element == EL_QUICKSAND_FILLING)
8442   {
8443     element = Feld[newx][newy] = get_next_element(element);
8444     Store[newx][newy] = Store[x][y];
8445   }
8446   else if (element == EL_QUICKSAND_EMPTYING)
8447   {
8448     Feld[x][y] = get_next_element(element);
8449     element = Feld[newx][newy] = Store[x][y];
8450   }
8451   else if (element == EL_QUICKSAND_FAST_FILLING)
8452   {
8453     element = Feld[newx][newy] = get_next_element(element);
8454     Store[newx][newy] = Store[x][y];
8455   }
8456   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8457   {
8458     Feld[x][y] = get_next_element(element);
8459     element = Feld[newx][newy] = Store[x][y];
8460   }
8461   else if (element == EL_MAGIC_WALL_FILLING)
8462   {
8463     element = Feld[newx][newy] = get_next_element(element);
8464     if (!game.magic_wall_active)
8465       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8466     Store[newx][newy] = Store[x][y];
8467   }
8468   else if (element == EL_MAGIC_WALL_EMPTYING)
8469   {
8470     Feld[x][y] = get_next_element(element);
8471     if (!game.magic_wall_active)
8472       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8473     element = Feld[newx][newy] = Store[x][y];
8474
8475     InitField(newx, newy, FALSE);
8476   }
8477   else if (element == EL_BD_MAGIC_WALL_FILLING)
8478   {
8479     element = Feld[newx][newy] = get_next_element(element);
8480     if (!game.magic_wall_active)
8481       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8482     Store[newx][newy] = Store[x][y];
8483   }
8484   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8485   {
8486     Feld[x][y] = get_next_element(element);
8487     if (!game.magic_wall_active)
8488       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8489     element = Feld[newx][newy] = Store[x][y];
8490
8491     InitField(newx, newy, FALSE);
8492   }
8493   else if (element == EL_DC_MAGIC_WALL_FILLING)
8494   {
8495     element = Feld[newx][newy] = get_next_element(element);
8496     if (!game.magic_wall_active)
8497       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8498     Store[newx][newy] = Store[x][y];
8499   }
8500   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8501   {
8502     Feld[x][y] = get_next_element(element);
8503     if (!game.magic_wall_active)
8504       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8505     element = Feld[newx][newy] = Store[x][y];
8506
8507     InitField(newx, newy, FALSE);
8508   }
8509   else if (element == EL_AMOEBA_DROPPING)
8510   {
8511     Feld[x][y] = get_next_element(element);
8512     element = Feld[newx][newy] = Store[x][y];
8513   }
8514   else if (element == EL_SOKOBAN_OBJECT)
8515   {
8516     if (Back[x][y])
8517       Feld[x][y] = Back[x][y];
8518
8519     if (Back[newx][newy])
8520       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8521
8522     Back[x][y] = Back[newx][newy] = 0;
8523   }
8524
8525   Store[x][y] = EL_EMPTY;
8526   MovPos[x][y] = 0;
8527   MovDir[x][y] = 0;
8528   MovDelay[x][y] = 0;
8529
8530   MovDelay[newx][newy] = 0;
8531
8532   if (CAN_CHANGE_OR_HAS_ACTION(element))
8533   {
8534     // copy element change control values to new field
8535     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8536     ChangePage[newx][newy]  = ChangePage[x][y];
8537     ChangeCount[newx][newy] = ChangeCount[x][y];
8538     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8539   }
8540
8541   CustomValue[newx][newy] = CustomValue[x][y];
8542
8543   ChangeDelay[x][y] = 0;
8544   ChangePage[x][y] = -1;
8545   ChangeCount[x][y] = 0;
8546   ChangeEvent[x][y] = -1;
8547
8548   CustomValue[x][y] = 0;
8549
8550   // copy animation control values to new field
8551   GfxFrame[newx][newy]  = GfxFrame[x][y];
8552   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8553   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8554   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8555
8556   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8557
8558   // some elements can leave other elements behind after moving
8559   if (ei->move_leave_element != EL_EMPTY &&
8560       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8561       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8562   {
8563     int move_leave_element = ei->move_leave_element;
8564
8565     // this makes it possible to leave the removed element again
8566     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8567       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8568
8569     Feld[x][y] = move_leave_element;
8570
8571     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8572       MovDir[x][y] = direction;
8573
8574     InitField(x, y, FALSE);
8575
8576     if (GFX_CRUMBLED(Feld[x][y]))
8577       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8578
8579     if (ELEM_IS_PLAYER(move_leave_element))
8580       RelocatePlayer(x, y, move_leave_element);
8581   }
8582
8583   // do this after checking for left-behind element
8584   ResetGfxAnimation(x, y);      // reset animation values for old field
8585
8586   if (!CAN_MOVE(element) ||
8587       (CAN_FALL(element) && direction == MV_DOWN &&
8588        (element == EL_SPRING ||
8589         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8590         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8591     GfxDir[x][y] = MovDir[newx][newy] = 0;
8592
8593   TEST_DrawLevelField(x, y);
8594   TEST_DrawLevelField(newx, newy);
8595
8596   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8597
8598   // prevent pushed element from moving on in pushed direction
8599   if (pushed_by_player && CAN_MOVE(element) &&
8600       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8601       !(element_info[element].move_pattern & direction))
8602     TurnRound(newx, newy);
8603
8604   // prevent elements on conveyor belt from moving on in last direction
8605   if (pushed_by_conveyor && CAN_FALL(element) &&
8606       direction & MV_HORIZONTAL)
8607     MovDir[newx][newy] = 0;
8608
8609   if (!pushed_by_player)
8610   {
8611     int nextx = newx + dx, nexty = newy + dy;
8612     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8613
8614     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8615
8616     if (CAN_FALL(element) && direction == MV_DOWN)
8617       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8618
8619     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8620       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8621
8622     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8623       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8624   }
8625
8626   if (DONT_TOUCH(element))      // object may be nasty to player or others
8627   {
8628     TestIfBadThingTouchesPlayer(newx, newy);
8629     TestIfBadThingTouchesFriend(newx, newy);
8630
8631     if (!IS_CUSTOM_ELEMENT(element))
8632       TestIfBadThingTouchesOtherBadThing(newx, newy);
8633   }
8634   else if (element == EL_PENGUIN)
8635     TestIfFriendTouchesBadThing(newx, newy);
8636
8637   if (DONT_GET_HIT_BY(element))
8638   {
8639     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8640   }
8641
8642   // give the player one last chance (one more frame) to move away
8643   if (CAN_FALL(element) && direction == MV_DOWN &&
8644       (last_line || (!IS_FREE(x, newy + 1) &&
8645                      (!IS_PLAYER(x, newy + 1) ||
8646                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8647     Impact(x, newy);
8648
8649   if (pushed_by_player && !game.use_change_when_pushing_bug)
8650   {
8651     int push_side = MV_DIR_OPPOSITE(direction);
8652     struct PlayerInfo *player = PLAYERINFO(x, y);
8653
8654     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8655                                player->index_bit, push_side);
8656     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8657                                         player->index_bit, push_side);
8658   }
8659
8660   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8661     MovDelay[newx][newy] = 1;
8662
8663   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8664
8665   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8666   TestIfElementHitsCustomElement(newx, newy, direction);
8667   TestIfPlayerTouchesCustomElement(newx, newy);
8668   TestIfElementTouchesCustomElement(newx, newy);
8669
8670   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8671       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8672     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8673                              MV_DIR_OPPOSITE(direction));
8674 }
8675
8676 int AmoebeNachbarNr(int ax, int ay)
8677 {
8678   int i;
8679   int element = Feld[ax][ay];
8680   int group_nr = 0;
8681   static int xy[4][2] =
8682   {
8683     { 0, -1 },
8684     { -1, 0 },
8685     { +1, 0 },
8686     { 0, +1 }
8687   };
8688
8689   for (i = 0; i < NUM_DIRECTIONS; i++)
8690   {
8691     int x = ax + xy[i][0];
8692     int y = ay + xy[i][1];
8693
8694     if (!IN_LEV_FIELD(x, y))
8695       continue;
8696
8697     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8698       group_nr = AmoebaNr[x][y];
8699   }
8700
8701   return group_nr;
8702 }
8703
8704 static void AmoebenVereinigen(int ax, int ay)
8705 {
8706   int i, x, y, xx, yy;
8707   int new_group_nr = AmoebaNr[ax][ay];
8708   static int xy[4][2] =
8709   {
8710     { 0, -1 },
8711     { -1, 0 },
8712     { +1, 0 },
8713     { 0, +1 }
8714   };
8715
8716   if (new_group_nr == 0)
8717     return;
8718
8719   for (i = 0; i < NUM_DIRECTIONS; i++)
8720   {
8721     x = ax + xy[i][0];
8722     y = ay + xy[i][1];
8723
8724     if (!IN_LEV_FIELD(x, y))
8725       continue;
8726
8727     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8728          Feld[x][y] == EL_BD_AMOEBA ||
8729          Feld[x][y] == EL_AMOEBA_DEAD) &&
8730         AmoebaNr[x][y] != new_group_nr)
8731     {
8732       int old_group_nr = AmoebaNr[x][y];
8733
8734       if (old_group_nr == 0)
8735         return;
8736
8737       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8738       AmoebaCnt[old_group_nr] = 0;
8739       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8740       AmoebaCnt2[old_group_nr] = 0;
8741
8742       SCAN_PLAYFIELD(xx, yy)
8743       {
8744         if (AmoebaNr[xx][yy] == old_group_nr)
8745           AmoebaNr[xx][yy] = new_group_nr;
8746       }
8747     }
8748   }
8749 }
8750
8751 void AmoebeUmwandeln(int ax, int ay)
8752 {
8753   int i, x, y;
8754
8755   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8756   {
8757     int group_nr = AmoebaNr[ax][ay];
8758
8759 #ifdef DEBUG
8760     if (group_nr == 0)
8761     {
8762       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8763       printf("AmoebeUmwandeln(): This should never happen!\n");
8764       return;
8765     }
8766 #endif
8767
8768     SCAN_PLAYFIELD(x, y)
8769     {
8770       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8771       {
8772         AmoebaNr[x][y] = 0;
8773         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8774       }
8775     }
8776
8777     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8778                             SND_AMOEBA_TURNING_TO_GEM :
8779                             SND_AMOEBA_TURNING_TO_ROCK));
8780     Bang(ax, ay);
8781   }
8782   else
8783   {
8784     static int xy[4][2] =
8785     {
8786       { 0, -1 },
8787       { -1, 0 },
8788       { +1, 0 },
8789       { 0, +1 }
8790     };
8791
8792     for (i = 0; i < NUM_DIRECTIONS; i++)
8793     {
8794       x = ax + xy[i][0];
8795       y = ay + xy[i][1];
8796
8797       if (!IN_LEV_FIELD(x, y))
8798         continue;
8799
8800       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8801       {
8802         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8803                               SND_AMOEBA_TURNING_TO_GEM :
8804                               SND_AMOEBA_TURNING_TO_ROCK));
8805         Bang(x, y);
8806       }
8807     }
8808   }
8809 }
8810
8811 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8812 {
8813   int x, y;
8814   int group_nr = AmoebaNr[ax][ay];
8815   boolean done = FALSE;
8816
8817 #ifdef DEBUG
8818   if (group_nr == 0)
8819   {
8820     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8821     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8822     return;
8823   }
8824 #endif
8825
8826   SCAN_PLAYFIELD(x, y)
8827   {
8828     if (AmoebaNr[x][y] == group_nr &&
8829         (Feld[x][y] == EL_AMOEBA_DEAD ||
8830          Feld[x][y] == EL_BD_AMOEBA ||
8831          Feld[x][y] == EL_AMOEBA_GROWING))
8832     {
8833       AmoebaNr[x][y] = 0;
8834       Feld[x][y] = new_element;
8835       InitField(x, y, FALSE);
8836       TEST_DrawLevelField(x, y);
8837       done = TRUE;
8838     }
8839   }
8840
8841   if (done)
8842     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8843                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8844                             SND_BD_AMOEBA_TURNING_TO_GEM));
8845 }
8846
8847 static void AmoebeWaechst(int x, int y)
8848 {
8849   static unsigned int sound_delay = 0;
8850   static unsigned int sound_delay_value = 0;
8851
8852   if (!MovDelay[x][y])          // start new growing cycle
8853   {
8854     MovDelay[x][y] = 7;
8855
8856     if (DelayReached(&sound_delay, sound_delay_value))
8857     {
8858       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8859       sound_delay_value = 30;
8860     }
8861   }
8862
8863   if (MovDelay[x][y])           // wait some time before growing bigger
8864   {
8865     MovDelay[x][y]--;
8866     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8867     {
8868       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8869                                            6 - MovDelay[x][y]);
8870
8871       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8872     }
8873
8874     if (!MovDelay[x][y])
8875     {
8876       Feld[x][y] = Store[x][y];
8877       Store[x][y] = 0;
8878       TEST_DrawLevelField(x, y);
8879     }
8880   }
8881 }
8882
8883 static void AmoebaDisappearing(int x, int y)
8884 {
8885   static unsigned int sound_delay = 0;
8886   static unsigned int sound_delay_value = 0;
8887
8888   if (!MovDelay[x][y])          // start new shrinking cycle
8889   {
8890     MovDelay[x][y] = 7;
8891
8892     if (DelayReached(&sound_delay, sound_delay_value))
8893       sound_delay_value = 30;
8894   }
8895
8896   if (MovDelay[x][y])           // wait some time before shrinking
8897   {
8898     MovDelay[x][y]--;
8899     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8900     {
8901       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8902                                            6 - MovDelay[x][y]);
8903
8904       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8905     }
8906
8907     if (!MovDelay[x][y])
8908     {
8909       Feld[x][y] = EL_EMPTY;
8910       TEST_DrawLevelField(x, y);
8911
8912       // don't let mole enter this field in this cycle;
8913       // (give priority to objects falling to this field from above)
8914       Stop[x][y] = TRUE;
8915     }
8916   }
8917 }
8918
8919 static void AmoebeAbleger(int ax, int ay)
8920 {
8921   int i;
8922   int element = Feld[ax][ay];
8923   int graphic = el2img(element);
8924   int newax = ax, neway = ay;
8925   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8926   static int xy[4][2] =
8927   {
8928     { 0, -1 },
8929     { -1, 0 },
8930     { +1, 0 },
8931     { 0, +1 }
8932   };
8933
8934   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8935   {
8936     Feld[ax][ay] = EL_AMOEBA_DEAD;
8937     TEST_DrawLevelField(ax, ay);
8938     return;
8939   }
8940
8941   if (IS_ANIMATED(graphic))
8942     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8943
8944   if (!MovDelay[ax][ay])        // start making new amoeba field
8945     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8946
8947   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8948   {
8949     MovDelay[ax][ay]--;
8950     if (MovDelay[ax][ay])
8951       return;
8952   }
8953
8954   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8955   {
8956     int start = RND(4);
8957     int x = ax + xy[start][0];
8958     int y = ay + xy[start][1];
8959
8960     if (!IN_LEV_FIELD(x, y))
8961       return;
8962
8963     if (IS_FREE(x, y) ||
8964         CAN_GROW_INTO(Feld[x][y]) ||
8965         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8966         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8967     {
8968       newax = x;
8969       neway = y;
8970     }
8971
8972     if (newax == ax && neway == ay)
8973       return;
8974   }
8975   else                          // normal or "filled" (BD style) amoeba
8976   {
8977     int start = RND(4);
8978     boolean waiting_for_player = FALSE;
8979
8980     for (i = 0; i < NUM_DIRECTIONS; i++)
8981     {
8982       int j = (start + i) % 4;
8983       int x = ax + xy[j][0];
8984       int y = ay + xy[j][1];
8985
8986       if (!IN_LEV_FIELD(x, y))
8987         continue;
8988
8989       if (IS_FREE(x, y) ||
8990           CAN_GROW_INTO(Feld[x][y]) ||
8991           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8992           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8993       {
8994         newax = x;
8995         neway = y;
8996         break;
8997       }
8998       else if (IS_PLAYER(x, y))
8999         waiting_for_player = TRUE;
9000     }
9001
9002     if (newax == ax && neway == ay)             // amoeba cannot grow
9003     {
9004       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9005       {
9006         Feld[ax][ay] = EL_AMOEBA_DEAD;
9007         TEST_DrawLevelField(ax, ay);
9008         AmoebaCnt[AmoebaNr[ax][ay]]--;
9009
9010         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9011         {
9012           if (element == EL_AMOEBA_FULL)
9013             AmoebeUmwandeln(ax, ay);
9014           else if (element == EL_BD_AMOEBA)
9015             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9016         }
9017       }
9018       return;
9019     }
9020     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9021     {
9022       // amoeba gets larger by growing in some direction
9023
9024       int new_group_nr = AmoebaNr[ax][ay];
9025
9026 #ifdef DEBUG
9027   if (new_group_nr == 0)
9028   {
9029     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9030     printf("AmoebeAbleger(): This should never happen!\n");
9031     return;
9032   }
9033 #endif
9034
9035       AmoebaNr[newax][neway] = new_group_nr;
9036       AmoebaCnt[new_group_nr]++;
9037       AmoebaCnt2[new_group_nr]++;
9038
9039       // if amoeba touches other amoeba(s) after growing, unify them
9040       AmoebenVereinigen(newax, neway);
9041
9042       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9043       {
9044         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9045         return;
9046       }
9047     }
9048   }
9049
9050   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9051       (neway == lev_fieldy - 1 && newax != ax))
9052   {
9053     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9054     Store[newax][neway] = element;
9055   }
9056   else if (neway == ay || element == EL_EMC_DRIPPER)
9057   {
9058     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9059
9060     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9061   }
9062   else
9063   {
9064     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9065     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9066     Store[ax][ay] = EL_AMOEBA_DROP;
9067     ContinueMoving(ax, ay);
9068     return;
9069   }
9070
9071   TEST_DrawLevelField(newax, neway);
9072 }
9073
9074 static void Life(int ax, int ay)
9075 {
9076   int x1, y1, x2, y2;
9077   int life_time = 40;
9078   int element = Feld[ax][ay];
9079   int graphic = el2img(element);
9080   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9081                          level.biomaze);
9082   boolean changed = FALSE;
9083
9084   if (IS_ANIMATED(graphic))
9085     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9086
9087   if (Stop[ax][ay])
9088     return;
9089
9090   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9091     MovDelay[ax][ay] = life_time;
9092
9093   if (MovDelay[ax][ay])         // wait some time before next cycle
9094   {
9095     MovDelay[ax][ay]--;
9096     if (MovDelay[ax][ay])
9097       return;
9098   }
9099
9100   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9101   {
9102     int xx = ax+x1, yy = ay+y1;
9103     int old_element = Feld[xx][yy];
9104     int num_neighbours = 0;
9105
9106     if (!IN_LEV_FIELD(xx, yy))
9107       continue;
9108
9109     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9110     {
9111       int x = xx+x2, y = yy+y2;
9112
9113       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9114         continue;
9115
9116       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9117       boolean is_neighbour = FALSE;
9118
9119       if (level.use_life_bugs)
9120         is_neighbour =
9121           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9122            (IS_FREE(x, y)                             &&  Stop[x][y]));
9123       else
9124         is_neighbour =
9125           (Last[x][y] == element || is_player_cell);
9126
9127       if (is_neighbour)
9128         num_neighbours++;
9129     }
9130
9131     boolean is_free = FALSE;
9132
9133     if (level.use_life_bugs)
9134       is_free = (IS_FREE(xx, yy));
9135     else
9136       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9137
9138     if (xx == ax && yy == ay)           // field in the middle
9139     {
9140       if (num_neighbours < life_parameter[0] ||
9141           num_neighbours > life_parameter[1])
9142       {
9143         Feld[xx][yy] = EL_EMPTY;
9144         if (Feld[xx][yy] != old_element)
9145           TEST_DrawLevelField(xx, yy);
9146         Stop[xx][yy] = TRUE;
9147         changed = TRUE;
9148       }
9149     }
9150     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9151     {                                   // free border field
9152       if (num_neighbours >= life_parameter[2] &&
9153           num_neighbours <= life_parameter[3])
9154       {
9155         Feld[xx][yy] = element;
9156         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9157         if (Feld[xx][yy] != old_element)
9158           TEST_DrawLevelField(xx, yy);
9159         Stop[xx][yy] = TRUE;
9160         changed = TRUE;
9161       }
9162     }
9163   }
9164
9165   if (changed)
9166     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9167                    SND_GAME_OF_LIFE_GROWING);
9168 }
9169
9170 static void InitRobotWheel(int x, int y)
9171 {
9172   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9173 }
9174
9175 static void RunRobotWheel(int x, int y)
9176 {
9177   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9178 }
9179
9180 static void StopRobotWheel(int x, int y)
9181 {
9182   if (game.robot_wheel_x == x &&
9183       game.robot_wheel_y == y)
9184   {
9185     game.robot_wheel_x = -1;
9186     game.robot_wheel_y = -1;
9187     game.robot_wheel_active = FALSE;
9188   }
9189 }
9190
9191 static void InitTimegateWheel(int x, int y)
9192 {
9193   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9194 }
9195
9196 static void RunTimegateWheel(int x, int y)
9197 {
9198   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9199 }
9200
9201 static void InitMagicBallDelay(int x, int y)
9202 {
9203   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9204 }
9205
9206 static void ActivateMagicBall(int bx, int by)
9207 {
9208   int x, y;
9209
9210   if (level.ball_random)
9211   {
9212     int pos_border = RND(8);    // select one of the eight border elements
9213     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9214     int xx = pos_content % 3;
9215     int yy = pos_content / 3;
9216
9217     x = bx - 1 + xx;
9218     y = by - 1 + yy;
9219
9220     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9221       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9222   }
9223   else
9224   {
9225     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9226     {
9227       int xx = x - bx + 1;
9228       int yy = y - by + 1;
9229
9230       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9231         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9232     }
9233   }
9234
9235   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9236 }
9237
9238 static void CheckExit(int x, int y)
9239 {
9240   if (game.gems_still_needed > 0 ||
9241       game.sokoban_fields_still_needed > 0 ||
9242       game.sokoban_objects_still_needed > 0 ||
9243       game.lights_still_needed > 0)
9244   {
9245     int element = Feld[x][y];
9246     int graphic = el2img(element);
9247
9248     if (IS_ANIMATED(graphic))
9249       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9250
9251     return;
9252   }
9253
9254   // do not re-open exit door closed after last player
9255   if (game.all_players_gone)
9256     return;
9257
9258   Feld[x][y] = EL_EXIT_OPENING;
9259
9260   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9261 }
9262
9263 static void CheckExitEM(int x, int y)
9264 {
9265   if (game.gems_still_needed > 0 ||
9266       game.sokoban_fields_still_needed > 0 ||
9267       game.sokoban_objects_still_needed > 0 ||
9268       game.lights_still_needed > 0)
9269   {
9270     int element = Feld[x][y];
9271     int graphic = el2img(element);
9272
9273     if (IS_ANIMATED(graphic))
9274       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9275
9276     return;
9277   }
9278
9279   // do not re-open exit door closed after last player
9280   if (game.all_players_gone)
9281     return;
9282
9283   Feld[x][y] = EL_EM_EXIT_OPENING;
9284
9285   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9286 }
9287
9288 static void CheckExitSteel(int x, int y)
9289 {
9290   if (game.gems_still_needed > 0 ||
9291       game.sokoban_fields_still_needed > 0 ||
9292       game.sokoban_objects_still_needed > 0 ||
9293       game.lights_still_needed > 0)
9294   {
9295     int element = Feld[x][y];
9296     int graphic = el2img(element);
9297
9298     if (IS_ANIMATED(graphic))
9299       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9300
9301     return;
9302   }
9303
9304   // do not re-open exit door closed after last player
9305   if (game.all_players_gone)
9306     return;
9307
9308   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9309
9310   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9311 }
9312
9313 static void CheckExitSteelEM(int x, int y)
9314 {
9315   if (game.gems_still_needed > 0 ||
9316       game.sokoban_fields_still_needed > 0 ||
9317       game.sokoban_objects_still_needed > 0 ||
9318       game.lights_still_needed > 0)
9319   {
9320     int element = Feld[x][y];
9321     int graphic = el2img(element);
9322
9323     if (IS_ANIMATED(graphic))
9324       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9325
9326     return;
9327   }
9328
9329   // do not re-open exit door closed after last player
9330   if (game.all_players_gone)
9331     return;
9332
9333   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9334
9335   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9336 }
9337
9338 static void CheckExitSP(int x, int y)
9339 {
9340   if (game.gems_still_needed > 0)
9341   {
9342     int element = Feld[x][y];
9343     int graphic = el2img(element);
9344
9345     if (IS_ANIMATED(graphic))
9346       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9347
9348     return;
9349   }
9350
9351   // do not re-open exit door closed after last player
9352   if (game.all_players_gone)
9353     return;
9354
9355   Feld[x][y] = EL_SP_EXIT_OPENING;
9356
9357   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9358 }
9359
9360 static void CloseAllOpenTimegates(void)
9361 {
9362   int x, y;
9363
9364   SCAN_PLAYFIELD(x, y)
9365   {
9366     int element = Feld[x][y];
9367
9368     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9369     {
9370       Feld[x][y] = EL_TIMEGATE_CLOSING;
9371
9372       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9373     }
9374   }
9375 }
9376
9377 static void DrawTwinkleOnField(int x, int y)
9378 {
9379   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9380     return;
9381
9382   if (Feld[x][y] == EL_BD_DIAMOND)
9383     return;
9384
9385   if (MovDelay[x][y] == 0)      // next animation frame
9386     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9387
9388   if (MovDelay[x][y] != 0)      // wait some time before next frame
9389   {
9390     MovDelay[x][y]--;
9391
9392     DrawLevelElementAnimation(x, y, Feld[x][y]);
9393
9394     if (MovDelay[x][y] != 0)
9395     {
9396       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9397                                            10 - MovDelay[x][y]);
9398
9399       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9400     }
9401   }
9402 }
9403
9404 static void MauerWaechst(int x, int y)
9405 {
9406   int delay = 6;
9407
9408   if (!MovDelay[x][y])          // next animation frame
9409     MovDelay[x][y] = 3 * delay;
9410
9411   if (MovDelay[x][y])           // wait some time before next frame
9412   {
9413     MovDelay[x][y]--;
9414
9415     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9416     {
9417       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9418       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9419
9420       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9421     }
9422
9423     if (!MovDelay[x][y])
9424     {
9425       if (MovDir[x][y] == MV_LEFT)
9426       {
9427         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9428           TEST_DrawLevelField(x - 1, y);
9429       }
9430       else if (MovDir[x][y] == MV_RIGHT)
9431       {
9432         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9433           TEST_DrawLevelField(x + 1, y);
9434       }
9435       else if (MovDir[x][y] == MV_UP)
9436       {
9437         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9438           TEST_DrawLevelField(x, y - 1);
9439       }
9440       else
9441       {
9442         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9443           TEST_DrawLevelField(x, y + 1);
9444       }
9445
9446       Feld[x][y] = Store[x][y];
9447       Store[x][y] = 0;
9448       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9449       TEST_DrawLevelField(x, y);
9450     }
9451   }
9452 }
9453
9454 static void MauerAbleger(int ax, int ay)
9455 {
9456   int element = Feld[ax][ay];
9457   int graphic = el2img(element);
9458   boolean oben_frei = FALSE, unten_frei = FALSE;
9459   boolean links_frei = FALSE, rechts_frei = FALSE;
9460   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9461   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9462   boolean new_wall = FALSE;
9463
9464   if (IS_ANIMATED(graphic))
9465     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9466
9467   if (!MovDelay[ax][ay])        // start building new wall
9468     MovDelay[ax][ay] = 6;
9469
9470   if (MovDelay[ax][ay])         // wait some time before building new wall
9471   {
9472     MovDelay[ax][ay]--;
9473     if (MovDelay[ax][ay])
9474       return;
9475   }
9476
9477   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9478     oben_frei = TRUE;
9479   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9480     unten_frei = TRUE;
9481   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9482     links_frei = TRUE;
9483   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9484     rechts_frei = TRUE;
9485
9486   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9487       element == EL_EXPANDABLE_WALL_ANY)
9488   {
9489     if (oben_frei)
9490     {
9491       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9492       Store[ax][ay-1] = element;
9493       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9494       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9495         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9496                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9497       new_wall = TRUE;
9498     }
9499     if (unten_frei)
9500     {
9501       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9502       Store[ax][ay+1] = element;
9503       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9504       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9505         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9506                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9507       new_wall = TRUE;
9508     }
9509   }
9510
9511   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9512       element == EL_EXPANDABLE_WALL_ANY ||
9513       element == EL_EXPANDABLE_WALL ||
9514       element == EL_BD_EXPANDABLE_WALL)
9515   {
9516     if (links_frei)
9517     {
9518       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9519       Store[ax-1][ay] = element;
9520       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9521       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9522         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9523                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9524       new_wall = TRUE;
9525     }
9526
9527     if (rechts_frei)
9528     {
9529       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9530       Store[ax+1][ay] = element;
9531       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9532       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9533         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9534                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9535       new_wall = TRUE;
9536     }
9537   }
9538
9539   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9540     TEST_DrawLevelField(ax, ay);
9541
9542   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9543     oben_massiv = TRUE;
9544   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9545     unten_massiv = TRUE;
9546   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9547     links_massiv = TRUE;
9548   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9549     rechts_massiv = TRUE;
9550
9551   if (((oben_massiv && unten_massiv) ||
9552        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9553        element == EL_EXPANDABLE_WALL) &&
9554       ((links_massiv && rechts_massiv) ||
9555        element == EL_EXPANDABLE_WALL_VERTICAL))
9556     Feld[ax][ay] = EL_WALL;
9557
9558   if (new_wall)
9559     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9560 }
9561
9562 static void MauerAblegerStahl(int ax, int ay)
9563 {
9564   int element = Feld[ax][ay];
9565   int graphic = el2img(element);
9566   boolean oben_frei = FALSE, unten_frei = FALSE;
9567   boolean links_frei = FALSE, rechts_frei = FALSE;
9568   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9569   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9570   boolean new_wall = FALSE;
9571
9572   if (IS_ANIMATED(graphic))
9573     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9574
9575   if (!MovDelay[ax][ay])        // start building new wall
9576     MovDelay[ax][ay] = 6;
9577
9578   if (MovDelay[ax][ay])         // wait some time before building new wall
9579   {
9580     MovDelay[ax][ay]--;
9581     if (MovDelay[ax][ay])
9582       return;
9583   }
9584
9585   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9586     oben_frei = TRUE;
9587   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9588     unten_frei = TRUE;
9589   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9590     links_frei = TRUE;
9591   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9592     rechts_frei = TRUE;
9593
9594   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9595       element == EL_EXPANDABLE_STEELWALL_ANY)
9596   {
9597     if (oben_frei)
9598     {
9599       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9600       Store[ax][ay-1] = element;
9601       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9602       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9603         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9604                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9605       new_wall = TRUE;
9606     }
9607     if (unten_frei)
9608     {
9609       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9610       Store[ax][ay+1] = element;
9611       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9612       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9613         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9614                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9615       new_wall = TRUE;
9616     }
9617   }
9618
9619   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9620       element == EL_EXPANDABLE_STEELWALL_ANY)
9621   {
9622     if (links_frei)
9623     {
9624       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9625       Store[ax-1][ay] = element;
9626       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9627       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9628         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9629                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9630       new_wall = TRUE;
9631     }
9632
9633     if (rechts_frei)
9634     {
9635       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9636       Store[ax+1][ay] = element;
9637       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9638       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9639         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9640                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9641       new_wall = TRUE;
9642     }
9643   }
9644
9645   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9646     oben_massiv = TRUE;
9647   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9648     unten_massiv = TRUE;
9649   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9650     links_massiv = TRUE;
9651   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9652     rechts_massiv = TRUE;
9653
9654   if (((oben_massiv && unten_massiv) ||
9655        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9656       ((links_massiv && rechts_massiv) ||
9657        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9658     Feld[ax][ay] = EL_STEELWALL;
9659
9660   if (new_wall)
9661     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9662 }
9663
9664 static void CheckForDragon(int x, int y)
9665 {
9666   int i, j;
9667   boolean dragon_found = FALSE;
9668   static int xy[4][2] =
9669   {
9670     { 0, -1 },
9671     { -1, 0 },
9672     { +1, 0 },
9673     { 0, +1 }
9674   };
9675
9676   for (i = 0; i < NUM_DIRECTIONS; i++)
9677   {
9678     for (j = 0; j < 4; j++)
9679     {
9680       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9681
9682       if (IN_LEV_FIELD(xx, yy) &&
9683           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9684       {
9685         if (Feld[xx][yy] == EL_DRAGON)
9686           dragon_found = TRUE;
9687       }
9688       else
9689         break;
9690     }
9691   }
9692
9693   if (!dragon_found)
9694   {
9695     for (i = 0; i < NUM_DIRECTIONS; i++)
9696     {
9697       for (j = 0; j < 3; j++)
9698       {
9699         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9700   
9701         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9702         {
9703           Feld[xx][yy] = EL_EMPTY;
9704           TEST_DrawLevelField(xx, yy);
9705         }
9706         else
9707           break;
9708       }
9709     }
9710   }
9711 }
9712
9713 static void InitBuggyBase(int x, int y)
9714 {
9715   int element = Feld[x][y];
9716   int activating_delay = FRAMES_PER_SECOND / 4;
9717
9718   ChangeDelay[x][y] =
9719     (element == EL_SP_BUGGY_BASE ?
9720      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9721      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9722      activating_delay :
9723      element == EL_SP_BUGGY_BASE_ACTIVE ?
9724      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9725 }
9726
9727 static void WarnBuggyBase(int x, int y)
9728 {
9729   int i;
9730   static int xy[4][2] =
9731   {
9732     { 0, -1 },
9733     { -1, 0 },
9734     { +1, 0 },
9735     { 0, +1 }
9736   };
9737
9738   for (i = 0; i < NUM_DIRECTIONS; i++)
9739   {
9740     int xx = x + xy[i][0];
9741     int yy = y + xy[i][1];
9742
9743     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9744     {
9745       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9746
9747       break;
9748     }
9749   }
9750 }
9751
9752 static void InitTrap(int x, int y)
9753 {
9754   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9755 }
9756
9757 static void ActivateTrap(int x, int y)
9758 {
9759   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9760 }
9761
9762 static void ChangeActiveTrap(int x, int y)
9763 {
9764   int graphic = IMG_TRAP_ACTIVE;
9765
9766   // if new animation frame was drawn, correct crumbled sand border
9767   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9768     TEST_DrawLevelFieldCrumbled(x, y);
9769 }
9770
9771 static int getSpecialActionElement(int element, int number, int base_element)
9772 {
9773   return (element != EL_EMPTY ? element :
9774           number != -1 ? base_element + number - 1 :
9775           EL_EMPTY);
9776 }
9777
9778 static int getModifiedActionNumber(int value_old, int operator, int operand,
9779                                    int value_min, int value_max)
9780 {
9781   int value_new = (operator == CA_MODE_SET      ? operand :
9782                    operator == CA_MODE_ADD      ? value_old + operand :
9783                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9784                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9785                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9786                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9787                    value_old);
9788
9789   return (value_new < value_min ? value_min :
9790           value_new > value_max ? value_max :
9791           value_new);
9792 }
9793
9794 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9795 {
9796   struct ElementInfo *ei = &element_info[element];
9797   struct ElementChangeInfo *change = &ei->change_page[page];
9798   int target_element = change->target_element;
9799   int action_type = change->action_type;
9800   int action_mode = change->action_mode;
9801   int action_arg = change->action_arg;
9802   int action_element = change->action_element;
9803   int i;
9804
9805   if (!change->has_action)
9806     return;
9807
9808   // ---------- determine action paramater values -----------------------------
9809
9810   int level_time_value =
9811     (level.time > 0 ? TimeLeft :
9812      TimePlayed);
9813
9814   int action_arg_element_raw =
9815     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9816      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9817      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9818      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9819      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9820      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9821      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9822      EL_EMPTY);
9823   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9824
9825   int action_arg_direction =
9826     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9827      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9828      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9829      change->actual_trigger_side :
9830      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9831      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9832      MV_NONE);
9833
9834   int action_arg_number_min =
9835     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9836      CA_ARG_MIN);
9837
9838   int action_arg_number_max =
9839     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9840      action_type == CA_SET_LEVEL_GEMS ? 999 :
9841      action_type == CA_SET_LEVEL_TIME ? 9999 :
9842      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9843      action_type == CA_SET_CE_VALUE ? 9999 :
9844      action_type == CA_SET_CE_SCORE ? 9999 :
9845      CA_ARG_MAX);
9846
9847   int action_arg_number_reset =
9848     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9849      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9850      action_type == CA_SET_LEVEL_TIME ? level.time :
9851      action_type == CA_SET_LEVEL_SCORE ? 0 :
9852      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9853      action_type == CA_SET_CE_SCORE ? 0 :
9854      0);
9855
9856   int action_arg_number =
9857     (action_arg <= CA_ARG_MAX ? action_arg :
9858      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9859      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9860      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9861      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9862      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9863      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9864      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9865      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9866      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9867      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9868      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9869      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9870      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9871      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9872      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9873      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9874      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9875      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9876      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9877      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9878      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9879      -1);
9880
9881   int action_arg_number_old =
9882     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9883      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9884      action_type == CA_SET_LEVEL_SCORE ? game.score :
9885      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9886      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9887      0);
9888
9889   int action_arg_number_new =
9890     getModifiedActionNumber(action_arg_number_old,
9891                             action_mode, action_arg_number,
9892                             action_arg_number_min, action_arg_number_max);
9893
9894   int trigger_player_bits =
9895     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9896      change->actual_trigger_player_bits : change->trigger_player);
9897
9898   int action_arg_player_bits =
9899     (action_arg >= CA_ARG_PLAYER_1 &&
9900      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9901      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9902      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9903      PLAYER_BITS_ANY);
9904
9905   // ---------- execute action  -----------------------------------------------
9906
9907   switch (action_type)
9908   {
9909     case CA_NO_ACTION:
9910     {
9911       return;
9912     }
9913
9914     // ---------- level actions  ----------------------------------------------
9915
9916     case CA_RESTART_LEVEL:
9917     {
9918       game.restart_level = TRUE;
9919
9920       break;
9921     }
9922
9923     case CA_SHOW_ENVELOPE:
9924     {
9925       int element = getSpecialActionElement(action_arg_element,
9926                                             action_arg_number, EL_ENVELOPE_1);
9927
9928       if (IS_ENVELOPE(element))
9929         local_player->show_envelope = element;
9930
9931       break;
9932     }
9933
9934     case CA_SET_LEVEL_TIME:
9935     {
9936       if (level.time > 0)       // only modify limited time value
9937       {
9938         TimeLeft = action_arg_number_new;
9939
9940         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9941
9942         DisplayGameControlValues();
9943
9944         if (!TimeLeft && setup.time_limit)
9945           for (i = 0; i < MAX_PLAYERS; i++)
9946             KillPlayer(&stored_player[i]);
9947       }
9948
9949       break;
9950     }
9951
9952     case CA_SET_LEVEL_SCORE:
9953     {
9954       game.score = action_arg_number_new;
9955
9956       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9957
9958       DisplayGameControlValues();
9959
9960       break;
9961     }
9962
9963     case CA_SET_LEVEL_GEMS:
9964     {
9965       game.gems_still_needed = action_arg_number_new;
9966
9967       game.snapshot.collected_item = TRUE;
9968
9969       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9970
9971       DisplayGameControlValues();
9972
9973       break;
9974     }
9975
9976     case CA_SET_LEVEL_WIND:
9977     {
9978       game.wind_direction = action_arg_direction;
9979
9980       break;
9981     }
9982
9983     case CA_SET_LEVEL_RANDOM_SEED:
9984     {
9985       // ensure that setting a new random seed while playing is predictable
9986       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9987
9988       break;
9989     }
9990
9991     // ---------- player actions  ---------------------------------------------
9992
9993     case CA_MOVE_PLAYER:
9994     case CA_MOVE_PLAYER_NEW:
9995     {
9996       // automatically move to the next field in specified direction
9997       for (i = 0; i < MAX_PLAYERS; i++)
9998         if (trigger_player_bits & (1 << i))
9999           if (action_type == CA_MOVE_PLAYER ||
10000               stored_player[i].MovPos == 0)
10001             stored_player[i].programmed_action = action_arg_direction;
10002
10003       break;
10004     }
10005
10006     case CA_EXIT_PLAYER:
10007     {
10008       for (i = 0; i < MAX_PLAYERS; i++)
10009         if (action_arg_player_bits & (1 << i))
10010           ExitPlayer(&stored_player[i]);
10011
10012       if (game.players_still_needed == 0)
10013         LevelSolved();
10014
10015       break;
10016     }
10017
10018     case CA_KILL_PLAYER:
10019     {
10020       for (i = 0; i < MAX_PLAYERS; i++)
10021         if (action_arg_player_bits & (1 << i))
10022           KillPlayer(&stored_player[i]);
10023
10024       break;
10025     }
10026
10027     case CA_SET_PLAYER_KEYS:
10028     {
10029       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10030       int element = getSpecialActionElement(action_arg_element,
10031                                             action_arg_number, EL_KEY_1);
10032
10033       if (IS_KEY(element))
10034       {
10035         for (i = 0; i < MAX_PLAYERS; i++)
10036         {
10037           if (trigger_player_bits & (1 << i))
10038           {
10039             stored_player[i].key[KEY_NR(element)] = key_state;
10040
10041             DrawGameDoorValues();
10042           }
10043         }
10044       }
10045
10046       break;
10047     }
10048
10049     case CA_SET_PLAYER_SPEED:
10050     {
10051       for (i = 0; i < MAX_PLAYERS; i++)
10052       {
10053         if (trigger_player_bits & (1 << i))
10054         {
10055           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10056
10057           if (action_arg == CA_ARG_SPEED_FASTER &&
10058               stored_player[i].cannot_move)
10059           {
10060             action_arg_number = STEPSIZE_VERY_SLOW;
10061           }
10062           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10063                    action_arg == CA_ARG_SPEED_FASTER)
10064           {
10065             action_arg_number = 2;
10066             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10067                            CA_MODE_MULTIPLY);
10068           }
10069           else if (action_arg == CA_ARG_NUMBER_RESET)
10070           {
10071             action_arg_number = level.initial_player_stepsize[i];
10072           }
10073
10074           move_stepsize =
10075             getModifiedActionNumber(move_stepsize,
10076                                     action_mode,
10077                                     action_arg_number,
10078                                     action_arg_number_min,
10079                                     action_arg_number_max);
10080
10081           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10082         }
10083       }
10084
10085       break;
10086     }
10087
10088     case CA_SET_PLAYER_SHIELD:
10089     {
10090       for (i = 0; i < MAX_PLAYERS; i++)
10091       {
10092         if (trigger_player_bits & (1 << i))
10093         {
10094           if (action_arg == CA_ARG_SHIELD_OFF)
10095           {
10096             stored_player[i].shield_normal_time_left = 0;
10097             stored_player[i].shield_deadly_time_left = 0;
10098           }
10099           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10100           {
10101             stored_player[i].shield_normal_time_left = 999999;
10102           }
10103           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10104           {
10105             stored_player[i].shield_normal_time_left = 999999;
10106             stored_player[i].shield_deadly_time_left = 999999;
10107           }
10108         }
10109       }
10110
10111       break;
10112     }
10113
10114     case CA_SET_PLAYER_GRAVITY:
10115     {
10116       for (i = 0; i < MAX_PLAYERS; i++)
10117       {
10118         if (trigger_player_bits & (1 << i))
10119         {
10120           stored_player[i].gravity =
10121             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10122              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10123              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10124              stored_player[i].gravity);
10125         }
10126       }
10127
10128       break;
10129     }
10130
10131     case CA_SET_PLAYER_ARTWORK:
10132     {
10133       for (i = 0; i < MAX_PLAYERS; i++)
10134       {
10135         if (trigger_player_bits & (1 << i))
10136         {
10137           int artwork_element = action_arg_element;
10138
10139           if (action_arg == CA_ARG_ELEMENT_RESET)
10140             artwork_element =
10141               (level.use_artwork_element[i] ? level.artwork_element[i] :
10142                stored_player[i].element_nr);
10143
10144           if (stored_player[i].artwork_element != artwork_element)
10145             stored_player[i].Frame = 0;
10146
10147           stored_player[i].artwork_element = artwork_element;
10148
10149           SetPlayerWaiting(&stored_player[i], FALSE);
10150
10151           // set number of special actions for bored and sleeping animation
10152           stored_player[i].num_special_action_bored =
10153             get_num_special_action(artwork_element,
10154                                    ACTION_BORING_1, ACTION_BORING_LAST);
10155           stored_player[i].num_special_action_sleeping =
10156             get_num_special_action(artwork_element,
10157                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10158         }
10159       }
10160
10161       break;
10162     }
10163
10164     case CA_SET_PLAYER_INVENTORY:
10165     {
10166       for (i = 0; i < MAX_PLAYERS; i++)
10167       {
10168         struct PlayerInfo *player = &stored_player[i];
10169         int j, k;
10170
10171         if (trigger_player_bits & (1 << i))
10172         {
10173           int inventory_element = action_arg_element;
10174
10175           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10176               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10177               action_arg == CA_ARG_ELEMENT_ACTION)
10178           {
10179             int element = inventory_element;
10180             int collect_count = element_info[element].collect_count_initial;
10181
10182             if (!IS_CUSTOM_ELEMENT(element))
10183               collect_count = 1;
10184
10185             if (collect_count == 0)
10186               player->inventory_infinite_element = element;
10187             else
10188               for (k = 0; k < collect_count; k++)
10189                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10190                   player->inventory_element[player->inventory_size++] =
10191                     element;
10192           }
10193           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10194                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10195                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10196           {
10197             if (player->inventory_infinite_element != EL_UNDEFINED &&
10198                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10199                                      action_arg_element_raw))
10200               player->inventory_infinite_element = EL_UNDEFINED;
10201
10202             for (k = 0, j = 0; j < player->inventory_size; j++)
10203             {
10204               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10205                                         action_arg_element_raw))
10206                 player->inventory_element[k++] = player->inventory_element[j];
10207             }
10208
10209             player->inventory_size = k;
10210           }
10211           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10212           {
10213             if (player->inventory_size > 0)
10214             {
10215               for (j = 0; j < player->inventory_size - 1; j++)
10216                 player->inventory_element[j] = player->inventory_element[j + 1];
10217
10218               player->inventory_size--;
10219             }
10220           }
10221           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10222           {
10223             if (player->inventory_size > 0)
10224               player->inventory_size--;
10225           }
10226           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10227           {
10228             player->inventory_infinite_element = EL_UNDEFINED;
10229             player->inventory_size = 0;
10230           }
10231           else if (action_arg == CA_ARG_INVENTORY_RESET)
10232           {
10233             player->inventory_infinite_element = EL_UNDEFINED;
10234             player->inventory_size = 0;
10235
10236             if (level.use_initial_inventory[i])
10237             {
10238               for (j = 0; j < level.initial_inventory_size[i]; j++)
10239               {
10240                 int element = level.initial_inventory_content[i][j];
10241                 int collect_count = element_info[element].collect_count_initial;
10242
10243                 if (!IS_CUSTOM_ELEMENT(element))
10244                   collect_count = 1;
10245
10246                 if (collect_count == 0)
10247                   player->inventory_infinite_element = element;
10248                 else
10249                   for (k = 0; k < collect_count; k++)
10250                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10251                       player->inventory_element[player->inventory_size++] =
10252                         element;
10253               }
10254             }
10255           }
10256         }
10257       }
10258
10259       break;
10260     }
10261
10262     // ---------- CE actions  -------------------------------------------------
10263
10264     case CA_SET_CE_VALUE:
10265     {
10266       int last_ce_value = CustomValue[x][y];
10267
10268       CustomValue[x][y] = action_arg_number_new;
10269
10270       if (CustomValue[x][y] != last_ce_value)
10271       {
10272         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10273         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10274
10275         if (CustomValue[x][y] == 0)
10276         {
10277           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10278           ChangeCount[x][y] = 0;        // allow at least one more change
10279
10280           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10281           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10282         }
10283       }
10284
10285       break;
10286     }
10287
10288     case CA_SET_CE_SCORE:
10289     {
10290       int last_ce_score = ei->collect_score;
10291
10292       ei->collect_score = action_arg_number_new;
10293
10294       if (ei->collect_score != last_ce_score)
10295       {
10296         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10297         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10298
10299         if (ei->collect_score == 0)
10300         {
10301           int xx, yy;
10302
10303           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10304           ChangeCount[x][y] = 0;        // allow at least one more change
10305
10306           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10307           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10308
10309           /*
10310             This is a very special case that seems to be a mixture between
10311             CheckElementChange() and CheckTriggeredElementChange(): while
10312             the first one only affects single elements that are triggered
10313             directly, the second one affects multiple elements in the playfield
10314             that are triggered indirectly by another element. This is a third
10315             case: Changing the CE score always affects multiple identical CEs,
10316             so every affected CE must be checked, not only the single CE for
10317             which the CE score was changed in the first place (as every instance
10318             of that CE shares the same CE score, and therefore also can change)!
10319           */
10320           SCAN_PLAYFIELD(xx, yy)
10321           {
10322             if (Feld[xx][yy] == element)
10323               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10324                                  CE_SCORE_GETS_ZERO);
10325           }
10326         }
10327       }
10328
10329       break;
10330     }
10331
10332     case CA_SET_CE_ARTWORK:
10333     {
10334       int artwork_element = action_arg_element;
10335       boolean reset_frame = FALSE;
10336       int xx, yy;
10337
10338       if (action_arg == CA_ARG_ELEMENT_RESET)
10339         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10340                            element);
10341
10342       if (ei->gfx_element != artwork_element)
10343         reset_frame = TRUE;
10344
10345       ei->gfx_element = artwork_element;
10346
10347       SCAN_PLAYFIELD(xx, yy)
10348       {
10349         if (Feld[xx][yy] == element)
10350         {
10351           if (reset_frame)
10352           {
10353             ResetGfxAnimation(xx, yy);
10354             ResetRandomAnimationValue(xx, yy);
10355           }
10356
10357           TEST_DrawLevelField(xx, yy);
10358         }
10359       }
10360
10361       break;
10362     }
10363
10364     // ---------- engine actions  ---------------------------------------------
10365
10366     case CA_SET_ENGINE_SCAN_MODE:
10367     {
10368       InitPlayfieldScanMode(action_arg);
10369
10370       break;
10371     }
10372
10373     default:
10374       break;
10375   }
10376 }
10377
10378 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10379 {
10380   int old_element = Feld[x][y];
10381   int new_element = GetElementFromGroupElement(element);
10382   int previous_move_direction = MovDir[x][y];
10383   int last_ce_value = CustomValue[x][y];
10384   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10385   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10386   boolean add_player_onto_element = (new_element_is_player &&
10387                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10388                                      IS_WALKABLE(old_element));
10389
10390   if (!add_player_onto_element)
10391   {
10392     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10393       RemoveMovingField(x, y);
10394     else
10395       RemoveField(x, y);
10396
10397     Feld[x][y] = new_element;
10398
10399     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10400       MovDir[x][y] = previous_move_direction;
10401
10402     if (element_info[new_element].use_last_ce_value)
10403       CustomValue[x][y] = last_ce_value;
10404
10405     InitField_WithBug1(x, y, FALSE);
10406
10407     new_element = Feld[x][y];   // element may have changed
10408
10409     ResetGfxAnimation(x, y);
10410     ResetRandomAnimationValue(x, y);
10411
10412     TEST_DrawLevelField(x, y);
10413
10414     if (GFX_CRUMBLED(new_element))
10415       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10416   }
10417
10418   // check if element under the player changes from accessible to unaccessible
10419   // (needed for special case of dropping element which then changes)
10420   // (must be checked after creating new element for walkable group elements)
10421   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10422       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10423   {
10424     Bang(x, y);
10425
10426     return;
10427   }
10428
10429   // "ChangeCount" not set yet to allow "entered by player" change one time
10430   if (new_element_is_player)
10431     RelocatePlayer(x, y, new_element);
10432
10433   if (is_change)
10434     ChangeCount[x][y]++;        // count number of changes in the same frame
10435
10436   TestIfBadThingTouchesPlayer(x, y);
10437   TestIfPlayerTouchesCustomElement(x, y);
10438   TestIfElementTouchesCustomElement(x, y);
10439 }
10440
10441 static void CreateField(int x, int y, int element)
10442 {
10443   CreateFieldExt(x, y, element, FALSE);
10444 }
10445
10446 static void CreateElementFromChange(int x, int y, int element)
10447 {
10448   element = GET_VALID_RUNTIME_ELEMENT(element);
10449
10450   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10451   {
10452     int old_element = Feld[x][y];
10453
10454     // prevent changed element from moving in same engine frame
10455     // unless both old and new element can either fall or move
10456     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10457         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10458       Stop[x][y] = TRUE;
10459   }
10460
10461   CreateFieldExt(x, y, element, TRUE);
10462 }
10463
10464 static boolean ChangeElement(int x, int y, int element, int page)
10465 {
10466   struct ElementInfo *ei = &element_info[element];
10467   struct ElementChangeInfo *change = &ei->change_page[page];
10468   int ce_value = CustomValue[x][y];
10469   int ce_score = ei->collect_score;
10470   int target_element;
10471   int old_element = Feld[x][y];
10472
10473   // always use default change event to prevent running into a loop
10474   if (ChangeEvent[x][y] == -1)
10475     ChangeEvent[x][y] = CE_DELAY;
10476
10477   if (ChangeEvent[x][y] == CE_DELAY)
10478   {
10479     // reset actual trigger element, trigger player and action element
10480     change->actual_trigger_element = EL_EMPTY;
10481     change->actual_trigger_player = EL_EMPTY;
10482     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10483     change->actual_trigger_side = CH_SIDE_NONE;
10484     change->actual_trigger_ce_value = 0;
10485     change->actual_trigger_ce_score = 0;
10486   }
10487
10488   // do not change elements more than a specified maximum number of changes
10489   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10490     return FALSE;
10491
10492   ChangeCount[x][y]++;          // count number of changes in the same frame
10493
10494   if (change->explode)
10495   {
10496     Bang(x, y);
10497
10498     return TRUE;
10499   }
10500
10501   if (change->use_target_content)
10502   {
10503     boolean complete_replace = TRUE;
10504     boolean can_replace[3][3];
10505     int xx, yy;
10506
10507     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10508     {
10509       boolean is_empty;
10510       boolean is_walkable;
10511       boolean is_diggable;
10512       boolean is_collectible;
10513       boolean is_removable;
10514       boolean is_destructible;
10515       int ex = x + xx - 1;
10516       int ey = y + yy - 1;
10517       int content_element = change->target_content.e[xx][yy];
10518       int e;
10519
10520       can_replace[xx][yy] = TRUE;
10521
10522       if (ex == x && ey == y)   // do not check changing element itself
10523         continue;
10524
10525       if (content_element == EL_EMPTY_SPACE)
10526       {
10527         can_replace[xx][yy] = FALSE;    // do not replace border with space
10528
10529         continue;
10530       }
10531
10532       if (!IN_LEV_FIELD(ex, ey))
10533       {
10534         can_replace[xx][yy] = FALSE;
10535         complete_replace = FALSE;
10536
10537         continue;
10538       }
10539
10540       e = Feld[ex][ey];
10541
10542       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10543         e = MovingOrBlocked2Element(ex, ey);
10544
10545       is_empty = (IS_FREE(ex, ey) ||
10546                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10547
10548       is_walkable     = (is_empty || IS_WALKABLE(e));
10549       is_diggable     = (is_empty || IS_DIGGABLE(e));
10550       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10551       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10552       is_removable    = (is_diggable || is_collectible);
10553
10554       can_replace[xx][yy] =
10555         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10556           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10557           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10558           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10559           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10560           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10561          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10562
10563       if (!can_replace[xx][yy])
10564         complete_replace = FALSE;
10565     }
10566
10567     if (!change->only_if_complete || complete_replace)
10568     {
10569       boolean something_has_changed = FALSE;
10570
10571       if (change->only_if_complete && change->use_random_replace &&
10572           RND(100) < change->random_percentage)
10573         return FALSE;
10574
10575       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10576       {
10577         int ex = x + xx - 1;
10578         int ey = y + yy - 1;
10579         int content_element;
10580
10581         if (can_replace[xx][yy] && (!change->use_random_replace ||
10582                                     RND(100) < change->random_percentage))
10583         {
10584           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10585             RemoveMovingField(ex, ey);
10586
10587           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10588
10589           content_element = change->target_content.e[xx][yy];
10590           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10591                                               ce_value, ce_score);
10592
10593           CreateElementFromChange(ex, ey, target_element);
10594
10595           something_has_changed = TRUE;
10596
10597           // for symmetry reasons, freeze newly created border elements
10598           if (ex != x || ey != y)
10599             Stop[ex][ey] = TRUE;        // no more moving in this frame
10600         }
10601       }
10602
10603       if (something_has_changed)
10604       {
10605         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10606         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10607       }
10608     }
10609   }
10610   else
10611   {
10612     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10613                                         ce_value, ce_score);
10614
10615     if (element == EL_DIAGONAL_GROWING ||
10616         element == EL_DIAGONAL_SHRINKING)
10617     {
10618       target_element = Store[x][y];
10619
10620       Store[x][y] = EL_EMPTY;
10621     }
10622
10623     CreateElementFromChange(x, y, target_element);
10624
10625     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10626     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10627   }
10628
10629   // this uses direct change before indirect change
10630   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10631
10632   return TRUE;
10633 }
10634
10635 static void HandleElementChange(int x, int y, int page)
10636 {
10637   int element = MovingOrBlocked2Element(x, y);
10638   struct ElementInfo *ei = &element_info[element];
10639   struct ElementChangeInfo *change = &ei->change_page[page];
10640   boolean handle_action_before_change = FALSE;
10641
10642 #ifdef DEBUG
10643   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10644       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10645   {
10646     printf("\n\n");
10647     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10648            x, y, element, element_info[element].token_name);
10649     printf("HandleElementChange(): This should never happen!\n");
10650     printf("\n\n");
10651   }
10652 #endif
10653
10654   // this can happen with classic bombs on walkable, changing elements
10655   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10656   {
10657     return;
10658   }
10659
10660   if (ChangeDelay[x][y] == 0)           // initialize element change
10661   {
10662     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10663
10664     if (change->can_change)
10665     {
10666       // !!! not clear why graphic animation should be reset at all here !!!
10667       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10668       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10669
10670       /*
10671         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10672
10673         When using an animation frame delay of 1 (this only happens with
10674         "sp_zonk.moving.left/right" in the classic graphics), the default
10675         (non-moving) animation shows wrong animation frames (while the
10676         moving animation, like "sp_zonk.moving.left/right", is correct,
10677         so this graphical bug never shows up with the classic graphics).
10678         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10679         be drawn instead of the correct frames 0,1,2,3. This is caused by
10680         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10681         an element change: First when the change delay ("ChangeDelay[][]")
10682         counter has reached zero after decrementing, then a second time in
10683         the next frame (after "GfxFrame[][]" was already incremented) when
10684         "ChangeDelay[][]" is reset to the initial delay value again.
10685
10686         This causes frame 0 to be drawn twice, while the last frame won't
10687         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10688
10689         As some animations may already be cleverly designed around this bug
10690         (at least the "Snake Bite" snake tail animation does this), it cannot
10691         simply be fixed here without breaking such existing animations.
10692         Unfortunately, it cannot easily be detected if a graphics set was
10693         designed "before" or "after" the bug was fixed. As a workaround,
10694         a new graphics set option "game.graphics_engine_version" was added
10695         to be able to specify the game's major release version for which the
10696         graphics set was designed, which can then be used to decide if the
10697         bugfix should be used (version 4 and above) or not (version 3 or
10698         below, or if no version was specified at all, as with old sets).
10699
10700         (The wrong/fixed animation frames can be tested with the test level set
10701         "test_gfxframe" and level "000", which contains a specially prepared
10702         custom element at level position (x/y) == (11/9) which uses the zonk
10703         animation mentioned above. Using "game.graphics_engine_version: 4"
10704         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10705         This can also be seen from the debug output for this test element.)
10706       */
10707
10708       // when a custom element is about to change (for example by change delay),
10709       // do not reset graphic animation when the custom element is moving
10710       if (game.graphics_engine_version < 4 &&
10711           !IS_MOVING(x, y))
10712       {
10713         ResetGfxAnimation(x, y);
10714         ResetRandomAnimationValue(x, y);
10715       }
10716
10717       if (change->pre_change_function)
10718         change->pre_change_function(x, y);
10719     }
10720   }
10721
10722   ChangeDelay[x][y]--;
10723
10724   if (ChangeDelay[x][y] != 0)           // continue element change
10725   {
10726     if (change->can_change)
10727     {
10728       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10729
10730       if (IS_ANIMATED(graphic))
10731         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10732
10733       if (change->change_function)
10734         change->change_function(x, y);
10735     }
10736   }
10737   else                                  // finish element change
10738   {
10739     if (ChangePage[x][y] != -1)         // remember page from delayed change
10740     {
10741       page = ChangePage[x][y];
10742       ChangePage[x][y] = -1;
10743
10744       change = &ei->change_page[page];
10745     }
10746
10747     if (IS_MOVING(x, y))                // never change a running system ;-)
10748     {
10749       ChangeDelay[x][y] = 1;            // try change after next move step
10750       ChangePage[x][y] = page;          // remember page to use for change
10751
10752       return;
10753     }
10754
10755     // special case: set new level random seed before changing element
10756     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10757       handle_action_before_change = TRUE;
10758
10759     if (change->has_action && handle_action_before_change)
10760       ExecuteCustomElementAction(x, y, element, page);
10761
10762     if (change->can_change)
10763     {
10764       if (ChangeElement(x, y, element, page))
10765       {
10766         if (change->post_change_function)
10767           change->post_change_function(x, y);
10768       }
10769     }
10770
10771     if (change->has_action && !handle_action_before_change)
10772       ExecuteCustomElementAction(x, y, element, page);
10773   }
10774 }
10775
10776 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10777                                               int trigger_element,
10778                                               int trigger_event,
10779                                               int trigger_player,
10780                                               int trigger_side,
10781                                               int trigger_page)
10782 {
10783   boolean change_done_any = FALSE;
10784   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10785   int i;
10786
10787   if (!(trigger_events[trigger_element][trigger_event]))
10788     return FALSE;
10789
10790   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10791
10792   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10793   {
10794     int element = EL_CUSTOM_START + i;
10795     boolean change_done = FALSE;
10796     int p;
10797
10798     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10799         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10800       continue;
10801
10802     for (p = 0; p < element_info[element].num_change_pages; p++)
10803     {
10804       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10805
10806       if (change->can_change_or_has_action &&
10807           change->has_event[trigger_event] &&
10808           change->trigger_side & trigger_side &&
10809           change->trigger_player & trigger_player &&
10810           change->trigger_page & trigger_page_bits &&
10811           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10812       {
10813         change->actual_trigger_element = trigger_element;
10814         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10815         change->actual_trigger_player_bits = trigger_player;
10816         change->actual_trigger_side = trigger_side;
10817         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10818         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10819
10820         if ((change->can_change && !change_done) || change->has_action)
10821         {
10822           int x, y;
10823
10824           SCAN_PLAYFIELD(x, y)
10825           {
10826             if (Feld[x][y] == element)
10827             {
10828               if (change->can_change && !change_done)
10829               {
10830                 // if element already changed in this frame, not only prevent
10831                 // another element change (checked in ChangeElement()), but
10832                 // also prevent additional element actions for this element
10833
10834                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10835                     !level.use_action_after_change_bug)
10836                   continue;
10837
10838                 ChangeDelay[x][y] = 1;
10839                 ChangeEvent[x][y] = trigger_event;
10840
10841                 HandleElementChange(x, y, p);
10842               }
10843               else if (change->has_action)
10844               {
10845                 // if element already changed in this frame, not only prevent
10846                 // another element change (checked in ChangeElement()), but
10847                 // also prevent additional element actions for this element
10848
10849                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10850                     !level.use_action_after_change_bug)
10851                   continue;
10852
10853                 ExecuteCustomElementAction(x, y, element, p);
10854                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10855               }
10856             }
10857           }
10858
10859           if (change->can_change)
10860           {
10861             change_done = TRUE;
10862             change_done_any = TRUE;
10863           }
10864         }
10865       }
10866     }
10867   }
10868
10869   RECURSION_LOOP_DETECTION_END();
10870
10871   return change_done_any;
10872 }
10873
10874 static boolean CheckElementChangeExt(int x, int y,
10875                                      int element,
10876                                      int trigger_element,
10877                                      int trigger_event,
10878                                      int trigger_player,
10879                                      int trigger_side)
10880 {
10881   boolean change_done = FALSE;
10882   int p;
10883
10884   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10885       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10886     return FALSE;
10887
10888   if (Feld[x][y] == EL_BLOCKED)
10889   {
10890     Blocked2Moving(x, y, &x, &y);
10891     element = Feld[x][y];
10892   }
10893
10894   // check if element has already changed or is about to change after moving
10895   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10896        Feld[x][y] != element) ||
10897
10898       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10899        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10900         ChangePage[x][y] != -1)))
10901     return FALSE;
10902
10903   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10904
10905   for (p = 0; p < element_info[element].num_change_pages; p++)
10906   {
10907     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10908
10909     /* check trigger element for all events where the element that is checked
10910        for changing interacts with a directly adjacent element -- this is
10911        different to element changes that affect other elements to change on the
10912        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10913     boolean check_trigger_element =
10914       (trigger_event == CE_TOUCHING_X ||
10915        trigger_event == CE_HITTING_X ||
10916        trigger_event == CE_HIT_BY_X ||
10917        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10918
10919     if (change->can_change_or_has_action &&
10920         change->has_event[trigger_event] &&
10921         change->trigger_side & trigger_side &&
10922         change->trigger_player & trigger_player &&
10923         (!check_trigger_element ||
10924          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10925     {
10926       change->actual_trigger_element = trigger_element;
10927       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10928       change->actual_trigger_player_bits = trigger_player;
10929       change->actual_trigger_side = trigger_side;
10930       change->actual_trigger_ce_value = CustomValue[x][y];
10931       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10932
10933       // special case: trigger element not at (x,y) position for some events
10934       if (check_trigger_element)
10935       {
10936         static struct
10937         {
10938           int dx, dy;
10939         } move_xy[] =
10940           {
10941             {  0,  0 },
10942             { -1,  0 },
10943             { +1,  0 },
10944             {  0,  0 },
10945             {  0, -1 },
10946             {  0,  0 }, { 0, 0 }, { 0, 0 },
10947             {  0, +1 }
10948           };
10949
10950         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10951         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10952
10953         change->actual_trigger_ce_value = CustomValue[xx][yy];
10954         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10955       }
10956
10957       if (change->can_change && !change_done)
10958       {
10959         ChangeDelay[x][y] = 1;
10960         ChangeEvent[x][y] = trigger_event;
10961
10962         HandleElementChange(x, y, p);
10963
10964         change_done = TRUE;
10965       }
10966       else if (change->has_action)
10967       {
10968         ExecuteCustomElementAction(x, y, element, p);
10969         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10970       }
10971     }
10972   }
10973
10974   RECURSION_LOOP_DETECTION_END();
10975
10976   return change_done;
10977 }
10978
10979 static void PlayPlayerSound(struct PlayerInfo *player)
10980 {
10981   int jx = player->jx, jy = player->jy;
10982   int sound_element = player->artwork_element;
10983   int last_action = player->last_action_waiting;
10984   int action = player->action_waiting;
10985
10986   if (player->is_waiting)
10987   {
10988     if (action != last_action)
10989       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10990     else
10991       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10992   }
10993   else
10994   {
10995     if (action != last_action)
10996       StopSound(element_info[sound_element].sound[last_action]);
10997
10998     if (last_action == ACTION_SLEEPING)
10999       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11000   }
11001 }
11002
11003 static void PlayAllPlayersSound(void)
11004 {
11005   int i;
11006
11007   for (i = 0; i < MAX_PLAYERS; i++)
11008     if (stored_player[i].active)
11009       PlayPlayerSound(&stored_player[i]);
11010 }
11011
11012 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11013 {
11014   boolean last_waiting = player->is_waiting;
11015   int move_dir = player->MovDir;
11016
11017   player->dir_waiting = move_dir;
11018   player->last_action_waiting = player->action_waiting;
11019
11020   if (is_waiting)
11021   {
11022     if (!last_waiting)          // not waiting -> waiting
11023     {
11024       player->is_waiting = TRUE;
11025
11026       player->frame_counter_bored =
11027         FrameCounter +
11028         game.player_boring_delay_fixed +
11029         GetSimpleRandom(game.player_boring_delay_random);
11030       player->frame_counter_sleeping =
11031         FrameCounter +
11032         game.player_sleeping_delay_fixed +
11033         GetSimpleRandom(game.player_sleeping_delay_random);
11034
11035       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11036     }
11037
11038     if (game.player_sleeping_delay_fixed +
11039         game.player_sleeping_delay_random > 0 &&
11040         player->anim_delay_counter == 0 &&
11041         player->post_delay_counter == 0 &&
11042         FrameCounter >= player->frame_counter_sleeping)
11043       player->is_sleeping = TRUE;
11044     else if (game.player_boring_delay_fixed +
11045              game.player_boring_delay_random > 0 &&
11046              FrameCounter >= player->frame_counter_bored)
11047       player->is_bored = TRUE;
11048
11049     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11050                               player->is_bored ? ACTION_BORING :
11051                               ACTION_WAITING);
11052
11053     if (player->is_sleeping && player->use_murphy)
11054     {
11055       // special case for sleeping Murphy when leaning against non-free tile
11056
11057       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11058           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11059            !IS_MOVING(player->jx - 1, player->jy)))
11060         move_dir = MV_LEFT;
11061       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11062                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11063                 !IS_MOVING(player->jx + 1, player->jy)))
11064         move_dir = MV_RIGHT;
11065       else
11066         player->is_sleeping = FALSE;
11067
11068       player->dir_waiting = move_dir;
11069     }
11070
11071     if (player->is_sleeping)
11072     {
11073       if (player->num_special_action_sleeping > 0)
11074       {
11075         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11076         {
11077           int last_special_action = player->special_action_sleeping;
11078           int num_special_action = player->num_special_action_sleeping;
11079           int special_action =
11080             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11081              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11082              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11083              last_special_action + 1 : ACTION_SLEEPING);
11084           int special_graphic =
11085             el_act_dir2img(player->artwork_element, special_action, move_dir);
11086
11087           player->anim_delay_counter =
11088             graphic_info[special_graphic].anim_delay_fixed +
11089             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11090           player->post_delay_counter =
11091             graphic_info[special_graphic].post_delay_fixed +
11092             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11093
11094           player->special_action_sleeping = special_action;
11095         }
11096
11097         if (player->anim_delay_counter > 0)
11098         {
11099           player->action_waiting = player->special_action_sleeping;
11100           player->anim_delay_counter--;
11101         }
11102         else if (player->post_delay_counter > 0)
11103         {
11104           player->post_delay_counter--;
11105         }
11106       }
11107     }
11108     else if (player->is_bored)
11109     {
11110       if (player->num_special_action_bored > 0)
11111       {
11112         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11113         {
11114           int special_action =
11115             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11116           int special_graphic =
11117             el_act_dir2img(player->artwork_element, special_action, move_dir);
11118
11119           player->anim_delay_counter =
11120             graphic_info[special_graphic].anim_delay_fixed +
11121             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11122           player->post_delay_counter =
11123             graphic_info[special_graphic].post_delay_fixed +
11124             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11125
11126           player->special_action_bored = special_action;
11127         }
11128
11129         if (player->anim_delay_counter > 0)
11130         {
11131           player->action_waiting = player->special_action_bored;
11132           player->anim_delay_counter--;
11133         }
11134         else if (player->post_delay_counter > 0)
11135         {
11136           player->post_delay_counter--;
11137         }
11138       }
11139     }
11140   }
11141   else if (last_waiting)        // waiting -> not waiting
11142   {
11143     player->is_waiting = FALSE;
11144     player->is_bored = FALSE;
11145     player->is_sleeping = FALSE;
11146
11147     player->frame_counter_bored = -1;
11148     player->frame_counter_sleeping = -1;
11149
11150     player->anim_delay_counter = 0;
11151     player->post_delay_counter = 0;
11152
11153     player->dir_waiting = player->MovDir;
11154     player->action_waiting = ACTION_DEFAULT;
11155
11156     player->special_action_bored = ACTION_DEFAULT;
11157     player->special_action_sleeping = ACTION_DEFAULT;
11158   }
11159 }
11160
11161 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11162 {
11163   if ((!player->is_moving  && player->was_moving) ||
11164       (player->MovPos == 0 && player->was_moving) ||
11165       (player->is_snapping && !player->was_snapping) ||
11166       (player->is_dropping && !player->was_dropping))
11167   {
11168     if (!CheckSaveEngineSnapshotToList())
11169       return;
11170
11171     player->was_moving = FALSE;
11172     player->was_snapping = TRUE;
11173     player->was_dropping = TRUE;
11174   }
11175   else
11176   {
11177     if (player->is_moving)
11178       player->was_moving = TRUE;
11179
11180     if (!player->is_snapping)
11181       player->was_snapping = FALSE;
11182
11183     if (!player->is_dropping)
11184       player->was_dropping = FALSE;
11185   }
11186 }
11187
11188 static void CheckSingleStepMode(struct PlayerInfo *player)
11189 {
11190   if (tape.single_step && tape.recording && !tape.pausing)
11191   {
11192     /* as it is called "single step mode", just return to pause mode when the
11193        player stopped moving after one tile (or never starts moving at all) */
11194     if (!player->is_moving &&
11195         !player->is_pushing &&
11196         !player->is_dropping_pressed)
11197       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11198   }
11199
11200   CheckSaveEngineSnapshot(player);
11201 }
11202
11203 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11204 {
11205   int left      = player_action & JOY_LEFT;
11206   int right     = player_action & JOY_RIGHT;
11207   int up        = player_action & JOY_UP;
11208   int down      = player_action & JOY_DOWN;
11209   int button1   = player_action & JOY_BUTTON_1;
11210   int button2   = player_action & JOY_BUTTON_2;
11211   int dx        = (left ? -1 : right ? 1 : 0);
11212   int dy        = (up   ? -1 : down  ? 1 : 0);
11213
11214   if (!player->active || tape.pausing)
11215     return 0;
11216
11217   if (player_action)
11218   {
11219     if (button1)
11220       SnapField(player, dx, dy);
11221     else
11222     {
11223       if (button2)
11224         DropElement(player);
11225
11226       MovePlayer(player, dx, dy);
11227     }
11228
11229     CheckSingleStepMode(player);
11230
11231     SetPlayerWaiting(player, FALSE);
11232
11233     return player_action;
11234   }
11235   else
11236   {
11237     // no actions for this player (no input at player's configured device)
11238
11239     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11240     SnapField(player, 0, 0);
11241     CheckGravityMovementWhenNotMoving(player);
11242
11243     if (player->MovPos == 0)
11244       SetPlayerWaiting(player, TRUE);
11245
11246     if (player->MovPos == 0)    // needed for tape.playing
11247       player->is_moving = FALSE;
11248
11249     player->is_dropping = FALSE;
11250     player->is_dropping_pressed = FALSE;
11251     player->drop_pressed_delay = 0;
11252
11253     CheckSingleStepMode(player);
11254
11255     return 0;
11256   }
11257 }
11258
11259 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11260                                          byte *tape_action)
11261 {
11262   if (!tape.use_mouse_actions)
11263     return;
11264
11265   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11266   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11267   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11268 }
11269
11270 static void SetTapeActionFromMouseAction(byte *tape_action,
11271                                          struct MouseActionInfo *mouse_action)
11272 {
11273   if (!tape.use_mouse_actions)
11274     return;
11275
11276   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11277   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11278   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11279 }
11280
11281 static void CheckLevelSolved(void)
11282 {
11283   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11284   {
11285     if (game_em.level_solved &&
11286         !game_em.game_over)                             // game won
11287     {
11288       LevelSolved();
11289
11290       game_em.game_over = TRUE;
11291
11292       game.all_players_gone = TRUE;
11293     }
11294
11295     if (game_em.game_over)                              // game lost
11296       game.all_players_gone = TRUE;
11297   }
11298   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11299   {
11300     if (game_sp.level_solved &&
11301         !game_sp.game_over)                             // game won
11302     {
11303       LevelSolved();
11304
11305       game_sp.game_over = TRUE;
11306
11307       game.all_players_gone = TRUE;
11308     }
11309
11310     if (game_sp.game_over)                              // game lost
11311       game.all_players_gone = TRUE;
11312   }
11313   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11314   {
11315     if (game_mm.level_solved &&
11316         !game_mm.game_over)                             // game won
11317     {
11318       LevelSolved();
11319
11320       game_mm.game_over = TRUE;
11321
11322       game.all_players_gone = TRUE;
11323     }
11324
11325     if (game_mm.game_over)                              // game lost
11326       game.all_players_gone = TRUE;
11327   }
11328 }
11329
11330 static void CheckLevelTime(void)
11331 {
11332   int i;
11333
11334   if (TimeFrames >= FRAMES_PER_SECOND)
11335   {
11336     TimeFrames = 0;
11337     TapeTime++;
11338
11339     for (i = 0; i < MAX_PLAYERS; i++)
11340     {
11341       struct PlayerInfo *player = &stored_player[i];
11342
11343       if (SHIELD_ON(player))
11344       {
11345         player->shield_normal_time_left--;
11346
11347         if (player->shield_deadly_time_left > 0)
11348           player->shield_deadly_time_left--;
11349       }
11350     }
11351
11352     if (!game.LevelSolved && !level.use_step_counter)
11353     {
11354       TimePlayed++;
11355
11356       if (TimeLeft > 0)
11357       {
11358         TimeLeft--;
11359
11360         if (TimeLeft <= 10 && setup.time_limit)
11361           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11362
11363         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11364            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11365
11366         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11367
11368         if (!TimeLeft && setup.time_limit)
11369         {
11370           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11371             game_em.lev->killed_out_of_time = TRUE;
11372           else
11373             for (i = 0; i < MAX_PLAYERS; i++)
11374               KillPlayer(&stored_player[i]);
11375         }
11376       }
11377       else if (game.no_time_limit && !game.all_players_gone)
11378       {
11379         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11380       }
11381
11382       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11383     }
11384
11385     if (tape.recording || tape.playing)
11386       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11387   }
11388
11389   if (tape.recording || tape.playing)
11390     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11391
11392   UpdateAndDisplayGameControlValues();
11393 }
11394
11395 void AdvanceFrameAndPlayerCounters(int player_nr)
11396 {
11397   int i;
11398
11399   // advance frame counters (global frame counter and time frame counter)
11400   FrameCounter++;
11401   TimeFrames++;
11402
11403   // advance player counters (counters for move delay, move animation etc.)
11404   for (i = 0; i < MAX_PLAYERS; i++)
11405   {
11406     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11407     int move_delay_value = stored_player[i].move_delay_value;
11408     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11409
11410     if (!advance_player_counters)       // not all players may be affected
11411       continue;
11412
11413     if (move_frames == 0)       // less than one move per game frame
11414     {
11415       int stepsize = TILEX / move_delay_value;
11416       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11417       int count = (stored_player[i].is_moving ?
11418                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11419
11420       if (count % delay == 0)
11421         move_frames = 1;
11422     }
11423
11424     stored_player[i].Frame += move_frames;
11425
11426     if (stored_player[i].MovPos != 0)
11427       stored_player[i].StepFrame += move_frames;
11428
11429     if (stored_player[i].move_delay > 0)
11430       stored_player[i].move_delay--;
11431
11432     // due to bugs in previous versions, counter must count up, not down
11433     if (stored_player[i].push_delay != -1)
11434       stored_player[i].push_delay++;
11435
11436     if (stored_player[i].drop_delay > 0)
11437       stored_player[i].drop_delay--;
11438
11439     if (stored_player[i].is_dropping_pressed)
11440       stored_player[i].drop_pressed_delay++;
11441   }
11442 }
11443
11444 void StartGameActions(boolean init_network_game, boolean record_tape,
11445                       int random_seed)
11446 {
11447   unsigned int new_random_seed = InitRND(random_seed);
11448
11449   if (record_tape)
11450     TapeStartRecording(new_random_seed);
11451
11452   if (init_network_game)
11453   {
11454     SendToServer_LevelFile();
11455     SendToServer_StartPlaying();
11456
11457     return;
11458   }
11459
11460   InitGame();
11461 }
11462
11463 static void GameActionsExt(void)
11464 {
11465 #if 0
11466   static unsigned int game_frame_delay = 0;
11467 #endif
11468   unsigned int game_frame_delay_value;
11469   byte *recorded_player_action;
11470   byte summarized_player_action = 0;
11471   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11472   int i;
11473
11474   // detect endless loops, caused by custom element programming
11475   if (recursion_loop_detected && recursion_loop_depth == 0)
11476   {
11477     char *message = getStringCat3("Internal Error! Element ",
11478                                   EL_NAME(recursion_loop_element),
11479                                   " caused endless loop! Quit the game?");
11480
11481     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11482           EL_NAME(recursion_loop_element));
11483
11484     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11485
11486     recursion_loop_detected = FALSE;    // if game should be continued
11487
11488     free(message);
11489
11490     return;
11491   }
11492
11493   if (game.restart_level)
11494     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11495
11496   CheckLevelSolved();
11497
11498   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11499     GameWon();
11500
11501   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11502     TapeStop();
11503
11504   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11505     return;
11506
11507   game_frame_delay_value =
11508     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11509
11510   if (tape.playing && tape.warp_forward && !tape.pausing)
11511     game_frame_delay_value = 0;
11512
11513   SetVideoFrameDelay(game_frame_delay_value);
11514
11515   // (de)activate virtual buttons depending on current game status
11516   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11517   {
11518     if (game.all_players_gone)  // if no players there to be controlled anymore
11519       SetOverlayActive(FALSE);
11520     else if (!tape.playing)     // if game continues after tape stopped playing
11521       SetOverlayActive(TRUE);
11522   }
11523
11524 #if 0
11525 #if 0
11526   // ---------- main game synchronization point ----------
11527
11528   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11529
11530   printf("::: skip == %d\n", skip);
11531
11532 #else
11533   // ---------- main game synchronization point ----------
11534
11535   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11536 #endif
11537 #endif
11538
11539   if (network_playing && !network_player_action_received)
11540   {
11541     // try to get network player actions in time
11542
11543     // last chance to get network player actions without main loop delay
11544     HandleNetworking();
11545
11546     // game was quit by network peer
11547     if (game_status != GAME_MODE_PLAYING)
11548       return;
11549
11550     // check if network player actions still missing and game still running
11551     if (!network_player_action_received && !checkGameEnded())
11552       return;           // failed to get network player actions in time
11553
11554     // do not yet reset "network_player_action_received" (for tape.pausing)
11555   }
11556
11557   if (tape.pausing)
11558     return;
11559
11560   // at this point we know that we really continue executing the game
11561
11562   network_player_action_received = FALSE;
11563
11564   // when playing tape, read previously recorded player input from tape data
11565   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11566
11567   local_player->effective_mouse_action = local_player->mouse_action;
11568
11569   if (recorded_player_action != NULL)
11570     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11571                                  recorded_player_action);
11572
11573   // TapePlayAction() may return NULL when toggling to "pause before death"
11574   if (tape.pausing)
11575     return;
11576
11577   if (tape.set_centered_player)
11578   {
11579     game.centered_player_nr_next = tape.centered_player_nr_next;
11580     game.set_centered_player = TRUE;
11581   }
11582
11583   for (i = 0; i < MAX_PLAYERS; i++)
11584   {
11585     summarized_player_action |= stored_player[i].action;
11586
11587     if (!network_playing && (game.team_mode || tape.playing))
11588       stored_player[i].effective_action = stored_player[i].action;
11589   }
11590
11591   if (network_playing && !checkGameEnded())
11592     SendToServer_MovePlayer(summarized_player_action);
11593
11594   // summarize all actions at local players mapped input device position
11595   // (this allows using different input devices in single player mode)
11596   if (!network.enabled && !game.team_mode)
11597     stored_player[map_player_action[local_player->index_nr]].effective_action =
11598       summarized_player_action;
11599
11600   // summarize all actions at centered player in local team mode
11601   if (tape.recording &&
11602       setup.team_mode && !network.enabled &&
11603       setup.input_on_focus &&
11604       game.centered_player_nr != -1)
11605   {
11606     for (i = 0; i < MAX_PLAYERS; i++)
11607       stored_player[map_player_action[i]].effective_action =
11608         (i == game.centered_player_nr ? summarized_player_action : 0);
11609   }
11610
11611   if (recorded_player_action != NULL)
11612     for (i = 0; i < MAX_PLAYERS; i++)
11613       stored_player[i].effective_action = recorded_player_action[i];
11614
11615   for (i = 0; i < MAX_PLAYERS; i++)
11616   {
11617     tape_action[i] = stored_player[i].effective_action;
11618
11619     /* (this may happen in the RND game engine if a player was not present on
11620        the playfield on level start, but appeared later from a custom element */
11621     if (setup.team_mode &&
11622         tape.recording &&
11623         tape_action[i] &&
11624         !tape.player_participates[i])
11625       tape.player_participates[i] = TRUE;
11626   }
11627
11628   SetTapeActionFromMouseAction(tape_action,
11629                                &local_player->effective_mouse_action);
11630
11631   // only record actions from input devices, but not programmed actions
11632   if (tape.recording)
11633     TapeRecordAction(tape_action);
11634
11635   // remember if game was played (especially after tape stopped playing)
11636   if (!tape.playing && summarized_player_action)
11637     game.GamePlayed = TRUE;
11638
11639 #if USE_NEW_PLAYER_ASSIGNMENTS
11640   // !!! also map player actions in single player mode !!!
11641   // if (game.team_mode)
11642   if (1)
11643   {
11644     byte mapped_action[MAX_PLAYERS];
11645
11646 #if DEBUG_PLAYER_ACTIONS
11647     printf(":::");
11648     for (i = 0; i < MAX_PLAYERS; i++)
11649       printf(" %d, ", stored_player[i].effective_action);
11650 #endif
11651
11652     for (i = 0; i < MAX_PLAYERS; i++)
11653       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11654
11655     for (i = 0; i < MAX_PLAYERS; i++)
11656       stored_player[i].effective_action = mapped_action[i];
11657
11658 #if DEBUG_PLAYER_ACTIONS
11659     printf(" =>");
11660     for (i = 0; i < MAX_PLAYERS; i++)
11661       printf(" %d, ", stored_player[i].effective_action);
11662     printf("\n");
11663 #endif
11664   }
11665 #if DEBUG_PLAYER_ACTIONS
11666   else
11667   {
11668     printf(":::");
11669     for (i = 0; i < MAX_PLAYERS; i++)
11670       printf(" %d, ", stored_player[i].effective_action);
11671     printf("\n");
11672   }
11673 #endif
11674 #endif
11675
11676   for (i = 0; i < MAX_PLAYERS; i++)
11677   {
11678     // allow engine snapshot in case of changed movement attempt
11679     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11680         (stored_player[i].effective_action & KEY_MOTION))
11681       game.snapshot.changed_action = TRUE;
11682
11683     // allow engine snapshot in case of snapping/dropping attempt
11684     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11685         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11686       game.snapshot.changed_action = TRUE;
11687
11688     game.snapshot.last_action[i] = stored_player[i].effective_action;
11689   }
11690
11691   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11692   {
11693     GameActions_EM_Main();
11694   }
11695   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11696   {
11697     GameActions_SP_Main();
11698   }
11699   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11700   {
11701     GameActions_MM_Main();
11702   }
11703   else
11704   {
11705     GameActions_RND_Main();
11706   }
11707
11708   BlitScreenToBitmap(backbuffer);
11709
11710   CheckLevelSolved();
11711   CheckLevelTime();
11712
11713   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11714
11715   if (global.show_frames_per_second)
11716   {
11717     static unsigned int fps_counter = 0;
11718     static int fps_frames = 0;
11719     unsigned int fps_delay_ms = Counter() - fps_counter;
11720
11721     fps_frames++;
11722
11723     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11724     {
11725       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11726
11727       fps_frames = 0;
11728       fps_counter = Counter();
11729
11730       // always draw FPS to screen after FPS value was updated
11731       redraw_mask |= REDRAW_FPS;
11732     }
11733
11734     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11735     if (GetDrawDeactivationMask() == REDRAW_NONE)
11736       redraw_mask |= REDRAW_FPS;
11737   }
11738 }
11739
11740 static void GameActions_CheckSaveEngineSnapshot(void)
11741 {
11742   if (!game.snapshot.save_snapshot)
11743     return;
11744
11745   // clear flag for saving snapshot _before_ saving snapshot
11746   game.snapshot.save_snapshot = FALSE;
11747
11748   SaveEngineSnapshotToList();
11749 }
11750
11751 void GameActions(void)
11752 {
11753   GameActionsExt();
11754
11755   GameActions_CheckSaveEngineSnapshot();
11756 }
11757
11758 void GameActions_EM_Main(void)
11759 {
11760   byte effective_action[MAX_PLAYERS];
11761   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11762   int i;
11763
11764   for (i = 0; i < MAX_PLAYERS; i++)
11765     effective_action[i] = stored_player[i].effective_action;
11766
11767   GameActions_EM(effective_action, warp_mode);
11768 }
11769
11770 void GameActions_SP_Main(void)
11771 {
11772   byte effective_action[MAX_PLAYERS];
11773   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11774   int i;
11775
11776   for (i = 0; i < MAX_PLAYERS; i++)
11777     effective_action[i] = stored_player[i].effective_action;
11778
11779   GameActions_SP(effective_action, warp_mode);
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782   {
11783     if (stored_player[i].force_dropping)
11784       stored_player[i].action |= KEY_BUTTON_DROP;
11785
11786     stored_player[i].force_dropping = FALSE;
11787   }
11788 }
11789
11790 void GameActions_MM_Main(void)
11791 {
11792   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11793
11794   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11795 }
11796
11797 void GameActions_RND_Main(void)
11798 {
11799   GameActions_RND();
11800 }
11801
11802 void GameActions_RND(void)
11803 {
11804   static struct MouseActionInfo mouse_action_last = { 0 };
11805   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11806   int magic_wall_x = 0, magic_wall_y = 0;
11807   int i, x, y, element, graphic, last_gfx_frame;
11808
11809   InitPlayfieldScanModeVars();
11810
11811   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11812   {
11813     SCAN_PLAYFIELD(x, y)
11814     {
11815       ChangeCount[x][y] = 0;
11816       ChangeEvent[x][y] = -1;
11817     }
11818   }
11819
11820   if (game.set_centered_player)
11821   {
11822     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11823
11824     // switching to "all players" only possible if all players fit to screen
11825     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11826     {
11827       game.centered_player_nr_next = game.centered_player_nr;
11828       game.set_centered_player = FALSE;
11829     }
11830
11831     // do not switch focus to non-existing (or non-active) player
11832     if (game.centered_player_nr_next >= 0 &&
11833         !stored_player[game.centered_player_nr_next].active)
11834     {
11835       game.centered_player_nr_next = game.centered_player_nr;
11836       game.set_centered_player = FALSE;
11837     }
11838   }
11839
11840   if (game.set_centered_player &&
11841       ScreenMovPos == 0)        // screen currently aligned at tile position
11842   {
11843     int sx, sy;
11844
11845     if (game.centered_player_nr_next == -1)
11846     {
11847       setScreenCenteredToAllPlayers(&sx, &sy);
11848     }
11849     else
11850     {
11851       sx = stored_player[game.centered_player_nr_next].jx;
11852       sy = stored_player[game.centered_player_nr_next].jy;
11853     }
11854
11855     game.centered_player_nr = game.centered_player_nr_next;
11856     game.set_centered_player = FALSE;
11857
11858     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11859     DrawGameDoorValues();
11860   }
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863   {
11864     int actual_player_action = stored_player[i].effective_action;
11865
11866 #if 1
11867     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11868        - rnd_equinox_tetrachloride 048
11869        - rnd_equinox_tetrachloride_ii 096
11870        - rnd_emanuel_schmieg 002
11871        - doctor_sloan_ww 001, 020
11872     */
11873     if (stored_player[i].MovPos == 0)
11874       CheckGravityMovement(&stored_player[i]);
11875 #endif
11876
11877     // overwrite programmed action with tape action
11878     if (stored_player[i].programmed_action)
11879       actual_player_action = stored_player[i].programmed_action;
11880
11881     PlayerActions(&stored_player[i], actual_player_action);
11882
11883     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11884   }
11885
11886   ScrollScreen(NULL, SCROLL_GO_ON);
11887
11888   /* for backwards compatibility, the following code emulates a fixed bug that
11889      occured when pushing elements (causing elements that just made their last
11890      pushing step to already (if possible) make their first falling step in the
11891      same game frame, which is bad); this code is also needed to use the famous
11892      "spring push bug" which is used in older levels and might be wanted to be
11893      used also in newer levels, but in this case the buggy pushing code is only
11894      affecting the "spring" element and no other elements */
11895
11896   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11897   {
11898     for (i = 0; i < MAX_PLAYERS; i++)
11899     {
11900       struct PlayerInfo *player = &stored_player[i];
11901       int x = player->jx;
11902       int y = player->jy;
11903
11904       if (player->active && player->is_pushing && player->is_moving &&
11905           IS_MOVING(x, y) &&
11906           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11907            Feld[x][y] == EL_SPRING))
11908       {
11909         ContinueMoving(x, y);
11910
11911         // continue moving after pushing (this is actually a bug)
11912         if (!IS_MOVING(x, y))
11913           Stop[x][y] = FALSE;
11914       }
11915     }
11916   }
11917
11918   SCAN_PLAYFIELD(x, y)
11919   {
11920     Last[x][y] = Feld[x][y];
11921
11922     ChangeCount[x][y] = 0;
11923     ChangeEvent[x][y] = -1;
11924
11925     // this must be handled before main playfield loop
11926     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11927     {
11928       MovDelay[x][y]--;
11929       if (MovDelay[x][y] <= 0)
11930         RemoveField(x, y);
11931     }
11932
11933     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11934     {
11935       MovDelay[x][y]--;
11936       if (MovDelay[x][y] <= 0)
11937       {
11938         RemoveField(x, y);
11939         TEST_DrawLevelField(x, y);
11940
11941         TestIfElementTouchesCustomElement(x, y);        // for empty space
11942       }
11943     }
11944
11945 #if DEBUG
11946     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11947     {
11948       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11949       printf("GameActions(): This should never happen!\n");
11950
11951       ChangePage[x][y] = -1;
11952     }
11953 #endif
11954
11955     Stop[x][y] = FALSE;
11956     if (WasJustMoving[x][y] > 0)
11957       WasJustMoving[x][y]--;
11958     if (WasJustFalling[x][y] > 0)
11959       WasJustFalling[x][y]--;
11960     if (CheckCollision[x][y] > 0)
11961       CheckCollision[x][y]--;
11962     if (CheckImpact[x][y] > 0)
11963       CheckImpact[x][y]--;
11964
11965     GfxFrame[x][y]++;
11966
11967     /* reset finished pushing action (not done in ContinueMoving() to allow
11968        continuous pushing animation for elements with zero push delay) */
11969     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11970     {
11971       ResetGfxAnimation(x, y);
11972       TEST_DrawLevelField(x, y);
11973     }
11974
11975 #if DEBUG
11976     if (IS_BLOCKED(x, y))
11977     {
11978       int oldx, oldy;
11979
11980       Blocked2Moving(x, y, &oldx, &oldy);
11981       if (!IS_MOVING(oldx, oldy))
11982       {
11983         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11984         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11985         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11986         printf("GameActions(): This should never happen!\n");
11987       }
11988     }
11989 #endif
11990   }
11991
11992   if (mouse_action.button)
11993   {
11994     int new_button = (mouse_action.button && mouse_action_last.button == 0);
11995
11996     x = mouse_action.lx;
11997     y = mouse_action.ly;
11998     element = Feld[x][y];
11999
12000     if (new_button)
12001     {
12002       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12003       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12004     }
12005
12006     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12007     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12008   }
12009
12010   SCAN_PLAYFIELD(x, y)
12011   {
12012     element = Feld[x][y];
12013     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12014     last_gfx_frame = GfxFrame[x][y];
12015
12016     ResetGfxFrame(x, y);
12017
12018     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12019       DrawLevelGraphicAnimation(x, y, graphic);
12020
12021     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12022         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12023       ResetRandomAnimationValue(x, y);
12024
12025     SetRandomAnimationValue(x, y);
12026
12027     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12028
12029     if (IS_INACTIVE(element))
12030     {
12031       if (IS_ANIMATED(graphic))
12032         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12033
12034       continue;
12035     }
12036
12037     // this may take place after moving, so 'element' may have changed
12038     if (IS_CHANGING(x, y) &&
12039         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12040     {
12041       int page = element_info[element].event_page_nr[CE_DELAY];
12042
12043       HandleElementChange(x, y, page);
12044
12045       element = Feld[x][y];
12046       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12047     }
12048
12049     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12050     {
12051       StartMoving(x, y);
12052
12053       element = Feld[x][y];
12054       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12055
12056       if (IS_ANIMATED(graphic) &&
12057           !IS_MOVING(x, y) &&
12058           !Stop[x][y])
12059         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12060
12061       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12062         TEST_DrawTwinkleOnField(x, y);
12063     }
12064     else if (element == EL_ACID)
12065     {
12066       if (!Stop[x][y])
12067         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12068     }
12069     else if ((element == EL_EXIT_OPEN ||
12070               element == EL_EM_EXIT_OPEN ||
12071               element == EL_SP_EXIT_OPEN ||
12072               element == EL_STEEL_EXIT_OPEN ||
12073               element == EL_EM_STEEL_EXIT_OPEN ||
12074               element == EL_SP_TERMINAL ||
12075               element == EL_SP_TERMINAL_ACTIVE ||
12076               element == EL_EXTRA_TIME ||
12077               element == EL_SHIELD_NORMAL ||
12078               element == EL_SHIELD_DEADLY) &&
12079              IS_ANIMATED(graphic))
12080       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12081     else if (IS_MOVING(x, y))
12082       ContinueMoving(x, y);
12083     else if (IS_ACTIVE_BOMB(element))
12084       CheckDynamite(x, y);
12085     else if (element == EL_AMOEBA_GROWING)
12086       AmoebeWaechst(x, y);
12087     else if (element == EL_AMOEBA_SHRINKING)
12088       AmoebaDisappearing(x, y);
12089
12090 #if !USE_NEW_AMOEBA_CODE
12091     else if (IS_AMOEBALIVE(element))
12092       AmoebeAbleger(x, y);
12093 #endif
12094
12095     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12096       Life(x, y);
12097     else if (element == EL_EXIT_CLOSED)
12098       CheckExit(x, y);
12099     else if (element == EL_EM_EXIT_CLOSED)
12100       CheckExitEM(x, y);
12101     else if (element == EL_STEEL_EXIT_CLOSED)
12102       CheckExitSteel(x, y);
12103     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12104       CheckExitSteelEM(x, y);
12105     else if (element == EL_SP_EXIT_CLOSED)
12106       CheckExitSP(x, y);
12107     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12108              element == EL_EXPANDABLE_STEELWALL_GROWING)
12109       MauerWaechst(x, y);
12110     else if (element == EL_EXPANDABLE_WALL ||
12111              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12112              element == EL_EXPANDABLE_WALL_VERTICAL ||
12113              element == EL_EXPANDABLE_WALL_ANY ||
12114              element == EL_BD_EXPANDABLE_WALL)
12115       MauerAbleger(x, y);
12116     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12117              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12118              element == EL_EXPANDABLE_STEELWALL_ANY)
12119       MauerAblegerStahl(x, y);
12120     else if (element == EL_FLAMES)
12121       CheckForDragon(x, y);
12122     else if (element == EL_EXPLOSION)
12123       ; // drawing of correct explosion animation is handled separately
12124     else if (element == EL_ELEMENT_SNAPPING ||
12125              element == EL_DIAGONAL_SHRINKING ||
12126              element == EL_DIAGONAL_GROWING)
12127     {
12128       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12129
12130       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12131     }
12132     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12133       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12134
12135     if (IS_BELT_ACTIVE(element))
12136       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12137
12138     if (game.magic_wall_active)
12139     {
12140       int jx = local_player->jx, jy = local_player->jy;
12141
12142       // play the element sound at the position nearest to the player
12143       if ((element == EL_MAGIC_WALL_FULL ||
12144            element == EL_MAGIC_WALL_ACTIVE ||
12145            element == EL_MAGIC_WALL_EMPTYING ||
12146            element == EL_BD_MAGIC_WALL_FULL ||
12147            element == EL_BD_MAGIC_WALL_ACTIVE ||
12148            element == EL_BD_MAGIC_WALL_EMPTYING ||
12149            element == EL_DC_MAGIC_WALL_FULL ||
12150            element == EL_DC_MAGIC_WALL_ACTIVE ||
12151            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12152           ABS(x - jx) + ABS(y - jy) <
12153           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12154       {
12155         magic_wall_x = x;
12156         magic_wall_y = y;
12157       }
12158     }
12159   }
12160
12161 #if USE_NEW_AMOEBA_CODE
12162   // new experimental amoeba growth stuff
12163   if (!(FrameCounter % 8))
12164   {
12165     static unsigned int random = 1684108901;
12166
12167     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12168     {
12169       x = RND(lev_fieldx);
12170       y = RND(lev_fieldy);
12171       element = Feld[x][y];
12172
12173       if (!IS_PLAYER(x,y) &&
12174           (element == EL_EMPTY ||
12175            CAN_GROW_INTO(element) ||
12176            element == EL_QUICKSAND_EMPTY ||
12177            element == EL_QUICKSAND_FAST_EMPTY ||
12178            element == EL_ACID_SPLASH_LEFT ||
12179            element == EL_ACID_SPLASH_RIGHT))
12180       {
12181         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12182             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12183             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12184             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12185           Feld[x][y] = EL_AMOEBA_DROP;
12186       }
12187
12188       random = random * 129 + 1;
12189     }
12190   }
12191 #endif
12192
12193   game.explosions_delayed = FALSE;
12194
12195   SCAN_PLAYFIELD(x, y)
12196   {
12197     element = Feld[x][y];
12198
12199     if (ExplodeField[x][y])
12200       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12201     else if (element == EL_EXPLOSION)
12202       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12203
12204     ExplodeField[x][y] = EX_TYPE_NONE;
12205   }
12206
12207   game.explosions_delayed = TRUE;
12208
12209   if (game.magic_wall_active)
12210   {
12211     if (!(game.magic_wall_time_left % 4))
12212     {
12213       int element = Feld[magic_wall_x][magic_wall_y];
12214
12215       if (element == EL_BD_MAGIC_WALL_FULL ||
12216           element == EL_BD_MAGIC_WALL_ACTIVE ||
12217           element == EL_BD_MAGIC_WALL_EMPTYING)
12218         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12219       else if (element == EL_DC_MAGIC_WALL_FULL ||
12220                element == EL_DC_MAGIC_WALL_ACTIVE ||
12221                element == EL_DC_MAGIC_WALL_EMPTYING)
12222         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12223       else
12224         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12225     }
12226
12227     if (game.magic_wall_time_left > 0)
12228     {
12229       game.magic_wall_time_left--;
12230
12231       if (!game.magic_wall_time_left)
12232       {
12233         SCAN_PLAYFIELD(x, y)
12234         {
12235           element = Feld[x][y];
12236
12237           if (element == EL_MAGIC_WALL_ACTIVE ||
12238               element == EL_MAGIC_WALL_FULL)
12239           {
12240             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12241             TEST_DrawLevelField(x, y);
12242           }
12243           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12244                    element == EL_BD_MAGIC_WALL_FULL)
12245           {
12246             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12247             TEST_DrawLevelField(x, y);
12248           }
12249           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12250                    element == EL_DC_MAGIC_WALL_FULL)
12251           {
12252             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12253             TEST_DrawLevelField(x, y);
12254           }
12255         }
12256
12257         game.magic_wall_active = FALSE;
12258       }
12259     }
12260   }
12261
12262   if (game.light_time_left > 0)
12263   {
12264     game.light_time_left--;
12265
12266     if (game.light_time_left == 0)
12267       RedrawAllLightSwitchesAndInvisibleElements();
12268   }
12269
12270   if (game.timegate_time_left > 0)
12271   {
12272     game.timegate_time_left--;
12273
12274     if (game.timegate_time_left == 0)
12275       CloseAllOpenTimegates();
12276   }
12277
12278   if (game.lenses_time_left > 0)
12279   {
12280     game.lenses_time_left--;
12281
12282     if (game.lenses_time_left == 0)
12283       RedrawAllInvisibleElementsForLenses();
12284   }
12285
12286   if (game.magnify_time_left > 0)
12287   {
12288     game.magnify_time_left--;
12289
12290     if (game.magnify_time_left == 0)
12291       RedrawAllInvisibleElementsForMagnifier();
12292   }
12293
12294   for (i = 0; i < MAX_PLAYERS; i++)
12295   {
12296     struct PlayerInfo *player = &stored_player[i];
12297
12298     if (SHIELD_ON(player))
12299     {
12300       if (player->shield_deadly_time_left)
12301         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12302       else if (player->shield_normal_time_left)
12303         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12304     }
12305   }
12306
12307 #if USE_DELAYED_GFX_REDRAW
12308   SCAN_PLAYFIELD(x, y)
12309   {
12310     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12311     {
12312       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12313          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12314
12315       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12316         DrawLevelField(x, y);
12317
12318       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12319         DrawLevelFieldCrumbled(x, y);
12320
12321       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12322         DrawLevelFieldCrumbledNeighbours(x, y);
12323
12324       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12325         DrawTwinkleOnField(x, y);
12326     }
12327
12328     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12329   }
12330 #endif
12331
12332   DrawAllPlayers();
12333   PlayAllPlayersSound();
12334
12335   for (i = 0; i < MAX_PLAYERS; i++)
12336   {
12337     struct PlayerInfo *player = &stored_player[i];
12338
12339     if (player->show_envelope != 0 && (!player->active ||
12340                                        player->MovPos == 0))
12341     {
12342       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12343
12344       player->show_envelope = 0;
12345     }
12346   }
12347
12348   // use random number generator in every frame to make it less predictable
12349   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12350     RND(1);
12351
12352   mouse_action_last = mouse_action;
12353 }
12354
12355 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12356 {
12357   int min_x = x, min_y = y, max_x = x, max_y = y;
12358   int i;
12359
12360   for (i = 0; i < MAX_PLAYERS; i++)
12361   {
12362     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12363
12364     if (!stored_player[i].active || &stored_player[i] == player)
12365       continue;
12366
12367     min_x = MIN(min_x, jx);
12368     min_y = MIN(min_y, jy);
12369     max_x = MAX(max_x, jx);
12370     max_y = MAX(max_y, jy);
12371   }
12372
12373   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12374 }
12375
12376 static boolean AllPlayersInVisibleScreen(void)
12377 {
12378   int i;
12379
12380   for (i = 0; i < MAX_PLAYERS; i++)
12381   {
12382     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12383
12384     if (!stored_player[i].active)
12385       continue;
12386
12387     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12388       return FALSE;
12389   }
12390
12391   return TRUE;
12392 }
12393
12394 void ScrollLevel(int dx, int dy)
12395 {
12396   int scroll_offset = 2 * TILEX_VAR;
12397   int x, y;
12398
12399   BlitBitmap(drawto_field, drawto_field,
12400              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12401              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12402              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12403              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12404              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12405              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12406
12407   if (dx != 0)
12408   {
12409     x = (dx == 1 ? BX1 : BX2);
12410     for (y = BY1; y <= BY2; y++)
12411       DrawScreenField(x, y);
12412   }
12413
12414   if (dy != 0)
12415   {
12416     y = (dy == 1 ? BY1 : BY2);
12417     for (x = BX1; x <= BX2; x++)
12418       DrawScreenField(x, y);
12419   }
12420
12421   redraw_mask |= REDRAW_FIELD;
12422 }
12423
12424 static boolean canFallDown(struct PlayerInfo *player)
12425 {
12426   int jx = player->jx, jy = player->jy;
12427
12428   return (IN_LEV_FIELD(jx, jy + 1) &&
12429           (IS_FREE(jx, jy + 1) ||
12430            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12431           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12432           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12433 }
12434
12435 static boolean canPassField(int x, int y, int move_dir)
12436 {
12437   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12438   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12439   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12440   int nextx = x + dx;
12441   int nexty = y + dy;
12442   int element = Feld[x][y];
12443
12444   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12445           !CAN_MOVE(element) &&
12446           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12447           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12448           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12449 }
12450
12451 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12452 {
12453   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12454   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12455   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12456   int newx = x + dx;
12457   int newy = y + dy;
12458
12459   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12460           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12461           (IS_DIGGABLE(Feld[newx][newy]) ||
12462            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12463            canPassField(newx, newy, move_dir)));
12464 }
12465
12466 static void CheckGravityMovement(struct PlayerInfo *player)
12467 {
12468   if (player->gravity && !player->programmed_action)
12469   {
12470     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12471     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12472     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12473     int jx = player->jx, jy = player->jy;
12474     boolean player_is_moving_to_valid_field =
12475       (!player_is_snapping &&
12476        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12477         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12478     boolean player_can_fall_down = canFallDown(player);
12479
12480     if (player_can_fall_down &&
12481         !player_is_moving_to_valid_field)
12482       player->programmed_action = MV_DOWN;
12483   }
12484 }
12485
12486 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12487 {
12488   return CheckGravityMovement(player);
12489
12490   if (player->gravity && !player->programmed_action)
12491   {
12492     int jx = player->jx, jy = player->jy;
12493     boolean field_under_player_is_free =
12494       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12495     boolean player_is_standing_on_valid_field =
12496       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12497        (IS_WALKABLE(Feld[jx][jy]) &&
12498         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12499
12500     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12501       player->programmed_action = MV_DOWN;
12502   }
12503 }
12504
12505 /*
12506   MovePlayerOneStep()
12507   -----------------------------------------------------------------------------
12508   dx, dy:               direction (non-diagonal) to try to move the player to
12509   real_dx, real_dy:     direction as read from input device (can be diagonal)
12510 */
12511
12512 boolean MovePlayerOneStep(struct PlayerInfo *player,
12513                           int dx, int dy, int real_dx, int real_dy)
12514 {
12515   int jx = player->jx, jy = player->jy;
12516   int new_jx = jx + dx, new_jy = jy + dy;
12517   int can_move;
12518   boolean player_can_move = !player->cannot_move;
12519
12520   if (!player->active || (!dx && !dy))
12521     return MP_NO_ACTION;
12522
12523   player->MovDir = (dx < 0 ? MV_LEFT :
12524                     dx > 0 ? MV_RIGHT :
12525                     dy < 0 ? MV_UP :
12526                     dy > 0 ? MV_DOWN :  MV_NONE);
12527
12528   if (!IN_LEV_FIELD(new_jx, new_jy))
12529     return MP_NO_ACTION;
12530
12531   if (!player_can_move)
12532   {
12533     if (player->MovPos == 0)
12534     {
12535       player->is_moving = FALSE;
12536       player->is_digging = FALSE;
12537       player->is_collecting = FALSE;
12538       player->is_snapping = FALSE;
12539       player->is_pushing = FALSE;
12540     }
12541   }
12542
12543   if (!network.enabled && game.centered_player_nr == -1 &&
12544       !AllPlayersInSight(player, new_jx, new_jy))
12545     return MP_NO_ACTION;
12546
12547   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12548   if (can_move != MP_MOVING)
12549     return can_move;
12550
12551   // check if DigField() has caused relocation of the player
12552   if (player->jx != jx || player->jy != jy)
12553     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12554
12555   StorePlayer[jx][jy] = 0;
12556   player->last_jx = jx;
12557   player->last_jy = jy;
12558   player->jx = new_jx;
12559   player->jy = new_jy;
12560   StorePlayer[new_jx][new_jy] = player->element_nr;
12561
12562   if (player->move_delay_value_next != -1)
12563   {
12564     player->move_delay_value = player->move_delay_value_next;
12565     player->move_delay_value_next = -1;
12566   }
12567
12568   player->MovPos =
12569     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12570
12571   player->step_counter++;
12572
12573   PlayerVisit[jx][jy] = FrameCounter;
12574
12575   player->is_moving = TRUE;
12576
12577 #if 1
12578   // should better be called in MovePlayer(), but this breaks some tapes
12579   ScrollPlayer(player, SCROLL_INIT);
12580 #endif
12581
12582   return MP_MOVING;
12583 }
12584
12585 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12586 {
12587   int jx = player->jx, jy = player->jy;
12588   int old_jx = jx, old_jy = jy;
12589   int moved = MP_NO_ACTION;
12590
12591   if (!player->active)
12592     return FALSE;
12593
12594   if (!dx && !dy)
12595   {
12596     if (player->MovPos == 0)
12597     {
12598       player->is_moving = FALSE;
12599       player->is_digging = FALSE;
12600       player->is_collecting = FALSE;
12601       player->is_snapping = FALSE;
12602       player->is_pushing = FALSE;
12603     }
12604
12605     return FALSE;
12606   }
12607
12608   if (player->move_delay > 0)
12609     return FALSE;
12610
12611   player->move_delay = -1;              // set to "uninitialized" value
12612
12613   // store if player is automatically moved to next field
12614   player->is_auto_moving = (player->programmed_action != MV_NONE);
12615
12616   // remove the last programmed player action
12617   player->programmed_action = 0;
12618
12619   if (player->MovPos)
12620   {
12621     // should only happen if pre-1.2 tape recordings are played
12622     // this is only for backward compatibility
12623
12624     int original_move_delay_value = player->move_delay_value;
12625
12626 #if DEBUG
12627     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12628            tape.counter);
12629 #endif
12630
12631     // scroll remaining steps with finest movement resolution
12632     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12633
12634     while (player->MovPos)
12635     {
12636       ScrollPlayer(player, SCROLL_GO_ON);
12637       ScrollScreen(NULL, SCROLL_GO_ON);
12638
12639       AdvanceFrameAndPlayerCounters(player->index_nr);
12640
12641       DrawAllPlayers();
12642       BackToFront_WithFrameDelay(0);
12643     }
12644
12645     player->move_delay_value = original_move_delay_value;
12646   }
12647
12648   player->is_active = FALSE;
12649
12650   if (player->last_move_dir & MV_HORIZONTAL)
12651   {
12652     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12653       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12654   }
12655   else
12656   {
12657     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12658       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12659   }
12660
12661   if (!moved && !player->is_active)
12662   {
12663     player->is_moving = FALSE;
12664     player->is_digging = FALSE;
12665     player->is_collecting = FALSE;
12666     player->is_snapping = FALSE;
12667     player->is_pushing = FALSE;
12668   }
12669
12670   jx = player->jx;
12671   jy = player->jy;
12672
12673   if (moved & MP_MOVING && !ScreenMovPos &&
12674       (player->index_nr == game.centered_player_nr ||
12675        game.centered_player_nr == -1))
12676   {
12677     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12678
12679     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12680     {
12681       // actual player has left the screen -- scroll in that direction
12682       if (jx != old_jx)         // player has moved horizontally
12683         scroll_x += (jx - old_jx);
12684       else                      // player has moved vertically
12685         scroll_y += (jy - old_jy);
12686     }
12687     else
12688     {
12689       int offset_raw = game.scroll_delay_value;
12690
12691       if (jx != old_jx)         // player has moved horizontally
12692       {
12693         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12694         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12695         int new_scroll_x = jx - MIDPOSX + offset_x;
12696
12697         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12698             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12699           scroll_x = new_scroll_x;
12700
12701         // don't scroll over playfield boundaries
12702         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12703
12704         // don't scroll more than one field at a time
12705         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12706
12707         // don't scroll against the player's moving direction
12708         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12709             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12710           scroll_x = old_scroll_x;
12711       }
12712       else                      // player has moved vertically
12713       {
12714         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12715         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12716         int new_scroll_y = jy - MIDPOSY + offset_y;
12717
12718         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12719             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12720           scroll_y = new_scroll_y;
12721
12722         // don't scroll over playfield boundaries
12723         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12724
12725         // don't scroll more than one field at a time
12726         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12727
12728         // don't scroll against the player's moving direction
12729         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12730             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12731           scroll_y = old_scroll_y;
12732       }
12733     }
12734
12735     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12736     {
12737       if (!network.enabled && game.centered_player_nr == -1 &&
12738           !AllPlayersInVisibleScreen())
12739       {
12740         scroll_x = old_scroll_x;
12741         scroll_y = old_scroll_y;
12742       }
12743       else
12744       {
12745         ScrollScreen(player, SCROLL_INIT);
12746         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12747       }
12748     }
12749   }
12750
12751   player->StepFrame = 0;
12752
12753   if (moved & MP_MOVING)
12754   {
12755     if (old_jx != jx && old_jy == jy)
12756       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12757     else if (old_jx == jx && old_jy != jy)
12758       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12759
12760     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12761
12762     player->last_move_dir = player->MovDir;
12763     player->is_moving = TRUE;
12764     player->is_snapping = FALSE;
12765     player->is_switching = FALSE;
12766     player->is_dropping = FALSE;
12767     player->is_dropping_pressed = FALSE;
12768     player->drop_pressed_delay = 0;
12769
12770 #if 0
12771     // should better be called here than above, but this breaks some tapes
12772     ScrollPlayer(player, SCROLL_INIT);
12773 #endif
12774   }
12775   else
12776   {
12777     CheckGravityMovementWhenNotMoving(player);
12778
12779     player->is_moving = FALSE;
12780
12781     /* at this point, the player is allowed to move, but cannot move right now
12782        (e.g. because of something blocking the way) -- ensure that the player
12783        is also allowed to move in the next frame (in old versions before 3.1.1,
12784        the player was forced to wait again for eight frames before next try) */
12785
12786     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12787       player->move_delay = 0;   // allow direct movement in the next frame
12788   }
12789
12790   if (player->move_delay == -1)         // not yet initialized by DigField()
12791     player->move_delay = player->move_delay_value;
12792
12793   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12794   {
12795     TestIfPlayerTouchesBadThing(jx, jy);
12796     TestIfPlayerTouchesCustomElement(jx, jy);
12797   }
12798
12799   if (!player->active)
12800     RemovePlayer(player);
12801
12802   return moved;
12803 }
12804
12805 void ScrollPlayer(struct PlayerInfo *player, int mode)
12806 {
12807   int jx = player->jx, jy = player->jy;
12808   int last_jx = player->last_jx, last_jy = player->last_jy;
12809   int move_stepsize = TILEX / player->move_delay_value;
12810
12811   if (!player->active)
12812     return;
12813
12814   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12815     return;
12816
12817   if (mode == SCROLL_INIT)
12818   {
12819     player->actual_frame_counter = FrameCounter;
12820     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12821
12822     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12823         Feld[last_jx][last_jy] == EL_EMPTY)
12824     {
12825       int last_field_block_delay = 0;   // start with no blocking at all
12826       int block_delay_adjustment = player->block_delay_adjustment;
12827
12828       // if player blocks last field, add delay for exactly one move
12829       if (player->block_last_field)
12830       {
12831         last_field_block_delay += player->move_delay_value;
12832
12833         // when blocking enabled, prevent moving up despite gravity
12834         if (player->gravity && player->MovDir == MV_UP)
12835           block_delay_adjustment = -1;
12836       }
12837
12838       // add block delay adjustment (also possible when not blocking)
12839       last_field_block_delay += block_delay_adjustment;
12840
12841       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12842       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12843     }
12844
12845     if (player->MovPos != 0)    // player has not yet reached destination
12846       return;
12847   }
12848   else if (!FrameReached(&player->actual_frame_counter, 1))
12849     return;
12850
12851   if (player->MovPos != 0)
12852   {
12853     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12854     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12855
12856     // before DrawPlayer() to draw correct player graphic for this case
12857     if (player->MovPos == 0)
12858       CheckGravityMovement(player);
12859   }
12860
12861   if (player->MovPos == 0)      // player reached destination field
12862   {
12863     if (player->move_delay_reset_counter > 0)
12864     {
12865       player->move_delay_reset_counter--;
12866
12867       if (player->move_delay_reset_counter == 0)
12868       {
12869         // continue with normal speed after quickly moving through gate
12870         HALVE_PLAYER_SPEED(player);
12871
12872         // be able to make the next move without delay
12873         player->move_delay = 0;
12874       }
12875     }
12876
12877     player->last_jx = jx;
12878     player->last_jy = jy;
12879
12880     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12881         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12882         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12883         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12884         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12885         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12886         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12887         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12888     {
12889       ExitPlayer(player);
12890
12891       if (game.players_still_needed == 0 &&
12892           (game.friends_still_needed == 0 ||
12893            IS_SP_ELEMENT(Feld[jx][jy])))
12894         LevelSolved();
12895     }
12896
12897     // this breaks one level: "machine", level 000
12898     {
12899       int move_direction = player->MovDir;
12900       int enter_side = MV_DIR_OPPOSITE(move_direction);
12901       int leave_side = move_direction;
12902       int old_jx = last_jx;
12903       int old_jy = last_jy;
12904       int old_element = Feld[old_jx][old_jy];
12905       int new_element = Feld[jx][jy];
12906
12907       if (IS_CUSTOM_ELEMENT(old_element))
12908         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12909                                    CE_LEFT_BY_PLAYER,
12910                                    player->index_bit, leave_side);
12911
12912       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12913                                           CE_PLAYER_LEAVES_X,
12914                                           player->index_bit, leave_side);
12915
12916       if (IS_CUSTOM_ELEMENT(new_element))
12917         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12918                                    player->index_bit, enter_side);
12919
12920       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12921                                           CE_PLAYER_ENTERS_X,
12922                                           player->index_bit, enter_side);
12923
12924       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12925                                         CE_MOVE_OF_X, move_direction);
12926     }
12927
12928     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12929     {
12930       TestIfPlayerTouchesBadThing(jx, jy);
12931       TestIfPlayerTouchesCustomElement(jx, jy);
12932
12933       /* needed because pushed element has not yet reached its destination,
12934          so it would trigger a change event at its previous field location */
12935       if (!player->is_pushing)
12936         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12937
12938       if (!player->active)
12939         RemovePlayer(player);
12940     }
12941
12942     if (!game.LevelSolved && level.use_step_counter)
12943     {
12944       int i;
12945
12946       TimePlayed++;
12947
12948       if (TimeLeft > 0)
12949       {
12950         TimeLeft--;
12951
12952         if (TimeLeft <= 10 && setup.time_limit)
12953           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12954
12955         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12956
12957         DisplayGameControlValues();
12958
12959         if (!TimeLeft && setup.time_limit)
12960           for (i = 0; i < MAX_PLAYERS; i++)
12961             KillPlayer(&stored_player[i]);
12962       }
12963       else if (game.no_time_limit && !game.all_players_gone)
12964       {
12965         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12966
12967         DisplayGameControlValues();
12968       }
12969     }
12970
12971     if (tape.single_step && tape.recording && !tape.pausing &&
12972         !player->programmed_action)
12973       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12974
12975     if (!player->programmed_action)
12976       CheckSaveEngineSnapshot(player);
12977   }
12978 }
12979
12980 void ScrollScreen(struct PlayerInfo *player, int mode)
12981 {
12982   static unsigned int screen_frame_counter = 0;
12983
12984   if (mode == SCROLL_INIT)
12985   {
12986     // set scrolling step size according to actual player's moving speed
12987     ScrollStepSize = TILEX / player->move_delay_value;
12988
12989     screen_frame_counter = FrameCounter;
12990     ScreenMovDir = player->MovDir;
12991     ScreenMovPos = player->MovPos;
12992     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12993     return;
12994   }
12995   else if (!FrameReached(&screen_frame_counter, 1))
12996     return;
12997
12998   if (ScreenMovPos)
12999   {
13000     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13001     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13002     redraw_mask |= REDRAW_FIELD;
13003   }
13004   else
13005     ScreenMovDir = MV_NONE;
13006 }
13007
13008 void TestIfPlayerTouchesCustomElement(int x, int y)
13009 {
13010   static int xy[4][2] =
13011   {
13012     { 0, -1 },
13013     { -1, 0 },
13014     { +1, 0 },
13015     { 0, +1 }
13016   };
13017   static int trigger_sides[4][2] =
13018   {
13019     // center side       border side
13020     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13021     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13022     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13023     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13024   };
13025   static int touch_dir[4] =
13026   {
13027     MV_LEFT | MV_RIGHT,
13028     MV_UP   | MV_DOWN,
13029     MV_UP   | MV_DOWN,
13030     MV_LEFT | MV_RIGHT
13031   };
13032   int center_element = Feld[x][y];      // should always be non-moving!
13033   int i;
13034
13035   for (i = 0; i < NUM_DIRECTIONS; i++)
13036   {
13037     int xx = x + xy[i][0];
13038     int yy = y + xy[i][1];
13039     int center_side = trigger_sides[i][0];
13040     int border_side = trigger_sides[i][1];
13041     int border_element;
13042
13043     if (!IN_LEV_FIELD(xx, yy))
13044       continue;
13045
13046     if (IS_PLAYER(x, y))                // player found at center element
13047     {
13048       struct PlayerInfo *player = PLAYERINFO(x, y);
13049
13050       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13051         border_element = Feld[xx][yy];          // may be moving!
13052       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13053         border_element = Feld[xx][yy];
13054       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13055         border_element = MovingOrBlocked2Element(xx, yy);
13056       else
13057         continue;               // center and border element do not touch
13058
13059       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13060                                  player->index_bit, border_side);
13061       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13062                                           CE_PLAYER_TOUCHES_X,
13063                                           player->index_bit, border_side);
13064
13065       {
13066         /* use player element that is initially defined in the level playfield,
13067            not the player element that corresponds to the runtime player number
13068            (example: a level that contains EL_PLAYER_3 as the only player would
13069            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13070         int player_element = PLAYERINFO(x, y)->initial_element;
13071
13072         CheckElementChangeBySide(xx, yy, border_element, player_element,
13073                                  CE_TOUCHING_X, border_side);
13074       }
13075     }
13076     else if (IS_PLAYER(xx, yy))         // player found at border element
13077     {
13078       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13079
13080       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13081       {
13082         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13083           continue;             // center and border element do not touch
13084       }
13085
13086       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13087                                  player->index_bit, center_side);
13088       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13089                                           CE_PLAYER_TOUCHES_X,
13090                                           player->index_bit, center_side);
13091
13092       {
13093         /* use player element that is initially defined in the level playfield,
13094            not the player element that corresponds to the runtime player number
13095            (example: a level that contains EL_PLAYER_3 as the only player would
13096            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13097         int player_element = PLAYERINFO(xx, yy)->initial_element;
13098
13099         CheckElementChangeBySide(x, y, center_element, player_element,
13100                                  CE_TOUCHING_X, center_side);
13101       }
13102
13103       break;
13104     }
13105   }
13106 }
13107
13108 void TestIfElementTouchesCustomElement(int x, int y)
13109 {
13110   static int xy[4][2] =
13111   {
13112     { 0, -1 },
13113     { -1, 0 },
13114     { +1, 0 },
13115     { 0, +1 }
13116   };
13117   static int trigger_sides[4][2] =
13118   {
13119     // center side      border side
13120     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13121     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13122     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13123     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13124   };
13125   static int touch_dir[4] =
13126   {
13127     MV_LEFT | MV_RIGHT,
13128     MV_UP   | MV_DOWN,
13129     MV_UP   | MV_DOWN,
13130     MV_LEFT | MV_RIGHT
13131   };
13132   boolean change_center_element = FALSE;
13133   int center_element = Feld[x][y];      // should always be non-moving!
13134   int border_element_old[NUM_DIRECTIONS];
13135   int i;
13136
13137   for (i = 0; i < NUM_DIRECTIONS; i++)
13138   {
13139     int xx = x + xy[i][0];
13140     int yy = y + xy[i][1];
13141     int border_element;
13142
13143     border_element_old[i] = -1;
13144
13145     if (!IN_LEV_FIELD(xx, yy))
13146       continue;
13147
13148     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13149       border_element = Feld[xx][yy];    // may be moving!
13150     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13151       border_element = Feld[xx][yy];
13152     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13153       border_element = MovingOrBlocked2Element(xx, yy);
13154     else
13155       continue;                 // center and border element do not touch
13156
13157     border_element_old[i] = border_element;
13158   }
13159
13160   for (i = 0; i < NUM_DIRECTIONS; i++)
13161   {
13162     int xx = x + xy[i][0];
13163     int yy = y + xy[i][1];
13164     int center_side = trigger_sides[i][0];
13165     int border_element = border_element_old[i];
13166
13167     if (border_element == -1)
13168       continue;
13169
13170     // check for change of border element
13171     CheckElementChangeBySide(xx, yy, border_element, center_element,
13172                              CE_TOUCHING_X, center_side);
13173
13174     // (center element cannot be player, so we dont have to check this here)
13175   }
13176
13177   for (i = 0; i < NUM_DIRECTIONS; i++)
13178   {
13179     int xx = x + xy[i][0];
13180     int yy = y + xy[i][1];
13181     int border_side = trigger_sides[i][1];
13182     int border_element = border_element_old[i];
13183
13184     if (border_element == -1)
13185       continue;
13186
13187     // check for change of center element (but change it only once)
13188     if (!change_center_element)
13189       change_center_element =
13190         CheckElementChangeBySide(x, y, center_element, border_element,
13191                                  CE_TOUCHING_X, border_side);
13192
13193     if (IS_PLAYER(xx, yy))
13194     {
13195       /* use player element that is initially defined in the level playfield,
13196          not the player element that corresponds to the runtime player number
13197          (example: a level that contains EL_PLAYER_3 as the only player would
13198          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13199       int player_element = PLAYERINFO(xx, yy)->initial_element;
13200
13201       CheckElementChangeBySide(x, y, center_element, player_element,
13202                                CE_TOUCHING_X, border_side);
13203     }
13204   }
13205 }
13206
13207 void TestIfElementHitsCustomElement(int x, int y, int direction)
13208 {
13209   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13210   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13211   int hitx = x + dx, hity = y + dy;
13212   int hitting_element = Feld[x][y];
13213   int touched_element;
13214
13215   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13216     return;
13217
13218   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13219                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13220
13221   if (IN_LEV_FIELD(hitx, hity))
13222   {
13223     int opposite_direction = MV_DIR_OPPOSITE(direction);
13224     int hitting_side = direction;
13225     int touched_side = opposite_direction;
13226     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13227                           MovDir[hitx][hity] != direction ||
13228                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13229
13230     object_hit = TRUE;
13231
13232     if (object_hit)
13233     {
13234       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13235                                CE_HITTING_X, touched_side);
13236
13237       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13238                                CE_HIT_BY_X, hitting_side);
13239
13240       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13241                                CE_HIT_BY_SOMETHING, opposite_direction);
13242
13243       if (IS_PLAYER(hitx, hity))
13244       {
13245         /* use player element that is initially defined in the level playfield,
13246            not the player element that corresponds to the runtime player number
13247            (example: a level that contains EL_PLAYER_3 as the only player would
13248            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13249         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13250
13251         CheckElementChangeBySide(x, y, hitting_element, player_element,
13252                                  CE_HITTING_X, touched_side);
13253       }
13254     }
13255   }
13256
13257   // "hitting something" is also true when hitting the playfield border
13258   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13259                            CE_HITTING_SOMETHING, direction);
13260 }
13261
13262 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13263 {
13264   int i, kill_x = -1, kill_y = -1;
13265
13266   int bad_element = -1;
13267   static int test_xy[4][2] =
13268   {
13269     { 0, -1 },
13270     { -1, 0 },
13271     { +1, 0 },
13272     { 0, +1 }
13273   };
13274   static int test_dir[4] =
13275   {
13276     MV_UP,
13277     MV_LEFT,
13278     MV_RIGHT,
13279     MV_DOWN
13280   };
13281
13282   for (i = 0; i < NUM_DIRECTIONS; i++)
13283   {
13284     int test_x, test_y, test_move_dir, test_element;
13285
13286     test_x = good_x + test_xy[i][0];
13287     test_y = good_y + test_xy[i][1];
13288
13289     if (!IN_LEV_FIELD(test_x, test_y))
13290       continue;
13291
13292     test_move_dir =
13293       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13294
13295     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13296
13297     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13298        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13299     */
13300     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13301         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13302     {
13303       kill_x = test_x;
13304       kill_y = test_y;
13305       bad_element = test_element;
13306
13307       break;
13308     }
13309   }
13310
13311   if (kill_x != -1 || kill_y != -1)
13312   {
13313     if (IS_PLAYER(good_x, good_y))
13314     {
13315       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13316
13317       if (player->shield_deadly_time_left > 0 &&
13318           !IS_INDESTRUCTIBLE(bad_element))
13319         Bang(kill_x, kill_y);
13320       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13321         KillPlayer(player);
13322     }
13323     else
13324       Bang(good_x, good_y);
13325   }
13326 }
13327
13328 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13329 {
13330   int i, kill_x = -1, kill_y = -1;
13331   int bad_element = Feld[bad_x][bad_y];
13332   static int test_xy[4][2] =
13333   {
13334     { 0, -1 },
13335     { -1, 0 },
13336     { +1, 0 },
13337     { 0, +1 }
13338   };
13339   static int touch_dir[4] =
13340   {
13341     MV_LEFT | MV_RIGHT,
13342     MV_UP   | MV_DOWN,
13343     MV_UP   | MV_DOWN,
13344     MV_LEFT | MV_RIGHT
13345   };
13346   static int test_dir[4] =
13347   {
13348     MV_UP,
13349     MV_LEFT,
13350     MV_RIGHT,
13351     MV_DOWN
13352   };
13353
13354   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13355     return;
13356
13357   for (i = 0; i < NUM_DIRECTIONS; i++)
13358   {
13359     int test_x, test_y, test_move_dir, test_element;
13360
13361     test_x = bad_x + test_xy[i][0];
13362     test_y = bad_y + test_xy[i][1];
13363
13364     if (!IN_LEV_FIELD(test_x, test_y))
13365       continue;
13366
13367     test_move_dir =
13368       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13369
13370     test_element = Feld[test_x][test_y];
13371
13372     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13373        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13374     */
13375     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13376         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13377     {
13378       // good thing is player or penguin that does not move away
13379       if (IS_PLAYER(test_x, test_y))
13380       {
13381         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13382
13383         if (bad_element == EL_ROBOT && player->is_moving)
13384           continue;     // robot does not kill player if he is moving
13385
13386         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13387         {
13388           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13389             continue;           // center and border element do not touch
13390         }
13391
13392         kill_x = test_x;
13393         kill_y = test_y;
13394
13395         break;
13396       }
13397       else if (test_element == EL_PENGUIN)
13398       {
13399         kill_x = test_x;
13400         kill_y = test_y;
13401
13402         break;
13403       }
13404     }
13405   }
13406
13407   if (kill_x != -1 || kill_y != -1)
13408   {
13409     if (IS_PLAYER(kill_x, kill_y))
13410     {
13411       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13412
13413       if (player->shield_deadly_time_left > 0 &&
13414           !IS_INDESTRUCTIBLE(bad_element))
13415         Bang(bad_x, bad_y);
13416       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13417         KillPlayer(player);
13418     }
13419     else
13420       Bang(kill_x, kill_y);
13421   }
13422 }
13423
13424 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13425 {
13426   int bad_element = Feld[bad_x][bad_y];
13427   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13428   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13429   int test_x = bad_x + dx, test_y = bad_y + dy;
13430   int test_move_dir, test_element;
13431   int kill_x = -1, kill_y = -1;
13432
13433   if (!IN_LEV_FIELD(test_x, test_y))
13434     return;
13435
13436   test_move_dir =
13437     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13438
13439   test_element = Feld[test_x][test_y];
13440
13441   if (test_move_dir != bad_move_dir)
13442   {
13443     // good thing can be player or penguin that does not move away
13444     if (IS_PLAYER(test_x, test_y))
13445     {
13446       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13447
13448       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13449          player as being hit when he is moving towards the bad thing, because
13450          the "get hit by" condition would be lost after the player stops) */
13451       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13452         return;         // player moves away from bad thing
13453
13454       kill_x = test_x;
13455       kill_y = test_y;
13456     }
13457     else if (test_element == EL_PENGUIN)
13458     {
13459       kill_x = test_x;
13460       kill_y = test_y;
13461     }
13462   }
13463
13464   if (kill_x != -1 || kill_y != -1)
13465   {
13466     if (IS_PLAYER(kill_x, kill_y))
13467     {
13468       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13469
13470       if (player->shield_deadly_time_left > 0 &&
13471           !IS_INDESTRUCTIBLE(bad_element))
13472         Bang(bad_x, bad_y);
13473       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13474         KillPlayer(player);
13475     }
13476     else
13477       Bang(kill_x, kill_y);
13478   }
13479 }
13480
13481 void TestIfPlayerTouchesBadThing(int x, int y)
13482 {
13483   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13484 }
13485
13486 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13487 {
13488   TestIfGoodThingHitsBadThing(x, y, move_dir);
13489 }
13490
13491 void TestIfBadThingTouchesPlayer(int x, int y)
13492 {
13493   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13494 }
13495
13496 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13497 {
13498   TestIfBadThingHitsGoodThing(x, y, move_dir);
13499 }
13500
13501 void TestIfFriendTouchesBadThing(int x, int y)
13502 {
13503   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13504 }
13505
13506 void TestIfBadThingTouchesFriend(int x, int y)
13507 {
13508   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13509 }
13510
13511 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13512 {
13513   int i, kill_x = bad_x, kill_y = bad_y;
13514   static int xy[4][2] =
13515   {
13516     { 0, -1 },
13517     { -1, 0 },
13518     { +1, 0 },
13519     { 0, +1 }
13520   };
13521
13522   for (i = 0; i < NUM_DIRECTIONS; i++)
13523   {
13524     int x, y, element;
13525
13526     x = bad_x + xy[i][0];
13527     y = bad_y + xy[i][1];
13528     if (!IN_LEV_FIELD(x, y))
13529       continue;
13530
13531     element = Feld[x][y];
13532     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13533         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13534     {
13535       kill_x = x;
13536       kill_y = y;
13537       break;
13538     }
13539   }
13540
13541   if (kill_x != bad_x || kill_y != bad_y)
13542     Bang(bad_x, bad_y);
13543 }
13544
13545 void KillPlayer(struct PlayerInfo *player)
13546 {
13547   int jx = player->jx, jy = player->jy;
13548
13549   if (!player->active)
13550     return;
13551
13552 #if 0
13553   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13554          player->killed, player->active, player->reanimated);
13555 #endif
13556
13557   /* the following code was introduced to prevent an infinite loop when calling
13558      -> Bang()
13559      -> CheckTriggeredElementChangeExt()
13560      -> ExecuteCustomElementAction()
13561      -> KillPlayer()
13562      -> (infinitely repeating the above sequence of function calls)
13563      which occurs when killing the player while having a CE with the setting
13564      "kill player X when explosion of <player X>"; the solution using a new
13565      field "player->killed" was chosen for backwards compatibility, although
13566      clever use of the fields "player->active" etc. would probably also work */
13567 #if 1
13568   if (player->killed)
13569     return;
13570 #endif
13571
13572   player->killed = TRUE;
13573
13574   // remove accessible field at the player's position
13575   Feld[jx][jy] = EL_EMPTY;
13576
13577   // deactivate shield (else Bang()/Explode() would not work right)
13578   player->shield_normal_time_left = 0;
13579   player->shield_deadly_time_left = 0;
13580
13581 #if 0
13582   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13583          player->killed, player->active, player->reanimated);
13584 #endif
13585
13586   Bang(jx, jy);
13587
13588 #if 0
13589   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13590          player->killed, player->active, player->reanimated);
13591 #endif
13592
13593   if (player->reanimated)       // killed player may have been reanimated
13594     player->killed = player->reanimated = FALSE;
13595   else
13596     BuryPlayer(player);
13597 }
13598
13599 static void KillPlayerUnlessEnemyProtected(int x, int y)
13600 {
13601   if (!PLAYER_ENEMY_PROTECTED(x, y))
13602     KillPlayer(PLAYERINFO(x, y));
13603 }
13604
13605 static void KillPlayerUnlessExplosionProtected(int x, int y)
13606 {
13607   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13608     KillPlayer(PLAYERINFO(x, y));
13609 }
13610
13611 void BuryPlayer(struct PlayerInfo *player)
13612 {
13613   int jx = player->jx, jy = player->jy;
13614
13615   if (!player->active)
13616     return;
13617
13618   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13619   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13620
13621   RemovePlayer(player);
13622
13623   player->buried = TRUE;
13624
13625   if (game.all_players_gone)
13626     game.GameOver = TRUE;
13627 }
13628
13629 void RemovePlayer(struct PlayerInfo *player)
13630 {
13631   int jx = player->jx, jy = player->jy;
13632   int i, found = FALSE;
13633
13634   player->present = FALSE;
13635   player->active = FALSE;
13636
13637   // required for some CE actions (even if the player is not active anymore)
13638   player->MovPos = 0;
13639
13640   if (!ExplodeField[jx][jy])
13641     StorePlayer[jx][jy] = 0;
13642
13643   if (player->is_moving)
13644     TEST_DrawLevelField(player->last_jx, player->last_jy);
13645
13646   for (i = 0; i < MAX_PLAYERS; i++)
13647     if (stored_player[i].active)
13648       found = TRUE;
13649
13650   if (!found)
13651   {
13652     game.all_players_gone = TRUE;
13653     game.GameOver = TRUE;
13654   }
13655
13656   game.exit_x = game.robot_wheel_x = jx;
13657   game.exit_y = game.robot_wheel_y = jy;
13658 }
13659
13660 void ExitPlayer(struct PlayerInfo *player)
13661 {
13662   DrawPlayer(player);   // needed here only to cleanup last field
13663   RemovePlayer(player);
13664
13665   if (game.players_still_needed > 0)
13666     game.players_still_needed--;
13667 }
13668
13669 static void setFieldForSnapping(int x, int y, int element, int direction)
13670 {
13671   struct ElementInfo *ei = &element_info[element];
13672   int direction_bit = MV_DIR_TO_BIT(direction);
13673   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13674   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13675                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13676
13677   Feld[x][y] = EL_ELEMENT_SNAPPING;
13678   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13679
13680   ResetGfxAnimation(x, y);
13681
13682   GfxElement[x][y] = element;
13683   GfxAction[x][y] = action;
13684   GfxDir[x][y] = direction;
13685   GfxFrame[x][y] = -1;
13686 }
13687
13688 /*
13689   =============================================================================
13690   checkDiagonalPushing()
13691   -----------------------------------------------------------------------------
13692   check if diagonal input device direction results in pushing of object
13693   (by checking if the alternative direction is walkable, diggable, ...)
13694   =============================================================================
13695 */
13696
13697 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13698                                     int x, int y, int real_dx, int real_dy)
13699 {
13700   int jx, jy, dx, dy, xx, yy;
13701
13702   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13703     return TRUE;
13704
13705   // diagonal direction: check alternative direction
13706   jx = player->jx;
13707   jy = player->jy;
13708   dx = x - jx;
13709   dy = y - jy;
13710   xx = jx + (dx == 0 ? real_dx : 0);
13711   yy = jy + (dy == 0 ? real_dy : 0);
13712
13713   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13714 }
13715
13716 /*
13717   =============================================================================
13718   DigField()
13719   -----------------------------------------------------------------------------
13720   x, y:                 field next to player (non-diagonal) to try to dig to
13721   real_dx, real_dy:     direction as read from input device (can be diagonal)
13722   =============================================================================
13723 */
13724
13725 static int DigField(struct PlayerInfo *player,
13726                     int oldx, int oldy, int x, int y,
13727                     int real_dx, int real_dy, int mode)
13728 {
13729   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13730   boolean player_was_pushing = player->is_pushing;
13731   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13732   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13733   int jx = oldx, jy = oldy;
13734   int dx = x - jx, dy = y - jy;
13735   int nextx = x + dx, nexty = y + dy;
13736   int move_direction = (dx == -1 ? MV_LEFT  :
13737                         dx == +1 ? MV_RIGHT :
13738                         dy == -1 ? MV_UP    :
13739                         dy == +1 ? MV_DOWN  : MV_NONE);
13740   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13741   int dig_side = MV_DIR_OPPOSITE(move_direction);
13742   int old_element = Feld[jx][jy];
13743   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13744   int collect_count;
13745
13746   if (is_player)                // function can also be called by EL_PENGUIN
13747   {
13748     if (player->MovPos == 0)
13749     {
13750       player->is_digging = FALSE;
13751       player->is_collecting = FALSE;
13752     }
13753
13754     if (player->MovPos == 0)    // last pushing move finished
13755       player->is_pushing = FALSE;
13756
13757     if (mode == DF_NO_PUSH)     // player just stopped pushing
13758     {
13759       player->is_switching = FALSE;
13760       player->push_delay = -1;
13761
13762       return MP_NO_ACTION;
13763     }
13764   }
13765
13766   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13767     old_element = Back[jx][jy];
13768
13769   // in case of element dropped at player position, check background
13770   else if (Back[jx][jy] != EL_EMPTY &&
13771            game.engine_version >= VERSION_IDENT(2,2,0,0))
13772     old_element = Back[jx][jy];
13773
13774   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13775     return MP_NO_ACTION;        // field has no opening in this direction
13776
13777   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13778     return MP_NO_ACTION;        // field has no opening in this direction
13779
13780   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13781   {
13782     SplashAcid(x, y);
13783
13784     Feld[jx][jy] = player->artwork_element;
13785     InitMovingField(jx, jy, MV_DOWN);
13786     Store[jx][jy] = EL_ACID;
13787     ContinueMoving(jx, jy);
13788     BuryPlayer(player);
13789
13790     return MP_DONT_RUN_INTO;
13791   }
13792
13793   if (player_can_move && DONT_RUN_INTO(element))
13794   {
13795     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13796
13797     return MP_DONT_RUN_INTO;
13798   }
13799
13800   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13801     return MP_NO_ACTION;
13802
13803   collect_count = element_info[element].collect_count_initial;
13804
13805   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13806     return MP_NO_ACTION;
13807
13808   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13809     player_can_move = player_can_move_or_snap;
13810
13811   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13812       game.engine_version >= VERSION_IDENT(2,2,0,0))
13813   {
13814     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13815                                player->index_bit, dig_side);
13816     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13817                                         player->index_bit, dig_side);
13818
13819     if (element == EL_DC_LANDMINE)
13820       Bang(x, y);
13821
13822     if (Feld[x][y] != element)          // field changed by snapping
13823       return MP_ACTION;
13824
13825     return MP_NO_ACTION;
13826   }
13827
13828   if (player->gravity && is_player && !player->is_auto_moving &&
13829       canFallDown(player) && move_direction != MV_DOWN &&
13830       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13831     return MP_NO_ACTION;        // player cannot walk here due to gravity
13832
13833   if (player_can_move &&
13834       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13835   {
13836     int sound_element = SND_ELEMENT(element);
13837     int sound_action = ACTION_WALKING;
13838
13839     if (IS_RND_GATE(element))
13840     {
13841       if (!player->key[RND_GATE_NR(element)])
13842         return MP_NO_ACTION;
13843     }
13844     else if (IS_RND_GATE_GRAY(element))
13845     {
13846       if (!player->key[RND_GATE_GRAY_NR(element)])
13847         return MP_NO_ACTION;
13848     }
13849     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13850     {
13851       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13852         return MP_NO_ACTION;
13853     }
13854     else if (element == EL_EXIT_OPEN ||
13855              element == EL_EM_EXIT_OPEN ||
13856              element == EL_EM_EXIT_OPENING ||
13857              element == EL_STEEL_EXIT_OPEN ||
13858              element == EL_EM_STEEL_EXIT_OPEN ||
13859              element == EL_EM_STEEL_EXIT_OPENING ||
13860              element == EL_SP_EXIT_OPEN ||
13861              element == EL_SP_EXIT_OPENING)
13862     {
13863       sound_action = ACTION_PASSING;    // player is passing exit
13864     }
13865     else if (element == EL_EMPTY)
13866     {
13867       sound_action = ACTION_MOVING;             // nothing to walk on
13868     }
13869
13870     // play sound from background or player, whatever is available
13871     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13872       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13873     else
13874       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13875   }
13876   else if (player_can_move &&
13877            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13878   {
13879     if (!ACCESS_FROM(element, opposite_direction))
13880       return MP_NO_ACTION;      // field not accessible from this direction
13881
13882     if (CAN_MOVE(element))      // only fixed elements can be passed!
13883       return MP_NO_ACTION;
13884
13885     if (IS_EM_GATE(element))
13886     {
13887       if (!player->key[EM_GATE_NR(element)])
13888         return MP_NO_ACTION;
13889     }
13890     else if (IS_EM_GATE_GRAY(element))
13891     {
13892       if (!player->key[EM_GATE_GRAY_NR(element)])
13893         return MP_NO_ACTION;
13894     }
13895     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13896     {
13897       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13898         return MP_NO_ACTION;
13899     }
13900     else if (IS_EMC_GATE(element))
13901     {
13902       if (!player->key[EMC_GATE_NR(element)])
13903         return MP_NO_ACTION;
13904     }
13905     else if (IS_EMC_GATE_GRAY(element))
13906     {
13907       if (!player->key[EMC_GATE_GRAY_NR(element)])
13908         return MP_NO_ACTION;
13909     }
13910     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13911     {
13912       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13913         return MP_NO_ACTION;
13914     }
13915     else if (element == EL_DC_GATE_WHITE ||
13916              element == EL_DC_GATE_WHITE_GRAY ||
13917              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13918     {
13919       if (player->num_white_keys == 0)
13920         return MP_NO_ACTION;
13921
13922       player->num_white_keys--;
13923     }
13924     else if (IS_SP_PORT(element))
13925     {
13926       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13927           element == EL_SP_GRAVITY_PORT_RIGHT ||
13928           element == EL_SP_GRAVITY_PORT_UP ||
13929           element == EL_SP_GRAVITY_PORT_DOWN)
13930         player->gravity = !player->gravity;
13931       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13932                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13933                element == EL_SP_GRAVITY_ON_PORT_UP ||
13934                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13935         player->gravity = TRUE;
13936       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13937                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13938                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13939                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13940         player->gravity = FALSE;
13941     }
13942
13943     // automatically move to the next field with double speed
13944     player->programmed_action = move_direction;
13945
13946     if (player->move_delay_reset_counter == 0)
13947     {
13948       player->move_delay_reset_counter = 2;     // two double speed steps
13949
13950       DOUBLE_PLAYER_SPEED(player);
13951     }
13952
13953     PlayLevelSoundAction(x, y, ACTION_PASSING);
13954   }
13955   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13956   {
13957     RemoveField(x, y);
13958
13959     if (mode != DF_SNAP)
13960     {
13961       GfxElement[x][y] = GFX_ELEMENT(element);
13962       player->is_digging = TRUE;
13963     }
13964
13965     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13966
13967     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13968                                         player->index_bit, dig_side);
13969
13970     if (mode == DF_SNAP)
13971     {
13972       if (level.block_snap_field)
13973         setFieldForSnapping(x, y, element, move_direction);
13974       else
13975         TestIfElementTouchesCustomElement(x, y);        // for empty space
13976
13977       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13978                                           player->index_bit, dig_side);
13979     }
13980   }
13981   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13982   {
13983     RemoveField(x, y);
13984
13985     if (is_player && mode != DF_SNAP)
13986     {
13987       GfxElement[x][y] = element;
13988       player->is_collecting = TRUE;
13989     }
13990
13991     if (element == EL_SPEED_PILL)
13992     {
13993       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13994     }
13995     else if (element == EL_EXTRA_TIME && level.time > 0)
13996     {
13997       TimeLeft += level.extra_time;
13998
13999       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14000
14001       DisplayGameControlValues();
14002     }
14003     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14004     {
14005       player->shield_normal_time_left += level.shield_normal_time;
14006       if (element == EL_SHIELD_DEADLY)
14007         player->shield_deadly_time_left += level.shield_deadly_time;
14008     }
14009     else if (element == EL_DYNAMITE ||
14010              element == EL_EM_DYNAMITE ||
14011              element == EL_SP_DISK_RED)
14012     {
14013       if (player->inventory_size < MAX_INVENTORY_SIZE)
14014         player->inventory_element[player->inventory_size++] = element;
14015
14016       DrawGameDoorValues();
14017     }
14018     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14019     {
14020       player->dynabomb_count++;
14021       player->dynabombs_left++;
14022     }
14023     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14024     {
14025       player->dynabomb_size++;
14026     }
14027     else if (element == EL_DYNABOMB_INCREASE_POWER)
14028     {
14029       player->dynabomb_xl = TRUE;
14030     }
14031     else if (IS_KEY(element))
14032     {
14033       player->key[KEY_NR(element)] = TRUE;
14034
14035       DrawGameDoorValues();
14036     }
14037     else if (element == EL_DC_KEY_WHITE)
14038     {
14039       player->num_white_keys++;
14040
14041       // display white keys?
14042       // DrawGameDoorValues();
14043     }
14044     else if (IS_ENVELOPE(element))
14045     {
14046       player->show_envelope = element;
14047     }
14048     else if (element == EL_EMC_LENSES)
14049     {
14050       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14051
14052       RedrawAllInvisibleElementsForLenses();
14053     }
14054     else if (element == EL_EMC_MAGNIFIER)
14055     {
14056       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14057
14058       RedrawAllInvisibleElementsForMagnifier();
14059     }
14060     else if (IS_DROPPABLE(element) ||
14061              IS_THROWABLE(element))     // can be collected and dropped
14062     {
14063       int i;
14064
14065       if (collect_count == 0)
14066         player->inventory_infinite_element = element;
14067       else
14068         for (i = 0; i < collect_count; i++)
14069           if (player->inventory_size < MAX_INVENTORY_SIZE)
14070             player->inventory_element[player->inventory_size++] = element;
14071
14072       DrawGameDoorValues();
14073     }
14074     else if (collect_count > 0)
14075     {
14076       game.gems_still_needed -= collect_count;
14077       if (game.gems_still_needed < 0)
14078         game.gems_still_needed = 0;
14079
14080       game.snapshot.collected_item = TRUE;
14081
14082       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14083
14084       DisplayGameControlValues();
14085     }
14086
14087     RaiseScoreElement(element);
14088     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14089
14090     if (is_player)
14091       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14092                                           player->index_bit, dig_side);
14093
14094     if (mode == DF_SNAP)
14095     {
14096       if (level.block_snap_field)
14097         setFieldForSnapping(x, y, element, move_direction);
14098       else
14099         TestIfElementTouchesCustomElement(x, y);        // for empty space
14100
14101       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14102                                           player->index_bit, dig_side);
14103     }
14104   }
14105   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14106   {
14107     if (mode == DF_SNAP && element != EL_BD_ROCK)
14108       return MP_NO_ACTION;
14109
14110     if (CAN_FALL(element) && dy)
14111       return MP_NO_ACTION;
14112
14113     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14114         !(element == EL_SPRING && level.use_spring_bug))
14115       return MP_NO_ACTION;
14116
14117     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14118         ((move_direction & MV_VERTICAL &&
14119           ((element_info[element].move_pattern & MV_LEFT &&
14120             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14121            (element_info[element].move_pattern & MV_RIGHT &&
14122             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14123          (move_direction & MV_HORIZONTAL &&
14124           ((element_info[element].move_pattern & MV_UP &&
14125             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14126            (element_info[element].move_pattern & MV_DOWN &&
14127             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14128       return MP_NO_ACTION;
14129
14130     // do not push elements already moving away faster than player
14131     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14132         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14133       return MP_NO_ACTION;
14134
14135     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14136     {
14137       if (player->push_delay_value == -1 || !player_was_pushing)
14138         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14139     }
14140     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14141     {
14142       if (player->push_delay_value == -1)
14143         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14144     }
14145     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14146     {
14147       if (!player->is_pushing)
14148         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14149     }
14150
14151     player->is_pushing = TRUE;
14152     player->is_active = TRUE;
14153
14154     if (!(IN_LEV_FIELD(nextx, nexty) &&
14155           (IS_FREE(nextx, nexty) ||
14156            (IS_SB_ELEMENT(element) &&
14157             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14158            (IS_CUSTOM_ELEMENT(element) &&
14159             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14160       return MP_NO_ACTION;
14161
14162     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14163       return MP_NO_ACTION;
14164
14165     if (player->push_delay == -1)       // new pushing; restart delay
14166       player->push_delay = 0;
14167
14168     if (player->push_delay < player->push_delay_value &&
14169         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14170         element != EL_SPRING && element != EL_BALLOON)
14171     {
14172       // make sure that there is no move delay before next try to push
14173       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14174         player->move_delay = 0;
14175
14176       return MP_NO_ACTION;
14177     }
14178
14179     if (IS_CUSTOM_ELEMENT(element) &&
14180         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14181     {
14182       if (!DigFieldByCE(nextx, nexty, element))
14183         return MP_NO_ACTION;
14184     }
14185
14186     if (IS_SB_ELEMENT(element))
14187     {
14188       boolean sokoban_task_solved = FALSE;
14189
14190       if (element == EL_SOKOBAN_FIELD_FULL)
14191       {
14192         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14193
14194         IncrementSokobanFieldsNeeded();
14195         IncrementSokobanObjectsNeeded();
14196       }
14197
14198       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14199       {
14200         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14201
14202         DecrementSokobanFieldsNeeded();
14203         DecrementSokobanObjectsNeeded();
14204
14205         // sokoban object was pushed from empty field to sokoban field
14206         if (Back[x][y] == EL_EMPTY)
14207           sokoban_task_solved = TRUE;
14208       }
14209
14210       Feld[x][y] = EL_SOKOBAN_OBJECT;
14211
14212       if (Back[x][y] == Back[nextx][nexty])
14213         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14214       else if (Back[x][y] != 0)
14215         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14216                                     ACTION_EMPTYING);
14217       else
14218         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14219                                     ACTION_FILLING);
14220
14221       if (sokoban_task_solved &&
14222           game.sokoban_fields_still_needed == 0 &&
14223           game.sokoban_objects_still_needed == 0 &&
14224           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14225       {
14226         game.players_still_needed = 0;
14227
14228         LevelSolved();
14229
14230         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14231       }
14232     }
14233     else
14234       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14235
14236     InitMovingField(x, y, move_direction);
14237     GfxAction[x][y] = ACTION_PUSHING;
14238
14239     if (mode == DF_SNAP)
14240       ContinueMoving(x, y);
14241     else
14242       MovPos[x][y] = (dx != 0 ? dx : dy);
14243
14244     Pushed[x][y] = TRUE;
14245     Pushed[nextx][nexty] = TRUE;
14246
14247     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14248       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14249     else
14250       player->push_delay_value = -1;    // get new value later
14251
14252     // check for element change _after_ element has been pushed
14253     if (game.use_change_when_pushing_bug)
14254     {
14255       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14256                                  player->index_bit, dig_side);
14257       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14258                                           player->index_bit, dig_side);
14259     }
14260   }
14261   else if (IS_SWITCHABLE(element))
14262   {
14263     if (PLAYER_SWITCHING(player, x, y))
14264     {
14265       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14266                                           player->index_bit, dig_side);
14267
14268       return MP_ACTION;
14269     }
14270
14271     player->is_switching = TRUE;
14272     player->switch_x = x;
14273     player->switch_y = y;
14274
14275     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14276
14277     if (element == EL_ROBOT_WHEEL)
14278     {
14279       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14280
14281       game.robot_wheel_x = x;
14282       game.robot_wheel_y = y;
14283       game.robot_wheel_active = TRUE;
14284
14285       TEST_DrawLevelField(x, y);
14286     }
14287     else if (element == EL_SP_TERMINAL)
14288     {
14289       int xx, yy;
14290
14291       SCAN_PLAYFIELD(xx, yy)
14292       {
14293         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14294         {
14295           Bang(xx, yy);
14296         }
14297         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14298         {
14299           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14300
14301           ResetGfxAnimation(xx, yy);
14302           TEST_DrawLevelField(xx, yy);
14303         }
14304       }
14305     }
14306     else if (IS_BELT_SWITCH(element))
14307     {
14308       ToggleBeltSwitch(x, y);
14309     }
14310     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14311              element == EL_SWITCHGATE_SWITCH_DOWN ||
14312              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14313              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14314     {
14315       ToggleSwitchgateSwitch(x, y);
14316     }
14317     else if (element == EL_LIGHT_SWITCH ||
14318              element == EL_LIGHT_SWITCH_ACTIVE)
14319     {
14320       ToggleLightSwitch(x, y);
14321     }
14322     else if (element == EL_TIMEGATE_SWITCH ||
14323              element == EL_DC_TIMEGATE_SWITCH)
14324     {
14325       ActivateTimegateSwitch(x, y);
14326     }
14327     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14328              element == EL_BALLOON_SWITCH_RIGHT ||
14329              element == EL_BALLOON_SWITCH_UP    ||
14330              element == EL_BALLOON_SWITCH_DOWN  ||
14331              element == EL_BALLOON_SWITCH_NONE  ||
14332              element == EL_BALLOON_SWITCH_ANY)
14333     {
14334       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14335                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14336                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14337                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14338                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14339                              move_direction);
14340     }
14341     else if (element == EL_LAMP)
14342     {
14343       Feld[x][y] = EL_LAMP_ACTIVE;
14344       game.lights_still_needed--;
14345
14346       ResetGfxAnimation(x, y);
14347       TEST_DrawLevelField(x, y);
14348     }
14349     else if (element == EL_TIME_ORB_FULL)
14350     {
14351       Feld[x][y] = EL_TIME_ORB_EMPTY;
14352
14353       if (level.time > 0 || level.use_time_orb_bug)
14354       {
14355         TimeLeft += level.time_orb_time;
14356         game.no_time_limit = FALSE;
14357
14358         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14359
14360         DisplayGameControlValues();
14361       }
14362
14363       ResetGfxAnimation(x, y);
14364       TEST_DrawLevelField(x, y);
14365     }
14366     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14367              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14368     {
14369       int xx, yy;
14370
14371       game.ball_active = !game.ball_active;
14372
14373       SCAN_PLAYFIELD(xx, yy)
14374       {
14375         int e = Feld[xx][yy];
14376
14377         if (game.ball_active)
14378         {
14379           if (e == EL_EMC_MAGIC_BALL)
14380             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14381           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14382             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14383         }
14384         else
14385         {
14386           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14387             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14388           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14389             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14390         }
14391       }
14392     }
14393
14394     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14395                                         player->index_bit, dig_side);
14396
14397     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14398                                         player->index_bit, dig_side);
14399
14400     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14401                                         player->index_bit, dig_side);
14402
14403     return MP_ACTION;
14404   }
14405   else
14406   {
14407     if (!PLAYER_SWITCHING(player, x, y))
14408     {
14409       player->is_switching = TRUE;
14410       player->switch_x = x;
14411       player->switch_y = y;
14412
14413       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14414                                  player->index_bit, dig_side);
14415       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14416                                           player->index_bit, dig_side);
14417
14418       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14419                                  player->index_bit, dig_side);
14420       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14421                                           player->index_bit, dig_side);
14422     }
14423
14424     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14425                                player->index_bit, dig_side);
14426     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14427                                         player->index_bit, dig_side);
14428
14429     return MP_NO_ACTION;
14430   }
14431
14432   player->push_delay = -1;
14433
14434   if (is_player)                // function can also be called by EL_PENGUIN
14435   {
14436     if (Feld[x][y] != element)          // really digged/collected something
14437     {
14438       player->is_collecting = !player->is_digging;
14439       player->is_active = TRUE;
14440     }
14441   }
14442
14443   return MP_MOVING;
14444 }
14445
14446 static boolean DigFieldByCE(int x, int y, int digging_element)
14447 {
14448   int element = Feld[x][y];
14449
14450   if (!IS_FREE(x, y))
14451   {
14452     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14453                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14454                   ACTION_BREAKING);
14455
14456     // no element can dig solid indestructible elements
14457     if (IS_INDESTRUCTIBLE(element) &&
14458         !IS_DIGGABLE(element) &&
14459         !IS_COLLECTIBLE(element))
14460       return FALSE;
14461
14462     if (AmoebaNr[x][y] &&
14463         (element == EL_AMOEBA_FULL ||
14464          element == EL_BD_AMOEBA ||
14465          element == EL_AMOEBA_GROWING))
14466     {
14467       AmoebaCnt[AmoebaNr[x][y]]--;
14468       AmoebaCnt2[AmoebaNr[x][y]]--;
14469     }
14470
14471     if (IS_MOVING(x, y))
14472       RemoveMovingField(x, y);
14473     else
14474     {
14475       RemoveField(x, y);
14476       TEST_DrawLevelField(x, y);
14477     }
14478
14479     // if digged element was about to explode, prevent the explosion
14480     ExplodeField[x][y] = EX_TYPE_NONE;
14481
14482     PlayLevelSoundAction(x, y, action);
14483   }
14484
14485   Store[x][y] = EL_EMPTY;
14486
14487   // this makes it possible to leave the removed element again
14488   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14489     Store[x][y] = element;
14490
14491   return TRUE;
14492 }
14493
14494 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14495 {
14496   int jx = player->jx, jy = player->jy;
14497   int x = jx + dx, y = jy + dy;
14498   int snap_direction = (dx == -1 ? MV_LEFT  :
14499                         dx == +1 ? MV_RIGHT :
14500                         dy == -1 ? MV_UP    :
14501                         dy == +1 ? MV_DOWN  : MV_NONE);
14502   boolean can_continue_snapping = (level.continuous_snapping &&
14503                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14504
14505   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14506     return FALSE;
14507
14508   if (!player->active || !IN_LEV_FIELD(x, y))
14509     return FALSE;
14510
14511   if (dx && dy)
14512     return FALSE;
14513
14514   if (!dx && !dy)
14515   {
14516     if (player->MovPos == 0)
14517       player->is_pushing = FALSE;
14518
14519     player->is_snapping = FALSE;
14520
14521     if (player->MovPos == 0)
14522     {
14523       player->is_moving = FALSE;
14524       player->is_digging = FALSE;
14525       player->is_collecting = FALSE;
14526     }
14527
14528     return FALSE;
14529   }
14530
14531   // prevent snapping with already pressed snap key when not allowed
14532   if (player->is_snapping && !can_continue_snapping)
14533     return FALSE;
14534
14535   player->MovDir = snap_direction;
14536
14537   if (player->MovPos == 0)
14538   {
14539     player->is_moving = FALSE;
14540     player->is_digging = FALSE;
14541     player->is_collecting = FALSE;
14542   }
14543
14544   player->is_dropping = FALSE;
14545   player->is_dropping_pressed = FALSE;
14546   player->drop_pressed_delay = 0;
14547
14548   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14549     return FALSE;
14550
14551   player->is_snapping = TRUE;
14552   player->is_active = TRUE;
14553
14554   if (player->MovPos == 0)
14555   {
14556     player->is_moving = FALSE;
14557     player->is_digging = FALSE;
14558     player->is_collecting = FALSE;
14559   }
14560
14561   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14562     TEST_DrawLevelField(player->last_jx, player->last_jy);
14563
14564   TEST_DrawLevelField(x, y);
14565
14566   return TRUE;
14567 }
14568
14569 static boolean DropElement(struct PlayerInfo *player)
14570 {
14571   int old_element, new_element;
14572   int dropx = player->jx, dropy = player->jy;
14573   int drop_direction = player->MovDir;
14574   int drop_side = drop_direction;
14575   int drop_element = get_next_dropped_element(player);
14576
14577   /* do not drop an element on top of another element; when holding drop key
14578      pressed without moving, dropped element must move away before the next
14579      element can be dropped (this is especially important if the next element
14580      is dynamite, which can be placed on background for historical reasons) */
14581   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14582     return MP_ACTION;
14583
14584   if (IS_THROWABLE(drop_element))
14585   {
14586     dropx += GET_DX_FROM_DIR(drop_direction);
14587     dropy += GET_DY_FROM_DIR(drop_direction);
14588
14589     if (!IN_LEV_FIELD(dropx, dropy))
14590       return FALSE;
14591   }
14592
14593   old_element = Feld[dropx][dropy];     // old element at dropping position
14594   new_element = drop_element;           // default: no change when dropping
14595
14596   // check if player is active, not moving and ready to drop
14597   if (!player->active || player->MovPos || player->drop_delay > 0)
14598     return FALSE;
14599
14600   // check if player has anything that can be dropped
14601   if (new_element == EL_UNDEFINED)
14602     return FALSE;
14603
14604   // only set if player has anything that can be dropped
14605   player->is_dropping_pressed = TRUE;
14606
14607   // check if drop key was pressed long enough for EM style dynamite
14608   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14609     return FALSE;
14610
14611   // check if anything can be dropped at the current position
14612   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14613     return FALSE;
14614
14615   // collected custom elements can only be dropped on empty fields
14616   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14617     return FALSE;
14618
14619   if (old_element != EL_EMPTY)
14620     Back[dropx][dropy] = old_element;   // store old element on this field
14621
14622   ResetGfxAnimation(dropx, dropy);
14623   ResetRandomAnimationValue(dropx, dropy);
14624
14625   if (player->inventory_size > 0 ||
14626       player->inventory_infinite_element != EL_UNDEFINED)
14627   {
14628     if (player->inventory_size > 0)
14629     {
14630       player->inventory_size--;
14631
14632       DrawGameDoorValues();
14633
14634       if (new_element == EL_DYNAMITE)
14635         new_element = EL_DYNAMITE_ACTIVE;
14636       else if (new_element == EL_EM_DYNAMITE)
14637         new_element = EL_EM_DYNAMITE_ACTIVE;
14638       else if (new_element == EL_SP_DISK_RED)
14639         new_element = EL_SP_DISK_RED_ACTIVE;
14640     }
14641
14642     Feld[dropx][dropy] = new_element;
14643
14644     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14645       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14646                           el2img(Feld[dropx][dropy]), 0);
14647
14648     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14649
14650     // needed if previous element just changed to "empty" in the last frame
14651     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14652
14653     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14654                                player->index_bit, drop_side);
14655     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14656                                         CE_PLAYER_DROPS_X,
14657                                         player->index_bit, drop_side);
14658
14659     TestIfElementTouchesCustomElement(dropx, dropy);
14660   }
14661   else          // player is dropping a dyna bomb
14662   {
14663     player->dynabombs_left--;
14664
14665     Feld[dropx][dropy] = new_element;
14666
14667     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14668       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14669                           el2img(Feld[dropx][dropy]), 0);
14670
14671     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14672   }
14673
14674   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14675     InitField_WithBug1(dropx, dropy, FALSE);
14676
14677   new_element = Feld[dropx][dropy];     // element might have changed
14678
14679   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14680       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14681   {
14682     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14683       MovDir[dropx][dropy] = drop_direction;
14684
14685     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14686
14687     // do not cause impact style collision by dropping elements that can fall
14688     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14689   }
14690
14691   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14692   player->is_dropping = TRUE;
14693
14694   player->drop_pressed_delay = 0;
14695   player->is_dropping_pressed = FALSE;
14696
14697   player->drop_x = dropx;
14698   player->drop_y = dropy;
14699
14700   return TRUE;
14701 }
14702
14703 // ----------------------------------------------------------------------------
14704 // game sound playing functions
14705 // ----------------------------------------------------------------------------
14706
14707 static int *loop_sound_frame = NULL;
14708 static int *loop_sound_volume = NULL;
14709
14710 void InitPlayLevelSound(void)
14711 {
14712   int num_sounds = getSoundListSize();
14713
14714   checked_free(loop_sound_frame);
14715   checked_free(loop_sound_volume);
14716
14717   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14718   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14719 }
14720
14721 static void PlayLevelSound(int x, int y, int nr)
14722 {
14723   int sx = SCREENX(x), sy = SCREENY(y);
14724   int volume, stereo_position;
14725   int max_distance = 8;
14726   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14727
14728   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14729       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14730     return;
14731
14732   if (!IN_LEV_FIELD(x, y) ||
14733       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14734       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14735     return;
14736
14737   volume = SOUND_MAX_VOLUME;
14738
14739   if (!IN_SCR_FIELD(sx, sy))
14740   {
14741     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14742     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14743
14744     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14745   }
14746
14747   stereo_position = (SOUND_MAX_LEFT +
14748                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14749                      (SCR_FIELDX + 2 * max_distance));
14750
14751   if (IS_LOOP_SOUND(nr))
14752   {
14753     /* This assures that quieter loop sounds do not overwrite louder ones,
14754        while restarting sound volume comparison with each new game frame. */
14755
14756     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14757       return;
14758
14759     loop_sound_volume[nr] = volume;
14760     loop_sound_frame[nr] = FrameCounter;
14761   }
14762
14763   PlaySoundExt(nr, volume, stereo_position, type);
14764 }
14765
14766 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14767 {
14768   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14769                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14770                  y < LEVELY(BY1) ? LEVELY(BY1) :
14771                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14772                  sound_action);
14773 }
14774
14775 static void PlayLevelSoundAction(int x, int y, int action)
14776 {
14777   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14778 }
14779
14780 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14781 {
14782   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14783
14784   if (sound_effect != SND_UNDEFINED)
14785     PlayLevelSound(x, y, sound_effect);
14786 }
14787
14788 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14789                                               int action)
14790 {
14791   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14792
14793   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14794     PlayLevelSound(x, y, sound_effect);
14795 }
14796
14797 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14798 {
14799   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14800
14801   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14802     PlayLevelSound(x, y, sound_effect);
14803 }
14804
14805 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14806 {
14807   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14808
14809   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14810     StopSound(sound_effect);
14811 }
14812
14813 static int getLevelMusicNr(void)
14814 {
14815   if (levelset.music[level_nr] != MUS_UNDEFINED)
14816     return levelset.music[level_nr];            // from config file
14817   else
14818     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14819 }
14820
14821 static void FadeLevelSounds(void)
14822 {
14823   FadeSounds();
14824 }
14825
14826 static void FadeLevelMusic(void)
14827 {
14828   int music_nr = getLevelMusicNr();
14829   char *curr_music = getCurrentlyPlayingMusicFilename();
14830   char *next_music = getMusicInfoEntryFilename(music_nr);
14831
14832   if (!strEqual(curr_music, next_music))
14833     FadeMusic();
14834 }
14835
14836 void FadeLevelSoundsAndMusic(void)
14837 {
14838   FadeLevelSounds();
14839   FadeLevelMusic();
14840 }
14841
14842 static void PlayLevelMusic(void)
14843 {
14844   int music_nr = getLevelMusicNr();
14845   char *curr_music = getCurrentlyPlayingMusicFilename();
14846   char *next_music = getMusicInfoEntryFilename(music_nr);
14847
14848   if (!strEqual(curr_music, next_music))
14849     PlayMusicLoop(music_nr);
14850 }
14851
14852 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14853 {
14854   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14855   int offset = 0;
14856   int x = xx - offset;
14857   int y = yy - offset;
14858
14859   switch (sample)
14860   {
14861     case SOUND_blank:
14862       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14863       break;
14864
14865     case SOUND_roll:
14866       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14867       break;
14868
14869     case SOUND_stone:
14870       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14871       break;
14872
14873     case SOUND_nut:
14874       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14875       break;
14876
14877     case SOUND_crack:
14878       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14879       break;
14880
14881     case SOUND_bug:
14882       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14883       break;
14884
14885     case SOUND_tank:
14886       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14887       break;
14888
14889     case SOUND_android_clone:
14890       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14891       break;
14892
14893     case SOUND_android_move:
14894       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14895       break;
14896
14897     case SOUND_spring:
14898       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14899       break;
14900
14901     case SOUND_slurp:
14902       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14903       break;
14904
14905     case SOUND_eater:
14906       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14907       break;
14908
14909     case SOUND_eater_eat:
14910       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14911       break;
14912
14913     case SOUND_alien:
14914       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14915       break;
14916
14917     case SOUND_collect:
14918       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14919       break;
14920
14921     case SOUND_diamond:
14922       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14923       break;
14924
14925     case SOUND_squash:
14926       // !!! CHECK THIS !!!
14927 #if 1
14928       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14929 #else
14930       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14931 #endif
14932       break;
14933
14934     case SOUND_wonderfall:
14935       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14936       break;
14937
14938     case SOUND_drip:
14939       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14940       break;
14941
14942     case SOUND_push:
14943       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14944       break;
14945
14946     case SOUND_dirt:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14948       break;
14949
14950     case SOUND_acid:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14952       break;
14953
14954     case SOUND_ball:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14956       break;
14957
14958     case SOUND_slide:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14960       break;
14961
14962     case SOUND_wonder:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14964       break;
14965
14966     case SOUND_door:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14968       break;
14969
14970     case SOUND_exit_open:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14972       break;
14973
14974     case SOUND_exit_leave:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14976       break;
14977
14978     case SOUND_dynamite:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14980       break;
14981
14982     case SOUND_tick:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14984       break;
14985
14986     case SOUND_press:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14988       break;
14989
14990     case SOUND_wheel:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14992       break;
14993
14994     case SOUND_boom:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14996       break;
14997
14998     case SOUND_die:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15000       break;
15001
15002     case SOUND_time:
15003       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15004       break;
15005
15006     default:
15007       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15008       break;
15009   }
15010 }
15011
15012 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15013 {
15014   int element = map_element_SP_to_RND(element_sp);
15015   int action = map_action_SP_to_RND(action_sp);
15016   int offset = (setup.sp_show_border_elements ? 0 : 1);
15017   int x = xx - offset;
15018   int y = yy - offset;
15019
15020   PlayLevelSoundElementAction(x, y, element, action);
15021 }
15022
15023 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15024 {
15025   int element = map_element_MM_to_RND(element_mm);
15026   int action = map_action_MM_to_RND(action_mm);
15027   int offset = 0;
15028   int x = xx - offset;
15029   int y = yy - offset;
15030
15031   if (!IS_MM_ELEMENT(element))
15032     element = EL_MM_DEFAULT;
15033
15034   PlayLevelSoundElementAction(x, y, element, action);
15035 }
15036
15037 void PlaySound_MM(int sound_mm)
15038 {
15039   int sound = map_sound_MM_to_RND(sound_mm);
15040
15041   if (sound == SND_UNDEFINED)
15042     return;
15043
15044   PlaySound(sound);
15045 }
15046
15047 void PlaySoundLoop_MM(int sound_mm)
15048 {
15049   int sound = map_sound_MM_to_RND(sound_mm);
15050
15051   if (sound == SND_UNDEFINED)
15052     return;
15053
15054   PlaySoundLoop(sound);
15055 }
15056
15057 void StopSound_MM(int sound_mm)
15058 {
15059   int sound = map_sound_MM_to_RND(sound_mm);
15060
15061   if (sound == SND_UNDEFINED)
15062     return;
15063
15064   StopSound(sound);
15065 }
15066
15067 void RaiseScore(int value)
15068 {
15069   game.score += value;
15070
15071   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15072
15073   DisplayGameControlValues();
15074 }
15075
15076 void RaiseScoreElement(int element)
15077 {
15078   switch (element)
15079   {
15080     case EL_EMERALD:
15081     case EL_BD_DIAMOND:
15082     case EL_EMERALD_YELLOW:
15083     case EL_EMERALD_RED:
15084     case EL_EMERALD_PURPLE:
15085     case EL_SP_INFOTRON:
15086       RaiseScore(level.score[SC_EMERALD]);
15087       break;
15088     case EL_DIAMOND:
15089       RaiseScore(level.score[SC_DIAMOND]);
15090       break;
15091     case EL_CRYSTAL:
15092       RaiseScore(level.score[SC_CRYSTAL]);
15093       break;
15094     case EL_PEARL:
15095       RaiseScore(level.score[SC_PEARL]);
15096       break;
15097     case EL_BUG:
15098     case EL_BD_BUTTERFLY:
15099     case EL_SP_ELECTRON:
15100       RaiseScore(level.score[SC_BUG]);
15101       break;
15102     case EL_SPACESHIP:
15103     case EL_BD_FIREFLY:
15104     case EL_SP_SNIKSNAK:
15105       RaiseScore(level.score[SC_SPACESHIP]);
15106       break;
15107     case EL_YAMYAM:
15108     case EL_DARK_YAMYAM:
15109       RaiseScore(level.score[SC_YAMYAM]);
15110       break;
15111     case EL_ROBOT:
15112       RaiseScore(level.score[SC_ROBOT]);
15113       break;
15114     case EL_PACMAN:
15115       RaiseScore(level.score[SC_PACMAN]);
15116       break;
15117     case EL_NUT:
15118       RaiseScore(level.score[SC_NUT]);
15119       break;
15120     case EL_DYNAMITE:
15121     case EL_EM_DYNAMITE:
15122     case EL_SP_DISK_RED:
15123     case EL_DYNABOMB_INCREASE_NUMBER:
15124     case EL_DYNABOMB_INCREASE_SIZE:
15125     case EL_DYNABOMB_INCREASE_POWER:
15126       RaiseScore(level.score[SC_DYNAMITE]);
15127       break;
15128     case EL_SHIELD_NORMAL:
15129     case EL_SHIELD_DEADLY:
15130       RaiseScore(level.score[SC_SHIELD]);
15131       break;
15132     case EL_EXTRA_TIME:
15133       RaiseScore(level.extra_time_score);
15134       break;
15135     case EL_KEY_1:
15136     case EL_KEY_2:
15137     case EL_KEY_3:
15138     case EL_KEY_4:
15139     case EL_EM_KEY_1:
15140     case EL_EM_KEY_2:
15141     case EL_EM_KEY_3:
15142     case EL_EM_KEY_4:
15143     case EL_EMC_KEY_5:
15144     case EL_EMC_KEY_6:
15145     case EL_EMC_KEY_7:
15146     case EL_EMC_KEY_8:
15147     case EL_DC_KEY_WHITE:
15148       RaiseScore(level.score[SC_KEY]);
15149       break;
15150     default:
15151       RaiseScore(element_info[element].collect_score);
15152       break;
15153   }
15154 }
15155
15156 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15157 {
15158   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15159   {
15160     // closing door required in case of envelope style request dialogs
15161     if (!skip_request)
15162     {
15163       // prevent short reactivation of overlay buttons while closing door
15164       SetOverlayActive(FALSE);
15165
15166       CloseDoor(DOOR_CLOSE_1);
15167     }
15168
15169     if (network.enabled)
15170       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15171     else
15172     {
15173       if (quick_quit)
15174         FadeSkipNextFadeIn();
15175
15176       SetGameStatus(GAME_MODE_MAIN);
15177
15178       DrawMainMenu();
15179     }
15180   }
15181   else          // continue playing the game
15182   {
15183     if (tape.playing && tape.deactivate_display)
15184       TapeDeactivateDisplayOff(TRUE);
15185
15186     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15187
15188     if (tape.playing && tape.deactivate_display)
15189       TapeDeactivateDisplayOn();
15190   }
15191 }
15192
15193 void RequestQuitGame(boolean ask_if_really_quit)
15194 {
15195   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15196   boolean skip_request = game.all_players_gone || quick_quit;
15197
15198   RequestQuitGameExt(skip_request, quick_quit,
15199                      "Do you really want to quit the game?");
15200 }
15201
15202 void RequestRestartGame(char *message)
15203 {
15204   game.restart_game_message = NULL;
15205
15206   boolean has_started_game = hasStartedNetworkGame();
15207   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15208
15209   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15210   {
15211     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15212   }
15213   else
15214   {
15215     SetGameStatus(GAME_MODE_MAIN);
15216
15217     DrawMainMenu();
15218   }
15219 }
15220
15221 void CheckGameOver(void)
15222 {
15223   static boolean last_game_over = FALSE;
15224   static int game_over_delay = 0;
15225   int game_over_delay_value = 50;
15226   boolean game_over = checkGameFailed();
15227
15228   // do not handle game over if request dialog is already active
15229   if (game.request_active)
15230     return;
15231
15232   // do not ask to play again if game was never actually played
15233   if (!game.GamePlayed)
15234     return;
15235
15236   if (!game_over)
15237   {
15238     last_game_over = FALSE;
15239     game_over_delay = game_over_delay_value;
15240
15241     return;
15242   }
15243
15244   if (game_over_delay > 0)
15245   {
15246     game_over_delay--;
15247
15248     return;
15249   }
15250
15251   if (last_game_over != game_over)
15252     game.restart_game_message = (hasStartedNetworkGame() ?
15253                                  "Game over! Play it again?" :
15254                                  "Game over!");
15255
15256   last_game_over = game_over;
15257 }
15258
15259 boolean checkGameSolved(void)
15260 {
15261   // set for all game engines if level was solved
15262   return game.LevelSolved_GameEnd;
15263 }
15264
15265 boolean checkGameFailed(void)
15266 {
15267   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15268     return (game_em.game_over && !game_em.level_solved);
15269   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15270     return (game_sp.game_over && !game_sp.level_solved);
15271   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15272     return (game_mm.game_over && !game_mm.level_solved);
15273   else                          // GAME_ENGINE_TYPE_RND
15274     return (game.GameOver && !game.LevelSolved);
15275 }
15276
15277 boolean checkGameEnded(void)
15278 {
15279   return (checkGameSolved() || checkGameFailed());
15280 }
15281
15282
15283 // ----------------------------------------------------------------------------
15284 // random generator functions
15285 // ----------------------------------------------------------------------------
15286
15287 unsigned int InitEngineRandom_RND(int seed)
15288 {
15289   game.num_random_calls = 0;
15290
15291   return InitEngineRandom(seed);
15292 }
15293
15294 unsigned int RND(int max)
15295 {
15296   if (max > 0)
15297   {
15298     game.num_random_calls++;
15299
15300     return GetEngineRandom(max);
15301   }
15302
15303   return 0;
15304 }
15305
15306
15307 // ----------------------------------------------------------------------------
15308 // game engine snapshot handling functions
15309 // ----------------------------------------------------------------------------
15310
15311 struct EngineSnapshotInfo
15312 {
15313   // runtime values for custom element collect score
15314   int collect_score[NUM_CUSTOM_ELEMENTS];
15315
15316   // runtime values for group element choice position
15317   int choice_pos[NUM_GROUP_ELEMENTS];
15318
15319   // runtime values for belt position animations
15320   int belt_graphic[4][NUM_BELT_PARTS];
15321   int belt_anim_mode[4][NUM_BELT_PARTS];
15322 };
15323
15324 static struct EngineSnapshotInfo engine_snapshot_rnd;
15325 static char *snapshot_level_identifier = NULL;
15326 static int snapshot_level_nr = -1;
15327
15328 static void SaveEngineSnapshotValues_RND(void)
15329 {
15330   static int belt_base_active_element[4] =
15331   {
15332     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15333     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15334     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15335     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15336   };
15337   int i, j;
15338
15339   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15340   {
15341     int element = EL_CUSTOM_START + i;
15342
15343     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15344   }
15345
15346   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15347   {
15348     int element = EL_GROUP_START + i;
15349
15350     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15351   }
15352
15353   for (i = 0; i < 4; i++)
15354   {
15355     for (j = 0; j < NUM_BELT_PARTS; j++)
15356     {
15357       int element = belt_base_active_element[i] + j;
15358       int graphic = el2img(element);
15359       int anim_mode = graphic_info[graphic].anim_mode;
15360
15361       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15362       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15363     }
15364   }
15365 }
15366
15367 static void LoadEngineSnapshotValues_RND(void)
15368 {
15369   unsigned int num_random_calls = game.num_random_calls;
15370   int i, j;
15371
15372   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15373   {
15374     int element = EL_CUSTOM_START + i;
15375
15376     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15377   }
15378
15379   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15380   {
15381     int element = EL_GROUP_START + i;
15382
15383     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15384   }
15385
15386   for (i = 0; i < 4; i++)
15387   {
15388     for (j = 0; j < NUM_BELT_PARTS; j++)
15389     {
15390       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15391       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15392
15393       graphic_info[graphic].anim_mode = anim_mode;
15394     }
15395   }
15396
15397   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15398   {
15399     InitRND(tape.random_seed);
15400     for (i = 0; i < num_random_calls; i++)
15401       RND(1);
15402   }
15403
15404   if (game.num_random_calls != num_random_calls)
15405   {
15406     Error(ERR_INFO, "number of random calls out of sync");
15407     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15408     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15409     Error(ERR_EXIT, "this should not happen -- please debug");
15410   }
15411 }
15412
15413 void FreeEngineSnapshotSingle(void)
15414 {
15415   FreeSnapshotSingle();
15416
15417   setString(&snapshot_level_identifier, NULL);
15418   snapshot_level_nr = -1;
15419 }
15420
15421 void FreeEngineSnapshotList(void)
15422 {
15423   FreeSnapshotList();
15424 }
15425
15426 static ListNode *SaveEngineSnapshotBuffers(void)
15427 {
15428   ListNode *buffers = NULL;
15429
15430   // copy some special values to a structure better suited for the snapshot
15431
15432   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15433     SaveEngineSnapshotValues_RND();
15434   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15435     SaveEngineSnapshotValues_EM();
15436   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15437     SaveEngineSnapshotValues_SP(&buffers);
15438   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15439     SaveEngineSnapshotValues_MM(&buffers);
15440
15441   // save values stored in special snapshot structure
15442
15443   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15444     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15445   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15446     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15447   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15448     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15449   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15450     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15451
15452   // save further RND engine values
15453
15454   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15455   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15456   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15457
15458   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15459   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15460   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15461   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15462   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15463
15464   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15465   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15466   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15467
15468   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15469
15470   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15472
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15475   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15491
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15494
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15498
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15501
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15507
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15510
15511 #if 0
15512   ListNode *node = engine_snapshot_list_rnd;
15513   int num_bytes = 0;
15514
15515   while (node != NULL)
15516   {
15517     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15518
15519     node = node->next;
15520   }
15521
15522   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15523 #endif
15524
15525   return buffers;
15526 }
15527
15528 void SaveEngineSnapshotSingle(void)
15529 {
15530   ListNode *buffers = SaveEngineSnapshotBuffers();
15531
15532   // finally save all snapshot buffers to single snapshot
15533   SaveSnapshotSingle(buffers);
15534
15535   // save level identification information
15536   setString(&snapshot_level_identifier, leveldir_current->identifier);
15537   snapshot_level_nr = level_nr;
15538 }
15539
15540 boolean CheckSaveEngineSnapshotToList(void)
15541 {
15542   boolean save_snapshot =
15543     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15544      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15545       game.snapshot.changed_action) ||
15546      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15547       game.snapshot.collected_item));
15548
15549   game.snapshot.changed_action = FALSE;
15550   game.snapshot.collected_item = FALSE;
15551   game.snapshot.save_snapshot = save_snapshot;
15552
15553   return save_snapshot;
15554 }
15555
15556 void SaveEngineSnapshotToList(void)
15557 {
15558   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15559       tape.quick_resume)
15560     return;
15561
15562   ListNode *buffers = SaveEngineSnapshotBuffers();
15563
15564   // finally save all snapshot buffers to snapshot list
15565   SaveSnapshotToList(buffers);
15566 }
15567
15568 void SaveEngineSnapshotToListInitial(void)
15569 {
15570   FreeEngineSnapshotList();
15571
15572   SaveEngineSnapshotToList();
15573 }
15574
15575 static void LoadEngineSnapshotValues(void)
15576 {
15577   // restore special values from snapshot structure
15578
15579   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15580     LoadEngineSnapshotValues_RND();
15581   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15582     LoadEngineSnapshotValues_EM();
15583   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15584     LoadEngineSnapshotValues_SP();
15585   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15586     LoadEngineSnapshotValues_MM();
15587 }
15588
15589 void LoadEngineSnapshotSingle(void)
15590 {
15591   LoadSnapshotSingle();
15592
15593   LoadEngineSnapshotValues();
15594 }
15595
15596 static void LoadEngineSnapshot_Undo(int steps)
15597 {
15598   LoadSnapshotFromList_Older(steps);
15599
15600   LoadEngineSnapshotValues();
15601 }
15602
15603 static void LoadEngineSnapshot_Redo(int steps)
15604 {
15605   LoadSnapshotFromList_Newer(steps);
15606
15607   LoadEngineSnapshotValues();
15608 }
15609
15610 boolean CheckEngineSnapshotSingle(void)
15611 {
15612   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15613           snapshot_level_nr == level_nr);
15614 }
15615
15616 boolean CheckEngineSnapshotList(void)
15617 {
15618   return CheckSnapshotList();
15619 }
15620
15621
15622 // ---------- new game button stuff -------------------------------------------
15623
15624 static struct
15625 {
15626   int graphic;
15627   struct XY *pos;
15628   int gadget_id;
15629   boolean *setup_value;
15630   boolean allowed_on_tape;
15631   boolean is_touch_button;
15632   char *infotext;
15633 } gamebutton_info[NUM_GAME_BUTTONS] =
15634 {
15635   {
15636     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15637     GAME_CTRL_ID_STOP,                          NULL,
15638     TRUE, FALSE,                                "stop game"
15639   },
15640   {
15641     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15642     GAME_CTRL_ID_PAUSE,                         NULL,
15643     TRUE, FALSE,                                "pause game"
15644   },
15645   {
15646     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15647     GAME_CTRL_ID_PLAY,                          NULL,
15648     TRUE, FALSE,                                "play game"
15649   },
15650   {
15651     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15652     GAME_CTRL_ID_UNDO,                          NULL,
15653     TRUE, FALSE,                                "undo step"
15654   },
15655   {
15656     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15657     GAME_CTRL_ID_REDO,                          NULL,
15658     TRUE, FALSE,                                "redo step"
15659   },
15660   {
15661     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15662     GAME_CTRL_ID_SAVE,                          NULL,
15663     TRUE, FALSE,                                "save game"
15664   },
15665   {
15666     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15667     GAME_CTRL_ID_PAUSE2,                        NULL,
15668     TRUE, FALSE,                                "pause game"
15669   },
15670   {
15671     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15672     GAME_CTRL_ID_LOAD,                          NULL,
15673     TRUE, FALSE,                                "load game"
15674   },
15675   {
15676     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15677     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15678     FALSE, FALSE,                               "stop game"
15679   },
15680   {
15681     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15682     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15683     FALSE, FALSE,                               "pause game"
15684   },
15685   {
15686     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15687     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15688     FALSE, FALSE,                               "play game"
15689   },
15690   {
15691     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15692     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15693     FALSE, TRUE,                                "stop game"
15694   },
15695   {
15696     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15697     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15698     FALSE, TRUE,                                "pause game"
15699   },
15700   {
15701     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15702     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15703     TRUE, FALSE,                                "background music on/off"
15704   },
15705   {
15706     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15707     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15708     TRUE, FALSE,                                "sound loops on/off"
15709   },
15710   {
15711     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15712     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15713     TRUE, FALSE,                                "normal sounds on/off"
15714   },
15715   {
15716     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15717     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15718     FALSE, FALSE,                               "background music on/off"
15719   },
15720   {
15721     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15722     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15723     FALSE, FALSE,                               "sound loops on/off"
15724   },
15725   {
15726     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15727     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15728     FALSE, FALSE,                               "normal sounds on/off"
15729   }
15730 };
15731
15732 void CreateGameButtons(void)
15733 {
15734   int i;
15735
15736   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15737   {
15738     int graphic = gamebutton_info[i].graphic;
15739     struct GraphicInfo *gfx = &graphic_info[graphic];
15740     struct XY *pos = gamebutton_info[i].pos;
15741     struct GadgetInfo *gi;
15742     int button_type;
15743     boolean checked;
15744     unsigned int event_mask;
15745     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15746     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15747     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15748     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15749     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15750     int gd_x   = gfx->src_x;
15751     int gd_y   = gfx->src_y;
15752     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15753     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15754     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15755     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15756     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15757     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15758     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15759     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15760     int id = i;
15761
15762     if (gfx->bitmap == NULL)
15763     {
15764       game_gadget[id] = NULL;
15765
15766       continue;
15767     }
15768
15769     if (id == GAME_CTRL_ID_STOP ||
15770         id == GAME_CTRL_ID_PANEL_STOP ||
15771         id == GAME_CTRL_ID_TOUCH_STOP ||
15772         id == GAME_CTRL_ID_PLAY ||
15773         id == GAME_CTRL_ID_PANEL_PLAY ||
15774         id == GAME_CTRL_ID_SAVE ||
15775         id == GAME_CTRL_ID_LOAD)
15776     {
15777       button_type = GD_TYPE_NORMAL_BUTTON;
15778       checked = FALSE;
15779       event_mask = GD_EVENT_RELEASED;
15780     }
15781     else if (id == GAME_CTRL_ID_UNDO ||
15782              id == GAME_CTRL_ID_REDO)
15783     {
15784       button_type = GD_TYPE_NORMAL_BUTTON;
15785       checked = FALSE;
15786       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15787     }
15788     else
15789     {
15790       button_type = GD_TYPE_CHECK_BUTTON;
15791       checked = (gamebutton_info[i].setup_value != NULL ?
15792                  *gamebutton_info[i].setup_value : FALSE);
15793       event_mask = GD_EVENT_PRESSED;
15794     }
15795
15796     gi = CreateGadget(GDI_CUSTOM_ID, id,
15797                       GDI_IMAGE_ID, graphic,
15798                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15799                       GDI_X, base_x + x,
15800                       GDI_Y, base_y + y,
15801                       GDI_WIDTH, gfx->width,
15802                       GDI_HEIGHT, gfx->height,
15803                       GDI_TYPE, button_type,
15804                       GDI_STATE, GD_BUTTON_UNPRESSED,
15805                       GDI_CHECKED, checked,
15806                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15807                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15808                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15809                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15810                       GDI_DIRECT_DRAW, FALSE,
15811                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15812                       GDI_EVENT_MASK, event_mask,
15813                       GDI_CALLBACK_ACTION, HandleGameButtons,
15814                       GDI_END);
15815
15816     if (gi == NULL)
15817       Error(ERR_EXIT, "cannot create gadget");
15818
15819     game_gadget[id] = gi;
15820   }
15821 }
15822
15823 void FreeGameButtons(void)
15824 {
15825   int i;
15826
15827   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15828     FreeGadget(game_gadget[i]);
15829 }
15830
15831 static void UnmapGameButtonsAtSamePosition(int id)
15832 {
15833   int i;
15834
15835   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15836     if (i != id &&
15837         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15838         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15839       UnmapGadget(game_gadget[i]);
15840 }
15841
15842 static void UnmapGameButtonsAtSamePosition_All(void)
15843 {
15844   if (setup.show_snapshot_buttons)
15845   {
15846     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15847     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15848     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15849   }
15850   else
15851   {
15852     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15853     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15854     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15855
15856     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15857     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15858     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15859   }
15860 }
15861
15862 static void MapGameButtonsAtSamePosition(int id)
15863 {
15864   int i;
15865
15866   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15867     if (i != id &&
15868         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15869         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15870       MapGadget(game_gadget[i]);
15871
15872   UnmapGameButtonsAtSamePosition_All();
15873 }
15874
15875 void MapUndoRedoButtons(void)
15876 {
15877   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15878   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15879
15880   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15881   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15882 }
15883
15884 void UnmapUndoRedoButtons(void)
15885 {
15886   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15887   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15888
15889   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15890   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15891 }
15892
15893 void ModifyPauseButtons(void)
15894 {
15895   static int ids[] =
15896   {
15897     GAME_CTRL_ID_PAUSE,
15898     GAME_CTRL_ID_PAUSE2,
15899     GAME_CTRL_ID_PANEL_PAUSE,
15900     GAME_CTRL_ID_TOUCH_PAUSE,
15901     -1
15902   };
15903   int i;
15904
15905   for (i = 0; ids[i] > -1; i++)
15906     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15907 }
15908
15909 static void MapGameButtonsExt(boolean on_tape)
15910 {
15911   int i;
15912
15913   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15914     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15915         i != GAME_CTRL_ID_UNDO &&
15916         i != GAME_CTRL_ID_REDO)
15917       MapGadget(game_gadget[i]);
15918
15919   UnmapGameButtonsAtSamePosition_All();
15920
15921   RedrawGameButtons();
15922 }
15923
15924 static void UnmapGameButtonsExt(boolean on_tape)
15925 {
15926   int i;
15927
15928   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15929     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15930       UnmapGadget(game_gadget[i]);
15931 }
15932
15933 static void RedrawGameButtonsExt(boolean on_tape)
15934 {
15935   int i;
15936
15937   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15938     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15939       RedrawGadget(game_gadget[i]);
15940 }
15941
15942 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15943 {
15944   if (gi == NULL)
15945     return;
15946
15947   gi->checked = state;
15948 }
15949
15950 static void RedrawSoundButtonGadget(int id)
15951 {
15952   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15953              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15954              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15955              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15956              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15957              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15958              id);
15959
15960   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15961   RedrawGadget(game_gadget[id2]);
15962 }
15963
15964 void MapGameButtons(void)
15965 {
15966   MapGameButtonsExt(FALSE);
15967 }
15968
15969 void UnmapGameButtons(void)
15970 {
15971   UnmapGameButtonsExt(FALSE);
15972 }
15973
15974 void RedrawGameButtons(void)
15975 {
15976   RedrawGameButtonsExt(FALSE);
15977 }
15978
15979 void MapGameButtonsOnTape(void)
15980 {
15981   MapGameButtonsExt(TRUE);
15982 }
15983
15984 void UnmapGameButtonsOnTape(void)
15985 {
15986   UnmapGameButtonsExt(TRUE);
15987 }
15988
15989 void RedrawGameButtonsOnTape(void)
15990 {
15991   RedrawGameButtonsExt(TRUE);
15992 }
15993
15994 static void GameUndoRedoExt(void)
15995 {
15996   ClearPlayerAction();
15997
15998   tape.pausing = TRUE;
15999
16000   RedrawPlayfield();
16001   UpdateAndDisplayGameControlValues();
16002
16003   DrawCompleteVideoDisplay();
16004   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16005   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16006   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16007
16008   BackToFront();
16009 }
16010
16011 static void GameUndo(int steps)
16012 {
16013   if (!CheckEngineSnapshotList())
16014     return;
16015
16016   LoadEngineSnapshot_Undo(steps);
16017
16018   GameUndoRedoExt();
16019 }
16020
16021 static void GameRedo(int steps)
16022 {
16023   if (!CheckEngineSnapshotList())
16024     return;
16025
16026   LoadEngineSnapshot_Redo(steps);
16027
16028   GameUndoRedoExt();
16029 }
16030
16031 static void HandleGameButtonsExt(int id, int button)
16032 {
16033   static boolean game_undo_executed = FALSE;
16034   int steps = BUTTON_STEPSIZE(button);
16035   boolean handle_game_buttons =
16036     (game_status == GAME_MODE_PLAYING ||
16037      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16038
16039   if (!handle_game_buttons)
16040     return;
16041
16042   switch (id)
16043   {
16044     case GAME_CTRL_ID_STOP:
16045     case GAME_CTRL_ID_PANEL_STOP:
16046     case GAME_CTRL_ID_TOUCH_STOP:
16047       if (game_status == GAME_MODE_MAIN)
16048         break;
16049
16050       if (tape.playing)
16051         TapeStop();
16052       else
16053         RequestQuitGame(TRUE);
16054
16055       break;
16056
16057     case GAME_CTRL_ID_PAUSE:
16058     case GAME_CTRL_ID_PAUSE2:
16059     case GAME_CTRL_ID_PANEL_PAUSE:
16060     case GAME_CTRL_ID_TOUCH_PAUSE:
16061       if (network.enabled && game_status == GAME_MODE_PLAYING)
16062       {
16063         if (tape.pausing)
16064           SendToServer_ContinuePlaying();
16065         else
16066           SendToServer_PausePlaying();
16067       }
16068       else
16069         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16070
16071       game_undo_executed = FALSE;
16072
16073       break;
16074
16075     case GAME_CTRL_ID_PLAY:
16076     case GAME_CTRL_ID_PANEL_PLAY:
16077       if (game_status == GAME_MODE_MAIN)
16078       {
16079         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16080       }
16081       else if (tape.pausing)
16082       {
16083         if (network.enabled)
16084           SendToServer_ContinuePlaying();
16085         else
16086           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16087       }
16088       break;
16089
16090     case GAME_CTRL_ID_UNDO:
16091       // Important: When using "save snapshot when collecting an item" mode,
16092       // load last (current) snapshot for first "undo" after pressing "pause"
16093       // (else the last-but-one snapshot would be loaded, because the snapshot
16094       // pointer already points to the last snapshot when pressing "pause",
16095       // which is fine for "every step/move" mode, but not for "every collect")
16096       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16097           !game_undo_executed)
16098         steps--;
16099
16100       game_undo_executed = TRUE;
16101
16102       GameUndo(steps);
16103       break;
16104
16105     case GAME_CTRL_ID_REDO:
16106       GameRedo(steps);
16107       break;
16108
16109     case GAME_CTRL_ID_SAVE:
16110       TapeQuickSave();
16111       break;
16112
16113     case GAME_CTRL_ID_LOAD:
16114       TapeQuickLoad();
16115       break;
16116
16117     case SOUND_CTRL_ID_MUSIC:
16118     case SOUND_CTRL_ID_PANEL_MUSIC:
16119       if (setup.sound_music)
16120       { 
16121         setup.sound_music = FALSE;
16122
16123         FadeMusic();
16124       }
16125       else if (audio.music_available)
16126       { 
16127         setup.sound = setup.sound_music = TRUE;
16128
16129         SetAudioMode(setup.sound);
16130
16131         if (game_status == GAME_MODE_PLAYING)
16132           PlayLevelMusic();
16133       }
16134
16135       RedrawSoundButtonGadget(id);
16136
16137       break;
16138
16139     case SOUND_CTRL_ID_LOOPS:
16140     case SOUND_CTRL_ID_PANEL_LOOPS:
16141       if (setup.sound_loops)
16142         setup.sound_loops = FALSE;
16143       else if (audio.loops_available)
16144       {
16145         setup.sound = setup.sound_loops = TRUE;
16146
16147         SetAudioMode(setup.sound);
16148       }
16149
16150       RedrawSoundButtonGadget(id);
16151
16152       break;
16153
16154     case SOUND_CTRL_ID_SIMPLE:
16155     case SOUND_CTRL_ID_PANEL_SIMPLE:
16156       if (setup.sound_simple)
16157         setup.sound_simple = FALSE;
16158       else if (audio.sound_available)
16159       {
16160         setup.sound = setup.sound_simple = TRUE;
16161
16162         SetAudioMode(setup.sound);
16163       }
16164
16165       RedrawSoundButtonGadget(id);
16166
16167       break;
16168
16169     default:
16170       break;
16171   }
16172 }
16173
16174 static void HandleGameButtons(struct GadgetInfo *gi)
16175 {
16176   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16177 }
16178
16179 void HandleSoundButtonKeys(Key key)
16180 {
16181   if (key == setup.shortcut.sound_simple)
16182     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16183   else if (key == setup.shortcut.sound_loops)
16184     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16185   else if (key == setup.shortcut.sound_music)
16186     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16187 }