added handling 64-bit random value bug with old tapes in EM engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://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     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Feld[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Feld[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Feld[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Feld[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2153       Error(ERR_EXIT, "this should not happen -- please debug");
2154     }
2155
2156     // force update of game controls after initialization
2157     gpc->value = gpc->last_value = -1;
2158     gpc->frame = gpc->last_frame = -1;
2159     gpc->gfx_frame = -1;
2160
2161     // determine panel value width for later calculation of alignment
2162     if (type == TYPE_INTEGER || type == TYPE_STRING)
2163     {
2164       pos->width = pos->size * getFontWidth(pos->font);
2165       pos->height = getFontHeight(pos->font);
2166     }
2167     else if (type == TYPE_ELEMENT)
2168     {
2169       pos->width = pos->size;
2170       pos->height = pos->size;
2171     }
2172
2173     // fill structure for game panel draw order
2174     gpo->nr = gpc->nr;
2175     gpo->sort_priority = pos->sort_priority;
2176   }
2177
2178   // sort game panel controls according to sort_priority and control number
2179   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2180         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2181 }
2182
2183 static void UpdatePlayfieldElementCount(void)
2184 {
2185   boolean use_element_count = FALSE;
2186   int i, j, x, y;
2187
2188   // first check if it is needed at all to calculate playfield element count
2189   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2190     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2191       use_element_count = TRUE;
2192
2193   if (!use_element_count)
2194     return;
2195
2196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2197     element_info[i].element_count = 0;
2198
2199   SCAN_PLAYFIELD(x, y)
2200   {
2201     element_info[Feld[x][y]].element_count++;
2202   }
2203
2204   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2205     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2206       if (IS_IN_GROUP(j, i))
2207         element_info[EL_GROUP_START + i].element_count +=
2208           element_info[j].element_count;
2209 }
2210
2211 static void UpdateGameControlValues(void)
2212 {
2213   int i, k;
2214   int time = (game.LevelSolved ?
2215               game.LevelSolved_CountingTime :
2216               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2217               game_em.lev->time :
2218               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2219               game_sp.time_played :
2220               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2221               game_mm.energy_left :
2222               game.no_time_limit ? TimePlayed : TimeLeft);
2223   int score = (game.LevelSolved ?
2224                game.LevelSolved_CountingScore :
2225                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226                game_em.lev->score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228                game_sp.score :
2229                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230                game_mm.score :
2231                game.score);
2232   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2233               game_em.lev->gems_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2235               game_sp.infotrons_still_needed :
2236               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2237               game_mm.kettles_still_needed :
2238               game.gems_still_needed);
2239   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2240                      game_em.lev->gems_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2242                      game_sp.infotrons_still_needed > 0 :
2243                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2244                      game_mm.kettles_still_needed > 0 ||
2245                      game_mm.lights_still_needed > 0 :
2246                      game.gems_still_needed > 0 ||
2247                      game.sokoban_fields_still_needed > 0 ||
2248                      game.sokoban_objects_still_needed > 0 ||
2249                      game.lights_still_needed > 0);
2250   int health = (game.LevelSolved ?
2251                 game.LevelSolved_CountingHealth :
2252                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                 MM_HEALTH(game_mm.laser_overload_value) :
2254                 game.health);
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, i);
2333     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2334       get_inventory_element_from_pos(local_player, -i - 1);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_SCORE].value = score;
2338   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2339
2340   game_panel_controls[GAME_PANEL_TIME].value = time;
2341
2342   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2343   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2344   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2345
2346   if (level.time == 0)
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2348   else
2349     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2350
2351   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2352   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2353
2354   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2355
2356   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2357     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2358      EL_EMPTY);
2359   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2360     local_player->shield_normal_time_left;
2361   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2362     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2363      EL_EMPTY);
2364   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2365     local_player->shield_deadly_time_left;
2366
2367   game_panel_controls[GAME_PANEL_EXIT].value =
2368     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2373     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2374      EL_EMC_MAGIC_BALL_SWITCH);
2375
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2377     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2379     game.light_time_left;
2380
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2382     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2384     game.timegate_time_left;
2385
2386   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2387     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2388
2389   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2390     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2391   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2392     game.lenses_time_left;
2393
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2395     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2397     game.magnify_time_left;
2398
2399   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2400     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2401      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2402      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2403      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2404      EL_BALLOON_SWITCH_NONE);
2405
2406   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2407     local_player->dynabomb_count;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2409     local_player->dynabomb_size;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2411     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2412
2413   game_panel_controls[GAME_PANEL_PENGUINS].value =
2414     game.friends_still_needed;
2415
2416   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2417     game.sokoban_objects_still_needed;
2418   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2419     game.sokoban_fields_still_needed;
2420
2421   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2422     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2423
2424   for (i = 0; i < NUM_BELTS; i++)
2425   {
2426     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2427       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2428        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2429     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2430       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2431   }
2432
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2434     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2436     game.magic_wall_time_left;
2437
2438   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2439     local_player->gravity;
2440
2441   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2442     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2443
2444   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2445     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2446       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2447        game.panel.element[i].id : EL_UNDEFINED);
2448
2449   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2450     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2451       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2452        element_info[game.panel.element_count[i].id].element_count : 0);
2453
2454   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2455     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2456       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2457        element_info[game.panel.ce_score[i].id].collect_score : 0);
2458
2459   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2460     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2461       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2462        element_info[game.panel.ce_score_element[i].id].collect_score :
2463        EL_UNDEFINED);
2464
2465   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2466   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2467   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2468
2469   // update game panel control frames
2470
2471   for (i = 0; game_panel_controls[i].nr != -1; i++)
2472   {
2473     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2474
2475     if (gpc->type == TYPE_ELEMENT)
2476     {
2477       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int element = gpc->value;
2481         int graphic = el2panelimg(element);
2482
2483         if (gpc->value != gpc->last_value)
2484         {
2485           gpc->gfx_frame = 0;
2486           gpc->gfx_random = INIT_GFX_RANDOM();
2487         }
2488         else
2489         {
2490           gpc->gfx_frame++;
2491
2492           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494             gpc->gfx_random = INIT_GFX_RANDOM();
2495         }
2496
2497         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498           gfx.anim_random_frame = gpc->gfx_random;
2499
2500         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2501           gpc->gfx_frame = element_info[element].collect_score;
2502
2503         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2504                                               gpc->gfx_frame);
2505
2506         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2507           gfx.anim_random_frame = last_anim_random_frame;
2508       }
2509     }
2510     else if (gpc->type == TYPE_GRAPHIC)
2511     {
2512       if (gpc->graphic != IMG_UNDEFINED)
2513       {
2514         int last_anim_random_frame = gfx.anim_random_frame;
2515         int graphic = gpc->graphic;
2516
2517         if (gpc->value != gpc->last_value)
2518         {
2519           gpc->gfx_frame = 0;
2520           gpc->gfx_random = INIT_GFX_RANDOM();
2521         }
2522         else
2523         {
2524           gpc->gfx_frame++;
2525
2526           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2527               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2528             gpc->gfx_random = INIT_GFX_RANDOM();
2529         }
2530
2531         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2532           gfx.anim_random_frame = gpc->gfx_random;
2533
2534         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2535
2536         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2537           gfx.anim_random_frame = last_anim_random_frame;
2538       }
2539     }
2540   }
2541 }
2542
2543 static void DisplayGameControlValues(void)
2544 {
2545   boolean redraw_panel = FALSE;
2546   int i;
2547
2548   for (i = 0; game_panel_controls[i].nr != -1; i++)
2549   {
2550     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2551
2552     if (PANEL_DEACTIVATED(gpc->pos))
2553       continue;
2554
2555     if (gpc->value == gpc->last_value &&
2556         gpc->frame == gpc->last_frame)
2557       continue;
2558
2559     redraw_panel = TRUE;
2560   }
2561
2562   if (!redraw_panel)
2563     return;
2564
2565   // copy default game door content to main double buffer
2566
2567   // !!! CHECK AGAIN !!!
2568   SetPanelBackground();
2569   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2570   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2571
2572   // redraw game control buttons
2573   RedrawGameButtons();
2574
2575   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2576
2577   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2578   {
2579     int nr = game_panel_order[i].nr;
2580     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2581     struct TextPosInfo *pos = gpc->pos;
2582     int type = gpc->type;
2583     int value = gpc->value;
2584     int frame = gpc->frame;
2585     int size = pos->size;
2586     int font = pos->font;
2587     boolean draw_masked = pos->draw_masked;
2588     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2589
2590     if (PANEL_DEACTIVATED(pos))
2591       continue;
2592
2593     gpc->last_value = value;
2594     gpc->last_frame = frame;
2595
2596     if (type == TYPE_INTEGER)
2597     {
2598       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599           nr == GAME_PANEL_TIME)
2600       {
2601         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602
2603         if (use_dynamic_size)           // use dynamic number of digits
2604         {
2605           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607           int size2 = size1 + 1;
2608           int font1 = pos->font;
2609           int font2 = pos->font_alt;
2610
2611           size = (value < value_change ? size1 : size2);
2612           font = (value < value_change ? font1 : font2);
2613         }
2614       }
2615
2616       // correct text size if "digits" is zero or less
2617       if (size <= 0)
2618         size = strlen(int2str(value, size));
2619
2620       // dynamically correct text alignment
2621       pos->width = size * getFontWidth(font);
2622
2623       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2624                   int2str(value, size), font, mask_mode);
2625     }
2626     else if (type == TYPE_ELEMENT)
2627     {
2628       int element, graphic;
2629       Bitmap *src_bitmap;
2630       int src_x, src_y;
2631       int width, height;
2632       int dst_x = PANEL_XPOS(pos);
2633       int dst_y = PANEL_YPOS(pos);
2634
2635       if (value != EL_UNDEFINED && value != EL_EMPTY)
2636       {
2637         element = value;
2638         graphic = el2panelimg(value);
2639
2640         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2641
2642         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2643           size = TILESIZE;
2644
2645         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2646                               &src_x, &src_y);
2647
2648         width  = graphic_info[graphic].width  * size / TILESIZE;
2649         height = graphic_info[graphic].height * size / TILESIZE;
2650
2651         if (draw_masked)
2652           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2653                            dst_x, dst_y);
2654         else
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657       }
2658     }
2659     else if (type == TYPE_GRAPHIC)
2660     {
2661       int graphic        = gpc->graphic;
2662       int graphic_active = gpc->graphic_active;
2663       Bitmap *src_bitmap;
2664       int src_x, src_y;
2665       int width, height;
2666       int dst_x = PANEL_XPOS(pos);
2667       int dst_y = PANEL_YPOS(pos);
2668       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2669                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2670
2671       if (graphic != IMG_UNDEFINED && !skip)
2672       {
2673         if (pos->style == STYLE_REVERSE)
2674           value = 100 - value;
2675
2676         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2677
2678         if (pos->direction & MV_HORIZONTAL)
2679         {
2680           width  = graphic_info[graphic_active].width * value / 100;
2681           height = graphic_info[graphic_active].height;
2682
2683           if (pos->direction == MV_LEFT)
2684           {
2685             src_x += graphic_info[graphic_active].width - width;
2686             dst_x += graphic_info[graphic_active].width - width;
2687           }
2688         }
2689         else
2690         {
2691           width  = graphic_info[graphic_active].width;
2692           height = graphic_info[graphic_active].height * value / 100;
2693
2694           if (pos->direction == MV_UP)
2695           {
2696             src_y += graphic_info[graphic_active].height - height;
2697             dst_y += graphic_info[graphic_active].height - height;
2698           }
2699         }
2700
2701         if (draw_masked)
2702           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2703                            dst_x, dst_y);
2704         else
2705           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2706                      dst_x, dst_y);
2707
2708         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2709
2710         if (pos->direction & MV_HORIZONTAL)
2711         {
2712           if (pos->direction == MV_RIGHT)
2713           {
2714             src_x += width;
2715             dst_x += width;
2716           }
2717           else
2718           {
2719             dst_x = PANEL_XPOS(pos);
2720           }
2721
2722           width = graphic_info[graphic].width - width;
2723         }
2724         else
2725         {
2726           if (pos->direction == MV_DOWN)
2727           {
2728             src_y += height;
2729             dst_y += height;
2730           }
2731           else
2732           {
2733             dst_y = PANEL_YPOS(pos);
2734           }
2735
2736           height = graphic_info[graphic].height - height;
2737         }
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_STRING)
2748     {
2749       boolean active = (value != 0);
2750       char *state_normal = "off";
2751       char *state_active = "on";
2752       char *state = (active ? state_active : state_normal);
2753       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2754                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2755                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2756                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2757
2758       if (nr == GAME_PANEL_GRAVITY_STATE)
2759       {
2760         int font1 = pos->font;          // (used for normal state)
2761         int font2 = pos->font_alt;      // (used for active state)
2762
2763         font = (active ? font2 : font1);
2764       }
2765
2766       if (s != NULL)
2767       {
2768         char *s_cut;
2769
2770         if (size <= 0)
2771         {
2772           // don't truncate output if "chars" is zero or less
2773           size = strlen(s);
2774
2775           // dynamically correct text alignment
2776           pos->width = size * getFontWidth(font);
2777         }
2778
2779         s_cut = getStringCopyN(s, size);
2780
2781         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2782                     s_cut, font, mask_mode);
2783
2784         free(s_cut);
2785       }
2786     }
2787
2788     redraw_mask |= REDRAW_DOOR_1;
2789   }
2790
2791   SetGameStatus(GAME_MODE_PLAYING);
2792 }
2793
2794 void UpdateAndDisplayGameControlValues(void)
2795 {
2796   if (tape.deactivate_display)
2797     return;
2798
2799   UpdateGameControlValues();
2800   DisplayGameControlValues();
2801 }
2802
2803 #if 0
2804 static void UpdateGameDoorValues(void)
2805 {
2806   UpdateGameControlValues();
2807 }
2808 #endif
2809
2810 void DrawGameDoorValues(void)
2811 {
2812   DisplayGameControlValues();
2813 }
2814
2815
2816 // ============================================================================
2817 // InitGameEngine()
2818 // ----------------------------------------------------------------------------
2819 // initialize game engine due to level / tape version number
2820 // ============================================================================
2821
2822 static void InitGameEngine(void)
2823 {
2824   int i, j, k, l, x, y;
2825
2826   // set game engine from tape file when re-playing, else from level file
2827   game.engine_version = (tape.playing ? tape.engine_version :
2828                          level.game_version);
2829
2830   // set single or multi-player game mode (needed for re-playing tapes)
2831   game.team_mode = setup.team_mode;
2832
2833   if (tape.playing)
2834   {
2835     int num_players = 0;
2836
2837     for (i = 0; i < MAX_PLAYERS; i++)
2838       if (tape.player_participates[i])
2839         num_players++;
2840
2841     // multi-player tapes contain input data for more than one player
2842     game.team_mode = (num_players > 1);
2843   }
2844
2845 #if 0
2846   printf("level %d: level.game_version  == %06d\n", level_nr,
2847          level.game_version);
2848   printf("          tape.file_version   == %06d\n",
2849          tape.file_version);
2850   printf("          tape.game_version   == %06d\n",
2851          tape.game_version);
2852   printf("          tape.engine_version == %06d\n",
2853          tape.engine_version);
2854   printf("       => game.engine_version == %06d [tape mode: %s]\n",
2855          game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2856 #endif
2857
2858   // --------------------------------------------------------------------------
2859   // set flags for bugs and changes according to active game engine version
2860   // --------------------------------------------------------------------------
2861
2862   /*
2863     Summary of bugfix:
2864     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2865
2866     Bug was introduced in version:
2867     2.0.1
2868
2869     Bug was fixed in version:
2870     4.2.0.0
2871
2872     Description:
2873     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2874     but the property "can fall" was missing, which caused some levels to be
2875     unsolvable. This was fixed in version 4.2.0.0.
2876
2877     Affected levels/tapes:
2878     An example for a tape that was fixed by this bugfix is tape 029 from the
2879     level set "rnd_sam_bateman".
2880     The wrong behaviour will still be used for all levels or tapes that were
2881     created/recorded with it. An example for this is tape 023 from the level
2882     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2883   */
2884
2885   boolean use_amoeba_dropping_cannot_fall_bug =
2886     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2887       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2888      (tape.playing &&
2889       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2890       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2891
2892   /*
2893     Summary of bugfix/change:
2894     Fixed move speed of elements entering or leaving magic wall.
2895
2896     Fixed/changed in version:
2897     2.0.1
2898
2899     Description:
2900     Before 2.0.1, move speed of elements entering or leaving magic wall was
2901     twice as fast as it is now.
2902     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2903
2904     Affected levels/tapes:
2905     The first condition is generally needed for all levels/tapes before version
2906     2.0.1, which might use the old behaviour before it was changed; known tapes
2907     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2908     The second condition is an exception from the above case and is needed for
2909     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2910     above, but before it was known that this change would break tapes like the
2911     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2912     although the engine version while recording maybe was before 2.0.1. There
2913     are a lot of tapes that are affected by this exception, like tape 006 from
2914     the level set "rnd_conor_mancone".
2915   */
2916
2917   boolean use_old_move_stepsize_for_magic_wall =
2918     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2919      !(tape.playing &&
2920        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2921        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2922
2923   /*
2924     Summary of bugfix/change:
2925     Fixed handling for custom elements that change when pushed by the player.
2926
2927     Fixed/changed in version:
2928     3.1.0
2929
2930     Description:
2931     Before 3.1.0, custom elements that "change when pushing" changed directly
2932     after the player started pushing them (until then handled in "DigField()").
2933     Since 3.1.0, these custom elements are not changed until the "pushing"
2934     move of the element is finished (now handled in "ContinueMoving()").
2935
2936     Affected levels/tapes:
2937     The first condition is generally needed for all levels/tapes before version
2938     3.1.0, which might use the old behaviour before it was changed; known tapes
2939     that are affected are some tapes from the level set "Walpurgis Gardens" by
2940     Jamie Cullen.
2941     The second condition is an exception from the above case and is needed for
2942     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2943     above (including some development versions of 3.1.0), but before it was
2944     known that this change would break tapes like the above and was fixed in
2945     3.1.1, so that the changed behaviour was active although the engine version
2946     while recording maybe was before 3.1.0. There is at least one tape that is
2947     affected by this exception, which is the tape for the one-level set "Bug
2948     Machine" by Juergen Bonhagen.
2949   */
2950
2951   game.use_change_when_pushing_bug =
2952     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2953      !(tape.playing &&
2954        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2955        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2956
2957   /*
2958     Summary of bugfix/change:
2959     Fixed handling for blocking the field the player leaves when moving.
2960
2961     Fixed/changed in version:
2962     3.1.1
2963
2964     Description:
2965     Before 3.1.1, when "block last field when moving" was enabled, the field
2966     the player is leaving when moving was blocked for the time of the move,
2967     and was directly unblocked afterwards. This resulted in the last field
2968     being blocked for exactly one less than the number of frames of one player
2969     move. Additionally, even when blocking was disabled, the last field was
2970     blocked for exactly one frame.
2971     Since 3.1.1, due to changes in player movement handling, the last field
2972     is not blocked at all when blocking is disabled. When blocking is enabled,
2973     the last field is blocked for exactly the number of frames of one player
2974     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2975     last field is blocked for exactly one more than the number of frames of
2976     one player move.
2977
2978     Affected levels/tapes:
2979     (!!! yet to be determined -- probably many !!!)
2980   */
2981
2982   game.use_block_last_field_bug =
2983     (game.engine_version < VERSION_IDENT(3,1,1,0));
2984
2985   /* various special flags and settings for native Emerald Mine game engine */
2986
2987   game_em.use_single_button =
2988     (game.engine_version > VERSION_IDENT(4,0,0,2));
2989
2990   game_em.use_snap_key_bug =
2991     (game.engine_version < VERSION_IDENT(4,0,1,0));
2992
2993   game_em.use_random_bug =
2994     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
2995
2996   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
2997
2998   game_em.use_old_explosions            = use_old_em_engine;
2999   game_em.use_old_android               = use_old_em_engine;
3000   game_em.use_old_push_elements         = use_old_em_engine;
3001   game_em.use_old_push_into_acid        = use_old_em_engine;
3002
3003   game_em.use_wrap_around               = !use_old_em_engine;
3004
3005   // --------------------------------------------------------------------------
3006
3007   // set maximal allowed number of custom element changes per game frame
3008   game.max_num_changes_per_frame = 1;
3009
3010   // default scan direction: scan playfield from top/left to bottom/right
3011   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3012
3013   // dynamically adjust element properties according to game engine version
3014   InitElementPropertiesEngine(game.engine_version);
3015
3016   // ---------- initialize special element properties -------------------------
3017
3018   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3019   if (use_amoeba_dropping_cannot_fall_bug)
3020     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3021
3022   // ---------- initialize player's initial move delay ------------------------
3023
3024   // dynamically adjust player properties according to level information
3025   for (i = 0; i < MAX_PLAYERS; i++)
3026     game.initial_move_delay_value[i] =
3027       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3028
3029   // dynamically adjust player properties according to game engine version
3030   for (i = 0; i < MAX_PLAYERS; i++)
3031     game.initial_move_delay[i] =
3032       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3033        game.initial_move_delay_value[i] : 0);
3034
3035   // ---------- initialize player's initial push delay ------------------------
3036
3037   // dynamically adjust player properties according to game engine version
3038   game.initial_push_delay_value =
3039     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3040
3041   // ---------- initialize changing elements ----------------------------------
3042
3043   // initialize changing elements information
3044   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3045   {
3046     struct ElementInfo *ei = &element_info[i];
3047
3048     // this pointer might have been changed in the level editor
3049     ei->change = &ei->change_page[0];
3050
3051     if (!IS_CUSTOM_ELEMENT(i))
3052     {
3053       ei->change->target_element = EL_EMPTY_SPACE;
3054       ei->change->delay_fixed = 0;
3055       ei->change->delay_random = 0;
3056       ei->change->delay_frames = 1;
3057     }
3058
3059     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3060     {
3061       ei->has_change_event[j] = FALSE;
3062
3063       ei->event_page_nr[j] = 0;
3064       ei->event_page[j] = &ei->change_page[0];
3065     }
3066   }
3067
3068   // add changing elements from pre-defined list
3069   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3070   {
3071     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3072     struct ElementInfo *ei = &element_info[ch_delay->element];
3073
3074     ei->change->target_element       = ch_delay->target_element;
3075     ei->change->delay_fixed          = ch_delay->change_delay;
3076
3077     ei->change->pre_change_function  = ch_delay->pre_change_function;
3078     ei->change->change_function      = ch_delay->change_function;
3079     ei->change->post_change_function = ch_delay->post_change_function;
3080
3081     ei->change->can_change = TRUE;
3082     ei->change->can_change_or_has_action = TRUE;
3083
3084     ei->has_change_event[CE_DELAY] = TRUE;
3085
3086     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3087     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3088   }
3089
3090   // ---------- initialize internal run-time variables ------------------------
3091
3092   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3093   {
3094     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3095
3096     for (j = 0; j < ei->num_change_pages; j++)
3097     {
3098       ei->change_page[j].can_change_or_has_action =
3099         (ei->change_page[j].can_change |
3100          ei->change_page[j].has_action);
3101     }
3102   }
3103
3104   // add change events from custom element configuration
3105   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3106   {
3107     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3108
3109     for (j = 0; j < ei->num_change_pages; j++)
3110     {
3111       if (!ei->change_page[j].can_change_or_has_action)
3112         continue;
3113
3114       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3115       {
3116         // only add event page for the first page found with this event
3117         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3118         {
3119           ei->has_change_event[k] = TRUE;
3120
3121           ei->event_page_nr[k] = j;
3122           ei->event_page[k] = &ei->change_page[j];
3123         }
3124       }
3125     }
3126   }
3127
3128   // ---------- initialize reference elements in change conditions ------------
3129
3130   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3131   {
3132     int element = EL_CUSTOM_START + i;
3133     struct ElementInfo *ei = &element_info[element];
3134
3135     for (j = 0; j < ei->num_change_pages; j++)
3136     {
3137       int trigger_element = ei->change_page[j].initial_trigger_element;
3138
3139       if (trigger_element >= EL_PREV_CE_8 &&
3140           trigger_element <= EL_NEXT_CE_8)
3141         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3142
3143       ei->change_page[j].trigger_element = trigger_element;
3144     }
3145   }
3146
3147   // ---------- initialize run-time trigger player and element ----------------
3148
3149   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3150   {
3151     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3152
3153     for (j = 0; j < ei->num_change_pages; j++)
3154     {
3155       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3156       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3157       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3158       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3159       ei->change_page[j].actual_trigger_ce_value = 0;
3160       ei->change_page[j].actual_trigger_ce_score = 0;
3161     }
3162   }
3163
3164   // ---------- initialize trigger events -------------------------------------
3165
3166   // initialize trigger events information
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3169       trigger_events[i][j] = FALSE;
3170
3171   // add trigger events from element change event properties
3172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[i];
3175
3176     for (j = 0; j < ei->num_change_pages; j++)
3177     {
3178       if (!ei->change_page[j].can_change_or_has_action)
3179         continue;
3180
3181       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3182       {
3183         int trigger_element = ei->change_page[j].trigger_element;
3184
3185         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3186         {
3187           if (ei->change_page[j].has_event[k])
3188           {
3189             if (IS_GROUP_ELEMENT(trigger_element))
3190             {
3191               struct ElementGroupInfo *group =
3192                 element_info[trigger_element].group;
3193
3194               for (l = 0; l < group->num_elements_resolved; l++)
3195                 trigger_events[group->element_resolved[l]][k] = TRUE;
3196             }
3197             else if (trigger_element == EL_ANY_ELEMENT)
3198               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3199                 trigger_events[l][k] = TRUE;
3200             else
3201               trigger_events[trigger_element][k] = TRUE;
3202           }
3203         }
3204       }
3205     }
3206   }
3207
3208   // ---------- initialize push delay -----------------------------------------
3209
3210   // initialize push delay values to default
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (!IS_CUSTOM_ELEMENT(i))
3214     {
3215       // set default push delay values (corrected since version 3.0.7-1)
3216       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3217       {
3218         element_info[i].push_delay_fixed = 2;
3219         element_info[i].push_delay_random = 8;
3220       }
3221       else
3222       {
3223         element_info[i].push_delay_fixed = 8;
3224         element_info[i].push_delay_random = 8;
3225       }
3226     }
3227   }
3228
3229   // set push delay value for certain elements from pre-defined list
3230   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3231   {
3232     int e = push_delay_list[i].element;
3233
3234     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3235     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3236   }
3237
3238   // set push delay value for Supaplex elements for newer engine versions
3239   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3240   {
3241     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3242     {
3243       if (IS_SP_ELEMENT(i))
3244       {
3245         // set SP push delay to just enough to push under a falling zonk
3246         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3247
3248         element_info[i].push_delay_fixed  = delay;
3249         element_info[i].push_delay_random = 0;
3250       }
3251     }
3252   }
3253
3254   // ---------- initialize move stepsize --------------------------------------
3255
3256   // initialize move stepsize values to default
3257   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3258     if (!IS_CUSTOM_ELEMENT(i))
3259       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3260
3261   // set move stepsize value for certain elements from pre-defined list
3262   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3263   {
3264     int e = move_stepsize_list[i].element;
3265
3266     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3267
3268     // set move stepsize value for certain elements for older engine versions
3269     if (use_old_move_stepsize_for_magic_wall)
3270     {
3271       if (e == EL_MAGIC_WALL_FILLING ||
3272           e == EL_MAGIC_WALL_EMPTYING ||
3273           e == EL_BD_MAGIC_WALL_FILLING ||
3274           e == EL_BD_MAGIC_WALL_EMPTYING)
3275         element_info[e].move_stepsize *= 2;
3276     }
3277   }
3278
3279   // ---------- initialize collect score --------------------------------------
3280
3281   // initialize collect score values for custom elements from initial value
3282   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3283     if (IS_CUSTOM_ELEMENT(i))
3284       element_info[i].collect_score = element_info[i].collect_score_initial;
3285
3286   // ---------- initialize collect count --------------------------------------
3287
3288   // initialize collect count values for non-custom elements
3289   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3290     if (!IS_CUSTOM_ELEMENT(i))
3291       element_info[i].collect_count_initial = 0;
3292
3293   // add collect count values for all elements from pre-defined list
3294   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3295     element_info[collect_count_list[i].element].collect_count_initial =
3296       collect_count_list[i].count;
3297
3298   // ---------- initialize access direction -----------------------------------
3299
3300   // initialize access direction values to default (access from every side)
3301   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3302     if (!IS_CUSTOM_ELEMENT(i))
3303       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3304
3305   // set access direction value for certain elements from pre-defined list
3306   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3307     element_info[access_direction_list[i].element].access_direction =
3308       access_direction_list[i].direction;
3309
3310   // ---------- initialize explosion content ----------------------------------
3311   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3312   {
3313     if (IS_CUSTOM_ELEMENT(i))
3314       continue;
3315
3316     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3317     {
3318       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3319
3320       element_info[i].content.e[x][y] =
3321         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3322          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3323          i == EL_PLAYER_3 ? EL_EMERALD :
3324          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3325          i == EL_MOLE ? EL_EMERALD_RED :
3326          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3327          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3328          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3329          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3330          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3331          i == EL_WALL_EMERALD ? EL_EMERALD :
3332          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3333          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3334          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3335          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3336          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3337          i == EL_WALL_PEARL ? EL_PEARL :
3338          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3339          EL_EMPTY);
3340     }
3341   }
3342
3343   // ---------- initialize recursion detection --------------------------------
3344   recursion_loop_depth = 0;
3345   recursion_loop_detected = FALSE;
3346   recursion_loop_element = EL_UNDEFINED;
3347
3348   // ---------- initialize graphics engine ------------------------------------
3349   game.scroll_delay_value =
3350     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3351      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3352      !setup.forced_scroll_delay           ? 0 :
3353      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3354   game.scroll_delay_value =
3355     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3356
3357   // ---------- initialize game engine snapshots ------------------------------
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359     game.snapshot.last_action[i] = 0;
3360   game.snapshot.changed_action = FALSE;
3361   game.snapshot.collected_item = FALSE;
3362   game.snapshot.mode =
3363     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3364      SNAPSHOT_MODE_EVERY_STEP :
3365      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3366      SNAPSHOT_MODE_EVERY_MOVE :
3367      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3368      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3369   game.snapshot.save_snapshot = FALSE;
3370
3371   // ---------- initialize level time for Supaplex engine ---------------------
3372   // Supaplex levels with time limit currently unsupported -- should be added
3373   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3374     level.time = 0;
3375
3376   // ---------- initialize flags for handling game actions --------------------
3377
3378   // set flags for game actions to default values
3379   game.use_key_actions = TRUE;
3380   game.use_mouse_actions = FALSE;
3381
3382   // when using Mirror Magic game engine, handle mouse events only
3383   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3384   {
3385     game.use_key_actions = FALSE;
3386     game.use_mouse_actions = TRUE;
3387   }
3388
3389   // check for custom elements with mouse click events
3390   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3391   {
3392     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393     {
3394       int element = EL_CUSTOM_START + i;
3395
3396       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3397           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3398           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3399           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3400         game.use_mouse_actions = TRUE;
3401     }
3402   }
3403 }
3404
3405 static int get_num_special_action(int element, int action_first,
3406                                   int action_last)
3407 {
3408   int num_special_action = 0;
3409   int i, j;
3410
3411   for (i = action_first; i <= action_last; i++)
3412   {
3413     boolean found = FALSE;
3414
3415     for (j = 0; j < NUM_DIRECTIONS; j++)
3416       if (el_act_dir2img(element, i, j) !=
3417           el_act_dir2img(element, ACTION_DEFAULT, j))
3418         found = TRUE;
3419
3420     if (found)
3421       num_special_action++;
3422     else
3423       break;
3424   }
3425
3426   return num_special_action;
3427 }
3428
3429
3430 // ============================================================================
3431 // InitGame()
3432 // ----------------------------------------------------------------------------
3433 // initialize and start new game
3434 // ============================================================================
3435
3436 #if DEBUG_INIT_PLAYER
3437 static void DebugPrintPlayerStatus(char *message)
3438 {
3439   int i;
3440
3441   if (!options.debug)
3442     return;
3443
3444   printf("%s:\n", message);
3445
3446   for (i = 0; i < MAX_PLAYERS; i++)
3447   {
3448     struct PlayerInfo *player = &stored_player[i];
3449
3450     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3451            i + 1,
3452            player->present,
3453            player->connected,
3454            player->connected_locally,
3455            player->connected_network,
3456            player->active);
3457
3458     if (local_player == player)
3459       printf(" (local player)");
3460
3461     printf("\n");
3462   }
3463 }
3464 #endif
3465
3466 void InitGame(void)
3467 {
3468   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3469   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3470   int fade_mask = REDRAW_FIELD;
3471
3472   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3473   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3474   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3475   int initial_move_dir = MV_DOWN;
3476   int i, j, x, y;
3477
3478   // required here to update video display before fading (FIX THIS)
3479   DrawMaskedBorder(REDRAW_DOOR_2);
3480
3481   if (!game.restart_level)
3482     CloseDoor(DOOR_CLOSE_1);
3483
3484   SetGameStatus(GAME_MODE_PLAYING);
3485
3486   if (level_editor_test_game)
3487     FadeSkipNextFadeOut();
3488   else
3489     FadeSetEnterScreen();
3490
3491   if (CheckFadeAll())
3492     fade_mask = REDRAW_ALL;
3493
3494   FadeLevelSoundsAndMusic();
3495
3496   ExpireSoundLoops(TRUE);
3497
3498   FadeOut(fade_mask);
3499
3500   if (level_editor_test_game)
3501     FadeSkipNextFadeIn();
3502
3503   // needed if different viewport properties defined for playing
3504   ChangeViewportPropertiesIfNeeded();
3505
3506   ClearField();
3507
3508   DrawCompleteVideoDisplay();
3509
3510   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3511
3512   InitGameEngine();
3513   InitGameControlValues();
3514
3515   // initialize tape actions from game when recording tape
3516   if (tape.recording)
3517   {
3518     tape.use_key_actions   = game.use_key_actions;
3519     tape.use_mouse_actions = game.use_mouse_actions;
3520   }
3521
3522   // don't play tapes over network
3523   network_playing = (network.enabled && !tape.playing);
3524
3525   for (i = 0; i < MAX_PLAYERS; i++)
3526   {
3527     struct PlayerInfo *player = &stored_player[i];
3528
3529     player->index_nr = i;
3530     player->index_bit = (1 << i);
3531     player->element_nr = EL_PLAYER_1 + i;
3532
3533     player->present = FALSE;
3534     player->active = FALSE;
3535     player->mapped = FALSE;
3536
3537     player->killed = FALSE;
3538     player->reanimated = FALSE;
3539     player->buried = FALSE;
3540
3541     player->action = 0;
3542     player->effective_action = 0;
3543     player->programmed_action = 0;
3544     player->snap_action = 0;
3545
3546     player->mouse_action.lx = 0;
3547     player->mouse_action.ly = 0;
3548     player->mouse_action.button = 0;
3549     player->mouse_action.button_hint = 0;
3550
3551     player->effective_mouse_action.lx = 0;
3552     player->effective_mouse_action.ly = 0;
3553     player->effective_mouse_action.button = 0;
3554     player->effective_mouse_action.button_hint = 0;
3555
3556     for (j = 0; j < MAX_NUM_KEYS; j++)
3557       player->key[j] = FALSE;
3558
3559     player->num_white_keys = 0;
3560
3561     player->dynabomb_count = 0;
3562     player->dynabomb_size = 1;
3563     player->dynabombs_left = 0;
3564     player->dynabomb_xl = FALSE;
3565
3566     player->MovDir = initial_move_dir;
3567     player->MovPos = 0;
3568     player->GfxPos = 0;
3569     player->GfxDir = initial_move_dir;
3570     player->GfxAction = ACTION_DEFAULT;
3571     player->Frame = 0;
3572     player->StepFrame = 0;
3573
3574     player->initial_element = player->element_nr;
3575     player->artwork_element =
3576       (level.use_artwork_element[i] ? level.artwork_element[i] :
3577        player->element_nr);
3578     player->use_murphy = FALSE;
3579
3580     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3581     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3582
3583     player->gravity = level.initial_player_gravity[i];
3584
3585     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3586
3587     player->actual_frame_counter = 0;
3588
3589     player->step_counter = 0;
3590
3591     player->last_move_dir = initial_move_dir;
3592
3593     player->is_active = FALSE;
3594
3595     player->is_waiting = FALSE;
3596     player->is_moving = FALSE;
3597     player->is_auto_moving = FALSE;
3598     player->is_digging = FALSE;
3599     player->is_snapping = FALSE;
3600     player->is_collecting = FALSE;
3601     player->is_pushing = FALSE;
3602     player->is_switching = FALSE;
3603     player->is_dropping = FALSE;
3604     player->is_dropping_pressed = FALSE;
3605
3606     player->is_bored = FALSE;
3607     player->is_sleeping = FALSE;
3608
3609     player->was_waiting = TRUE;
3610     player->was_moving = FALSE;
3611     player->was_snapping = FALSE;
3612     player->was_dropping = FALSE;
3613
3614     player->force_dropping = FALSE;
3615
3616     player->frame_counter_bored = -1;
3617     player->frame_counter_sleeping = -1;
3618
3619     player->anim_delay_counter = 0;
3620     player->post_delay_counter = 0;
3621
3622     player->dir_waiting = initial_move_dir;
3623     player->action_waiting = ACTION_DEFAULT;
3624     player->last_action_waiting = ACTION_DEFAULT;
3625     player->special_action_bored = ACTION_DEFAULT;
3626     player->special_action_sleeping = ACTION_DEFAULT;
3627
3628     player->switch_x = -1;
3629     player->switch_y = -1;
3630
3631     player->drop_x = -1;
3632     player->drop_y = -1;
3633
3634     player->show_envelope = 0;
3635
3636     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3637
3638     player->push_delay       = -1;      // initialized when pushing starts
3639     player->push_delay_value = game.initial_push_delay_value;
3640
3641     player->drop_delay = 0;
3642     player->drop_pressed_delay = 0;
3643
3644     player->last_jx = -1;
3645     player->last_jy = -1;
3646     player->jx = -1;
3647     player->jy = -1;
3648
3649     player->shield_normal_time_left = 0;
3650     player->shield_deadly_time_left = 0;
3651
3652     player->inventory_infinite_element = EL_UNDEFINED;
3653     player->inventory_size = 0;
3654
3655     if (level.use_initial_inventory[i])
3656     {
3657       for (j = 0; j < level.initial_inventory_size[i]; j++)
3658       {
3659         int element = level.initial_inventory_content[i][j];
3660         int collect_count = element_info[element].collect_count_initial;
3661         int k;
3662
3663         if (!IS_CUSTOM_ELEMENT(element))
3664           collect_count = 1;
3665
3666         if (collect_count == 0)
3667           player->inventory_infinite_element = element;
3668         else
3669           for (k = 0; k < collect_count; k++)
3670             if (player->inventory_size < MAX_INVENTORY_SIZE)
3671               player->inventory_element[player->inventory_size++] = element;
3672       }
3673     }
3674
3675     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3676     SnapField(player, 0, 0);
3677
3678     map_player_action[i] = i;
3679   }
3680
3681   network_player_action_received = FALSE;
3682
3683   // initial null action
3684   if (network_playing)
3685     SendToServer_MovePlayer(MV_NONE);
3686
3687   FrameCounter = 0;
3688   TimeFrames = 0;
3689   TimePlayed = 0;
3690   TimeLeft = level.time;
3691   TapeTime = 0;
3692
3693   ScreenMovDir = MV_NONE;
3694   ScreenMovPos = 0;
3695   ScreenGfxPos = 0;
3696
3697   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3698
3699   game.robot_wheel_x = -1;
3700   game.robot_wheel_y = -1;
3701
3702   game.exit_x = -1;
3703   game.exit_y = -1;
3704
3705   game.all_players_gone = FALSE;
3706
3707   game.LevelSolved = FALSE;
3708   game.GameOver = FALSE;
3709
3710   game.GamePlayed = !tape.playing;
3711
3712   game.LevelSolved_GameWon = FALSE;
3713   game.LevelSolved_GameEnd = FALSE;
3714   game.LevelSolved_SaveTape = FALSE;
3715   game.LevelSolved_SaveScore = FALSE;
3716
3717   game.LevelSolved_CountingTime = 0;
3718   game.LevelSolved_CountingScore = 0;
3719   game.LevelSolved_CountingHealth = 0;
3720
3721   game.panel.active = TRUE;
3722
3723   game.no_time_limit = (level.time == 0);
3724
3725   game.yamyam_content_nr = 0;
3726   game.robot_wheel_active = FALSE;
3727   game.magic_wall_active = FALSE;
3728   game.magic_wall_time_left = 0;
3729   game.light_time_left = 0;
3730   game.timegate_time_left = 0;
3731   game.switchgate_pos = 0;
3732   game.wind_direction = level.wind_direction_initial;
3733
3734   game.score = 0;
3735   game.score_final = 0;
3736
3737   game.health = MAX_HEALTH;
3738   game.health_final = MAX_HEALTH;
3739
3740   game.gems_still_needed = level.gems_needed;
3741   game.sokoban_fields_still_needed = 0;
3742   game.sokoban_objects_still_needed = 0;
3743   game.lights_still_needed = 0;
3744   game.players_still_needed = 0;
3745   game.friends_still_needed = 0;
3746
3747   game.lenses_time_left = 0;
3748   game.magnify_time_left = 0;
3749
3750   game.ball_active = level.ball_active_initial;
3751   game.ball_content_nr = 0;
3752
3753   game.explosions_delayed = TRUE;
3754
3755   game.envelope_active = FALSE;
3756
3757   for (i = 0; i < NUM_BELTS; i++)
3758   {
3759     game.belt_dir[i] = MV_NONE;
3760     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3761   }
3762
3763   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3764     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3765
3766 #if DEBUG_INIT_PLAYER
3767   DebugPrintPlayerStatus("Player status at level initialization");
3768 #endif
3769
3770   SCAN_PLAYFIELD(x, y)
3771   {
3772     Feld[x][y] = Last[x][y] = level.field[x][y];
3773     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3774     ChangeDelay[x][y] = 0;
3775     ChangePage[x][y] = -1;
3776     CustomValue[x][y] = 0;              // initialized in InitField()
3777     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3778     AmoebaNr[x][y] = 0;
3779     WasJustMoving[x][y] = 0;
3780     WasJustFalling[x][y] = 0;
3781     CheckCollision[x][y] = 0;
3782     CheckImpact[x][y] = 0;
3783     Stop[x][y] = FALSE;
3784     Pushed[x][y] = FALSE;
3785
3786     ChangeCount[x][y] = 0;
3787     ChangeEvent[x][y] = -1;
3788
3789     ExplodePhase[x][y] = 0;
3790     ExplodeDelay[x][y] = 0;
3791     ExplodeField[x][y] = EX_TYPE_NONE;
3792
3793     RunnerVisit[x][y] = 0;
3794     PlayerVisit[x][y] = 0;
3795
3796     GfxFrame[x][y] = 0;
3797     GfxRandom[x][y] = INIT_GFX_RANDOM();
3798     GfxElement[x][y] = EL_UNDEFINED;
3799     GfxAction[x][y] = ACTION_DEFAULT;
3800     GfxDir[x][y] = MV_NONE;
3801     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3802   }
3803
3804   SCAN_PLAYFIELD(x, y)
3805   {
3806     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3807       emulate_bd = FALSE;
3808     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3809       emulate_sb = FALSE;
3810     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3811       emulate_sp = FALSE;
3812
3813     InitField(x, y, TRUE);
3814
3815     ResetGfxAnimation(x, y);
3816   }
3817
3818   InitBeltMovement();
3819
3820   for (i = 0; i < MAX_PLAYERS; i++)
3821   {
3822     struct PlayerInfo *player = &stored_player[i];
3823
3824     // set number of special actions for bored and sleeping animation
3825     player->num_special_action_bored =
3826       get_num_special_action(player->artwork_element,
3827                              ACTION_BORING_1, ACTION_BORING_LAST);
3828     player->num_special_action_sleeping =
3829       get_num_special_action(player->artwork_element,
3830                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3831   }
3832
3833   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3834                     emulate_sb ? EMU_SOKOBAN :
3835                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3836
3837   // initialize type of slippery elements
3838   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3839   {
3840     if (!IS_CUSTOM_ELEMENT(i))
3841     {
3842       // default: elements slip down either to the left or right randomly
3843       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3844
3845       // SP style elements prefer to slip down on the left side
3846       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3847         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3848
3849       // BD style elements prefer to slip down on the left side
3850       if (game.emulation == EMU_BOULDERDASH)
3851         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3852     }
3853   }
3854
3855   // initialize explosion and ignition delay
3856   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3857   {
3858     if (!IS_CUSTOM_ELEMENT(i))
3859     {
3860       int num_phase = 8;
3861       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3862                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3863                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3864       int last_phase = (num_phase + 1) * delay;
3865       int half_phase = (num_phase / 2) * delay;
3866
3867       element_info[i].explosion_delay = last_phase - 1;
3868       element_info[i].ignition_delay = half_phase;
3869
3870       if (i == EL_BLACK_ORB)
3871         element_info[i].ignition_delay = 1;
3872     }
3873   }
3874
3875   // correct non-moving belts to start moving left
3876   for (i = 0; i < NUM_BELTS; i++)
3877     if (game.belt_dir[i] == MV_NONE)
3878       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3879
3880 #if USE_NEW_PLAYER_ASSIGNMENTS
3881   // use preferred player also in local single-player mode
3882   if (!network.enabled && !game.team_mode)
3883   {
3884     int new_index_nr = setup.network_player_nr;
3885
3886     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3887     {
3888       for (i = 0; i < MAX_PLAYERS; i++)
3889         stored_player[i].connected_locally = FALSE;
3890
3891       stored_player[new_index_nr].connected_locally = TRUE;
3892     }
3893   }
3894
3895   for (i = 0; i < MAX_PLAYERS; i++)
3896   {
3897     stored_player[i].connected = FALSE;
3898
3899     // in network game mode, the local player might not be the first player
3900     if (stored_player[i].connected_locally)
3901       local_player = &stored_player[i];
3902   }
3903
3904   if (!network.enabled)
3905     local_player->connected = TRUE;
3906
3907   if (tape.playing)
3908   {
3909     for (i = 0; i < MAX_PLAYERS; i++)
3910       stored_player[i].connected = tape.player_participates[i];
3911   }
3912   else if (network.enabled)
3913   {
3914     // add team mode players connected over the network (needed for correct
3915     // assignment of player figures from level to locally playing players)
3916
3917     for (i = 0; i < MAX_PLAYERS; i++)
3918       if (stored_player[i].connected_network)
3919         stored_player[i].connected = TRUE;
3920   }
3921   else if (game.team_mode)
3922   {
3923     // try to guess locally connected team mode players (needed for correct
3924     // assignment of player figures from level to locally playing players)
3925
3926     for (i = 0; i < MAX_PLAYERS; i++)
3927       if (setup.input[i].use_joystick ||
3928           setup.input[i].key.left != KSYM_UNDEFINED)
3929         stored_player[i].connected = TRUE;
3930   }
3931
3932 #if DEBUG_INIT_PLAYER
3933   DebugPrintPlayerStatus("Player status after level initialization");
3934 #endif
3935
3936 #if DEBUG_INIT_PLAYER
3937   if (options.debug)
3938     printf("Reassigning players ...\n");
3939 #endif
3940
3941   // check if any connected player was not found in playfield
3942   for (i = 0; i < MAX_PLAYERS; i++)
3943   {
3944     struct PlayerInfo *player = &stored_player[i];
3945
3946     if (player->connected && !player->present)
3947     {
3948       struct PlayerInfo *field_player = NULL;
3949
3950 #if DEBUG_INIT_PLAYER
3951       if (options.debug)
3952         printf("- looking for field player for player %d ...\n", i + 1);
3953 #endif
3954
3955       // assign first free player found that is present in the playfield
3956
3957       // first try: look for unmapped playfield player that is not connected
3958       for (j = 0; j < MAX_PLAYERS; j++)
3959         if (field_player == NULL &&
3960             stored_player[j].present &&
3961             !stored_player[j].mapped &&
3962             !stored_player[j].connected)
3963           field_player = &stored_player[j];
3964
3965       // second try: look for *any* unmapped playfield player
3966       for (j = 0; j < MAX_PLAYERS; j++)
3967         if (field_player == NULL &&
3968             stored_player[j].present &&
3969             !stored_player[j].mapped)
3970           field_player = &stored_player[j];
3971
3972       if (field_player != NULL)
3973       {
3974         int jx = field_player->jx, jy = field_player->jy;
3975
3976 #if DEBUG_INIT_PLAYER
3977         if (options.debug)
3978           printf("- found player %d\n", field_player->index_nr + 1);
3979 #endif
3980
3981         player->present = FALSE;
3982         player->active = FALSE;
3983
3984         field_player->present = TRUE;
3985         field_player->active = TRUE;
3986
3987         /*
3988         player->initial_element = field_player->initial_element;
3989         player->artwork_element = field_player->artwork_element;
3990
3991         player->block_last_field       = field_player->block_last_field;
3992         player->block_delay_adjustment = field_player->block_delay_adjustment;
3993         */
3994
3995         StorePlayer[jx][jy] = field_player->element_nr;
3996
3997         field_player->jx = field_player->last_jx = jx;
3998         field_player->jy = field_player->last_jy = jy;
3999
4000         if (local_player == player)
4001           local_player = field_player;
4002
4003         map_player_action[field_player->index_nr] = i;
4004
4005         field_player->mapped = TRUE;
4006
4007 #if DEBUG_INIT_PLAYER
4008         if (options.debug)
4009           printf("- map_player_action[%d] == %d\n",
4010                  field_player->index_nr + 1, i + 1);
4011 #endif
4012       }
4013     }
4014
4015     if (player->connected && player->present)
4016       player->mapped = TRUE;
4017   }
4018
4019 #if DEBUG_INIT_PLAYER
4020   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4021 #endif
4022
4023 #else
4024
4025   // check if any connected player was not found in playfield
4026   for (i = 0; i < MAX_PLAYERS; i++)
4027   {
4028     struct PlayerInfo *player = &stored_player[i];
4029
4030     if (player->connected && !player->present)
4031     {
4032       for (j = 0; j < MAX_PLAYERS; j++)
4033       {
4034         struct PlayerInfo *field_player = &stored_player[j];
4035         int jx = field_player->jx, jy = field_player->jy;
4036
4037         // assign first free player found that is present in the playfield
4038         if (field_player->present && !field_player->connected)
4039         {
4040           player->present = TRUE;
4041           player->active = TRUE;
4042
4043           field_player->present = FALSE;
4044           field_player->active = FALSE;
4045
4046           player->initial_element = field_player->initial_element;
4047           player->artwork_element = field_player->artwork_element;
4048
4049           player->block_last_field       = field_player->block_last_field;
4050           player->block_delay_adjustment = field_player->block_delay_adjustment;
4051
4052           StorePlayer[jx][jy] = player->element_nr;
4053
4054           player->jx = player->last_jx = jx;
4055           player->jy = player->last_jy = jy;
4056
4057           break;
4058         }
4059       }
4060     }
4061   }
4062 #endif
4063
4064 #if 0
4065   printf("::: local_player->present == %d\n", local_player->present);
4066 #endif
4067
4068   // set focus to local player for network games, else to all players
4069   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4070   game.centered_player_nr_next = game.centered_player_nr;
4071   game.set_centered_player = FALSE;
4072   game.set_centered_player_wrap = FALSE;
4073
4074   if (network_playing && tape.recording)
4075   {
4076     // store client dependent player focus when recording network games
4077     tape.centered_player_nr_next = game.centered_player_nr_next;
4078     tape.set_centered_player = TRUE;
4079   }
4080
4081   if (tape.playing)
4082   {
4083     // when playing a tape, eliminate all players who do not participate
4084
4085 #if USE_NEW_PLAYER_ASSIGNMENTS
4086
4087     if (!game.team_mode)
4088     {
4089       for (i = 0; i < MAX_PLAYERS; i++)
4090       {
4091         if (stored_player[i].active &&
4092             !tape.player_participates[map_player_action[i]])
4093         {
4094           struct PlayerInfo *player = &stored_player[i];
4095           int jx = player->jx, jy = player->jy;
4096
4097 #if DEBUG_INIT_PLAYER
4098           if (options.debug)
4099             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4100 #endif
4101
4102           player->active = FALSE;
4103           StorePlayer[jx][jy] = 0;
4104           Feld[jx][jy] = EL_EMPTY;
4105         }
4106       }
4107     }
4108
4109 #else
4110
4111     for (i = 0; i < MAX_PLAYERS; i++)
4112     {
4113       if (stored_player[i].active &&
4114           !tape.player_participates[i])
4115       {
4116         struct PlayerInfo *player = &stored_player[i];
4117         int jx = player->jx, jy = player->jy;
4118
4119         player->active = FALSE;
4120         StorePlayer[jx][jy] = 0;
4121         Feld[jx][jy] = EL_EMPTY;
4122       }
4123     }
4124 #endif
4125   }
4126   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4127   {
4128     // when in single player mode, eliminate all but the local player
4129
4130     for (i = 0; i < MAX_PLAYERS; i++)
4131     {
4132       struct PlayerInfo *player = &stored_player[i];
4133
4134       if (player->active && player != local_player)
4135       {
4136         int jx = player->jx, jy = player->jy;
4137
4138         player->active = FALSE;
4139         player->present = FALSE;
4140
4141         StorePlayer[jx][jy] = 0;
4142         Feld[jx][jy] = EL_EMPTY;
4143       }
4144     }
4145   }
4146
4147   for (i = 0; i < MAX_PLAYERS; i++)
4148     if (stored_player[i].active)
4149       game.players_still_needed++;
4150
4151   if (level.solved_by_one_player)
4152     game.players_still_needed = 1;
4153
4154   // when recording the game, store which players take part in the game
4155   if (tape.recording)
4156   {
4157 #if USE_NEW_PLAYER_ASSIGNMENTS
4158     for (i = 0; i < MAX_PLAYERS; i++)
4159       if (stored_player[i].connected)
4160         tape.player_participates[i] = TRUE;
4161 #else
4162     for (i = 0; i < MAX_PLAYERS; i++)
4163       if (stored_player[i].active)
4164         tape.player_participates[i] = TRUE;
4165 #endif
4166   }
4167
4168 #if DEBUG_INIT_PLAYER
4169   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4170 #endif
4171
4172   if (BorderElement == EL_EMPTY)
4173   {
4174     SBX_Left = 0;
4175     SBX_Right = lev_fieldx - SCR_FIELDX;
4176     SBY_Upper = 0;
4177     SBY_Lower = lev_fieldy - SCR_FIELDY;
4178   }
4179   else
4180   {
4181     SBX_Left = -1;
4182     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4183     SBY_Upper = -1;
4184     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4185   }
4186
4187   if (full_lev_fieldx <= SCR_FIELDX)
4188     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4189   if (full_lev_fieldy <= SCR_FIELDY)
4190     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4191
4192   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4193     SBX_Left--;
4194   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4195     SBY_Upper--;
4196
4197   // if local player not found, look for custom element that might create
4198   // the player (make some assumptions about the right custom element)
4199   if (!local_player->present)
4200   {
4201     int start_x = 0, start_y = 0;
4202     int found_rating = 0;
4203     int found_element = EL_UNDEFINED;
4204     int player_nr = local_player->index_nr;
4205
4206     SCAN_PLAYFIELD(x, y)
4207     {
4208       int element = Feld[x][y];
4209       int content;
4210       int xx, yy;
4211       boolean is_player;
4212
4213       if (level.use_start_element[player_nr] &&
4214           level.start_element[player_nr] == element &&
4215           found_rating < 4)
4216       {
4217         start_x = x;
4218         start_y = y;
4219
4220         found_rating = 4;
4221         found_element = element;
4222       }
4223
4224       if (!IS_CUSTOM_ELEMENT(element))
4225         continue;
4226
4227       if (CAN_CHANGE(element))
4228       {
4229         for (i = 0; i < element_info[element].num_change_pages; i++)
4230         {
4231           // check for player created from custom element as single target
4232           content = element_info[element].change_page[i].target_element;
4233           is_player = ELEM_IS_PLAYER(content);
4234
4235           if (is_player && (found_rating < 3 ||
4236                             (found_rating == 3 && element < found_element)))
4237           {
4238             start_x = x;
4239             start_y = y;
4240
4241             found_rating = 3;
4242             found_element = element;
4243           }
4244         }
4245       }
4246
4247       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4248       {
4249         // check for player created from custom element as explosion content
4250         content = element_info[element].content.e[xx][yy];
4251         is_player = ELEM_IS_PLAYER(content);
4252
4253         if (is_player && (found_rating < 2 ||
4254                           (found_rating == 2 && element < found_element)))
4255         {
4256           start_x = x + xx - 1;
4257           start_y = y + yy - 1;
4258
4259           found_rating = 2;
4260           found_element = element;
4261         }
4262
4263         if (!CAN_CHANGE(element))
4264           continue;
4265
4266         for (i = 0; i < element_info[element].num_change_pages; i++)
4267         {
4268           // check for player created from custom element as extended target
4269           content =
4270             element_info[element].change_page[i].target_content.e[xx][yy];
4271
4272           is_player = ELEM_IS_PLAYER(content);
4273
4274           if (is_player && (found_rating < 1 ||
4275                             (found_rating == 1 && element < found_element)))
4276           {
4277             start_x = x + xx - 1;
4278             start_y = y + yy - 1;
4279
4280             found_rating = 1;
4281             found_element = element;
4282           }
4283         }
4284       }
4285     }
4286
4287     scroll_x = SCROLL_POSITION_X(start_x);
4288     scroll_y = SCROLL_POSITION_Y(start_y);
4289   }
4290   else
4291   {
4292     scroll_x = SCROLL_POSITION_X(local_player->jx);
4293     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4294   }
4295
4296   // !!! FIX THIS (START) !!!
4297   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4298   {
4299     InitGameEngine_EM();
4300   }
4301   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4302   {
4303     InitGameEngine_SP();
4304   }
4305   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4306   {
4307     InitGameEngine_MM();
4308   }
4309   else
4310   {
4311     DrawLevel(REDRAW_FIELD);
4312     DrawAllPlayers();
4313
4314     // after drawing the level, correct some elements
4315     if (game.timegate_time_left == 0)
4316       CloseAllOpenTimegates();
4317   }
4318
4319   // blit playfield from scroll buffer to normal back buffer for fading in
4320   BlitScreenToBitmap(backbuffer);
4321   // !!! FIX THIS (END) !!!
4322
4323   DrawMaskedBorder(fade_mask);
4324
4325   FadeIn(fade_mask);
4326
4327 #if 1
4328   // full screen redraw is required at this point in the following cases:
4329   // - special editor door undrawn when game was started from level editor
4330   // - drawing area (playfield) was changed and has to be removed completely
4331   redraw_mask = REDRAW_ALL;
4332   BackToFront();
4333 #endif
4334
4335   if (!game.restart_level)
4336   {
4337     // copy default game door content to main double buffer
4338
4339     // !!! CHECK AGAIN !!!
4340     SetPanelBackground();
4341     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4342     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4343   }
4344
4345   SetPanelBackground();
4346   SetDrawBackgroundMask(REDRAW_DOOR_1);
4347
4348   UpdateAndDisplayGameControlValues();
4349
4350   if (!game.restart_level)
4351   {
4352     UnmapGameButtons();
4353     UnmapTapeButtons();
4354
4355     FreeGameButtons();
4356     CreateGameButtons();
4357
4358     MapGameButtons();
4359     MapTapeButtons();
4360
4361     // copy actual game door content to door double buffer for OpenDoor()
4362     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4363
4364     OpenDoor(DOOR_OPEN_ALL);
4365
4366     KeyboardAutoRepeatOffUnlessAutoplay();
4367
4368 #if DEBUG_INIT_PLAYER
4369     DebugPrintPlayerStatus("Player status (final)");
4370 #endif
4371   }
4372
4373   UnmapAllGadgets();
4374
4375   MapGameButtons();
4376   MapTapeButtons();
4377
4378   if (!game.restart_level && !tape.playing)
4379   {
4380     LevelStats_incPlayed(level_nr);
4381
4382     SaveLevelSetup_SeriesInfo();
4383   }
4384
4385   game.restart_level = FALSE;
4386   game.restart_game_message = NULL;
4387   game.request_active = FALSE;
4388
4389   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4390     InitGameActions_MM();
4391
4392   SaveEngineSnapshotToListInitial();
4393
4394   if (!game.restart_level)
4395   {
4396     PlaySound(SND_GAME_STARTING);
4397
4398     if (setup.sound_music)
4399       PlayLevelMusic();
4400   }
4401 }
4402
4403 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4404                         int actual_player_x, int actual_player_y)
4405 {
4406   // this is used for non-R'n'D game engines to update certain engine values
4407
4408   // needed to determine if sounds are played within the visible screen area
4409   scroll_x = actual_scroll_x;
4410   scroll_y = actual_scroll_y;
4411
4412   // needed to get player position for "follow finger" playing input method
4413   local_player->jx = actual_player_x;
4414   local_player->jy = actual_player_y;
4415 }
4416
4417 void InitMovDir(int x, int y)
4418 {
4419   int i, element = Feld[x][y];
4420   static int xy[4][2] =
4421   {
4422     {  0, +1 },
4423     { +1,  0 },
4424     {  0, -1 },
4425     { -1,  0 }
4426   };
4427   static int direction[3][4] =
4428   {
4429     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4430     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4431     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4432   };
4433
4434   switch (element)
4435   {
4436     case EL_BUG_RIGHT:
4437     case EL_BUG_UP:
4438     case EL_BUG_LEFT:
4439     case EL_BUG_DOWN:
4440       Feld[x][y] = EL_BUG;
4441       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4442       break;
4443
4444     case EL_SPACESHIP_RIGHT:
4445     case EL_SPACESHIP_UP:
4446     case EL_SPACESHIP_LEFT:
4447     case EL_SPACESHIP_DOWN:
4448       Feld[x][y] = EL_SPACESHIP;
4449       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4450       break;
4451
4452     case EL_BD_BUTTERFLY_RIGHT:
4453     case EL_BD_BUTTERFLY_UP:
4454     case EL_BD_BUTTERFLY_LEFT:
4455     case EL_BD_BUTTERFLY_DOWN:
4456       Feld[x][y] = EL_BD_BUTTERFLY;
4457       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4458       break;
4459
4460     case EL_BD_FIREFLY_RIGHT:
4461     case EL_BD_FIREFLY_UP:
4462     case EL_BD_FIREFLY_LEFT:
4463     case EL_BD_FIREFLY_DOWN:
4464       Feld[x][y] = EL_BD_FIREFLY;
4465       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4466       break;
4467
4468     case EL_PACMAN_RIGHT:
4469     case EL_PACMAN_UP:
4470     case EL_PACMAN_LEFT:
4471     case EL_PACMAN_DOWN:
4472       Feld[x][y] = EL_PACMAN;
4473       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4474       break;
4475
4476     case EL_YAMYAM_LEFT:
4477     case EL_YAMYAM_RIGHT:
4478     case EL_YAMYAM_UP:
4479     case EL_YAMYAM_DOWN:
4480       Feld[x][y] = EL_YAMYAM;
4481       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4482       break;
4483
4484     case EL_SP_SNIKSNAK:
4485       MovDir[x][y] = MV_UP;
4486       break;
4487
4488     case EL_SP_ELECTRON:
4489       MovDir[x][y] = MV_LEFT;
4490       break;
4491
4492     case EL_MOLE_LEFT:
4493     case EL_MOLE_RIGHT:
4494     case EL_MOLE_UP:
4495     case EL_MOLE_DOWN:
4496       Feld[x][y] = EL_MOLE;
4497       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4498       break;
4499
4500     case EL_SPRING_LEFT:
4501     case EL_SPRING_RIGHT:
4502       Feld[x][y] = EL_SPRING;
4503       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4504       break;
4505
4506     default:
4507       if (IS_CUSTOM_ELEMENT(element))
4508       {
4509         struct ElementInfo *ei = &element_info[element];
4510         int move_direction_initial = ei->move_direction_initial;
4511         int move_pattern = ei->move_pattern;
4512
4513         if (move_direction_initial == MV_START_PREVIOUS)
4514         {
4515           if (MovDir[x][y] != MV_NONE)
4516             return;
4517
4518           move_direction_initial = MV_START_AUTOMATIC;
4519         }
4520
4521         if (move_direction_initial == MV_START_RANDOM)
4522           MovDir[x][y] = 1 << RND(4);
4523         else if (move_direction_initial & MV_ANY_DIRECTION)
4524           MovDir[x][y] = move_direction_initial;
4525         else if (move_pattern == MV_ALL_DIRECTIONS ||
4526                  move_pattern == MV_TURNING_LEFT ||
4527                  move_pattern == MV_TURNING_RIGHT ||
4528                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4529                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4530                  move_pattern == MV_TURNING_RANDOM)
4531           MovDir[x][y] = 1 << RND(4);
4532         else if (move_pattern == MV_HORIZONTAL)
4533           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4534         else if (move_pattern == MV_VERTICAL)
4535           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4536         else if (move_pattern & MV_ANY_DIRECTION)
4537           MovDir[x][y] = element_info[element].move_pattern;
4538         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4539                  move_pattern == MV_ALONG_RIGHT_SIDE)
4540         {
4541           // use random direction as default start direction
4542           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4543             MovDir[x][y] = 1 << RND(4);
4544
4545           for (i = 0; i < NUM_DIRECTIONS; i++)
4546           {
4547             int x1 = x + xy[i][0];
4548             int y1 = y + xy[i][1];
4549
4550             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4551             {
4552               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4553                 MovDir[x][y] = direction[0][i];
4554               else
4555                 MovDir[x][y] = direction[1][i];
4556
4557               break;
4558             }
4559           }
4560         }                
4561       }
4562       else
4563       {
4564         MovDir[x][y] = 1 << RND(4);
4565
4566         if (element != EL_BUG &&
4567             element != EL_SPACESHIP &&
4568             element != EL_BD_BUTTERFLY &&
4569             element != EL_BD_FIREFLY)
4570           break;
4571
4572         for (i = 0; i < NUM_DIRECTIONS; i++)
4573         {
4574           int x1 = x + xy[i][0];
4575           int y1 = y + xy[i][1];
4576
4577           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4578           {
4579             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4580             {
4581               MovDir[x][y] = direction[0][i];
4582               break;
4583             }
4584             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4585                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4586             {
4587               MovDir[x][y] = direction[1][i];
4588               break;
4589             }
4590           }
4591         }
4592       }
4593       break;
4594   }
4595
4596   GfxDir[x][y] = MovDir[x][y];
4597 }
4598
4599 void InitAmoebaNr(int x, int y)
4600 {
4601   int i;
4602   int group_nr = AmoebeNachbarNr(x, y);
4603
4604   if (group_nr == 0)
4605   {
4606     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4607     {
4608       if (AmoebaCnt[i] == 0)
4609       {
4610         group_nr = i;
4611         break;
4612       }
4613     }
4614   }
4615
4616   AmoebaNr[x][y] = group_nr;
4617   AmoebaCnt[group_nr]++;
4618   AmoebaCnt2[group_nr]++;
4619 }
4620
4621 static void LevelSolved(void)
4622 {
4623   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4624       game.players_still_needed > 0)
4625     return;
4626
4627   game.LevelSolved = TRUE;
4628   game.GameOver = TRUE;
4629
4630   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4631                       game_em.lev->score :
4632                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4633                       game_mm.score :
4634                       game.score);
4635   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4636                        MM_HEALTH(game_mm.laser_overload_value) :
4637                        game.health);
4638
4639   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4640   game.LevelSolved_CountingScore = game.score_final;
4641   game.LevelSolved_CountingHealth = game.health_final;
4642 }
4643
4644 void GameWon(void)
4645 {
4646   static int time_count_steps;
4647   static int time, time_final;
4648   static int score, score_final;
4649   static int health, health_final;
4650   static int game_over_delay_1 = 0;
4651   static int game_over_delay_2 = 0;
4652   static int game_over_delay_3 = 0;
4653   int game_over_delay_value_1 = 50;
4654   int game_over_delay_value_2 = 25;
4655   int game_over_delay_value_3 = 50;
4656
4657   if (!game.LevelSolved_GameWon)
4658   {
4659     int i;
4660
4661     // do not start end game actions before the player stops moving (to exit)
4662     if (local_player->active && local_player->MovPos)
4663       return;
4664
4665     game.LevelSolved_GameWon = TRUE;
4666     game.LevelSolved_SaveTape = tape.recording;
4667     game.LevelSolved_SaveScore = !tape.playing;
4668
4669     if (!tape.playing)
4670     {
4671       LevelStats_incSolved(level_nr);
4672
4673       SaveLevelSetup_SeriesInfo();
4674     }
4675
4676     if (tape.auto_play)         // tape might already be stopped here
4677       tape.auto_play_level_solved = TRUE;
4678
4679     TapeStop();
4680
4681     game_over_delay_1 = 0;
4682     game_over_delay_2 = 0;
4683     game_over_delay_3 = game_over_delay_value_3;
4684
4685     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4686     score = score_final = game.score_final;
4687     health = health_final = game.health_final;
4688
4689     if (level.score[SC_TIME_BONUS] > 0)
4690     {
4691       if (TimeLeft > 0)
4692       {
4693         time_final = 0;
4694         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4695       }
4696       else if (game.no_time_limit && TimePlayed < 999)
4697       {
4698         time_final = 999;
4699         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4700       }
4701
4702       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4703
4704       game_over_delay_1 = game_over_delay_value_1;
4705
4706       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4707       {
4708         health_final = 0;
4709         score_final += health * level.score[SC_TIME_BONUS];
4710
4711         game_over_delay_2 = game_over_delay_value_2;
4712       }
4713
4714       game.score_final = score_final;
4715       game.health_final = health_final;
4716     }
4717
4718     if (level_editor_test_game)
4719     {
4720       time = time_final;
4721       score = score_final;
4722
4723       game.LevelSolved_CountingTime = time;
4724       game.LevelSolved_CountingScore = score;
4725
4726       game_panel_controls[GAME_PANEL_TIME].value = time;
4727       game_panel_controls[GAME_PANEL_SCORE].value = score;
4728
4729       DisplayGameControlValues();
4730     }
4731
4732     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4733     {
4734       // check if last player has left the level
4735       if (game.exit_x >= 0 &&
4736           game.exit_y >= 0)
4737       {
4738         int x = game.exit_x;
4739         int y = game.exit_y;
4740         int element = Feld[x][y];
4741
4742         // close exit door after last player
4743         if ((game.all_players_gone &&
4744              (element == EL_EXIT_OPEN ||
4745               element == EL_SP_EXIT_OPEN ||
4746               element == EL_STEEL_EXIT_OPEN)) ||
4747             element == EL_EM_EXIT_OPEN ||
4748             element == EL_EM_STEEL_EXIT_OPEN)
4749         {
4750
4751           Feld[x][y] =
4752             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4753              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4754              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4755              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4756              EL_EM_STEEL_EXIT_CLOSING);
4757
4758           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4759         }
4760
4761         // player disappears
4762         DrawLevelField(x, y);
4763       }
4764
4765       for (i = 0; i < MAX_PLAYERS; i++)
4766       {
4767         struct PlayerInfo *player = &stored_player[i];
4768
4769         if (player->present)
4770         {
4771           RemovePlayer(player);
4772
4773           // player disappears
4774           DrawLevelField(player->jx, player->jy);
4775         }
4776       }
4777     }
4778
4779     PlaySound(SND_GAME_WINNING);
4780   }
4781
4782   if (game_over_delay_1 > 0)
4783   {
4784     game_over_delay_1--;
4785
4786     return;
4787   }
4788
4789   if (time != time_final)
4790   {
4791     int time_to_go = ABS(time_final - time);
4792     int time_count_dir = (time < time_final ? +1 : -1);
4793
4794     if (time_to_go < time_count_steps)
4795       time_count_steps = 1;
4796
4797     time  += time_count_steps * time_count_dir;
4798     score += time_count_steps * level.score[SC_TIME_BONUS];
4799
4800     game.LevelSolved_CountingTime = time;
4801     game.LevelSolved_CountingScore = score;
4802
4803     game_panel_controls[GAME_PANEL_TIME].value = time;
4804     game_panel_controls[GAME_PANEL_SCORE].value = score;
4805
4806     DisplayGameControlValues();
4807
4808     if (time == time_final)
4809       StopSound(SND_GAME_LEVELTIME_BONUS);
4810     else if (setup.sound_loops)
4811       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4812     else
4813       PlaySound(SND_GAME_LEVELTIME_BONUS);
4814
4815     return;
4816   }
4817
4818   if (game_over_delay_2 > 0)
4819   {
4820     game_over_delay_2--;
4821
4822     return;
4823   }
4824
4825   if (health != health_final)
4826   {
4827     int health_count_dir = (health < health_final ? +1 : -1);
4828
4829     health += health_count_dir;
4830     score  += level.score[SC_TIME_BONUS];
4831
4832     game.LevelSolved_CountingHealth = health;
4833     game.LevelSolved_CountingScore = score;
4834
4835     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4836     game_panel_controls[GAME_PANEL_SCORE].value = score;
4837
4838     DisplayGameControlValues();
4839
4840     if (health == health_final)
4841       StopSound(SND_GAME_LEVELTIME_BONUS);
4842     else if (setup.sound_loops)
4843       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4844     else
4845       PlaySound(SND_GAME_LEVELTIME_BONUS);
4846
4847     return;
4848   }
4849
4850   game.panel.active = FALSE;
4851
4852   if (game_over_delay_3 > 0)
4853   {
4854     game_over_delay_3--;
4855
4856     return;
4857   }
4858
4859   GameEnd();
4860 }
4861
4862 void GameEnd(void)
4863 {
4864   // used instead of "level_nr" (needed for network games)
4865   int last_level_nr = levelset.level_nr;
4866   int hi_pos;
4867
4868   game.LevelSolved_GameEnd = TRUE;
4869
4870   if (game.LevelSolved_SaveTape)
4871   {
4872     // make sure that request dialog to save tape does not open door again
4873     if (!global.use_envelope_request)
4874       CloseDoor(DOOR_CLOSE_1);
4875
4876     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4877   }
4878
4879   // if no tape is to be saved, close both doors simultaneously
4880   CloseDoor(DOOR_CLOSE_ALL);
4881
4882   if (level_editor_test_game)
4883   {
4884     SetGameStatus(GAME_MODE_MAIN);
4885
4886     DrawMainMenu();
4887
4888     return;
4889   }
4890
4891   if (!game.LevelSolved_SaveScore)
4892   {
4893     SetGameStatus(GAME_MODE_MAIN);
4894
4895     DrawMainMenu();
4896
4897     return;
4898   }
4899
4900   if (level_nr == leveldir_current->handicap_level)
4901   {
4902     leveldir_current->handicap_level++;
4903
4904     SaveLevelSetup_SeriesInfo();
4905   }
4906
4907   if (setup.increment_levels &&
4908       level_nr < leveldir_current->last_level &&
4909       !network_playing)
4910   {
4911     level_nr++;         // advance to next level
4912     TapeErase();        // start with empty tape
4913
4914     if (setup.auto_play_next_level)
4915     {
4916       LoadLevel(level_nr);
4917
4918       SaveLevelSetup_SeriesInfo();
4919     }
4920   }
4921
4922   hi_pos = NewHiScore(last_level_nr);
4923
4924   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4925   {
4926     SetGameStatus(GAME_MODE_SCORES);
4927
4928     DrawHallOfFame(last_level_nr, hi_pos);
4929   }
4930   else if (setup.auto_play_next_level && setup.increment_levels &&
4931            last_level_nr < leveldir_current->last_level &&
4932            !network_playing)
4933   {
4934     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4935   }
4936   else
4937   {
4938     SetGameStatus(GAME_MODE_MAIN);
4939
4940     DrawMainMenu();
4941   }
4942 }
4943
4944 int NewHiScore(int level_nr)
4945 {
4946   int k, l;
4947   int position = -1;
4948   boolean one_score_entry_per_name = !program.many_scores_per_name;
4949
4950   LoadScore(level_nr);
4951
4952   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4953       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4954     return -1;
4955
4956   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4957   {
4958     if (game.score_final > highscore[k].Score)
4959     {
4960       // player has made it to the hall of fame
4961
4962       if (k < MAX_SCORE_ENTRIES - 1)
4963       {
4964         int m = MAX_SCORE_ENTRIES - 1;
4965
4966         if (one_score_entry_per_name)
4967         {
4968           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4969             if (strEqual(setup.player_name, highscore[l].Name))
4970               m = l;
4971
4972           if (m == k)   // player's new highscore overwrites his old one
4973             goto put_into_list;
4974         }
4975
4976         for (l = m; l > k; l--)
4977         {
4978           strcpy(highscore[l].Name, highscore[l - 1].Name);
4979           highscore[l].Score = highscore[l - 1].Score;
4980         }
4981       }
4982
4983       put_into_list:
4984
4985       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4986       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4987       highscore[k].Score = game.score_final;
4988       position = k;
4989
4990       break;
4991     }
4992     else if (one_score_entry_per_name &&
4993              !strncmp(setup.player_name, highscore[k].Name,
4994                       MAX_PLAYER_NAME_LEN))
4995       break;    // player already there with a higher score
4996   }
4997
4998   if (position >= 0) 
4999     SaveScore(level_nr);
5000
5001   return position;
5002 }
5003
5004 static int getElementMoveStepsizeExt(int x, int y, int direction)
5005 {
5006   int element = Feld[x][y];
5007   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5008   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5009   int horiz_move = (dx != 0);
5010   int sign = (horiz_move ? dx : dy);
5011   int step = sign * element_info[element].move_stepsize;
5012
5013   // special values for move stepsize for spring and things on conveyor belt
5014   if (horiz_move)
5015   {
5016     if (CAN_FALL(element) &&
5017         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5018       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5019     else if (element == EL_SPRING)
5020       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5021   }
5022
5023   return step;
5024 }
5025
5026 static int getElementMoveStepsize(int x, int y)
5027 {
5028   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5029 }
5030
5031 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5032 {
5033   if (player->GfxAction != action || player->GfxDir != dir)
5034   {
5035     player->GfxAction = action;
5036     player->GfxDir = dir;
5037     player->Frame = 0;
5038     player->StepFrame = 0;
5039   }
5040 }
5041
5042 static void ResetGfxFrame(int x, int y)
5043 {
5044   // profiling showed that "autotest" spends 10~20% of its time in this function
5045   if (DrawingDeactivatedField())
5046     return;
5047
5048   int element = Feld[x][y];
5049   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5050
5051   if (graphic_info[graphic].anim_global_sync)
5052     GfxFrame[x][y] = FrameCounter;
5053   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5054     GfxFrame[x][y] = CustomValue[x][y];
5055   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5056     GfxFrame[x][y] = element_info[element].collect_score;
5057   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5058     GfxFrame[x][y] = ChangeDelay[x][y];
5059 }
5060
5061 static void ResetGfxAnimation(int x, int y)
5062 {
5063   GfxAction[x][y] = ACTION_DEFAULT;
5064   GfxDir[x][y] = MovDir[x][y];
5065   GfxFrame[x][y] = 0;
5066
5067   ResetGfxFrame(x, y);
5068 }
5069
5070 static void ResetRandomAnimationValue(int x, int y)
5071 {
5072   GfxRandom[x][y] = INIT_GFX_RANDOM();
5073 }
5074
5075 static void InitMovingField(int x, int y, int direction)
5076 {
5077   int element = Feld[x][y];
5078   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5080   int newx = x + dx;
5081   int newy = y + dy;
5082   boolean is_moving_before, is_moving_after;
5083
5084   // check if element was/is moving or being moved before/after mode change
5085   is_moving_before = (WasJustMoving[x][y] != 0);
5086   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5087
5088   // reset animation only for moving elements which change direction of moving
5089   // or which just started or stopped moving
5090   // (else CEs with property "can move" / "not moving" are reset each frame)
5091   if (is_moving_before != is_moving_after ||
5092       direction != MovDir[x][y])
5093     ResetGfxAnimation(x, y);
5094
5095   MovDir[x][y] = direction;
5096   GfxDir[x][y] = direction;
5097
5098   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5099                      direction == MV_DOWN && CAN_FALL(element) ?
5100                      ACTION_FALLING : ACTION_MOVING);
5101
5102   // this is needed for CEs with property "can move" / "not moving"
5103
5104   if (is_moving_after)
5105   {
5106     if (Feld[newx][newy] == EL_EMPTY)
5107       Feld[newx][newy] = EL_BLOCKED;
5108
5109     MovDir[newx][newy] = MovDir[x][y];
5110
5111     CustomValue[newx][newy] = CustomValue[x][y];
5112
5113     GfxFrame[newx][newy] = GfxFrame[x][y];
5114     GfxRandom[newx][newy] = GfxRandom[x][y];
5115     GfxAction[newx][newy] = GfxAction[x][y];
5116     GfxDir[newx][newy] = GfxDir[x][y];
5117   }
5118 }
5119
5120 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5121 {
5122   int direction = MovDir[x][y];
5123   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5124   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5125
5126   *goes_to_x = newx;
5127   *goes_to_y = newy;
5128 }
5129
5130 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5131 {
5132   int oldx = x, oldy = y;
5133   int direction = MovDir[x][y];
5134
5135   if (direction == MV_LEFT)
5136     oldx++;
5137   else if (direction == MV_RIGHT)
5138     oldx--;
5139   else if (direction == MV_UP)
5140     oldy++;
5141   else if (direction == MV_DOWN)
5142     oldy--;
5143
5144   *comes_from_x = oldx;
5145   *comes_from_y = oldy;
5146 }
5147
5148 static int MovingOrBlocked2Element(int x, int y)
5149 {
5150   int element = Feld[x][y];
5151
5152   if (element == EL_BLOCKED)
5153   {
5154     int oldx, oldy;
5155
5156     Blocked2Moving(x, y, &oldx, &oldy);
5157     return Feld[oldx][oldy];
5158   }
5159   else
5160     return element;
5161 }
5162
5163 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5164 {
5165   // like MovingOrBlocked2Element(), but if element is moving
5166   // and (x,y) is the field the moving element is just leaving,
5167   // return EL_BLOCKED instead of the element value
5168   int element = Feld[x][y];
5169
5170   if (IS_MOVING(x, y))
5171   {
5172     if (element == EL_BLOCKED)
5173     {
5174       int oldx, oldy;
5175
5176       Blocked2Moving(x, y, &oldx, &oldy);
5177       return Feld[oldx][oldy];
5178     }
5179     else
5180       return EL_BLOCKED;
5181   }
5182   else
5183     return element;
5184 }
5185
5186 static void RemoveField(int x, int y)
5187 {
5188   Feld[x][y] = EL_EMPTY;
5189
5190   MovPos[x][y] = 0;
5191   MovDir[x][y] = 0;
5192   MovDelay[x][y] = 0;
5193
5194   CustomValue[x][y] = 0;
5195
5196   AmoebaNr[x][y] = 0;
5197   ChangeDelay[x][y] = 0;
5198   ChangePage[x][y] = -1;
5199   Pushed[x][y] = FALSE;
5200
5201   GfxElement[x][y] = EL_UNDEFINED;
5202   GfxAction[x][y] = ACTION_DEFAULT;
5203   GfxDir[x][y] = MV_NONE;
5204 }
5205
5206 static void RemoveMovingField(int x, int y)
5207 {
5208   int oldx = x, oldy = y, newx = x, newy = y;
5209   int element = Feld[x][y];
5210   int next_element = EL_UNDEFINED;
5211
5212   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5213     return;
5214
5215   if (IS_MOVING(x, y))
5216   {
5217     Moving2Blocked(x, y, &newx, &newy);
5218
5219     if (Feld[newx][newy] != EL_BLOCKED)
5220     {
5221       // element is moving, but target field is not free (blocked), but
5222       // already occupied by something different (example: acid pool);
5223       // in this case, only remove the moving field, but not the target
5224
5225       RemoveField(oldx, oldy);
5226
5227       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5228
5229       TEST_DrawLevelField(oldx, oldy);
5230
5231       return;
5232     }
5233   }
5234   else if (element == EL_BLOCKED)
5235   {
5236     Blocked2Moving(x, y, &oldx, &oldy);
5237     if (!IS_MOVING(oldx, oldy))
5238       return;
5239   }
5240
5241   if (element == EL_BLOCKED &&
5242       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5243        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5244        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5245        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5246        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5247        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5248     next_element = get_next_element(Feld[oldx][oldy]);
5249
5250   RemoveField(oldx, oldy);
5251   RemoveField(newx, newy);
5252
5253   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5254
5255   if (next_element != EL_UNDEFINED)
5256     Feld[oldx][oldy] = next_element;
5257
5258   TEST_DrawLevelField(oldx, oldy);
5259   TEST_DrawLevelField(newx, newy);
5260 }
5261
5262 void DrawDynamite(int x, int y)
5263 {
5264   int sx = SCREENX(x), sy = SCREENY(y);
5265   int graphic = el2img(Feld[x][y]);
5266   int frame;
5267
5268   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5269     return;
5270
5271   if (IS_WALKABLE_INSIDE(Back[x][y]))
5272     return;
5273
5274   if (Back[x][y])
5275     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5276   else if (Store[x][y])
5277     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5278
5279   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5280
5281   if (Back[x][y] || Store[x][y])
5282     DrawGraphicThruMask(sx, sy, graphic, frame);
5283   else
5284     DrawGraphic(sx, sy, graphic, frame);
5285 }
5286
5287 static void CheckDynamite(int x, int y)
5288 {
5289   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5290   {
5291     MovDelay[x][y]--;
5292
5293     if (MovDelay[x][y] != 0)
5294     {
5295       DrawDynamite(x, y);
5296       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5297
5298       return;
5299     }
5300   }
5301
5302   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5303
5304   Bang(x, y);
5305 }
5306
5307 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5308 {
5309   boolean num_checked_players = 0;
5310   int i;
5311
5312   for (i = 0; i < MAX_PLAYERS; i++)
5313   {
5314     if (stored_player[i].active)
5315     {
5316       int sx = stored_player[i].jx;
5317       int sy = stored_player[i].jy;
5318
5319       if (num_checked_players == 0)
5320       {
5321         *sx1 = *sx2 = sx;
5322         *sy1 = *sy2 = sy;
5323       }
5324       else
5325       {
5326         *sx1 = MIN(*sx1, sx);
5327         *sy1 = MIN(*sy1, sy);
5328         *sx2 = MAX(*sx2, sx);
5329         *sy2 = MAX(*sy2, sy);
5330       }
5331
5332       num_checked_players++;
5333     }
5334   }
5335 }
5336
5337 static boolean checkIfAllPlayersFitToScreen_RND(void)
5338 {
5339   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5340
5341   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5342
5343   return (sx2 - sx1 < SCR_FIELDX &&
5344           sy2 - sy1 < SCR_FIELDY);
5345 }
5346
5347 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5348 {
5349   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5350
5351   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5352
5353   *sx = (sx1 + sx2) / 2;
5354   *sy = (sy1 + sy2) / 2;
5355 }
5356
5357 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5358                                boolean center_screen, boolean quick_relocation)
5359 {
5360   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5361   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5362   boolean no_delay = (tape.warp_forward);
5363   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5364   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5365   int new_scroll_x, new_scroll_y;
5366
5367   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5368   {
5369     // case 1: quick relocation inside visible screen (without scrolling)
5370
5371     RedrawPlayfield();
5372
5373     return;
5374   }
5375
5376   if (!level.shifted_relocation || center_screen)
5377   {
5378     // relocation _with_ centering of screen
5379
5380     new_scroll_x = SCROLL_POSITION_X(x);
5381     new_scroll_y = SCROLL_POSITION_Y(y);
5382   }
5383   else
5384   {
5385     // relocation _without_ centering of screen
5386
5387     int center_scroll_x = SCROLL_POSITION_X(old_x);
5388     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5389     int offset_x = x + (scroll_x - center_scroll_x);
5390     int offset_y = y + (scroll_y - center_scroll_y);
5391
5392     // for new screen position, apply previous offset to center position
5393     new_scroll_x = SCROLL_POSITION_X(offset_x);
5394     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5395   }
5396
5397   if (quick_relocation)
5398   {
5399     // case 2: quick relocation (redraw without visible scrolling)
5400
5401     scroll_x = new_scroll_x;
5402     scroll_y = new_scroll_y;
5403
5404     RedrawPlayfield();
5405
5406     return;
5407   }
5408
5409   // case 3: visible relocation (with scrolling to new position)
5410
5411   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5412
5413   SetVideoFrameDelay(wait_delay_value);
5414
5415   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5416   {
5417     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5418     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5419
5420     if (dx == 0 && dy == 0)             // no scrolling needed at all
5421       break;
5422
5423     scroll_x -= dx;
5424     scroll_y -= dy;
5425
5426     // set values for horizontal/vertical screen scrolling (half tile size)
5427     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5428     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5429     int pos_x = dx * TILEX / 2;
5430     int pos_y = dy * TILEY / 2;
5431     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5432     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5433
5434     ScrollLevel(dx, dy);
5435     DrawAllPlayers();
5436
5437     // scroll in two steps of half tile size to make things smoother
5438     BlitScreenToBitmapExt_RND(window, fx, fy);
5439
5440     // scroll second step to align at full tile size
5441     BlitScreenToBitmap(window);
5442   }
5443
5444   DrawAllPlayers();
5445   BackToFront();
5446
5447   SetVideoFrameDelay(frame_delay_value_old);
5448 }
5449
5450 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5451 {
5452   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5453   int player_nr = GET_PLAYER_NR(el_player);
5454   struct PlayerInfo *player = &stored_player[player_nr];
5455   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5456   boolean no_delay = (tape.warp_forward);
5457   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5458   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5459   int old_jx = player->jx;
5460   int old_jy = player->jy;
5461   int old_element = Feld[old_jx][old_jy];
5462   int element = Feld[jx][jy];
5463   boolean player_relocated = (old_jx != jx || old_jy != jy);
5464
5465   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5466   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5467   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5468   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5469   int leave_side_horiz = move_dir_horiz;
5470   int leave_side_vert  = move_dir_vert;
5471   int enter_side = enter_side_horiz | enter_side_vert;
5472   int leave_side = leave_side_horiz | leave_side_vert;
5473
5474   if (player->buried)           // do not reanimate dead player
5475     return;
5476
5477   if (!player_relocated)        // no need to relocate the player
5478     return;
5479
5480   if (IS_PLAYER(jx, jy))        // player already placed at new position
5481   {
5482     RemoveField(jx, jy);        // temporarily remove newly placed player
5483     DrawLevelField(jx, jy);
5484   }
5485
5486   if (player->present)
5487   {
5488     while (player->MovPos)
5489     {
5490       ScrollPlayer(player, SCROLL_GO_ON);
5491       ScrollScreen(NULL, SCROLL_GO_ON);
5492
5493       AdvanceFrameAndPlayerCounters(player->index_nr);
5494
5495       DrawPlayer(player);
5496
5497       BackToFront_WithFrameDelay(wait_delay_value);
5498     }
5499
5500     DrawPlayer(player);         // needed here only to cleanup last field
5501     DrawLevelField(player->jx, player->jy);     // remove player graphic
5502
5503     player->is_moving = FALSE;
5504   }
5505
5506   if (IS_CUSTOM_ELEMENT(old_element))
5507     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5508                                CE_LEFT_BY_PLAYER,
5509                                player->index_bit, leave_side);
5510
5511   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5512                                       CE_PLAYER_LEAVES_X,
5513                                       player->index_bit, leave_side);
5514
5515   Feld[jx][jy] = el_player;
5516   InitPlayerField(jx, jy, el_player, TRUE);
5517
5518   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5519      possible that the relocation target field did not contain a player element,
5520      but a walkable element, to which the new player was relocated -- in this
5521      case, restore that (already initialized!) element on the player field */
5522   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5523   {
5524     Feld[jx][jy] = element;     // restore previously existing element
5525   }
5526
5527   // only visually relocate centered player
5528   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5529                      FALSE, level.instant_relocation);
5530
5531   TestIfPlayerTouchesBadThing(jx, jy);
5532   TestIfPlayerTouchesCustomElement(jx, jy);
5533
5534   if (IS_CUSTOM_ELEMENT(element))
5535     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5536                                player->index_bit, enter_side);
5537
5538   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5539                                       player->index_bit, enter_side);
5540
5541   if (player->is_switching)
5542   {
5543     /* ensure that relocation while still switching an element does not cause
5544        a new element to be treated as also switched directly after relocation
5545        (this is important for teleporter switches that teleport the player to
5546        a place where another teleporter switch is in the same direction, which
5547        would then incorrectly be treated as immediately switched before the
5548        direction key that caused the switch was released) */
5549
5550     player->switch_x += jx - old_jx;
5551     player->switch_y += jy - old_jy;
5552   }
5553 }
5554
5555 static void Explode(int ex, int ey, int phase, int mode)
5556 {
5557   int x, y;
5558   int last_phase;
5559   int border_element;
5560
5561   // !!! eliminate this variable !!!
5562   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5563
5564   if (game.explosions_delayed)
5565   {
5566     ExplodeField[ex][ey] = mode;
5567     return;
5568   }
5569
5570   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5571   {
5572     int center_element = Feld[ex][ey];
5573     int artwork_element, explosion_element;     // set these values later
5574
5575     // remove things displayed in background while burning dynamite
5576     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5577       Back[ex][ey] = 0;
5578
5579     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5580     {
5581       // put moving element to center field (and let it explode there)
5582       center_element = MovingOrBlocked2Element(ex, ey);
5583       RemoveMovingField(ex, ey);
5584       Feld[ex][ey] = center_element;
5585     }
5586
5587     // now "center_element" is finally determined -- set related values now
5588     artwork_element = center_element;           // for custom player artwork
5589     explosion_element = center_element;         // for custom player artwork
5590
5591     if (IS_PLAYER(ex, ey))
5592     {
5593       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5594
5595       artwork_element = stored_player[player_nr].artwork_element;
5596
5597       if (level.use_explosion_element[player_nr])
5598       {
5599         explosion_element = level.explosion_element[player_nr];
5600         artwork_element = explosion_element;
5601       }
5602     }
5603
5604     if (mode == EX_TYPE_NORMAL ||
5605         mode == EX_TYPE_CENTER ||
5606         mode == EX_TYPE_CROSS)
5607       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5608
5609     last_phase = element_info[explosion_element].explosion_delay + 1;
5610
5611     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5612     {
5613       int xx = x - ex + 1;
5614       int yy = y - ey + 1;
5615       int element;
5616
5617       if (!IN_LEV_FIELD(x, y) ||
5618           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5619           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5620         continue;
5621
5622       element = Feld[x][y];
5623
5624       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5625       {
5626         element = MovingOrBlocked2Element(x, y);
5627
5628         if (!IS_EXPLOSION_PROOF(element))
5629           RemoveMovingField(x, y);
5630       }
5631
5632       // indestructible elements can only explode in center (but not flames)
5633       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5634                                            mode == EX_TYPE_BORDER)) ||
5635           element == EL_FLAMES)
5636         continue;
5637
5638       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5639          behaviour, for example when touching a yamyam that explodes to rocks
5640          with active deadly shield, a rock is created under the player !!! */
5641       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5642 #if 0
5643       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5644           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5645            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5646 #else
5647       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5648 #endif
5649       {
5650         if (IS_ACTIVE_BOMB(element))
5651         {
5652           // re-activate things under the bomb like gate or penguin
5653           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5654           Back[x][y] = 0;
5655         }
5656
5657         continue;
5658       }
5659
5660       // save walkable background elements while explosion on same tile
5661       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5662           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5663         Back[x][y] = element;
5664
5665       // ignite explodable elements reached by other explosion
5666       if (element == EL_EXPLOSION)
5667         element = Store2[x][y];
5668
5669       if (AmoebaNr[x][y] &&
5670           (element == EL_AMOEBA_FULL ||
5671            element == EL_BD_AMOEBA ||
5672            element == EL_AMOEBA_GROWING))
5673       {
5674         AmoebaCnt[AmoebaNr[x][y]]--;
5675         AmoebaCnt2[AmoebaNr[x][y]]--;
5676       }
5677
5678       RemoveField(x, y);
5679
5680       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5681       {
5682         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5683
5684         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5685
5686         if (PLAYERINFO(ex, ey)->use_murphy)
5687           Store[x][y] = EL_EMPTY;
5688       }
5689
5690       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5691       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5692       else if (ELEM_IS_PLAYER(center_element))
5693         Store[x][y] = EL_EMPTY;
5694       else if (center_element == EL_YAMYAM)
5695         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5696       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5697         Store[x][y] = element_info[center_element].content.e[xx][yy];
5698 #if 1
5699       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5700       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5701       // otherwise) -- FIX THIS !!!
5702       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5703         Store[x][y] = element_info[element].content.e[1][1];
5704 #else
5705       else if (!CAN_EXPLODE(element))
5706         Store[x][y] = element_info[element].content.e[1][1];
5707 #endif
5708       else
5709         Store[x][y] = EL_EMPTY;
5710
5711       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5712           center_element == EL_AMOEBA_TO_DIAMOND)
5713         Store2[x][y] = element;
5714
5715       Feld[x][y] = EL_EXPLOSION;
5716       GfxElement[x][y] = artwork_element;
5717
5718       ExplodePhase[x][y] = 1;
5719       ExplodeDelay[x][y] = last_phase;
5720
5721       Stop[x][y] = TRUE;
5722     }
5723
5724     if (center_element == EL_YAMYAM)
5725       game.yamyam_content_nr =
5726         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5727
5728     return;
5729   }
5730
5731   if (Stop[ex][ey])
5732     return;
5733
5734   x = ex;
5735   y = ey;
5736
5737   if (phase == 1)
5738     GfxFrame[x][y] = 0;         // restart explosion animation
5739
5740   last_phase = ExplodeDelay[x][y];
5741
5742   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5743
5744   // this can happen if the player leaves an explosion just in time
5745   if (GfxElement[x][y] == EL_UNDEFINED)
5746     GfxElement[x][y] = EL_EMPTY;
5747
5748   border_element = Store2[x][y];
5749   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5750     border_element = StorePlayer[x][y];
5751
5752   if (phase == element_info[border_element].ignition_delay ||
5753       phase == last_phase)
5754   {
5755     boolean border_explosion = FALSE;
5756
5757     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5758         !PLAYER_EXPLOSION_PROTECTED(x, y))
5759     {
5760       KillPlayerUnlessExplosionProtected(x, y);
5761       border_explosion = TRUE;
5762     }
5763     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5764     {
5765       Feld[x][y] = Store2[x][y];
5766       Store2[x][y] = 0;
5767       Bang(x, y);
5768       border_explosion = TRUE;
5769     }
5770     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5771     {
5772       AmoebeUmwandeln(x, y);
5773       Store2[x][y] = 0;
5774       border_explosion = TRUE;
5775     }
5776
5777     // if an element just explodes due to another explosion (chain-reaction),
5778     // do not immediately end the new explosion when it was the last frame of
5779     // the explosion (as it would be done in the following "if"-statement!)
5780     if (border_explosion && phase == last_phase)
5781       return;
5782   }
5783
5784   if (phase == last_phase)
5785   {
5786     int element;
5787
5788     element = Feld[x][y] = Store[x][y];
5789     Store[x][y] = Store2[x][y] = 0;
5790     GfxElement[x][y] = EL_UNDEFINED;
5791
5792     // player can escape from explosions and might therefore be still alive
5793     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5794         element <= EL_PLAYER_IS_EXPLODING_4)
5795     {
5796       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5797       int explosion_element = EL_PLAYER_1 + player_nr;
5798       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5799       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5800
5801       if (level.use_explosion_element[player_nr])
5802         explosion_element = level.explosion_element[player_nr];
5803
5804       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5805                     element_info[explosion_element].content.e[xx][yy]);
5806     }
5807
5808     // restore probably existing indestructible background element
5809     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5810       element = Feld[x][y] = Back[x][y];
5811     Back[x][y] = 0;
5812
5813     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5814     GfxDir[x][y] = MV_NONE;
5815     ChangeDelay[x][y] = 0;
5816     ChangePage[x][y] = -1;
5817
5818     CustomValue[x][y] = 0;
5819
5820     InitField_WithBug2(x, y, FALSE);
5821
5822     TEST_DrawLevelField(x, y);
5823
5824     TestIfElementTouchesCustomElement(x, y);
5825
5826     if (GFX_CRUMBLED(element))
5827       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5828
5829     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5830       StorePlayer[x][y] = 0;
5831
5832     if (ELEM_IS_PLAYER(element))
5833       RelocatePlayer(x, y, element);
5834   }
5835   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5836   {
5837     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5838     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5839
5840     if (phase == delay)
5841       TEST_DrawLevelFieldCrumbled(x, y);
5842
5843     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5844     {
5845       DrawLevelElement(x, y, Back[x][y]);
5846       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5847     }
5848     else if (IS_WALKABLE_UNDER(Back[x][y]))
5849     {
5850       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5851       DrawLevelElementThruMask(x, y, Back[x][y]);
5852     }
5853     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5854       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5855   }
5856 }
5857
5858 static void DynaExplode(int ex, int ey)
5859 {
5860   int i, j;
5861   int dynabomb_element = Feld[ex][ey];
5862   int dynabomb_size = 1;
5863   boolean dynabomb_xl = FALSE;
5864   struct PlayerInfo *player;
5865   static int xy[4][2] =
5866   {
5867     { 0, -1 },
5868     { -1, 0 },
5869     { +1, 0 },
5870     { 0, +1 }
5871   };
5872
5873   if (IS_ACTIVE_BOMB(dynabomb_element))
5874   {
5875     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5876     dynabomb_size = player->dynabomb_size;
5877     dynabomb_xl = player->dynabomb_xl;
5878     player->dynabombs_left++;
5879   }
5880
5881   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5882
5883   for (i = 0; i < NUM_DIRECTIONS; i++)
5884   {
5885     for (j = 1; j <= dynabomb_size; j++)
5886     {
5887       int x = ex + j * xy[i][0];
5888       int y = ey + j * xy[i][1];
5889       int element;
5890
5891       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5892         break;
5893
5894       element = Feld[x][y];
5895
5896       // do not restart explosions of fields with active bombs
5897       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5898         continue;
5899
5900       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5901
5902       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5903           !IS_DIGGABLE(element) && !dynabomb_xl)
5904         break;
5905     }
5906   }
5907 }
5908
5909 void Bang(int x, int y)
5910 {
5911   int element = MovingOrBlocked2Element(x, y);
5912   int explosion_type = EX_TYPE_NORMAL;
5913
5914   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5915   {
5916     struct PlayerInfo *player = PLAYERINFO(x, y);
5917
5918     element = Feld[x][y] = player->initial_element;
5919
5920     if (level.use_explosion_element[player->index_nr])
5921     {
5922       int explosion_element = level.explosion_element[player->index_nr];
5923
5924       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5925         explosion_type = EX_TYPE_CROSS;
5926       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5927         explosion_type = EX_TYPE_CENTER;
5928     }
5929   }
5930
5931   switch (element)
5932   {
5933     case EL_BUG:
5934     case EL_SPACESHIP:
5935     case EL_BD_BUTTERFLY:
5936     case EL_BD_FIREFLY:
5937     case EL_YAMYAM:
5938     case EL_DARK_YAMYAM:
5939     case EL_ROBOT:
5940     case EL_PACMAN:
5941     case EL_MOLE:
5942       RaiseScoreElement(element);
5943       break;
5944
5945     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5946     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5947     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5948     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5949     case EL_DYNABOMB_INCREASE_NUMBER:
5950     case EL_DYNABOMB_INCREASE_SIZE:
5951     case EL_DYNABOMB_INCREASE_POWER:
5952       explosion_type = EX_TYPE_DYNA;
5953       break;
5954
5955     case EL_DC_LANDMINE:
5956       explosion_type = EX_TYPE_CENTER;
5957       break;
5958
5959     case EL_PENGUIN:
5960     case EL_LAMP:
5961     case EL_LAMP_ACTIVE:
5962     case EL_AMOEBA_TO_DIAMOND:
5963       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5964         explosion_type = EX_TYPE_CENTER;
5965       break;
5966
5967     default:
5968       if (element_info[element].explosion_type == EXPLODES_CROSS)
5969         explosion_type = EX_TYPE_CROSS;
5970       else if (element_info[element].explosion_type == EXPLODES_1X1)
5971         explosion_type = EX_TYPE_CENTER;
5972       break;
5973   }
5974
5975   if (explosion_type == EX_TYPE_DYNA)
5976     DynaExplode(x, y);
5977   else
5978     Explode(x, y, EX_PHASE_START, explosion_type);
5979
5980   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5981 }
5982
5983 static void SplashAcid(int x, int y)
5984 {
5985   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5986       (!IN_LEV_FIELD(x - 1, y - 2) ||
5987        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5988     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5989
5990   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5991       (!IN_LEV_FIELD(x + 1, y - 2) ||
5992        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5993     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5994
5995   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5996 }
5997
5998 static void InitBeltMovement(void)
5999 {
6000   static int belt_base_element[4] =
6001   {
6002     EL_CONVEYOR_BELT_1_LEFT,
6003     EL_CONVEYOR_BELT_2_LEFT,
6004     EL_CONVEYOR_BELT_3_LEFT,
6005     EL_CONVEYOR_BELT_4_LEFT
6006   };
6007   static int belt_base_active_element[4] =
6008   {
6009     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6010     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6011     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6012     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6013   };
6014
6015   int x, y, i, j;
6016
6017   // set frame order for belt animation graphic according to belt direction
6018   for (i = 0; i < NUM_BELTS; i++)
6019   {
6020     int belt_nr = i;
6021
6022     for (j = 0; j < NUM_BELT_PARTS; j++)
6023     {
6024       int element = belt_base_active_element[belt_nr] + j;
6025       int graphic_1 = el2img(element);
6026       int graphic_2 = el2panelimg(element);
6027
6028       if (game.belt_dir[i] == MV_LEFT)
6029       {
6030         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6031         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6032       }
6033       else
6034       {
6035         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6036         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6037       }
6038     }
6039   }
6040
6041   SCAN_PLAYFIELD(x, y)
6042   {
6043     int element = Feld[x][y];
6044
6045     for (i = 0; i < NUM_BELTS; i++)
6046     {
6047       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6048       {
6049         int e_belt_nr = getBeltNrFromBeltElement(element);
6050         int belt_nr = i;
6051
6052         if (e_belt_nr == belt_nr)
6053         {
6054           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6055
6056           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6057         }
6058       }
6059     }
6060   }
6061 }
6062
6063 static void ToggleBeltSwitch(int x, int y)
6064 {
6065   static int belt_base_element[4] =
6066   {
6067     EL_CONVEYOR_BELT_1_LEFT,
6068     EL_CONVEYOR_BELT_2_LEFT,
6069     EL_CONVEYOR_BELT_3_LEFT,
6070     EL_CONVEYOR_BELT_4_LEFT
6071   };
6072   static int belt_base_active_element[4] =
6073   {
6074     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6075     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6076     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6077     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6078   };
6079   static int belt_base_switch_element[4] =
6080   {
6081     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6082     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6083     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6084     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6085   };
6086   static int belt_move_dir[4] =
6087   {
6088     MV_LEFT,
6089     MV_NONE,
6090     MV_RIGHT,
6091     MV_NONE,
6092   };
6093
6094   int element = Feld[x][y];
6095   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6096   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6097   int belt_dir = belt_move_dir[belt_dir_nr];
6098   int xx, yy, i;
6099
6100   if (!IS_BELT_SWITCH(element))
6101     return;
6102
6103   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6104   game.belt_dir[belt_nr] = belt_dir;
6105
6106   if (belt_dir_nr == 3)
6107     belt_dir_nr = 1;
6108
6109   // set frame order for belt animation graphic according to belt direction
6110   for (i = 0; i < NUM_BELT_PARTS; i++)
6111   {
6112     int element = belt_base_active_element[belt_nr] + i;
6113     int graphic_1 = el2img(element);
6114     int graphic_2 = el2panelimg(element);
6115
6116     if (belt_dir == MV_LEFT)
6117     {
6118       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6119       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6120     }
6121     else
6122     {
6123       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6124       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6125     }
6126   }
6127
6128   SCAN_PLAYFIELD(xx, yy)
6129   {
6130     int element = Feld[xx][yy];
6131
6132     if (IS_BELT_SWITCH(element))
6133     {
6134       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6135
6136       if (e_belt_nr == belt_nr)
6137       {
6138         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6139         TEST_DrawLevelField(xx, yy);
6140       }
6141     }
6142     else if (IS_BELT(element) && belt_dir != MV_NONE)
6143     {
6144       int e_belt_nr = getBeltNrFromBeltElement(element);
6145
6146       if (e_belt_nr == belt_nr)
6147       {
6148         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6149
6150         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6151         TEST_DrawLevelField(xx, yy);
6152       }
6153     }
6154     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6155     {
6156       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6157
6158       if (e_belt_nr == belt_nr)
6159       {
6160         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6161
6162         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6163         TEST_DrawLevelField(xx, yy);
6164       }
6165     }
6166   }
6167 }
6168
6169 static void ToggleSwitchgateSwitch(int x, int y)
6170 {
6171   int xx, yy;
6172
6173   game.switchgate_pos = !game.switchgate_pos;
6174
6175   SCAN_PLAYFIELD(xx, yy)
6176   {
6177     int element = Feld[xx][yy];
6178
6179     if (element == EL_SWITCHGATE_SWITCH_UP)
6180     {
6181       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6182       TEST_DrawLevelField(xx, yy);
6183     }
6184     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6185     {
6186       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6187       TEST_DrawLevelField(xx, yy);
6188     }
6189     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6190     {
6191       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6192       TEST_DrawLevelField(xx, yy);
6193     }
6194     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6195     {
6196       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6197       TEST_DrawLevelField(xx, yy);
6198     }
6199     else if (element == EL_SWITCHGATE_OPEN ||
6200              element == EL_SWITCHGATE_OPENING)
6201     {
6202       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6203
6204       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6205     }
6206     else if (element == EL_SWITCHGATE_CLOSED ||
6207              element == EL_SWITCHGATE_CLOSING)
6208     {
6209       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6210
6211       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6212     }
6213   }
6214 }
6215
6216 static int getInvisibleActiveFromInvisibleElement(int element)
6217 {
6218   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6219           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6220           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6221           element);
6222 }
6223
6224 static int getInvisibleFromInvisibleActiveElement(int element)
6225 {
6226   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6227           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6228           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6229           element);
6230 }
6231
6232 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6233 {
6234   int x, y;
6235
6236   SCAN_PLAYFIELD(x, y)
6237   {
6238     int element = Feld[x][y];
6239
6240     if (element == EL_LIGHT_SWITCH &&
6241         game.light_time_left > 0)
6242     {
6243       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6244       TEST_DrawLevelField(x, y);
6245     }
6246     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6247              game.light_time_left == 0)
6248     {
6249       Feld[x][y] = EL_LIGHT_SWITCH;
6250       TEST_DrawLevelField(x, y);
6251     }
6252     else if (element == EL_EMC_DRIPPER &&
6253              game.light_time_left > 0)
6254     {
6255       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6256       TEST_DrawLevelField(x, y);
6257     }
6258     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6259              game.light_time_left == 0)
6260     {
6261       Feld[x][y] = EL_EMC_DRIPPER;
6262       TEST_DrawLevelField(x, y);
6263     }
6264     else if (element == EL_INVISIBLE_STEELWALL ||
6265              element == EL_INVISIBLE_WALL ||
6266              element == EL_INVISIBLE_SAND)
6267     {
6268       if (game.light_time_left > 0)
6269         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6270
6271       TEST_DrawLevelField(x, y);
6272
6273       // uncrumble neighbour fields, if needed
6274       if (element == EL_INVISIBLE_SAND)
6275         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6276     }
6277     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6278              element == EL_INVISIBLE_WALL_ACTIVE ||
6279              element == EL_INVISIBLE_SAND_ACTIVE)
6280     {
6281       if (game.light_time_left == 0)
6282         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6283
6284       TEST_DrawLevelField(x, y);
6285
6286       // re-crumble neighbour fields, if needed
6287       if (element == EL_INVISIBLE_SAND)
6288         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6289     }
6290   }
6291 }
6292
6293 static void RedrawAllInvisibleElementsForLenses(void)
6294 {
6295   int x, y;
6296
6297   SCAN_PLAYFIELD(x, y)
6298   {
6299     int element = Feld[x][y];
6300
6301     if (element == EL_EMC_DRIPPER &&
6302         game.lenses_time_left > 0)
6303     {
6304       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6305       TEST_DrawLevelField(x, y);
6306     }
6307     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6308              game.lenses_time_left == 0)
6309     {
6310       Feld[x][y] = EL_EMC_DRIPPER;
6311       TEST_DrawLevelField(x, y);
6312     }
6313     else if (element == EL_INVISIBLE_STEELWALL ||
6314              element == EL_INVISIBLE_WALL ||
6315              element == EL_INVISIBLE_SAND)
6316     {
6317       if (game.lenses_time_left > 0)
6318         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6319
6320       TEST_DrawLevelField(x, y);
6321
6322       // uncrumble neighbour fields, if needed
6323       if (element == EL_INVISIBLE_SAND)
6324         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6325     }
6326     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6327              element == EL_INVISIBLE_WALL_ACTIVE ||
6328              element == EL_INVISIBLE_SAND_ACTIVE)
6329     {
6330       if (game.lenses_time_left == 0)
6331         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6332
6333       TEST_DrawLevelField(x, y);
6334
6335       // re-crumble neighbour fields, if needed
6336       if (element == EL_INVISIBLE_SAND)
6337         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6338     }
6339   }
6340 }
6341
6342 static void RedrawAllInvisibleElementsForMagnifier(void)
6343 {
6344   int x, y;
6345
6346   SCAN_PLAYFIELD(x, y)
6347   {
6348     int element = Feld[x][y];
6349
6350     if (element == EL_EMC_FAKE_GRASS &&
6351         game.magnify_time_left > 0)
6352     {
6353       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6354       TEST_DrawLevelField(x, y);
6355     }
6356     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6357              game.magnify_time_left == 0)
6358     {
6359       Feld[x][y] = EL_EMC_FAKE_GRASS;
6360       TEST_DrawLevelField(x, y);
6361     }
6362     else if (IS_GATE_GRAY(element) &&
6363              game.magnify_time_left > 0)
6364     {
6365       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6366                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6367                     IS_EM_GATE_GRAY(element) ?
6368                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6369                     IS_EMC_GATE_GRAY(element) ?
6370                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6371                     IS_DC_GATE_GRAY(element) ?
6372                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6373                     element);
6374       TEST_DrawLevelField(x, y);
6375     }
6376     else if (IS_GATE_GRAY_ACTIVE(element) &&
6377              game.magnify_time_left == 0)
6378     {
6379       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6380                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6381                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6382                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6383                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6384                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6385                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6386                     EL_DC_GATE_WHITE_GRAY :
6387                     element);
6388       TEST_DrawLevelField(x, y);
6389     }
6390   }
6391 }
6392
6393 static void ToggleLightSwitch(int x, int y)
6394 {
6395   int element = Feld[x][y];
6396
6397   game.light_time_left =
6398     (element == EL_LIGHT_SWITCH ?
6399      level.time_light * FRAMES_PER_SECOND : 0);
6400
6401   RedrawAllLightSwitchesAndInvisibleElements();
6402 }
6403
6404 static void ActivateTimegateSwitch(int x, int y)
6405 {
6406   int xx, yy;
6407
6408   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6409
6410   SCAN_PLAYFIELD(xx, yy)
6411   {
6412     int element = Feld[xx][yy];
6413
6414     if (element == EL_TIMEGATE_CLOSED ||
6415         element == EL_TIMEGATE_CLOSING)
6416     {
6417       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6418       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6419     }
6420
6421     /*
6422     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6423     {
6424       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6425       TEST_DrawLevelField(xx, yy);
6426     }
6427     */
6428
6429   }
6430
6431   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6432                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6433 }
6434
6435 static void Impact(int x, int y)
6436 {
6437   boolean last_line = (y == lev_fieldy - 1);
6438   boolean object_hit = FALSE;
6439   boolean impact = (last_line || object_hit);
6440   int element = Feld[x][y];
6441   int smashed = EL_STEELWALL;
6442
6443   if (!last_line)       // check if element below was hit
6444   {
6445     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6446       return;
6447
6448     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6449                                          MovDir[x][y + 1] != MV_DOWN ||
6450                                          MovPos[x][y + 1] <= TILEY / 2));
6451
6452     // do not smash moving elements that left the smashed field in time
6453     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6454         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6455       object_hit = FALSE;
6456
6457 #if USE_QUICKSAND_IMPACT_BUGFIX
6458     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6459     {
6460       RemoveMovingField(x, y + 1);
6461       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6462       Feld[x][y + 2] = EL_ROCK;
6463       TEST_DrawLevelField(x, y + 2);
6464
6465       object_hit = TRUE;
6466     }
6467
6468     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6469     {
6470       RemoveMovingField(x, y + 1);
6471       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6472       Feld[x][y + 2] = EL_ROCK;
6473       TEST_DrawLevelField(x, y + 2);
6474
6475       object_hit = TRUE;
6476     }
6477 #endif
6478
6479     if (object_hit)
6480       smashed = MovingOrBlocked2Element(x, y + 1);
6481
6482     impact = (last_line || object_hit);
6483   }
6484
6485   if (!last_line && smashed == EL_ACID) // element falls into acid
6486   {
6487     SplashAcid(x, y + 1);
6488     return;
6489   }
6490
6491   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6492   // only reset graphic animation if graphic really changes after impact
6493   if (impact &&
6494       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6495   {
6496     ResetGfxAnimation(x, y);
6497     TEST_DrawLevelField(x, y);
6498   }
6499
6500   if (impact && CAN_EXPLODE_IMPACT(element))
6501   {
6502     Bang(x, y);
6503     return;
6504   }
6505   else if (impact && element == EL_PEARL &&
6506            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6507   {
6508     ResetGfxAnimation(x, y);
6509
6510     Feld[x][y] = EL_PEARL_BREAKING;
6511     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6512     return;
6513   }
6514   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6515   {
6516     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6517
6518     return;
6519   }
6520
6521   if (impact && element == EL_AMOEBA_DROP)
6522   {
6523     if (object_hit && IS_PLAYER(x, y + 1))
6524       KillPlayerUnlessEnemyProtected(x, y + 1);
6525     else if (object_hit && smashed == EL_PENGUIN)
6526       Bang(x, y + 1);
6527     else
6528     {
6529       Feld[x][y] = EL_AMOEBA_GROWING;
6530       Store[x][y] = EL_AMOEBA_WET;
6531
6532       ResetRandomAnimationValue(x, y);
6533     }
6534     return;
6535   }
6536
6537   if (object_hit)               // check which object was hit
6538   {
6539     if ((CAN_PASS_MAGIC_WALL(element) && 
6540          (smashed == EL_MAGIC_WALL ||
6541           smashed == EL_BD_MAGIC_WALL)) ||
6542         (CAN_PASS_DC_MAGIC_WALL(element) &&
6543          smashed == EL_DC_MAGIC_WALL))
6544     {
6545       int xx, yy;
6546       int activated_magic_wall =
6547         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6548          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6549          EL_DC_MAGIC_WALL_ACTIVE);
6550
6551       // activate magic wall / mill
6552       SCAN_PLAYFIELD(xx, yy)
6553       {
6554         if (Feld[xx][yy] == smashed)
6555           Feld[xx][yy] = activated_magic_wall;
6556       }
6557
6558       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6559       game.magic_wall_active = TRUE;
6560
6561       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6562                             SND_MAGIC_WALL_ACTIVATING :
6563                             smashed == EL_BD_MAGIC_WALL ?
6564                             SND_BD_MAGIC_WALL_ACTIVATING :
6565                             SND_DC_MAGIC_WALL_ACTIVATING));
6566     }
6567
6568     if (IS_PLAYER(x, y + 1))
6569     {
6570       if (CAN_SMASH_PLAYER(element))
6571       {
6572         KillPlayerUnlessEnemyProtected(x, y + 1);
6573         return;
6574       }
6575     }
6576     else if (smashed == EL_PENGUIN)
6577     {
6578       if (CAN_SMASH_PLAYER(element))
6579       {
6580         Bang(x, y + 1);
6581         return;
6582       }
6583     }
6584     else if (element == EL_BD_DIAMOND)
6585     {
6586       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6587       {
6588         Bang(x, y + 1);
6589         return;
6590       }
6591     }
6592     else if (((element == EL_SP_INFOTRON ||
6593                element == EL_SP_ZONK) &&
6594               (smashed == EL_SP_SNIKSNAK ||
6595                smashed == EL_SP_ELECTRON ||
6596                smashed == EL_SP_DISK_ORANGE)) ||
6597              (element == EL_SP_INFOTRON &&
6598               smashed == EL_SP_DISK_YELLOW))
6599     {
6600       Bang(x, y + 1);
6601       return;
6602     }
6603     else if (CAN_SMASH_EVERYTHING(element))
6604     {
6605       if (IS_CLASSIC_ENEMY(smashed) ||
6606           CAN_EXPLODE_SMASHED(smashed))
6607       {
6608         Bang(x, y + 1);
6609         return;
6610       }
6611       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6612       {
6613         if (smashed == EL_LAMP ||
6614             smashed == EL_LAMP_ACTIVE)
6615         {
6616           Bang(x, y + 1);
6617           return;
6618         }
6619         else if (smashed == EL_NUT)
6620         {
6621           Feld[x][y + 1] = EL_NUT_BREAKING;
6622           PlayLevelSound(x, y, SND_NUT_BREAKING);
6623           RaiseScoreElement(EL_NUT);
6624           return;
6625         }
6626         else if (smashed == EL_PEARL)
6627         {
6628           ResetGfxAnimation(x, y);
6629
6630           Feld[x][y + 1] = EL_PEARL_BREAKING;
6631           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6632           return;
6633         }
6634         else if (smashed == EL_DIAMOND)
6635         {
6636           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6637           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6638           return;
6639         }
6640         else if (IS_BELT_SWITCH(smashed))
6641         {
6642           ToggleBeltSwitch(x, y + 1);
6643         }
6644         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6645                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6646                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6647                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6648         {
6649           ToggleSwitchgateSwitch(x, y + 1);
6650         }
6651         else if (smashed == EL_LIGHT_SWITCH ||
6652                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6653         {
6654           ToggleLightSwitch(x, y + 1);
6655         }
6656         else
6657         {
6658           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6659
6660           CheckElementChangeBySide(x, y + 1, smashed, element,
6661                                    CE_SWITCHED, CH_SIDE_TOP);
6662           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6663                                             CH_SIDE_TOP);
6664         }
6665       }
6666       else
6667       {
6668         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6669       }
6670     }
6671   }
6672
6673   // play sound of magic wall / mill
6674   if (!last_line &&
6675       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6676        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6677        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6678   {
6679     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6680       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6681     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6682       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6683     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6684       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6685
6686     return;
6687   }
6688
6689   // play sound of object that hits the ground
6690   if (last_line || object_hit)
6691     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6692 }
6693
6694 static void TurnRoundExt(int x, int y)
6695 {
6696   static struct
6697   {
6698     int dx, dy;
6699   } move_xy[] =
6700   {
6701     {  0,  0 },
6702     { -1,  0 },
6703     { +1,  0 },
6704     {  0,  0 },
6705     {  0, -1 },
6706     {  0,  0 }, { 0, 0 }, { 0, 0 },
6707     {  0, +1 }
6708   };
6709   static struct
6710   {
6711     int left, right, back;
6712   } turn[] =
6713   {
6714     { 0,        0,              0        },
6715     { MV_DOWN,  MV_UP,          MV_RIGHT },
6716     { MV_UP,    MV_DOWN,        MV_LEFT  },
6717     { 0,        0,              0        },
6718     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6719     { 0,        0,              0        },
6720     { 0,        0,              0        },
6721     { 0,        0,              0        },
6722     { MV_RIGHT, MV_LEFT,        MV_UP    }
6723   };
6724
6725   int element = Feld[x][y];
6726   int move_pattern = element_info[element].move_pattern;
6727
6728   int old_move_dir = MovDir[x][y];
6729   int left_dir  = turn[old_move_dir].left;
6730   int right_dir = turn[old_move_dir].right;
6731   int back_dir  = turn[old_move_dir].back;
6732
6733   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6734   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6735   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6736   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6737
6738   int left_x  = x + left_dx,  left_y  = y + left_dy;
6739   int right_x = x + right_dx, right_y = y + right_dy;
6740   int move_x  = x + move_dx,  move_y  = y + move_dy;
6741
6742   int xx, yy;
6743
6744   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6745   {
6746     TestIfBadThingTouchesOtherBadThing(x, y);
6747
6748     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6749       MovDir[x][y] = right_dir;
6750     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6751       MovDir[x][y] = left_dir;
6752
6753     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6754       MovDelay[x][y] = 9;
6755     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6756       MovDelay[x][y] = 1;
6757   }
6758   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6759   {
6760     TestIfBadThingTouchesOtherBadThing(x, y);
6761
6762     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6763       MovDir[x][y] = left_dir;
6764     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6765       MovDir[x][y] = right_dir;
6766
6767     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6768       MovDelay[x][y] = 9;
6769     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6770       MovDelay[x][y] = 1;
6771   }
6772   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6773   {
6774     TestIfBadThingTouchesOtherBadThing(x, y);
6775
6776     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6777       MovDir[x][y] = left_dir;
6778     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6779       MovDir[x][y] = right_dir;
6780
6781     if (MovDir[x][y] != old_move_dir)
6782       MovDelay[x][y] = 9;
6783   }
6784   else if (element == EL_YAMYAM)
6785   {
6786     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6787     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6788
6789     if (can_turn_left && can_turn_right)
6790       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6791     else if (can_turn_left)
6792       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6793     else if (can_turn_right)
6794       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6795     else
6796       MovDir[x][y] = back_dir;
6797
6798     MovDelay[x][y] = 16 + 16 * RND(3);
6799   }
6800   else if (element == EL_DARK_YAMYAM)
6801   {
6802     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6803                                                          left_x, left_y);
6804     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6805                                                          right_x, right_y);
6806
6807     if (can_turn_left && can_turn_right)
6808       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6809     else if (can_turn_left)
6810       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6811     else if (can_turn_right)
6812       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6813     else
6814       MovDir[x][y] = back_dir;
6815
6816     MovDelay[x][y] = 16 + 16 * RND(3);
6817   }
6818   else if (element == EL_PACMAN)
6819   {
6820     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6821     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6822
6823     if (can_turn_left && can_turn_right)
6824       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6825     else if (can_turn_left)
6826       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6827     else if (can_turn_right)
6828       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6829     else
6830       MovDir[x][y] = back_dir;
6831
6832     MovDelay[x][y] = 6 + RND(40);
6833   }
6834   else if (element == EL_PIG)
6835   {
6836     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6837     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6838     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6839     boolean should_turn_left, should_turn_right, should_move_on;
6840     int rnd_value = 24;
6841     int rnd = RND(rnd_value);
6842
6843     should_turn_left = (can_turn_left &&
6844                         (!can_move_on ||
6845                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6846                                                    y + back_dy + left_dy)));
6847     should_turn_right = (can_turn_right &&
6848                          (!can_move_on ||
6849                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6850                                                     y + back_dy + right_dy)));
6851     should_move_on = (can_move_on &&
6852                       (!can_turn_left ||
6853                        !can_turn_right ||
6854                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6855                                                  y + move_dy + left_dy) ||
6856                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6857                                                  y + move_dy + right_dy)));
6858
6859     if (should_turn_left || should_turn_right || should_move_on)
6860     {
6861       if (should_turn_left && should_turn_right && should_move_on)
6862         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6863                         rnd < 2 * rnd_value / 3 ? right_dir :
6864                         old_move_dir);
6865       else if (should_turn_left && should_turn_right)
6866         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6867       else if (should_turn_left && should_move_on)
6868         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6869       else if (should_turn_right && should_move_on)
6870         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6871       else if (should_turn_left)
6872         MovDir[x][y] = left_dir;
6873       else if (should_turn_right)
6874         MovDir[x][y] = right_dir;
6875       else if (should_move_on)
6876         MovDir[x][y] = old_move_dir;
6877     }
6878     else if (can_move_on && rnd > rnd_value / 8)
6879       MovDir[x][y] = old_move_dir;
6880     else if (can_turn_left && can_turn_right)
6881       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6882     else if (can_turn_left && rnd > rnd_value / 8)
6883       MovDir[x][y] = left_dir;
6884     else if (can_turn_right && rnd > rnd_value/8)
6885       MovDir[x][y] = right_dir;
6886     else
6887       MovDir[x][y] = back_dir;
6888
6889     xx = x + move_xy[MovDir[x][y]].dx;
6890     yy = y + move_xy[MovDir[x][y]].dy;
6891
6892     if (!IN_LEV_FIELD(xx, yy) ||
6893         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6894       MovDir[x][y] = old_move_dir;
6895
6896     MovDelay[x][y] = 0;
6897   }
6898   else if (element == EL_DRAGON)
6899   {
6900     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6901     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6902     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6903     int rnd_value = 24;
6904     int rnd = RND(rnd_value);
6905
6906     if (can_move_on && rnd > rnd_value / 8)
6907       MovDir[x][y] = old_move_dir;
6908     else if (can_turn_left && can_turn_right)
6909       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6910     else if (can_turn_left && rnd > rnd_value / 8)
6911       MovDir[x][y] = left_dir;
6912     else if (can_turn_right && rnd > rnd_value / 8)
6913       MovDir[x][y] = right_dir;
6914     else
6915       MovDir[x][y] = back_dir;
6916
6917     xx = x + move_xy[MovDir[x][y]].dx;
6918     yy = y + move_xy[MovDir[x][y]].dy;
6919
6920     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6921       MovDir[x][y] = old_move_dir;
6922
6923     MovDelay[x][y] = 0;
6924   }
6925   else if (element == EL_MOLE)
6926   {
6927     boolean can_move_on =
6928       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6929                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6930                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6931     if (!can_move_on)
6932     {
6933       boolean can_turn_left =
6934         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6935                               IS_AMOEBOID(Feld[left_x][left_y])));
6936
6937       boolean can_turn_right =
6938         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6939                               IS_AMOEBOID(Feld[right_x][right_y])));
6940
6941       if (can_turn_left && can_turn_right)
6942         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6943       else if (can_turn_left)
6944         MovDir[x][y] = left_dir;
6945       else
6946         MovDir[x][y] = right_dir;
6947     }
6948
6949     if (MovDir[x][y] != old_move_dir)
6950       MovDelay[x][y] = 9;
6951   }
6952   else if (element == EL_BALLOON)
6953   {
6954     MovDir[x][y] = game.wind_direction;
6955     MovDelay[x][y] = 0;
6956   }
6957   else if (element == EL_SPRING)
6958   {
6959     if (MovDir[x][y] & MV_HORIZONTAL)
6960     {
6961       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6962           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6963       {
6964         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6965         ResetGfxAnimation(move_x, move_y);
6966         TEST_DrawLevelField(move_x, move_y);
6967
6968         MovDir[x][y] = back_dir;
6969       }
6970       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6971                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6972         MovDir[x][y] = MV_NONE;
6973     }
6974
6975     MovDelay[x][y] = 0;
6976   }
6977   else if (element == EL_ROBOT ||
6978            element == EL_SATELLITE ||
6979            element == EL_PENGUIN ||
6980            element == EL_EMC_ANDROID)
6981   {
6982     int attr_x = -1, attr_y = -1;
6983
6984     if (game.all_players_gone)
6985     {
6986       attr_x = game.exit_x;
6987       attr_y = game.exit_y;
6988     }
6989     else
6990     {
6991       int i;
6992
6993       for (i = 0; i < MAX_PLAYERS; i++)
6994       {
6995         struct PlayerInfo *player = &stored_player[i];
6996         int jx = player->jx, jy = player->jy;
6997
6998         if (!player->active)
6999           continue;
7000
7001         if (attr_x == -1 ||
7002             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7003         {
7004           attr_x = jx;
7005           attr_y = jy;
7006         }
7007       }
7008     }
7009
7010     if (element == EL_ROBOT &&
7011         game.robot_wheel_x >= 0 &&
7012         game.robot_wheel_y >= 0 &&
7013         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7014          game.engine_version < VERSION_IDENT(3,1,0,0)))
7015     {
7016       attr_x = game.robot_wheel_x;
7017       attr_y = game.robot_wheel_y;
7018     }
7019
7020     if (element == EL_PENGUIN)
7021     {
7022       int i;
7023       static int xy[4][2] =
7024       {
7025         { 0, -1 },
7026         { -1, 0 },
7027         { +1, 0 },
7028         { 0, +1 }
7029       };
7030
7031       for (i = 0; i < NUM_DIRECTIONS; i++)
7032       {
7033         int ex = x + xy[i][0];
7034         int ey = y + xy[i][1];
7035
7036         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7037                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7038                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7039                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7040         {
7041           attr_x = ex;
7042           attr_y = ey;
7043           break;
7044         }
7045       }
7046     }
7047
7048     MovDir[x][y] = MV_NONE;
7049     if (attr_x < x)
7050       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7051     else if (attr_x > x)
7052       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7053     if (attr_y < y)
7054       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7055     else if (attr_y > y)
7056       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7057
7058     if (element == EL_ROBOT)
7059     {
7060       int newx, newy;
7061
7062       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7063         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7064       Moving2Blocked(x, y, &newx, &newy);
7065
7066       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7067         MovDelay[x][y] = 8 + 8 * !RND(3);
7068       else
7069         MovDelay[x][y] = 16;
7070     }
7071     else if (element == EL_PENGUIN)
7072     {
7073       int newx, newy;
7074
7075       MovDelay[x][y] = 1;
7076
7077       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7078       {
7079         boolean first_horiz = RND(2);
7080         int new_move_dir = MovDir[x][y];
7081
7082         MovDir[x][y] =
7083           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7084         Moving2Blocked(x, y, &newx, &newy);
7085
7086         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7087           return;
7088
7089         MovDir[x][y] =
7090           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7091         Moving2Blocked(x, y, &newx, &newy);
7092
7093         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7094           return;
7095
7096         MovDir[x][y] = old_move_dir;
7097         return;
7098       }
7099     }
7100     else if (element == EL_SATELLITE)
7101     {
7102       int newx, newy;
7103
7104       MovDelay[x][y] = 1;
7105
7106       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7107       {
7108         boolean first_horiz = RND(2);
7109         int new_move_dir = MovDir[x][y];
7110
7111         MovDir[x][y] =
7112           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7113         Moving2Blocked(x, y, &newx, &newy);
7114
7115         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7116           return;
7117
7118         MovDir[x][y] =
7119           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7120         Moving2Blocked(x, y, &newx, &newy);
7121
7122         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7123           return;
7124
7125         MovDir[x][y] = old_move_dir;
7126         return;
7127       }
7128     }
7129     else if (element == EL_EMC_ANDROID)
7130     {
7131       static int check_pos[16] =
7132       {
7133         -1,             //  0 => (invalid)
7134         7,              //  1 => MV_LEFT
7135         3,              //  2 => MV_RIGHT
7136         -1,             //  3 => (invalid)
7137         1,              //  4 =>            MV_UP
7138         0,              //  5 => MV_LEFT  | MV_UP
7139         2,              //  6 => MV_RIGHT | MV_UP
7140         -1,             //  7 => (invalid)
7141         5,              //  8 =>            MV_DOWN
7142         6,              //  9 => MV_LEFT  | MV_DOWN
7143         4,              // 10 => MV_RIGHT | MV_DOWN
7144         -1,             // 11 => (invalid)
7145         -1,             // 12 => (invalid)
7146         -1,             // 13 => (invalid)
7147         -1,             // 14 => (invalid)
7148         -1,             // 15 => (invalid)
7149       };
7150       static struct
7151       {
7152         int dx, dy;
7153         int dir;
7154       } check_xy[8] =
7155       {
7156         { -1, -1,       MV_LEFT  | MV_UP   },
7157         {  0, -1,                  MV_UP   },
7158         { +1, -1,       MV_RIGHT | MV_UP   },
7159         { +1,  0,       MV_RIGHT           },
7160         { +1, +1,       MV_RIGHT | MV_DOWN },
7161         {  0, +1,                  MV_DOWN },
7162         { -1, +1,       MV_LEFT  | MV_DOWN },
7163         { -1,  0,       MV_LEFT            },
7164       };
7165       int start_pos, check_order;
7166       boolean can_clone = FALSE;
7167       int i;
7168
7169       // check if there is any free field around current position
7170       for (i = 0; i < 8; i++)
7171       {
7172         int newx = x + check_xy[i].dx;
7173         int newy = y + check_xy[i].dy;
7174
7175         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7176         {
7177           can_clone = TRUE;
7178
7179           break;
7180         }
7181       }
7182
7183       if (can_clone)            // randomly find an element to clone
7184       {
7185         can_clone = FALSE;
7186
7187         start_pos = check_pos[RND(8)];
7188         check_order = (RND(2) ? -1 : +1);
7189
7190         for (i = 0; i < 8; i++)
7191         {
7192           int pos_raw = start_pos + i * check_order;
7193           int pos = (pos_raw + 8) % 8;
7194           int newx = x + check_xy[pos].dx;
7195           int newy = y + check_xy[pos].dy;
7196
7197           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7198           {
7199             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7200             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7201
7202             Store[x][y] = Feld[newx][newy];
7203
7204             can_clone = TRUE;
7205
7206             break;
7207           }
7208         }
7209       }
7210
7211       if (can_clone)            // randomly find a direction to move
7212       {
7213         can_clone = FALSE;
7214
7215         start_pos = check_pos[RND(8)];
7216         check_order = (RND(2) ? -1 : +1);
7217
7218         for (i = 0; i < 8; i++)
7219         {
7220           int pos_raw = start_pos + i * check_order;
7221           int pos = (pos_raw + 8) % 8;
7222           int newx = x + check_xy[pos].dx;
7223           int newy = y + check_xy[pos].dy;
7224           int new_move_dir = check_xy[pos].dir;
7225
7226           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7227           {
7228             MovDir[x][y] = new_move_dir;
7229             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7230
7231             can_clone = TRUE;
7232
7233             break;
7234           }
7235         }
7236       }
7237
7238       if (can_clone)            // cloning and moving successful
7239         return;
7240
7241       // cannot clone -- try to move towards player
7242
7243       start_pos = check_pos[MovDir[x][y] & 0x0f];
7244       check_order = (RND(2) ? -1 : +1);
7245
7246       for (i = 0; i < 3; i++)
7247       {
7248         // first check start_pos, then previous/next or (next/previous) pos
7249         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7250         int pos = (pos_raw + 8) % 8;
7251         int newx = x + check_xy[pos].dx;
7252         int newy = y + check_xy[pos].dy;
7253         int new_move_dir = check_xy[pos].dir;
7254
7255         if (IS_PLAYER(newx, newy))
7256           break;
7257
7258         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7259         {
7260           MovDir[x][y] = new_move_dir;
7261           MovDelay[x][y] = level.android_move_time * 8 + 1;
7262
7263           break;
7264         }
7265       }
7266     }
7267   }
7268   else if (move_pattern == MV_TURNING_LEFT ||
7269            move_pattern == MV_TURNING_RIGHT ||
7270            move_pattern == MV_TURNING_LEFT_RIGHT ||
7271            move_pattern == MV_TURNING_RIGHT_LEFT ||
7272            move_pattern == MV_TURNING_RANDOM ||
7273            move_pattern == MV_ALL_DIRECTIONS)
7274   {
7275     boolean can_turn_left =
7276       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7277     boolean can_turn_right =
7278       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7279
7280     if (element_info[element].move_stepsize == 0)       // "not moving"
7281       return;
7282
7283     if (move_pattern == MV_TURNING_LEFT)
7284       MovDir[x][y] = left_dir;
7285     else if (move_pattern == MV_TURNING_RIGHT)
7286       MovDir[x][y] = right_dir;
7287     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7288       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7289     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7290       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7291     else if (move_pattern == MV_TURNING_RANDOM)
7292       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7293                       can_turn_right && !can_turn_left ? right_dir :
7294                       RND(2) ? left_dir : right_dir);
7295     else if (can_turn_left && can_turn_right)
7296       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7297     else if (can_turn_left)
7298       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7299     else if (can_turn_right)
7300       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7301     else
7302       MovDir[x][y] = back_dir;
7303
7304     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7305   }
7306   else if (move_pattern == MV_HORIZONTAL ||
7307            move_pattern == MV_VERTICAL)
7308   {
7309     if (move_pattern & old_move_dir)
7310       MovDir[x][y] = back_dir;
7311     else if (move_pattern == MV_HORIZONTAL)
7312       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7313     else if (move_pattern == MV_VERTICAL)
7314       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7315
7316     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7317   }
7318   else if (move_pattern & MV_ANY_DIRECTION)
7319   {
7320     MovDir[x][y] = move_pattern;
7321     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7322   }
7323   else if (move_pattern & MV_WIND_DIRECTION)
7324   {
7325     MovDir[x][y] = game.wind_direction;
7326     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7327   }
7328   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7329   {
7330     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7331       MovDir[x][y] = left_dir;
7332     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7333       MovDir[x][y] = right_dir;
7334
7335     if (MovDir[x][y] != old_move_dir)
7336       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7337   }
7338   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7339   {
7340     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7341       MovDir[x][y] = right_dir;
7342     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7343       MovDir[x][y] = left_dir;
7344
7345     if (MovDir[x][y] != old_move_dir)
7346       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7347   }
7348   else if (move_pattern == MV_TOWARDS_PLAYER ||
7349            move_pattern == MV_AWAY_FROM_PLAYER)
7350   {
7351     int attr_x = -1, attr_y = -1;
7352     int newx, newy;
7353     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7354
7355     if (game.all_players_gone)
7356     {
7357       attr_x = game.exit_x;
7358       attr_y = game.exit_y;
7359     }
7360     else
7361     {
7362       int i;
7363
7364       for (i = 0; i < MAX_PLAYERS; i++)
7365       {
7366         struct PlayerInfo *player = &stored_player[i];
7367         int jx = player->jx, jy = player->jy;
7368
7369         if (!player->active)
7370           continue;
7371
7372         if (attr_x == -1 ||
7373             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7374         {
7375           attr_x = jx;
7376           attr_y = jy;
7377         }
7378       }
7379     }
7380
7381     MovDir[x][y] = MV_NONE;
7382     if (attr_x < x)
7383       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7384     else if (attr_x > x)
7385       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7386     if (attr_y < y)
7387       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7388     else if (attr_y > y)
7389       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7390
7391     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7392
7393     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7394     {
7395       boolean first_horiz = RND(2);
7396       int new_move_dir = MovDir[x][y];
7397
7398       if (element_info[element].move_stepsize == 0)     // "not moving"
7399       {
7400         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7401         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7402
7403         return;
7404       }
7405
7406       MovDir[x][y] =
7407         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7408       Moving2Blocked(x, y, &newx, &newy);
7409
7410       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7411         return;
7412
7413       MovDir[x][y] =
7414         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7415       Moving2Blocked(x, y, &newx, &newy);
7416
7417       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7418         return;
7419
7420       MovDir[x][y] = old_move_dir;
7421     }
7422   }
7423   else if (move_pattern == MV_WHEN_PUSHED ||
7424            move_pattern == MV_WHEN_DROPPED)
7425   {
7426     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7427       MovDir[x][y] = MV_NONE;
7428
7429     MovDelay[x][y] = 0;
7430   }
7431   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7432   {
7433     static int test_xy[7][2] =
7434     {
7435       { 0, -1 },
7436       { -1, 0 },
7437       { +1, 0 },
7438       { 0, +1 },
7439       { 0, -1 },
7440       { -1, 0 },
7441       { +1, 0 },
7442     };
7443     static int test_dir[7] =
7444     {
7445       MV_UP,
7446       MV_LEFT,
7447       MV_RIGHT,
7448       MV_DOWN,
7449       MV_UP,
7450       MV_LEFT,
7451       MV_RIGHT,
7452     };
7453     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7454     int move_preference = -1000000;     // start with very low preference
7455     int new_move_dir = MV_NONE;
7456     int start_test = RND(4);
7457     int i;
7458
7459     for (i = 0; i < NUM_DIRECTIONS; i++)
7460     {
7461       int move_dir = test_dir[start_test + i];
7462       int move_dir_preference;
7463
7464       xx = x + test_xy[start_test + i][0];
7465       yy = y + test_xy[start_test + i][1];
7466
7467       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7468           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7469       {
7470         new_move_dir = move_dir;
7471
7472         break;
7473       }
7474
7475       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7476         continue;
7477
7478       move_dir_preference = -1 * RunnerVisit[xx][yy];
7479       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7480         move_dir_preference = PlayerVisit[xx][yy];
7481
7482       if (move_dir_preference > move_preference)
7483       {
7484         // prefer field that has not been visited for the longest time
7485         move_preference = move_dir_preference;
7486         new_move_dir = move_dir;
7487       }
7488       else if (move_dir_preference == move_preference &&
7489                move_dir == old_move_dir)
7490       {
7491         // prefer last direction when all directions are preferred equally
7492         move_preference = move_dir_preference;
7493         new_move_dir = move_dir;
7494       }
7495     }
7496
7497     MovDir[x][y] = new_move_dir;
7498     if (old_move_dir != new_move_dir)
7499       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7500   }
7501 }
7502
7503 static void TurnRound(int x, int y)
7504 {
7505   int direction = MovDir[x][y];
7506
7507   TurnRoundExt(x, y);
7508
7509   GfxDir[x][y] = MovDir[x][y];
7510
7511   if (direction != MovDir[x][y])
7512     GfxFrame[x][y] = 0;
7513
7514   if (MovDelay[x][y])
7515     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7516
7517   ResetGfxFrame(x, y);
7518 }
7519
7520 static boolean JustBeingPushed(int x, int y)
7521 {
7522   int i;
7523
7524   for (i = 0; i < MAX_PLAYERS; i++)
7525   {
7526     struct PlayerInfo *player = &stored_player[i];
7527
7528     if (player->active && player->is_pushing && player->MovPos)
7529     {
7530       int next_jx = player->jx + (player->jx - player->last_jx);
7531       int next_jy = player->jy + (player->jy - player->last_jy);
7532
7533       if (x == next_jx && y == next_jy)
7534         return TRUE;
7535     }
7536   }
7537
7538   return FALSE;
7539 }
7540
7541 static void StartMoving(int x, int y)
7542 {
7543   boolean started_moving = FALSE;       // some elements can fall _and_ move
7544   int element = Feld[x][y];
7545
7546   if (Stop[x][y])
7547     return;
7548
7549   if (MovDelay[x][y] == 0)
7550     GfxAction[x][y] = ACTION_DEFAULT;
7551
7552   if (CAN_FALL(element) && y < lev_fieldy - 1)
7553   {
7554     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7555         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7556       if (JustBeingPushed(x, y))
7557         return;
7558
7559     if (element == EL_QUICKSAND_FULL)
7560     {
7561       if (IS_FREE(x, y + 1))
7562       {
7563         InitMovingField(x, y, MV_DOWN);
7564         started_moving = TRUE;
7565
7566         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7567 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7568         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7569           Store[x][y] = EL_ROCK;
7570 #else
7571         Store[x][y] = EL_ROCK;
7572 #endif
7573
7574         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7575       }
7576       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7577       {
7578         if (!MovDelay[x][y])
7579         {
7580           MovDelay[x][y] = TILEY + 1;
7581
7582           ResetGfxAnimation(x, y);
7583           ResetGfxAnimation(x, y + 1);
7584         }
7585
7586         if (MovDelay[x][y])
7587         {
7588           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7589           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7590
7591           MovDelay[x][y]--;
7592           if (MovDelay[x][y])
7593             return;
7594         }
7595
7596         Feld[x][y] = EL_QUICKSAND_EMPTY;
7597         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7598         Store[x][y + 1] = Store[x][y];
7599         Store[x][y] = 0;
7600
7601         PlayLevelSoundAction(x, y, ACTION_FILLING);
7602       }
7603       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7604       {
7605         if (!MovDelay[x][y])
7606         {
7607           MovDelay[x][y] = TILEY + 1;
7608
7609           ResetGfxAnimation(x, y);
7610           ResetGfxAnimation(x, y + 1);
7611         }
7612
7613         if (MovDelay[x][y])
7614         {
7615           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7616           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7617
7618           MovDelay[x][y]--;
7619           if (MovDelay[x][y])
7620             return;
7621         }
7622
7623         Feld[x][y] = EL_QUICKSAND_EMPTY;
7624         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7625         Store[x][y + 1] = Store[x][y];
7626         Store[x][y] = 0;
7627
7628         PlayLevelSoundAction(x, y, ACTION_FILLING);
7629       }
7630     }
7631     else if (element == EL_QUICKSAND_FAST_FULL)
7632     {
7633       if (IS_FREE(x, y + 1))
7634       {
7635         InitMovingField(x, y, MV_DOWN);
7636         started_moving = TRUE;
7637
7638         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7639 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7640         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7641           Store[x][y] = EL_ROCK;
7642 #else
7643         Store[x][y] = EL_ROCK;
7644 #endif
7645
7646         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7647       }
7648       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7649       {
7650         if (!MovDelay[x][y])
7651         {
7652           MovDelay[x][y] = TILEY + 1;
7653
7654           ResetGfxAnimation(x, y);
7655           ResetGfxAnimation(x, y + 1);
7656         }
7657
7658         if (MovDelay[x][y])
7659         {
7660           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7661           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7662
7663           MovDelay[x][y]--;
7664           if (MovDelay[x][y])
7665             return;
7666         }
7667
7668         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7669         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7670         Store[x][y + 1] = Store[x][y];
7671         Store[x][y] = 0;
7672
7673         PlayLevelSoundAction(x, y, ACTION_FILLING);
7674       }
7675       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7676       {
7677         if (!MovDelay[x][y])
7678         {
7679           MovDelay[x][y] = TILEY + 1;
7680
7681           ResetGfxAnimation(x, y);
7682           ResetGfxAnimation(x, y + 1);
7683         }
7684
7685         if (MovDelay[x][y])
7686         {
7687           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7688           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7689
7690           MovDelay[x][y]--;
7691           if (MovDelay[x][y])
7692             return;
7693         }
7694
7695         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7696         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7697         Store[x][y + 1] = Store[x][y];
7698         Store[x][y] = 0;
7699
7700         PlayLevelSoundAction(x, y, ACTION_FILLING);
7701       }
7702     }
7703     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7704              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7705     {
7706       InitMovingField(x, y, MV_DOWN);
7707       started_moving = TRUE;
7708
7709       Feld[x][y] = EL_QUICKSAND_FILLING;
7710       Store[x][y] = element;
7711
7712       PlayLevelSoundAction(x, y, ACTION_FILLING);
7713     }
7714     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7715              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7716     {
7717       InitMovingField(x, y, MV_DOWN);
7718       started_moving = TRUE;
7719
7720       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7721       Store[x][y] = element;
7722
7723       PlayLevelSoundAction(x, y, ACTION_FILLING);
7724     }
7725     else if (element == EL_MAGIC_WALL_FULL)
7726     {
7727       if (IS_FREE(x, y + 1))
7728       {
7729         InitMovingField(x, y, MV_DOWN);
7730         started_moving = TRUE;
7731
7732         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7733         Store[x][y] = EL_CHANGED(Store[x][y]);
7734       }
7735       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7736       {
7737         if (!MovDelay[x][y])
7738           MovDelay[x][y] = TILEY / 4 + 1;
7739
7740         if (MovDelay[x][y])
7741         {
7742           MovDelay[x][y]--;
7743           if (MovDelay[x][y])
7744             return;
7745         }
7746
7747         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7748         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7749         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7750         Store[x][y] = 0;
7751       }
7752     }
7753     else if (element == EL_BD_MAGIC_WALL_FULL)
7754     {
7755       if (IS_FREE(x, y + 1))
7756       {
7757         InitMovingField(x, y, MV_DOWN);
7758         started_moving = TRUE;
7759
7760         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7761         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7762       }
7763       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7764       {
7765         if (!MovDelay[x][y])
7766           MovDelay[x][y] = TILEY / 4 + 1;
7767
7768         if (MovDelay[x][y])
7769         {
7770           MovDelay[x][y]--;
7771           if (MovDelay[x][y])
7772             return;
7773         }
7774
7775         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7776         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7777         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7778         Store[x][y] = 0;
7779       }
7780     }
7781     else if (element == EL_DC_MAGIC_WALL_FULL)
7782     {
7783       if (IS_FREE(x, y + 1))
7784       {
7785         InitMovingField(x, y, MV_DOWN);
7786         started_moving = TRUE;
7787
7788         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7789         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7790       }
7791       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7792       {
7793         if (!MovDelay[x][y])
7794           MovDelay[x][y] = TILEY / 4 + 1;
7795
7796         if (MovDelay[x][y])
7797         {
7798           MovDelay[x][y]--;
7799           if (MovDelay[x][y])
7800             return;
7801         }
7802
7803         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7804         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7805         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7806         Store[x][y] = 0;
7807       }
7808     }
7809     else if ((CAN_PASS_MAGIC_WALL(element) &&
7810               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7811                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7812              (CAN_PASS_DC_MAGIC_WALL(element) &&
7813               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7814
7815     {
7816       InitMovingField(x, y, MV_DOWN);
7817       started_moving = TRUE;
7818
7819       Feld[x][y] =
7820         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7821          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7822          EL_DC_MAGIC_WALL_FILLING);
7823       Store[x][y] = element;
7824     }
7825     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7826     {
7827       SplashAcid(x, y + 1);
7828
7829       InitMovingField(x, y, MV_DOWN);
7830       started_moving = TRUE;
7831
7832       Store[x][y] = EL_ACID;
7833     }
7834     else if (
7835              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7836               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7837              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7838               CAN_FALL(element) && WasJustFalling[x][y] &&
7839               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7840
7841              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7842               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7843               (Feld[x][y + 1] == EL_BLOCKED)))
7844     {
7845       /* this is needed for a special case not covered by calling "Impact()"
7846          from "ContinueMoving()": if an element moves to a tile directly below
7847          another element which was just falling on that tile (which was empty
7848          in the previous frame), the falling element above would just stop
7849          instead of smashing the element below (in previous version, the above
7850          element was just checked for "moving" instead of "falling", resulting
7851          in incorrect smashes caused by horizontal movement of the above
7852          element; also, the case of the player being the element to smash was
7853          simply not covered here... :-/ ) */
7854
7855       CheckCollision[x][y] = 0;
7856       CheckImpact[x][y] = 0;
7857
7858       Impact(x, y);
7859     }
7860     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7861     {
7862       if (MovDir[x][y] == MV_NONE)
7863       {
7864         InitMovingField(x, y, MV_DOWN);
7865         started_moving = TRUE;
7866       }
7867     }
7868     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7869     {
7870       if (WasJustFalling[x][y]) // prevent animation from being restarted
7871         MovDir[x][y] = MV_DOWN;
7872
7873       InitMovingField(x, y, MV_DOWN);
7874       started_moving = TRUE;
7875     }
7876     else if (element == EL_AMOEBA_DROP)
7877     {
7878       Feld[x][y] = EL_AMOEBA_GROWING;
7879       Store[x][y] = EL_AMOEBA_WET;
7880     }
7881     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7882               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7883              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7884              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7885     {
7886       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7887                                 (IS_FREE(x - 1, y + 1) ||
7888                                  Feld[x - 1][y + 1] == EL_ACID));
7889       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7890                                 (IS_FREE(x + 1, y + 1) ||
7891                                  Feld[x + 1][y + 1] == EL_ACID));
7892       boolean can_fall_any  = (can_fall_left || can_fall_right);
7893       boolean can_fall_both = (can_fall_left && can_fall_right);
7894       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7895
7896       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7897       {
7898         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7899           can_fall_right = FALSE;
7900         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7901           can_fall_left = FALSE;
7902         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7903           can_fall_right = FALSE;
7904         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7905           can_fall_left = FALSE;
7906
7907         can_fall_any  = (can_fall_left || can_fall_right);
7908         can_fall_both = FALSE;
7909       }
7910
7911       if (can_fall_both)
7912       {
7913         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7914           can_fall_right = FALSE;       // slip down on left side
7915         else
7916           can_fall_left = !(can_fall_right = RND(2));
7917
7918         can_fall_both = FALSE;
7919       }
7920
7921       if (can_fall_any)
7922       {
7923         // if not determined otherwise, prefer left side for slipping down
7924         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7925         started_moving = TRUE;
7926       }
7927     }
7928     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7929     {
7930       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7931       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7932       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7933       int belt_dir = game.belt_dir[belt_nr];
7934
7935       if ((belt_dir == MV_LEFT  && left_is_free) ||
7936           (belt_dir == MV_RIGHT && right_is_free))
7937       {
7938         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7939
7940         InitMovingField(x, y, belt_dir);
7941         started_moving = TRUE;
7942
7943         Pushed[x][y] = TRUE;
7944         Pushed[nextx][y] = TRUE;
7945
7946         GfxAction[x][y] = ACTION_DEFAULT;
7947       }
7948       else
7949       {
7950         MovDir[x][y] = 0;       // if element was moving, stop it
7951       }
7952     }
7953   }
7954
7955   // not "else if" because of elements that can fall and move (EL_SPRING)
7956   if (CAN_MOVE(element) && !started_moving)
7957   {
7958     int move_pattern = element_info[element].move_pattern;
7959     int newx, newy;
7960
7961     Moving2Blocked(x, y, &newx, &newy);
7962
7963     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7964       return;
7965
7966     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7967         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7968     {
7969       WasJustMoving[x][y] = 0;
7970       CheckCollision[x][y] = 0;
7971
7972       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7973
7974       if (Feld[x][y] != element)        // element has changed
7975         return;
7976     }
7977
7978     if (!MovDelay[x][y])        // start new movement phase
7979     {
7980       // all objects that can change their move direction after each step
7981       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7982
7983       if (element != EL_YAMYAM &&
7984           element != EL_DARK_YAMYAM &&
7985           element != EL_PACMAN &&
7986           !(move_pattern & MV_ANY_DIRECTION) &&
7987           move_pattern != MV_TURNING_LEFT &&
7988           move_pattern != MV_TURNING_RIGHT &&
7989           move_pattern != MV_TURNING_LEFT_RIGHT &&
7990           move_pattern != MV_TURNING_RIGHT_LEFT &&
7991           move_pattern != MV_TURNING_RANDOM)
7992       {
7993         TurnRound(x, y);
7994
7995         if (MovDelay[x][y] && (element == EL_BUG ||
7996                                element == EL_SPACESHIP ||
7997                                element == EL_SP_SNIKSNAK ||
7998                                element == EL_SP_ELECTRON ||
7999                                element == EL_MOLE))
8000           TEST_DrawLevelField(x, y);
8001       }
8002     }
8003
8004     if (MovDelay[x][y])         // wait some time before next movement
8005     {
8006       MovDelay[x][y]--;
8007
8008       if (element == EL_ROBOT ||
8009           element == EL_YAMYAM ||
8010           element == EL_DARK_YAMYAM)
8011       {
8012         DrawLevelElementAnimationIfNeeded(x, y, element);
8013         PlayLevelSoundAction(x, y, ACTION_WAITING);
8014       }
8015       else if (element == EL_SP_ELECTRON)
8016         DrawLevelElementAnimationIfNeeded(x, y, element);
8017       else if (element == EL_DRAGON)
8018       {
8019         int i;
8020         int dir = MovDir[x][y];
8021         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8022         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8023         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8024                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8025                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8026                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8027         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8028
8029         GfxAction[x][y] = ACTION_ATTACKING;
8030
8031         if (IS_PLAYER(x, y))
8032           DrawPlayerField(x, y);
8033         else
8034           TEST_DrawLevelField(x, y);
8035
8036         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8037
8038         for (i = 1; i <= 3; i++)
8039         {
8040           int xx = x + i * dx;
8041           int yy = y + i * dy;
8042           int sx = SCREENX(xx);
8043           int sy = SCREENY(yy);
8044           int flame_graphic = graphic + (i - 1);
8045
8046           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8047             break;
8048
8049           if (MovDelay[x][y])
8050           {
8051             int flamed = MovingOrBlocked2Element(xx, yy);
8052
8053             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8054               Bang(xx, yy);
8055             else
8056               RemoveMovingField(xx, yy);
8057
8058             ChangeDelay[xx][yy] = 0;
8059
8060             Feld[xx][yy] = EL_FLAMES;
8061
8062             if (IN_SCR_FIELD(sx, sy))
8063             {
8064               TEST_DrawLevelFieldCrumbled(xx, yy);
8065               DrawGraphic(sx, sy, flame_graphic, frame);
8066             }
8067           }
8068           else
8069           {
8070             if (Feld[xx][yy] == EL_FLAMES)
8071               Feld[xx][yy] = EL_EMPTY;
8072             TEST_DrawLevelField(xx, yy);
8073           }
8074         }
8075       }
8076
8077       if (MovDelay[x][y])       // element still has to wait some time
8078       {
8079         PlayLevelSoundAction(x, y, ACTION_WAITING);
8080
8081         return;
8082       }
8083     }
8084
8085     // now make next step
8086
8087     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8088
8089     if (DONT_COLLIDE_WITH(element) &&
8090         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8091         !PLAYER_ENEMY_PROTECTED(newx, newy))
8092     {
8093       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8094
8095       return;
8096     }
8097
8098     else if (CAN_MOVE_INTO_ACID(element) &&
8099              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8100              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8101              (MovDir[x][y] == MV_DOWN ||
8102               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8103     {
8104       SplashAcid(newx, newy);
8105       Store[x][y] = EL_ACID;
8106     }
8107     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8108     {
8109       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8110           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8111           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8112           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8113       {
8114         RemoveField(x, y);
8115         TEST_DrawLevelField(x, y);
8116
8117         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8118         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8119           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8120
8121         game.friends_still_needed--;
8122         if (!game.friends_still_needed &&
8123             !game.GameOver &&
8124             game.all_players_gone)
8125           LevelSolved();
8126
8127         return;
8128       }
8129       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8130       {
8131         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8132           TEST_DrawLevelField(newx, newy);
8133         else
8134           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8135       }
8136       else if (!IS_FREE(newx, newy))
8137       {
8138         GfxAction[x][y] = ACTION_WAITING;
8139
8140         if (IS_PLAYER(x, y))
8141           DrawPlayerField(x, y);
8142         else
8143           TEST_DrawLevelField(x, y);
8144
8145         return;
8146       }
8147     }
8148     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8149     {
8150       if (IS_FOOD_PIG(Feld[newx][newy]))
8151       {
8152         if (IS_MOVING(newx, newy))
8153           RemoveMovingField(newx, newy);
8154         else
8155         {
8156           Feld[newx][newy] = EL_EMPTY;
8157           TEST_DrawLevelField(newx, newy);
8158         }
8159
8160         PlayLevelSound(x, y, SND_PIG_DIGGING);
8161       }
8162       else if (!IS_FREE(newx, newy))
8163       {
8164         if (IS_PLAYER(x, y))
8165           DrawPlayerField(x, y);
8166         else
8167           TEST_DrawLevelField(x, y);
8168
8169         return;
8170       }
8171     }
8172     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8173     {
8174       if (Store[x][y] != EL_EMPTY)
8175       {
8176         boolean can_clone = FALSE;
8177         int xx, yy;
8178
8179         // check if element to clone is still there
8180         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8181         {
8182           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8183           {
8184             can_clone = TRUE;
8185
8186             break;
8187           }
8188         }
8189
8190         // cannot clone or target field not free anymore -- do not clone
8191         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8192           Store[x][y] = EL_EMPTY;
8193       }
8194
8195       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8196       {
8197         if (IS_MV_DIAGONAL(MovDir[x][y]))
8198         {
8199           int diagonal_move_dir = MovDir[x][y];
8200           int stored = Store[x][y];
8201           int change_delay = 8;
8202           int graphic;
8203
8204           // android is moving diagonally
8205
8206           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8207
8208           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8209           GfxElement[x][y] = EL_EMC_ANDROID;
8210           GfxAction[x][y] = ACTION_SHRINKING;
8211           GfxDir[x][y] = diagonal_move_dir;
8212           ChangeDelay[x][y] = change_delay;
8213
8214           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8215                                    GfxDir[x][y]);
8216
8217           DrawLevelGraphicAnimation(x, y, graphic);
8218           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8219
8220           if (Feld[newx][newy] == EL_ACID)
8221           {
8222             SplashAcid(newx, newy);
8223
8224             return;
8225           }
8226
8227           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8228
8229           Store[newx][newy] = EL_EMC_ANDROID;
8230           GfxElement[newx][newy] = EL_EMC_ANDROID;
8231           GfxAction[newx][newy] = ACTION_GROWING;
8232           GfxDir[newx][newy] = diagonal_move_dir;
8233           ChangeDelay[newx][newy] = change_delay;
8234
8235           graphic = el_act_dir2img(GfxElement[newx][newy],
8236                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8237
8238           DrawLevelGraphicAnimation(newx, newy, graphic);
8239           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8240
8241           return;
8242         }
8243         else
8244         {
8245           Feld[newx][newy] = EL_EMPTY;
8246           TEST_DrawLevelField(newx, newy);
8247
8248           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8249         }
8250       }
8251       else if (!IS_FREE(newx, newy))
8252       {
8253         return;
8254       }
8255     }
8256     else if (IS_CUSTOM_ELEMENT(element) &&
8257              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8258     {
8259       if (!DigFieldByCE(newx, newy, element))
8260         return;
8261
8262       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8263       {
8264         RunnerVisit[x][y] = FrameCounter;
8265         PlayerVisit[x][y] /= 8;         // expire player visit path
8266       }
8267     }
8268     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8269     {
8270       if (!IS_FREE(newx, newy))
8271       {
8272         if (IS_PLAYER(x, y))
8273           DrawPlayerField(x, y);
8274         else
8275           TEST_DrawLevelField(x, y);
8276
8277         return;
8278       }
8279       else
8280       {
8281         boolean wanna_flame = !RND(10);
8282         int dx = newx - x, dy = newy - y;
8283         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8284         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8285         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8286                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8287         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8288                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8289
8290         if ((wanna_flame ||
8291              IS_CLASSIC_ENEMY(element1) ||
8292              IS_CLASSIC_ENEMY(element2)) &&
8293             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8294             element1 != EL_FLAMES && element2 != EL_FLAMES)
8295         {
8296           ResetGfxAnimation(x, y);
8297           GfxAction[x][y] = ACTION_ATTACKING;
8298
8299           if (IS_PLAYER(x, y))
8300             DrawPlayerField(x, y);
8301           else
8302             TEST_DrawLevelField(x, y);
8303
8304           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8305
8306           MovDelay[x][y] = 50;
8307
8308           Feld[newx][newy] = EL_FLAMES;
8309           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8310             Feld[newx1][newy1] = EL_FLAMES;
8311           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8312             Feld[newx2][newy2] = EL_FLAMES;
8313
8314           return;
8315         }
8316       }
8317     }
8318     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8319              Feld[newx][newy] == EL_DIAMOND)
8320     {
8321       if (IS_MOVING(newx, newy))
8322         RemoveMovingField(newx, newy);
8323       else
8324       {
8325         Feld[newx][newy] = EL_EMPTY;
8326         TEST_DrawLevelField(newx, newy);
8327       }
8328
8329       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8330     }
8331     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8332              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8333     {
8334       if (AmoebaNr[newx][newy])
8335       {
8336         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8337         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8338             Feld[newx][newy] == EL_BD_AMOEBA)
8339           AmoebaCnt[AmoebaNr[newx][newy]]--;
8340       }
8341
8342       if (IS_MOVING(newx, newy))
8343       {
8344         RemoveMovingField(newx, newy);
8345       }
8346       else
8347       {
8348         Feld[newx][newy] = EL_EMPTY;
8349         TEST_DrawLevelField(newx, newy);
8350       }
8351
8352       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8353     }
8354     else if ((element == EL_PACMAN || element == EL_MOLE)
8355              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8356     {
8357       if (AmoebaNr[newx][newy])
8358       {
8359         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8360         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8361             Feld[newx][newy] == EL_BD_AMOEBA)
8362           AmoebaCnt[AmoebaNr[newx][newy]]--;
8363       }
8364
8365       if (element == EL_MOLE)
8366       {
8367         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8368         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8369
8370         ResetGfxAnimation(x, y);
8371         GfxAction[x][y] = ACTION_DIGGING;
8372         TEST_DrawLevelField(x, y);
8373
8374         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8375
8376         return;                         // wait for shrinking amoeba
8377       }
8378       else      // element == EL_PACMAN
8379       {
8380         Feld[newx][newy] = EL_EMPTY;
8381         TEST_DrawLevelField(newx, newy);
8382         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8383       }
8384     }
8385     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8386              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8387               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8388     {
8389       // wait for shrinking amoeba to completely disappear
8390       return;
8391     }
8392     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8393     {
8394       // object was running against a wall
8395
8396       TurnRound(x, y);
8397
8398       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8399         DrawLevelElementAnimation(x, y, element);
8400
8401       if (DONT_TOUCH(element))
8402         TestIfBadThingTouchesPlayer(x, y);
8403
8404       return;
8405     }
8406
8407     InitMovingField(x, y, MovDir[x][y]);
8408
8409     PlayLevelSoundAction(x, y, ACTION_MOVING);
8410   }
8411
8412   if (MovDir[x][y])
8413     ContinueMoving(x, y);
8414 }
8415
8416 void ContinueMoving(int x, int y)
8417 {
8418   int element = Feld[x][y];
8419   struct ElementInfo *ei = &element_info[element];
8420   int direction = MovDir[x][y];
8421   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8422   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8423   int newx = x + dx, newy = y + dy;
8424   int stored = Store[x][y];
8425   int stored_new = Store[newx][newy];
8426   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8427   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8428   boolean last_line = (newy == lev_fieldy - 1);
8429
8430   MovPos[x][y] += getElementMoveStepsize(x, y);
8431
8432   if (pushed_by_player) // special case: moving object pushed by player
8433     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8434
8435   if (ABS(MovPos[x][y]) < TILEX)
8436   {
8437     TEST_DrawLevelField(x, y);
8438
8439     return;     // element is still moving
8440   }
8441
8442   // element reached destination field
8443
8444   Feld[x][y] = EL_EMPTY;
8445   Feld[newx][newy] = element;
8446   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8447
8448   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8449   {
8450     element = Feld[newx][newy] = EL_ACID;
8451   }
8452   else if (element == EL_MOLE)
8453   {
8454     Feld[x][y] = EL_SAND;
8455
8456     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8457   }
8458   else if (element == EL_QUICKSAND_FILLING)
8459   {
8460     element = Feld[newx][newy] = get_next_element(element);
8461     Store[newx][newy] = Store[x][y];
8462   }
8463   else if (element == EL_QUICKSAND_EMPTYING)
8464   {
8465     Feld[x][y] = get_next_element(element);
8466     element = Feld[newx][newy] = Store[x][y];
8467   }
8468   else if (element == EL_QUICKSAND_FAST_FILLING)
8469   {
8470     element = Feld[newx][newy] = get_next_element(element);
8471     Store[newx][newy] = Store[x][y];
8472   }
8473   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8474   {
8475     Feld[x][y] = get_next_element(element);
8476     element = Feld[newx][newy] = Store[x][y];
8477   }
8478   else if (element == EL_MAGIC_WALL_FILLING)
8479   {
8480     element = Feld[newx][newy] = get_next_element(element);
8481     if (!game.magic_wall_active)
8482       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8483     Store[newx][newy] = Store[x][y];
8484   }
8485   else if (element == EL_MAGIC_WALL_EMPTYING)
8486   {
8487     Feld[x][y] = get_next_element(element);
8488     if (!game.magic_wall_active)
8489       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8490     element = Feld[newx][newy] = Store[x][y];
8491
8492     InitField(newx, newy, FALSE);
8493   }
8494   else if (element == EL_BD_MAGIC_WALL_FILLING)
8495   {
8496     element = Feld[newx][newy] = get_next_element(element);
8497     if (!game.magic_wall_active)
8498       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8499     Store[newx][newy] = Store[x][y];
8500   }
8501   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8502   {
8503     Feld[x][y] = get_next_element(element);
8504     if (!game.magic_wall_active)
8505       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8506     element = Feld[newx][newy] = Store[x][y];
8507
8508     InitField(newx, newy, FALSE);
8509   }
8510   else if (element == EL_DC_MAGIC_WALL_FILLING)
8511   {
8512     element = Feld[newx][newy] = get_next_element(element);
8513     if (!game.magic_wall_active)
8514       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8515     Store[newx][newy] = Store[x][y];
8516   }
8517   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8518   {
8519     Feld[x][y] = get_next_element(element);
8520     if (!game.magic_wall_active)
8521       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8522     element = Feld[newx][newy] = Store[x][y];
8523
8524     InitField(newx, newy, FALSE);
8525   }
8526   else if (element == EL_AMOEBA_DROPPING)
8527   {
8528     Feld[x][y] = get_next_element(element);
8529     element = Feld[newx][newy] = Store[x][y];
8530   }
8531   else if (element == EL_SOKOBAN_OBJECT)
8532   {
8533     if (Back[x][y])
8534       Feld[x][y] = Back[x][y];
8535
8536     if (Back[newx][newy])
8537       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8538
8539     Back[x][y] = Back[newx][newy] = 0;
8540   }
8541
8542   Store[x][y] = EL_EMPTY;
8543   MovPos[x][y] = 0;
8544   MovDir[x][y] = 0;
8545   MovDelay[x][y] = 0;
8546
8547   MovDelay[newx][newy] = 0;
8548
8549   if (CAN_CHANGE_OR_HAS_ACTION(element))
8550   {
8551     // copy element change control values to new field
8552     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8553     ChangePage[newx][newy]  = ChangePage[x][y];
8554     ChangeCount[newx][newy] = ChangeCount[x][y];
8555     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8556   }
8557
8558   CustomValue[newx][newy] = CustomValue[x][y];
8559
8560   ChangeDelay[x][y] = 0;
8561   ChangePage[x][y] = -1;
8562   ChangeCount[x][y] = 0;
8563   ChangeEvent[x][y] = -1;
8564
8565   CustomValue[x][y] = 0;
8566
8567   // copy animation control values to new field
8568   GfxFrame[newx][newy]  = GfxFrame[x][y];
8569   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8570   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8571   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8572
8573   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8574
8575   // some elements can leave other elements behind after moving
8576   if (ei->move_leave_element != EL_EMPTY &&
8577       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8578       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8579   {
8580     int move_leave_element = ei->move_leave_element;
8581
8582     // this makes it possible to leave the removed element again
8583     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8584       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8585
8586     Feld[x][y] = move_leave_element;
8587
8588     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8589       MovDir[x][y] = direction;
8590
8591     InitField(x, y, FALSE);
8592
8593     if (GFX_CRUMBLED(Feld[x][y]))
8594       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8595
8596     if (ELEM_IS_PLAYER(move_leave_element))
8597       RelocatePlayer(x, y, move_leave_element);
8598   }
8599
8600   // do this after checking for left-behind element
8601   ResetGfxAnimation(x, y);      // reset animation values for old field
8602
8603   if (!CAN_MOVE(element) ||
8604       (CAN_FALL(element) && direction == MV_DOWN &&
8605        (element == EL_SPRING ||
8606         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8607         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8608     GfxDir[x][y] = MovDir[newx][newy] = 0;
8609
8610   TEST_DrawLevelField(x, y);
8611   TEST_DrawLevelField(newx, newy);
8612
8613   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8614
8615   // prevent pushed element from moving on in pushed direction
8616   if (pushed_by_player && CAN_MOVE(element) &&
8617       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8618       !(element_info[element].move_pattern & direction))
8619     TurnRound(newx, newy);
8620
8621   // prevent elements on conveyor belt from moving on in last direction
8622   if (pushed_by_conveyor && CAN_FALL(element) &&
8623       direction & MV_HORIZONTAL)
8624     MovDir[newx][newy] = 0;
8625
8626   if (!pushed_by_player)
8627   {
8628     int nextx = newx + dx, nexty = newy + dy;
8629     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8630
8631     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8632
8633     if (CAN_FALL(element) && direction == MV_DOWN)
8634       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8635
8636     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8637       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8638
8639     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8640       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8641   }
8642
8643   if (DONT_TOUCH(element))      // object may be nasty to player or others
8644   {
8645     TestIfBadThingTouchesPlayer(newx, newy);
8646     TestIfBadThingTouchesFriend(newx, newy);
8647
8648     if (!IS_CUSTOM_ELEMENT(element))
8649       TestIfBadThingTouchesOtherBadThing(newx, newy);
8650   }
8651   else if (element == EL_PENGUIN)
8652     TestIfFriendTouchesBadThing(newx, newy);
8653
8654   if (DONT_GET_HIT_BY(element))
8655   {
8656     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8657   }
8658
8659   // give the player one last chance (one more frame) to move away
8660   if (CAN_FALL(element) && direction == MV_DOWN &&
8661       (last_line || (!IS_FREE(x, newy + 1) &&
8662                      (!IS_PLAYER(x, newy + 1) ||
8663                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8664     Impact(x, newy);
8665
8666   if (pushed_by_player && !game.use_change_when_pushing_bug)
8667   {
8668     int push_side = MV_DIR_OPPOSITE(direction);
8669     struct PlayerInfo *player = PLAYERINFO(x, y);
8670
8671     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8672                                player->index_bit, push_side);
8673     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8674                                         player->index_bit, push_side);
8675   }
8676
8677   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8678     MovDelay[newx][newy] = 1;
8679
8680   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8681
8682   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8683   TestIfElementHitsCustomElement(newx, newy, direction);
8684   TestIfPlayerTouchesCustomElement(newx, newy);
8685   TestIfElementTouchesCustomElement(newx, newy);
8686
8687   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8688       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8689     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8690                              MV_DIR_OPPOSITE(direction));
8691 }
8692
8693 int AmoebeNachbarNr(int ax, int ay)
8694 {
8695   int i;
8696   int element = Feld[ax][ay];
8697   int group_nr = 0;
8698   static int xy[4][2] =
8699   {
8700     { 0, -1 },
8701     { -1, 0 },
8702     { +1, 0 },
8703     { 0, +1 }
8704   };
8705
8706   for (i = 0; i < NUM_DIRECTIONS; i++)
8707   {
8708     int x = ax + xy[i][0];
8709     int y = ay + xy[i][1];
8710
8711     if (!IN_LEV_FIELD(x, y))
8712       continue;
8713
8714     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8715       group_nr = AmoebaNr[x][y];
8716   }
8717
8718   return group_nr;
8719 }
8720
8721 static void AmoebenVereinigen(int ax, int ay)
8722 {
8723   int i, x, y, xx, yy;
8724   int new_group_nr = AmoebaNr[ax][ay];
8725   static int xy[4][2] =
8726   {
8727     { 0, -1 },
8728     { -1, 0 },
8729     { +1, 0 },
8730     { 0, +1 }
8731   };
8732
8733   if (new_group_nr == 0)
8734     return;
8735
8736   for (i = 0; i < NUM_DIRECTIONS; i++)
8737   {
8738     x = ax + xy[i][0];
8739     y = ay + xy[i][1];
8740
8741     if (!IN_LEV_FIELD(x, y))
8742       continue;
8743
8744     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8745          Feld[x][y] == EL_BD_AMOEBA ||
8746          Feld[x][y] == EL_AMOEBA_DEAD) &&
8747         AmoebaNr[x][y] != new_group_nr)
8748     {
8749       int old_group_nr = AmoebaNr[x][y];
8750
8751       if (old_group_nr == 0)
8752         return;
8753
8754       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8755       AmoebaCnt[old_group_nr] = 0;
8756       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8757       AmoebaCnt2[old_group_nr] = 0;
8758
8759       SCAN_PLAYFIELD(xx, yy)
8760       {
8761         if (AmoebaNr[xx][yy] == old_group_nr)
8762           AmoebaNr[xx][yy] = new_group_nr;
8763       }
8764     }
8765   }
8766 }
8767
8768 void AmoebeUmwandeln(int ax, int ay)
8769 {
8770   int i, x, y;
8771
8772   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8773   {
8774     int group_nr = AmoebaNr[ax][ay];
8775
8776 #ifdef DEBUG
8777     if (group_nr == 0)
8778     {
8779       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8780       printf("AmoebeUmwandeln(): This should never happen!\n");
8781       return;
8782     }
8783 #endif
8784
8785     SCAN_PLAYFIELD(x, y)
8786     {
8787       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8788       {
8789         AmoebaNr[x][y] = 0;
8790         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8791       }
8792     }
8793
8794     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8795                             SND_AMOEBA_TURNING_TO_GEM :
8796                             SND_AMOEBA_TURNING_TO_ROCK));
8797     Bang(ax, ay);
8798   }
8799   else
8800   {
8801     static int xy[4][2] =
8802     {
8803       { 0, -1 },
8804       { -1, 0 },
8805       { +1, 0 },
8806       { 0, +1 }
8807     };
8808
8809     for (i = 0; i < NUM_DIRECTIONS; i++)
8810     {
8811       x = ax + xy[i][0];
8812       y = ay + xy[i][1];
8813
8814       if (!IN_LEV_FIELD(x, y))
8815         continue;
8816
8817       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8818       {
8819         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8820                               SND_AMOEBA_TURNING_TO_GEM :
8821                               SND_AMOEBA_TURNING_TO_ROCK));
8822         Bang(x, y);
8823       }
8824     }
8825   }
8826 }
8827
8828 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8829 {
8830   int x, y;
8831   int group_nr = AmoebaNr[ax][ay];
8832   boolean done = FALSE;
8833
8834 #ifdef DEBUG
8835   if (group_nr == 0)
8836   {
8837     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8838     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8839     return;
8840   }
8841 #endif
8842
8843   SCAN_PLAYFIELD(x, y)
8844   {
8845     if (AmoebaNr[x][y] == group_nr &&
8846         (Feld[x][y] == EL_AMOEBA_DEAD ||
8847          Feld[x][y] == EL_BD_AMOEBA ||
8848          Feld[x][y] == EL_AMOEBA_GROWING))
8849     {
8850       AmoebaNr[x][y] = 0;
8851       Feld[x][y] = new_element;
8852       InitField(x, y, FALSE);
8853       TEST_DrawLevelField(x, y);
8854       done = TRUE;
8855     }
8856   }
8857
8858   if (done)
8859     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8860                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8861                             SND_BD_AMOEBA_TURNING_TO_GEM));
8862 }
8863
8864 static void AmoebeWaechst(int x, int y)
8865 {
8866   static unsigned int sound_delay = 0;
8867   static unsigned int sound_delay_value = 0;
8868
8869   if (!MovDelay[x][y])          // start new growing cycle
8870   {
8871     MovDelay[x][y] = 7;
8872
8873     if (DelayReached(&sound_delay, sound_delay_value))
8874     {
8875       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8876       sound_delay_value = 30;
8877     }
8878   }
8879
8880   if (MovDelay[x][y])           // wait some time before growing bigger
8881   {
8882     MovDelay[x][y]--;
8883     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8884     {
8885       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8886                                            6 - MovDelay[x][y]);
8887
8888       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8889     }
8890
8891     if (!MovDelay[x][y])
8892     {
8893       Feld[x][y] = Store[x][y];
8894       Store[x][y] = 0;
8895       TEST_DrawLevelField(x, y);
8896     }
8897   }
8898 }
8899
8900 static void AmoebaDisappearing(int x, int y)
8901 {
8902   static unsigned int sound_delay = 0;
8903   static unsigned int sound_delay_value = 0;
8904
8905   if (!MovDelay[x][y])          // start new shrinking cycle
8906   {
8907     MovDelay[x][y] = 7;
8908
8909     if (DelayReached(&sound_delay, sound_delay_value))
8910       sound_delay_value = 30;
8911   }
8912
8913   if (MovDelay[x][y])           // wait some time before shrinking
8914   {
8915     MovDelay[x][y]--;
8916     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8917     {
8918       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8919                                            6 - MovDelay[x][y]);
8920
8921       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8922     }
8923
8924     if (!MovDelay[x][y])
8925     {
8926       Feld[x][y] = EL_EMPTY;
8927       TEST_DrawLevelField(x, y);
8928
8929       // don't let mole enter this field in this cycle;
8930       // (give priority to objects falling to this field from above)
8931       Stop[x][y] = TRUE;
8932     }
8933   }
8934 }
8935
8936 static void AmoebeAbleger(int ax, int ay)
8937 {
8938   int i;
8939   int element = Feld[ax][ay];
8940   int graphic = el2img(element);
8941   int newax = ax, neway = ay;
8942   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8943   static int xy[4][2] =
8944   {
8945     { 0, -1 },
8946     { -1, 0 },
8947     { +1, 0 },
8948     { 0, +1 }
8949   };
8950
8951   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8952   {
8953     Feld[ax][ay] = EL_AMOEBA_DEAD;
8954     TEST_DrawLevelField(ax, ay);
8955     return;
8956   }
8957
8958   if (IS_ANIMATED(graphic))
8959     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8960
8961   if (!MovDelay[ax][ay])        // start making new amoeba field
8962     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8963
8964   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8965   {
8966     MovDelay[ax][ay]--;
8967     if (MovDelay[ax][ay])
8968       return;
8969   }
8970
8971   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8972   {
8973     int start = RND(4);
8974     int x = ax + xy[start][0];
8975     int y = ay + xy[start][1];
8976
8977     if (!IN_LEV_FIELD(x, y))
8978       return;
8979
8980     if (IS_FREE(x, y) ||
8981         CAN_GROW_INTO(Feld[x][y]) ||
8982         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8983         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8984     {
8985       newax = x;
8986       neway = y;
8987     }
8988
8989     if (newax == ax && neway == ay)
8990       return;
8991   }
8992   else                          // normal or "filled" (BD style) amoeba
8993   {
8994     int start = RND(4);
8995     boolean waiting_for_player = FALSE;
8996
8997     for (i = 0; i < NUM_DIRECTIONS; i++)
8998     {
8999       int j = (start + i) % 4;
9000       int x = ax + xy[j][0];
9001       int y = ay + xy[j][1];
9002
9003       if (!IN_LEV_FIELD(x, y))
9004         continue;
9005
9006       if (IS_FREE(x, y) ||
9007           CAN_GROW_INTO(Feld[x][y]) ||
9008           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9009           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9010       {
9011         newax = x;
9012         neway = y;
9013         break;
9014       }
9015       else if (IS_PLAYER(x, y))
9016         waiting_for_player = TRUE;
9017     }
9018
9019     if (newax == ax && neway == ay)             // amoeba cannot grow
9020     {
9021       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9022       {
9023         Feld[ax][ay] = EL_AMOEBA_DEAD;
9024         TEST_DrawLevelField(ax, ay);
9025         AmoebaCnt[AmoebaNr[ax][ay]]--;
9026
9027         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9028         {
9029           if (element == EL_AMOEBA_FULL)
9030             AmoebeUmwandeln(ax, ay);
9031           else if (element == EL_BD_AMOEBA)
9032             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9033         }
9034       }
9035       return;
9036     }
9037     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9038     {
9039       // amoeba gets larger by growing in some direction
9040
9041       int new_group_nr = AmoebaNr[ax][ay];
9042
9043 #ifdef DEBUG
9044   if (new_group_nr == 0)
9045   {
9046     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9047     printf("AmoebeAbleger(): This should never happen!\n");
9048     return;
9049   }
9050 #endif
9051
9052       AmoebaNr[newax][neway] = new_group_nr;
9053       AmoebaCnt[new_group_nr]++;
9054       AmoebaCnt2[new_group_nr]++;
9055
9056       // if amoeba touches other amoeba(s) after growing, unify them
9057       AmoebenVereinigen(newax, neway);
9058
9059       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9060       {
9061         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9062         return;
9063       }
9064     }
9065   }
9066
9067   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9068       (neway == lev_fieldy - 1 && newax != ax))
9069   {
9070     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9071     Store[newax][neway] = element;
9072   }
9073   else if (neway == ay || element == EL_EMC_DRIPPER)
9074   {
9075     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9076
9077     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9078   }
9079   else
9080   {
9081     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9082     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9083     Store[ax][ay] = EL_AMOEBA_DROP;
9084     ContinueMoving(ax, ay);
9085     return;
9086   }
9087
9088   TEST_DrawLevelField(newax, neway);
9089 }
9090
9091 static void Life(int ax, int ay)
9092 {
9093   int x1, y1, x2, y2;
9094   int life_time = 40;
9095   int element = Feld[ax][ay];
9096   int graphic = el2img(element);
9097   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9098                          level.biomaze);
9099   boolean changed = FALSE;
9100
9101   if (IS_ANIMATED(graphic))
9102     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9103
9104   if (Stop[ax][ay])
9105     return;
9106
9107   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9108     MovDelay[ax][ay] = life_time;
9109
9110   if (MovDelay[ax][ay])         // wait some time before next cycle
9111   {
9112     MovDelay[ax][ay]--;
9113     if (MovDelay[ax][ay])
9114       return;
9115   }
9116
9117   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9118   {
9119     int xx = ax+x1, yy = ay+y1;
9120     int old_element = Feld[xx][yy];
9121     int num_neighbours = 0;
9122
9123     if (!IN_LEV_FIELD(xx, yy))
9124       continue;
9125
9126     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9127     {
9128       int x = xx+x2, y = yy+y2;
9129
9130       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9131         continue;
9132
9133       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9134       boolean is_neighbour = FALSE;
9135
9136       if (level.use_life_bugs)
9137         is_neighbour =
9138           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9139            (IS_FREE(x, y)                             &&  Stop[x][y]));
9140       else
9141         is_neighbour =
9142           (Last[x][y] == element || is_player_cell);
9143
9144       if (is_neighbour)
9145         num_neighbours++;
9146     }
9147
9148     boolean is_free = FALSE;
9149
9150     if (level.use_life_bugs)
9151       is_free = (IS_FREE(xx, yy));
9152     else
9153       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9154
9155     if (xx == ax && yy == ay)           // field in the middle
9156     {
9157       if (num_neighbours < life_parameter[0] ||
9158           num_neighbours > life_parameter[1])
9159       {
9160         Feld[xx][yy] = EL_EMPTY;
9161         if (Feld[xx][yy] != old_element)
9162           TEST_DrawLevelField(xx, yy);
9163         Stop[xx][yy] = TRUE;
9164         changed = TRUE;
9165       }
9166     }
9167     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9168     {                                   // free border field
9169       if (num_neighbours >= life_parameter[2] &&
9170           num_neighbours <= life_parameter[3])
9171       {
9172         Feld[xx][yy] = element;
9173         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9174         if (Feld[xx][yy] != old_element)
9175           TEST_DrawLevelField(xx, yy);
9176         Stop[xx][yy] = TRUE;
9177         changed = TRUE;
9178       }
9179     }
9180   }
9181
9182   if (changed)
9183     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9184                    SND_GAME_OF_LIFE_GROWING);
9185 }
9186
9187 static void InitRobotWheel(int x, int y)
9188 {
9189   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9190 }
9191
9192 static void RunRobotWheel(int x, int y)
9193 {
9194   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9195 }
9196
9197 static void StopRobotWheel(int x, int y)
9198 {
9199   if (game.robot_wheel_x == x &&
9200       game.robot_wheel_y == y)
9201   {
9202     game.robot_wheel_x = -1;
9203     game.robot_wheel_y = -1;
9204     game.robot_wheel_active = FALSE;
9205   }
9206 }
9207
9208 static void InitTimegateWheel(int x, int y)
9209 {
9210   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9211 }
9212
9213 static void RunTimegateWheel(int x, int y)
9214 {
9215   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9216 }
9217
9218 static void InitMagicBallDelay(int x, int y)
9219 {
9220   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9221 }
9222
9223 static void ActivateMagicBall(int bx, int by)
9224 {
9225   int x, y;
9226
9227   if (level.ball_random)
9228   {
9229     int pos_border = RND(8);    // select one of the eight border elements
9230     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9231     int xx = pos_content % 3;
9232     int yy = pos_content / 3;
9233
9234     x = bx - 1 + xx;
9235     y = by - 1 + yy;
9236
9237     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9238       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9239   }
9240   else
9241   {
9242     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9243     {
9244       int xx = x - bx + 1;
9245       int yy = y - by + 1;
9246
9247       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9248         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9249     }
9250   }
9251
9252   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9253 }
9254
9255 static void CheckExit(int x, int y)
9256 {
9257   if (game.gems_still_needed > 0 ||
9258       game.sokoban_fields_still_needed > 0 ||
9259       game.sokoban_objects_still_needed > 0 ||
9260       game.lights_still_needed > 0)
9261   {
9262     int element = Feld[x][y];
9263     int graphic = el2img(element);
9264
9265     if (IS_ANIMATED(graphic))
9266       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9267
9268     return;
9269   }
9270
9271   // do not re-open exit door closed after last player
9272   if (game.all_players_gone)
9273     return;
9274
9275   Feld[x][y] = EL_EXIT_OPENING;
9276
9277   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9278 }
9279
9280 static void CheckExitEM(int x, int y)
9281 {
9282   if (game.gems_still_needed > 0 ||
9283       game.sokoban_fields_still_needed > 0 ||
9284       game.sokoban_objects_still_needed > 0 ||
9285       game.lights_still_needed > 0)
9286   {
9287     int element = Feld[x][y];
9288     int graphic = el2img(element);
9289
9290     if (IS_ANIMATED(graphic))
9291       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9292
9293     return;
9294   }
9295
9296   // do not re-open exit door closed after last player
9297   if (game.all_players_gone)
9298     return;
9299
9300   Feld[x][y] = EL_EM_EXIT_OPENING;
9301
9302   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9303 }
9304
9305 static void CheckExitSteel(int x, int y)
9306 {
9307   if (game.gems_still_needed > 0 ||
9308       game.sokoban_fields_still_needed > 0 ||
9309       game.sokoban_objects_still_needed > 0 ||
9310       game.lights_still_needed > 0)
9311   {
9312     int element = Feld[x][y];
9313     int graphic = el2img(element);
9314
9315     if (IS_ANIMATED(graphic))
9316       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9317
9318     return;
9319   }
9320
9321   // do not re-open exit door closed after last player
9322   if (game.all_players_gone)
9323     return;
9324
9325   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9326
9327   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9328 }
9329
9330 static void CheckExitSteelEM(int x, int y)
9331 {
9332   if (game.gems_still_needed > 0 ||
9333       game.sokoban_fields_still_needed > 0 ||
9334       game.sokoban_objects_still_needed > 0 ||
9335       game.lights_still_needed > 0)
9336   {
9337     int element = Feld[x][y];
9338     int graphic = el2img(element);
9339
9340     if (IS_ANIMATED(graphic))
9341       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9342
9343     return;
9344   }
9345
9346   // do not re-open exit door closed after last player
9347   if (game.all_players_gone)
9348     return;
9349
9350   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9351
9352   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9353 }
9354
9355 static void CheckExitSP(int x, int y)
9356 {
9357   if (game.gems_still_needed > 0)
9358   {
9359     int element = Feld[x][y];
9360     int graphic = el2img(element);
9361
9362     if (IS_ANIMATED(graphic))
9363       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9364
9365     return;
9366   }
9367
9368   // do not re-open exit door closed after last player
9369   if (game.all_players_gone)
9370     return;
9371
9372   Feld[x][y] = EL_SP_EXIT_OPENING;
9373
9374   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9375 }
9376
9377 static void CloseAllOpenTimegates(void)
9378 {
9379   int x, y;
9380
9381   SCAN_PLAYFIELD(x, y)
9382   {
9383     int element = Feld[x][y];
9384
9385     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9386     {
9387       Feld[x][y] = EL_TIMEGATE_CLOSING;
9388
9389       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9390     }
9391   }
9392 }
9393
9394 static void DrawTwinkleOnField(int x, int y)
9395 {
9396   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9397     return;
9398
9399   if (Feld[x][y] == EL_BD_DIAMOND)
9400     return;
9401
9402   if (MovDelay[x][y] == 0)      // next animation frame
9403     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9404
9405   if (MovDelay[x][y] != 0)      // wait some time before next frame
9406   {
9407     MovDelay[x][y]--;
9408
9409     DrawLevelElementAnimation(x, y, Feld[x][y]);
9410
9411     if (MovDelay[x][y] != 0)
9412     {
9413       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9414                                            10 - MovDelay[x][y]);
9415
9416       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9417     }
9418   }
9419 }
9420
9421 static void MauerWaechst(int x, int y)
9422 {
9423   int delay = 6;
9424
9425   if (!MovDelay[x][y])          // next animation frame
9426     MovDelay[x][y] = 3 * delay;
9427
9428   if (MovDelay[x][y])           // wait some time before next frame
9429   {
9430     MovDelay[x][y]--;
9431
9432     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9433     {
9434       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9435       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9436
9437       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9438     }
9439
9440     if (!MovDelay[x][y])
9441     {
9442       if (MovDir[x][y] == MV_LEFT)
9443       {
9444         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9445           TEST_DrawLevelField(x - 1, y);
9446       }
9447       else if (MovDir[x][y] == MV_RIGHT)
9448       {
9449         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9450           TEST_DrawLevelField(x + 1, y);
9451       }
9452       else if (MovDir[x][y] == MV_UP)
9453       {
9454         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9455           TEST_DrawLevelField(x, y - 1);
9456       }
9457       else
9458       {
9459         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9460           TEST_DrawLevelField(x, y + 1);
9461       }
9462
9463       Feld[x][y] = Store[x][y];
9464       Store[x][y] = 0;
9465       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9466       TEST_DrawLevelField(x, y);
9467     }
9468   }
9469 }
9470
9471 static void MauerAbleger(int ax, int ay)
9472 {
9473   int element = Feld[ax][ay];
9474   int graphic = el2img(element);
9475   boolean oben_frei = FALSE, unten_frei = FALSE;
9476   boolean links_frei = FALSE, rechts_frei = FALSE;
9477   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9478   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9479   boolean new_wall = FALSE;
9480
9481   if (IS_ANIMATED(graphic))
9482     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9483
9484   if (!MovDelay[ax][ay])        // start building new wall
9485     MovDelay[ax][ay] = 6;
9486
9487   if (MovDelay[ax][ay])         // wait some time before building new wall
9488   {
9489     MovDelay[ax][ay]--;
9490     if (MovDelay[ax][ay])
9491       return;
9492   }
9493
9494   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9495     oben_frei = TRUE;
9496   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9497     unten_frei = TRUE;
9498   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9499     links_frei = TRUE;
9500   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9501     rechts_frei = TRUE;
9502
9503   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9504       element == EL_EXPANDABLE_WALL_ANY)
9505   {
9506     if (oben_frei)
9507     {
9508       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9509       Store[ax][ay-1] = element;
9510       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9511       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9512         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9513                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9514       new_wall = TRUE;
9515     }
9516     if (unten_frei)
9517     {
9518       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9519       Store[ax][ay+1] = element;
9520       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9521       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9522         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9523                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9524       new_wall = TRUE;
9525     }
9526   }
9527
9528   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9529       element == EL_EXPANDABLE_WALL_ANY ||
9530       element == EL_EXPANDABLE_WALL ||
9531       element == EL_BD_EXPANDABLE_WALL)
9532   {
9533     if (links_frei)
9534     {
9535       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9536       Store[ax-1][ay] = element;
9537       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9538       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9539         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9540                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9541       new_wall = TRUE;
9542     }
9543
9544     if (rechts_frei)
9545     {
9546       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9547       Store[ax+1][ay] = element;
9548       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9549       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9550         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9551                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9552       new_wall = TRUE;
9553     }
9554   }
9555
9556   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9557     TEST_DrawLevelField(ax, ay);
9558
9559   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9560     oben_massiv = TRUE;
9561   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9562     unten_massiv = TRUE;
9563   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9564     links_massiv = TRUE;
9565   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9566     rechts_massiv = TRUE;
9567
9568   if (((oben_massiv && unten_massiv) ||
9569        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9570        element == EL_EXPANDABLE_WALL) &&
9571       ((links_massiv && rechts_massiv) ||
9572        element == EL_EXPANDABLE_WALL_VERTICAL))
9573     Feld[ax][ay] = EL_WALL;
9574
9575   if (new_wall)
9576     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9577 }
9578
9579 static void MauerAblegerStahl(int ax, int ay)
9580 {
9581   int element = Feld[ax][ay];
9582   int graphic = el2img(element);
9583   boolean oben_frei = FALSE, unten_frei = FALSE;
9584   boolean links_frei = FALSE, rechts_frei = FALSE;
9585   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9586   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9587   boolean new_wall = FALSE;
9588
9589   if (IS_ANIMATED(graphic))
9590     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9591
9592   if (!MovDelay[ax][ay])        // start building new wall
9593     MovDelay[ax][ay] = 6;
9594
9595   if (MovDelay[ax][ay])         // wait some time before building new wall
9596   {
9597     MovDelay[ax][ay]--;
9598     if (MovDelay[ax][ay])
9599       return;
9600   }
9601
9602   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9603     oben_frei = TRUE;
9604   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9605     unten_frei = TRUE;
9606   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9607     links_frei = TRUE;
9608   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9609     rechts_frei = TRUE;
9610
9611   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9612       element == EL_EXPANDABLE_STEELWALL_ANY)
9613   {
9614     if (oben_frei)
9615     {
9616       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9617       Store[ax][ay-1] = element;
9618       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9619       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9620         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9621                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9622       new_wall = TRUE;
9623     }
9624     if (unten_frei)
9625     {
9626       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9627       Store[ax][ay+1] = element;
9628       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9629       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9630         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9631                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9632       new_wall = TRUE;
9633     }
9634   }
9635
9636   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9637       element == EL_EXPANDABLE_STEELWALL_ANY)
9638   {
9639     if (links_frei)
9640     {
9641       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9642       Store[ax-1][ay] = element;
9643       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9644       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9645         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9646                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9647       new_wall = TRUE;
9648     }
9649
9650     if (rechts_frei)
9651     {
9652       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9653       Store[ax+1][ay] = element;
9654       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9655       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9656         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9657                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9658       new_wall = TRUE;
9659     }
9660   }
9661
9662   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9663     oben_massiv = TRUE;
9664   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9665     unten_massiv = TRUE;
9666   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9667     links_massiv = TRUE;
9668   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9669     rechts_massiv = TRUE;
9670
9671   if (((oben_massiv && unten_massiv) ||
9672        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9673       ((links_massiv && rechts_massiv) ||
9674        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9675     Feld[ax][ay] = EL_STEELWALL;
9676
9677   if (new_wall)
9678     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9679 }
9680
9681 static void CheckForDragon(int x, int y)
9682 {
9683   int i, j;
9684   boolean dragon_found = FALSE;
9685   static int xy[4][2] =
9686   {
9687     { 0, -1 },
9688     { -1, 0 },
9689     { +1, 0 },
9690     { 0, +1 }
9691   };
9692
9693   for (i = 0; i < NUM_DIRECTIONS; i++)
9694   {
9695     for (j = 0; j < 4; j++)
9696     {
9697       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9698
9699       if (IN_LEV_FIELD(xx, yy) &&
9700           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9701       {
9702         if (Feld[xx][yy] == EL_DRAGON)
9703           dragon_found = TRUE;
9704       }
9705       else
9706         break;
9707     }
9708   }
9709
9710   if (!dragon_found)
9711   {
9712     for (i = 0; i < NUM_DIRECTIONS; i++)
9713     {
9714       for (j = 0; j < 3; j++)
9715       {
9716         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9717   
9718         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9719         {
9720           Feld[xx][yy] = EL_EMPTY;
9721           TEST_DrawLevelField(xx, yy);
9722         }
9723         else
9724           break;
9725       }
9726     }
9727   }
9728 }
9729
9730 static void InitBuggyBase(int x, int y)
9731 {
9732   int element = Feld[x][y];
9733   int activating_delay = FRAMES_PER_SECOND / 4;
9734
9735   ChangeDelay[x][y] =
9736     (element == EL_SP_BUGGY_BASE ?
9737      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9738      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9739      activating_delay :
9740      element == EL_SP_BUGGY_BASE_ACTIVE ?
9741      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9742 }
9743
9744 static void WarnBuggyBase(int x, int y)
9745 {
9746   int i;
9747   static int xy[4][2] =
9748   {
9749     { 0, -1 },
9750     { -1, 0 },
9751     { +1, 0 },
9752     { 0, +1 }
9753   };
9754
9755   for (i = 0; i < NUM_DIRECTIONS; i++)
9756   {
9757     int xx = x + xy[i][0];
9758     int yy = y + xy[i][1];
9759
9760     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9761     {
9762       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9763
9764       break;
9765     }
9766   }
9767 }
9768
9769 static void InitTrap(int x, int y)
9770 {
9771   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9772 }
9773
9774 static void ActivateTrap(int x, int y)
9775 {
9776   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9777 }
9778
9779 static void ChangeActiveTrap(int x, int y)
9780 {
9781   int graphic = IMG_TRAP_ACTIVE;
9782
9783   // if new animation frame was drawn, correct crumbled sand border
9784   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9785     TEST_DrawLevelFieldCrumbled(x, y);
9786 }
9787
9788 static int getSpecialActionElement(int element, int number, int base_element)
9789 {
9790   return (element != EL_EMPTY ? element :
9791           number != -1 ? base_element + number - 1 :
9792           EL_EMPTY);
9793 }
9794
9795 static int getModifiedActionNumber(int value_old, int operator, int operand,
9796                                    int value_min, int value_max)
9797 {
9798   int value_new = (operator == CA_MODE_SET      ? operand :
9799                    operator == CA_MODE_ADD      ? value_old + operand :
9800                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9801                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9802                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9803                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9804                    value_old);
9805
9806   return (value_new < value_min ? value_min :
9807           value_new > value_max ? value_max :
9808           value_new);
9809 }
9810
9811 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9812 {
9813   struct ElementInfo *ei = &element_info[element];
9814   struct ElementChangeInfo *change = &ei->change_page[page];
9815   int target_element = change->target_element;
9816   int action_type = change->action_type;
9817   int action_mode = change->action_mode;
9818   int action_arg = change->action_arg;
9819   int action_element = change->action_element;
9820   int i;
9821
9822   if (!change->has_action)
9823     return;
9824
9825   // ---------- determine action paramater values -----------------------------
9826
9827   int level_time_value =
9828     (level.time > 0 ? TimeLeft :
9829      TimePlayed);
9830
9831   int action_arg_element_raw =
9832     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9833      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9834      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9835      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9836      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9837      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9838      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9839      EL_EMPTY);
9840   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9841
9842   int action_arg_direction =
9843     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9844      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9845      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9846      change->actual_trigger_side :
9847      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9848      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9849      MV_NONE);
9850
9851   int action_arg_number_min =
9852     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9853      CA_ARG_MIN);
9854
9855   int action_arg_number_max =
9856     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9857      action_type == CA_SET_LEVEL_GEMS ? 999 :
9858      action_type == CA_SET_LEVEL_TIME ? 9999 :
9859      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9860      action_type == CA_SET_CE_VALUE ? 9999 :
9861      action_type == CA_SET_CE_SCORE ? 9999 :
9862      CA_ARG_MAX);
9863
9864   int action_arg_number_reset =
9865     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9866      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9867      action_type == CA_SET_LEVEL_TIME ? level.time :
9868      action_type == CA_SET_LEVEL_SCORE ? 0 :
9869      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9870      action_type == CA_SET_CE_SCORE ? 0 :
9871      0);
9872
9873   int action_arg_number =
9874     (action_arg <= CA_ARG_MAX ? action_arg :
9875      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9876      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9877      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9878      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9879      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9880      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9881      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9882      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9883      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9884      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9885      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9886      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9887      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9888      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9889      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9890      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9891      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9892      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9893      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9894      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9895      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9896      -1);
9897
9898   int action_arg_number_old =
9899     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9900      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9901      action_type == CA_SET_LEVEL_SCORE ? game.score :
9902      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9903      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9904      0);
9905
9906   int action_arg_number_new =
9907     getModifiedActionNumber(action_arg_number_old,
9908                             action_mode, action_arg_number,
9909                             action_arg_number_min, action_arg_number_max);
9910
9911   int trigger_player_bits =
9912     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9913      change->actual_trigger_player_bits : change->trigger_player);
9914
9915   int action_arg_player_bits =
9916     (action_arg >= CA_ARG_PLAYER_1 &&
9917      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9918      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9919      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9920      PLAYER_BITS_ANY);
9921
9922   // ---------- execute action  -----------------------------------------------
9923
9924   switch (action_type)
9925   {
9926     case CA_NO_ACTION:
9927     {
9928       return;
9929     }
9930
9931     // ---------- level actions  ----------------------------------------------
9932
9933     case CA_RESTART_LEVEL:
9934     {
9935       game.restart_level = TRUE;
9936
9937       break;
9938     }
9939
9940     case CA_SHOW_ENVELOPE:
9941     {
9942       int element = getSpecialActionElement(action_arg_element,
9943                                             action_arg_number, EL_ENVELOPE_1);
9944
9945       if (IS_ENVELOPE(element))
9946         local_player->show_envelope = element;
9947
9948       break;
9949     }
9950
9951     case CA_SET_LEVEL_TIME:
9952     {
9953       if (level.time > 0)       // only modify limited time value
9954       {
9955         TimeLeft = action_arg_number_new;
9956
9957         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9958
9959         DisplayGameControlValues();
9960
9961         if (!TimeLeft && setup.time_limit)
9962           for (i = 0; i < MAX_PLAYERS; i++)
9963             KillPlayer(&stored_player[i]);
9964       }
9965
9966       break;
9967     }
9968
9969     case CA_SET_LEVEL_SCORE:
9970     {
9971       game.score = action_arg_number_new;
9972
9973       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9974
9975       DisplayGameControlValues();
9976
9977       break;
9978     }
9979
9980     case CA_SET_LEVEL_GEMS:
9981     {
9982       game.gems_still_needed = action_arg_number_new;
9983
9984       game.snapshot.collected_item = TRUE;
9985
9986       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9987
9988       DisplayGameControlValues();
9989
9990       break;
9991     }
9992
9993     case CA_SET_LEVEL_WIND:
9994     {
9995       game.wind_direction = action_arg_direction;
9996
9997       break;
9998     }
9999
10000     case CA_SET_LEVEL_RANDOM_SEED:
10001     {
10002       // ensure that setting a new random seed while playing is predictable
10003       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10004
10005       break;
10006     }
10007
10008     // ---------- player actions  ---------------------------------------------
10009
10010     case CA_MOVE_PLAYER:
10011     case CA_MOVE_PLAYER_NEW:
10012     {
10013       // automatically move to the next field in specified direction
10014       for (i = 0; i < MAX_PLAYERS; i++)
10015         if (trigger_player_bits & (1 << i))
10016           if (action_type == CA_MOVE_PLAYER ||
10017               stored_player[i].MovPos == 0)
10018             stored_player[i].programmed_action = action_arg_direction;
10019
10020       break;
10021     }
10022
10023     case CA_EXIT_PLAYER:
10024     {
10025       for (i = 0; i < MAX_PLAYERS; i++)
10026         if (action_arg_player_bits & (1 << i))
10027           ExitPlayer(&stored_player[i]);
10028
10029       if (game.players_still_needed == 0)
10030         LevelSolved();
10031
10032       break;
10033     }
10034
10035     case CA_KILL_PLAYER:
10036     {
10037       for (i = 0; i < MAX_PLAYERS; i++)
10038         if (action_arg_player_bits & (1 << i))
10039           KillPlayer(&stored_player[i]);
10040
10041       break;
10042     }
10043
10044     case CA_SET_PLAYER_KEYS:
10045     {
10046       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10047       int element = getSpecialActionElement(action_arg_element,
10048                                             action_arg_number, EL_KEY_1);
10049
10050       if (IS_KEY(element))
10051       {
10052         for (i = 0; i < MAX_PLAYERS; i++)
10053         {
10054           if (trigger_player_bits & (1 << i))
10055           {
10056             stored_player[i].key[KEY_NR(element)] = key_state;
10057
10058             DrawGameDoorValues();
10059           }
10060         }
10061       }
10062
10063       break;
10064     }
10065
10066     case CA_SET_PLAYER_SPEED:
10067     {
10068       for (i = 0; i < MAX_PLAYERS; i++)
10069       {
10070         if (trigger_player_bits & (1 << i))
10071         {
10072           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10073
10074           if (action_arg == CA_ARG_SPEED_FASTER &&
10075               stored_player[i].cannot_move)
10076           {
10077             action_arg_number = STEPSIZE_VERY_SLOW;
10078           }
10079           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10080                    action_arg == CA_ARG_SPEED_FASTER)
10081           {
10082             action_arg_number = 2;
10083             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10084                            CA_MODE_MULTIPLY);
10085           }
10086           else if (action_arg == CA_ARG_NUMBER_RESET)
10087           {
10088             action_arg_number = level.initial_player_stepsize[i];
10089           }
10090
10091           move_stepsize =
10092             getModifiedActionNumber(move_stepsize,
10093                                     action_mode,
10094                                     action_arg_number,
10095                                     action_arg_number_min,
10096                                     action_arg_number_max);
10097
10098           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10099         }
10100       }
10101
10102       break;
10103     }
10104
10105     case CA_SET_PLAYER_SHIELD:
10106     {
10107       for (i = 0; i < MAX_PLAYERS; i++)
10108       {
10109         if (trigger_player_bits & (1 << i))
10110         {
10111           if (action_arg == CA_ARG_SHIELD_OFF)
10112           {
10113             stored_player[i].shield_normal_time_left = 0;
10114             stored_player[i].shield_deadly_time_left = 0;
10115           }
10116           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10117           {
10118             stored_player[i].shield_normal_time_left = 999999;
10119           }
10120           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10121           {
10122             stored_player[i].shield_normal_time_left = 999999;
10123             stored_player[i].shield_deadly_time_left = 999999;
10124           }
10125         }
10126       }
10127
10128       break;
10129     }
10130
10131     case CA_SET_PLAYER_GRAVITY:
10132     {
10133       for (i = 0; i < MAX_PLAYERS; i++)
10134       {
10135         if (trigger_player_bits & (1 << i))
10136         {
10137           stored_player[i].gravity =
10138             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10139              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10140              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10141              stored_player[i].gravity);
10142         }
10143       }
10144
10145       break;
10146     }
10147
10148     case CA_SET_PLAYER_ARTWORK:
10149     {
10150       for (i = 0; i < MAX_PLAYERS; i++)
10151       {
10152         if (trigger_player_bits & (1 << i))
10153         {
10154           int artwork_element = action_arg_element;
10155
10156           if (action_arg == CA_ARG_ELEMENT_RESET)
10157             artwork_element =
10158               (level.use_artwork_element[i] ? level.artwork_element[i] :
10159                stored_player[i].element_nr);
10160
10161           if (stored_player[i].artwork_element != artwork_element)
10162             stored_player[i].Frame = 0;
10163
10164           stored_player[i].artwork_element = artwork_element;
10165
10166           SetPlayerWaiting(&stored_player[i], FALSE);
10167
10168           // set number of special actions for bored and sleeping animation
10169           stored_player[i].num_special_action_bored =
10170             get_num_special_action(artwork_element,
10171                                    ACTION_BORING_1, ACTION_BORING_LAST);
10172           stored_player[i].num_special_action_sleeping =
10173             get_num_special_action(artwork_element,
10174                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10175         }
10176       }
10177
10178       break;
10179     }
10180
10181     case CA_SET_PLAYER_INVENTORY:
10182     {
10183       for (i = 0; i < MAX_PLAYERS; i++)
10184       {
10185         struct PlayerInfo *player = &stored_player[i];
10186         int j, k;
10187
10188         if (trigger_player_bits & (1 << i))
10189         {
10190           int inventory_element = action_arg_element;
10191
10192           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10193               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10194               action_arg == CA_ARG_ELEMENT_ACTION)
10195           {
10196             int element = inventory_element;
10197             int collect_count = element_info[element].collect_count_initial;
10198
10199             if (!IS_CUSTOM_ELEMENT(element))
10200               collect_count = 1;
10201
10202             if (collect_count == 0)
10203               player->inventory_infinite_element = element;
10204             else
10205               for (k = 0; k < collect_count; k++)
10206                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10207                   player->inventory_element[player->inventory_size++] =
10208                     element;
10209           }
10210           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10211                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10212                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10213           {
10214             if (player->inventory_infinite_element != EL_UNDEFINED &&
10215                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10216                                      action_arg_element_raw))
10217               player->inventory_infinite_element = EL_UNDEFINED;
10218
10219             for (k = 0, j = 0; j < player->inventory_size; j++)
10220             {
10221               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10222                                         action_arg_element_raw))
10223                 player->inventory_element[k++] = player->inventory_element[j];
10224             }
10225
10226             player->inventory_size = k;
10227           }
10228           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10229           {
10230             if (player->inventory_size > 0)
10231             {
10232               for (j = 0; j < player->inventory_size - 1; j++)
10233                 player->inventory_element[j] = player->inventory_element[j + 1];
10234
10235               player->inventory_size--;
10236             }
10237           }
10238           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10239           {
10240             if (player->inventory_size > 0)
10241               player->inventory_size--;
10242           }
10243           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10244           {
10245             player->inventory_infinite_element = EL_UNDEFINED;
10246             player->inventory_size = 0;
10247           }
10248           else if (action_arg == CA_ARG_INVENTORY_RESET)
10249           {
10250             player->inventory_infinite_element = EL_UNDEFINED;
10251             player->inventory_size = 0;
10252
10253             if (level.use_initial_inventory[i])
10254             {
10255               for (j = 0; j < level.initial_inventory_size[i]; j++)
10256               {
10257                 int element = level.initial_inventory_content[i][j];
10258                 int collect_count = element_info[element].collect_count_initial;
10259
10260                 if (!IS_CUSTOM_ELEMENT(element))
10261                   collect_count = 1;
10262
10263                 if (collect_count == 0)
10264                   player->inventory_infinite_element = element;
10265                 else
10266                   for (k = 0; k < collect_count; k++)
10267                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10268                       player->inventory_element[player->inventory_size++] =
10269                         element;
10270               }
10271             }
10272           }
10273         }
10274       }
10275
10276       break;
10277     }
10278
10279     // ---------- CE actions  -------------------------------------------------
10280
10281     case CA_SET_CE_VALUE:
10282     {
10283       int last_ce_value = CustomValue[x][y];
10284
10285       CustomValue[x][y] = action_arg_number_new;
10286
10287       if (CustomValue[x][y] != last_ce_value)
10288       {
10289         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10290         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10291
10292         if (CustomValue[x][y] == 0)
10293         {
10294           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10295           ChangeCount[x][y] = 0;        // allow at least one more change
10296
10297           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10298           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10299         }
10300       }
10301
10302       break;
10303     }
10304
10305     case CA_SET_CE_SCORE:
10306     {
10307       int last_ce_score = ei->collect_score;
10308
10309       ei->collect_score = action_arg_number_new;
10310
10311       if (ei->collect_score != last_ce_score)
10312       {
10313         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10314         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10315
10316         if (ei->collect_score == 0)
10317         {
10318           int xx, yy;
10319
10320           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10321           ChangeCount[x][y] = 0;        // allow at least one more change
10322
10323           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10324           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10325
10326           /*
10327             This is a very special case that seems to be a mixture between
10328             CheckElementChange() and CheckTriggeredElementChange(): while
10329             the first one only affects single elements that are triggered
10330             directly, the second one affects multiple elements in the playfield
10331             that are triggered indirectly by another element. This is a third
10332             case: Changing the CE score always affects multiple identical CEs,
10333             so every affected CE must be checked, not only the single CE for
10334             which the CE score was changed in the first place (as every instance
10335             of that CE shares the same CE score, and therefore also can change)!
10336           */
10337           SCAN_PLAYFIELD(xx, yy)
10338           {
10339             if (Feld[xx][yy] == element)
10340               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10341                                  CE_SCORE_GETS_ZERO);
10342           }
10343         }
10344       }
10345
10346       break;
10347     }
10348
10349     case CA_SET_CE_ARTWORK:
10350     {
10351       int artwork_element = action_arg_element;
10352       boolean reset_frame = FALSE;
10353       int xx, yy;
10354
10355       if (action_arg == CA_ARG_ELEMENT_RESET)
10356         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10357                            element);
10358
10359       if (ei->gfx_element != artwork_element)
10360         reset_frame = TRUE;
10361
10362       ei->gfx_element = artwork_element;
10363
10364       SCAN_PLAYFIELD(xx, yy)
10365       {
10366         if (Feld[xx][yy] == element)
10367         {
10368           if (reset_frame)
10369           {
10370             ResetGfxAnimation(xx, yy);
10371             ResetRandomAnimationValue(xx, yy);
10372           }
10373
10374           TEST_DrawLevelField(xx, yy);
10375         }
10376       }
10377
10378       break;
10379     }
10380
10381     // ---------- engine actions  ---------------------------------------------
10382
10383     case CA_SET_ENGINE_SCAN_MODE:
10384     {
10385       InitPlayfieldScanMode(action_arg);
10386
10387       break;
10388     }
10389
10390     default:
10391       break;
10392   }
10393 }
10394
10395 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10396 {
10397   int old_element = Feld[x][y];
10398   int new_element = GetElementFromGroupElement(element);
10399   int previous_move_direction = MovDir[x][y];
10400   int last_ce_value = CustomValue[x][y];
10401   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10402   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10403   boolean add_player_onto_element = (new_element_is_player &&
10404                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10405                                      IS_WALKABLE(old_element));
10406
10407   if (!add_player_onto_element)
10408   {
10409     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10410       RemoveMovingField(x, y);
10411     else
10412       RemoveField(x, y);
10413
10414     Feld[x][y] = new_element;
10415
10416     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10417       MovDir[x][y] = previous_move_direction;
10418
10419     if (element_info[new_element].use_last_ce_value)
10420       CustomValue[x][y] = last_ce_value;
10421
10422     InitField_WithBug1(x, y, FALSE);
10423
10424     new_element = Feld[x][y];   // element may have changed
10425
10426     ResetGfxAnimation(x, y);
10427     ResetRandomAnimationValue(x, y);
10428
10429     TEST_DrawLevelField(x, y);
10430
10431     if (GFX_CRUMBLED(new_element))
10432       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10433   }
10434
10435   // check if element under the player changes from accessible to unaccessible
10436   // (needed for special case of dropping element which then changes)
10437   // (must be checked after creating new element for walkable group elements)
10438   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10439       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10440   {
10441     Bang(x, y);
10442
10443     return;
10444   }
10445
10446   // "ChangeCount" not set yet to allow "entered by player" change one time
10447   if (new_element_is_player)
10448     RelocatePlayer(x, y, new_element);
10449
10450   if (is_change)
10451     ChangeCount[x][y]++;        // count number of changes in the same frame
10452
10453   TestIfBadThingTouchesPlayer(x, y);
10454   TestIfPlayerTouchesCustomElement(x, y);
10455   TestIfElementTouchesCustomElement(x, y);
10456 }
10457
10458 static void CreateField(int x, int y, int element)
10459 {
10460   CreateFieldExt(x, y, element, FALSE);
10461 }
10462
10463 static void CreateElementFromChange(int x, int y, int element)
10464 {
10465   element = GET_VALID_RUNTIME_ELEMENT(element);
10466
10467   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10468   {
10469     int old_element = Feld[x][y];
10470
10471     // prevent changed element from moving in same engine frame
10472     // unless both old and new element can either fall or move
10473     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10474         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10475       Stop[x][y] = TRUE;
10476   }
10477
10478   CreateFieldExt(x, y, element, TRUE);
10479 }
10480
10481 static boolean ChangeElement(int x, int y, int element, int page)
10482 {
10483   struct ElementInfo *ei = &element_info[element];
10484   struct ElementChangeInfo *change = &ei->change_page[page];
10485   int ce_value = CustomValue[x][y];
10486   int ce_score = ei->collect_score;
10487   int target_element;
10488   int old_element = Feld[x][y];
10489
10490   // always use default change event to prevent running into a loop
10491   if (ChangeEvent[x][y] == -1)
10492     ChangeEvent[x][y] = CE_DELAY;
10493
10494   if (ChangeEvent[x][y] == CE_DELAY)
10495   {
10496     // reset actual trigger element, trigger player and action element
10497     change->actual_trigger_element = EL_EMPTY;
10498     change->actual_trigger_player = EL_EMPTY;
10499     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10500     change->actual_trigger_side = CH_SIDE_NONE;
10501     change->actual_trigger_ce_value = 0;
10502     change->actual_trigger_ce_score = 0;
10503   }
10504
10505   // do not change elements more than a specified maximum number of changes
10506   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10507     return FALSE;
10508
10509   ChangeCount[x][y]++;          // count number of changes in the same frame
10510
10511   if (change->explode)
10512   {
10513     Bang(x, y);
10514
10515     return TRUE;
10516   }
10517
10518   if (change->use_target_content)
10519   {
10520     boolean complete_replace = TRUE;
10521     boolean can_replace[3][3];
10522     int xx, yy;
10523
10524     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10525     {
10526       boolean is_empty;
10527       boolean is_walkable;
10528       boolean is_diggable;
10529       boolean is_collectible;
10530       boolean is_removable;
10531       boolean is_destructible;
10532       int ex = x + xx - 1;
10533       int ey = y + yy - 1;
10534       int content_element = change->target_content.e[xx][yy];
10535       int e;
10536
10537       can_replace[xx][yy] = TRUE;
10538
10539       if (ex == x && ey == y)   // do not check changing element itself
10540         continue;
10541
10542       if (content_element == EL_EMPTY_SPACE)
10543       {
10544         can_replace[xx][yy] = FALSE;    // do not replace border with space
10545
10546         continue;
10547       }
10548
10549       if (!IN_LEV_FIELD(ex, ey))
10550       {
10551         can_replace[xx][yy] = FALSE;
10552         complete_replace = FALSE;
10553
10554         continue;
10555       }
10556
10557       e = Feld[ex][ey];
10558
10559       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10560         e = MovingOrBlocked2Element(ex, ey);
10561
10562       is_empty = (IS_FREE(ex, ey) ||
10563                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10564
10565       is_walkable     = (is_empty || IS_WALKABLE(e));
10566       is_diggable     = (is_empty || IS_DIGGABLE(e));
10567       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10568       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10569       is_removable    = (is_diggable || is_collectible);
10570
10571       can_replace[xx][yy] =
10572         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10573           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10574           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10575           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10576           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10577           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10578          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10579
10580       if (!can_replace[xx][yy])
10581         complete_replace = FALSE;
10582     }
10583
10584     if (!change->only_if_complete || complete_replace)
10585     {
10586       boolean something_has_changed = FALSE;
10587
10588       if (change->only_if_complete && change->use_random_replace &&
10589           RND(100) < change->random_percentage)
10590         return FALSE;
10591
10592       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10593       {
10594         int ex = x + xx - 1;
10595         int ey = y + yy - 1;
10596         int content_element;
10597
10598         if (can_replace[xx][yy] && (!change->use_random_replace ||
10599                                     RND(100) < change->random_percentage))
10600         {
10601           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10602             RemoveMovingField(ex, ey);
10603
10604           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10605
10606           content_element = change->target_content.e[xx][yy];
10607           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10608                                               ce_value, ce_score);
10609
10610           CreateElementFromChange(ex, ey, target_element);
10611
10612           something_has_changed = TRUE;
10613
10614           // for symmetry reasons, freeze newly created border elements
10615           if (ex != x || ey != y)
10616             Stop[ex][ey] = TRUE;        // no more moving in this frame
10617         }
10618       }
10619
10620       if (something_has_changed)
10621       {
10622         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10623         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10624       }
10625     }
10626   }
10627   else
10628   {
10629     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10630                                         ce_value, ce_score);
10631
10632     if (element == EL_DIAGONAL_GROWING ||
10633         element == EL_DIAGONAL_SHRINKING)
10634     {
10635       target_element = Store[x][y];
10636
10637       Store[x][y] = EL_EMPTY;
10638     }
10639
10640     CreateElementFromChange(x, y, target_element);
10641
10642     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10643     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10644   }
10645
10646   // this uses direct change before indirect change
10647   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10648
10649   return TRUE;
10650 }
10651
10652 static void HandleElementChange(int x, int y, int page)
10653 {
10654   int element = MovingOrBlocked2Element(x, y);
10655   struct ElementInfo *ei = &element_info[element];
10656   struct ElementChangeInfo *change = &ei->change_page[page];
10657   boolean handle_action_before_change = FALSE;
10658
10659 #ifdef DEBUG
10660   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10661       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10662   {
10663     printf("\n\n");
10664     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10665            x, y, element, element_info[element].token_name);
10666     printf("HandleElementChange(): This should never happen!\n");
10667     printf("\n\n");
10668   }
10669 #endif
10670
10671   // this can happen with classic bombs on walkable, changing elements
10672   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10673   {
10674     return;
10675   }
10676
10677   if (ChangeDelay[x][y] == 0)           // initialize element change
10678   {
10679     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10680
10681     if (change->can_change)
10682     {
10683       // !!! not clear why graphic animation should be reset at all here !!!
10684       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10685       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10686
10687       /*
10688         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10689
10690         When using an animation frame delay of 1 (this only happens with
10691         "sp_zonk.moving.left/right" in the classic graphics), the default
10692         (non-moving) animation shows wrong animation frames (while the
10693         moving animation, like "sp_zonk.moving.left/right", is correct,
10694         so this graphical bug never shows up with the classic graphics).
10695         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10696         be drawn instead of the correct frames 0,1,2,3. This is caused by
10697         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10698         an element change: First when the change delay ("ChangeDelay[][]")
10699         counter has reached zero after decrementing, then a second time in
10700         the next frame (after "GfxFrame[][]" was already incremented) when
10701         "ChangeDelay[][]" is reset to the initial delay value again.
10702
10703         This causes frame 0 to be drawn twice, while the last frame won't
10704         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10705
10706         As some animations may already be cleverly designed around this bug
10707         (at least the "Snake Bite" snake tail animation does this), it cannot
10708         simply be fixed here without breaking such existing animations.
10709         Unfortunately, it cannot easily be detected if a graphics set was
10710         designed "before" or "after" the bug was fixed. As a workaround,
10711         a new graphics set option "game.graphics_engine_version" was added
10712         to be able to specify the game's major release version for which the
10713         graphics set was designed, which can then be used to decide if the
10714         bugfix should be used (version 4 and above) or not (version 3 or
10715         below, or if no version was specified at all, as with old sets).
10716
10717         (The wrong/fixed animation frames can be tested with the test level set
10718         "test_gfxframe" and level "000", which contains a specially prepared
10719         custom element at level position (x/y) == (11/9) which uses the zonk
10720         animation mentioned above. Using "game.graphics_engine_version: 4"
10721         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10722         This can also be seen from the debug output for this test element.)
10723       */
10724
10725       // when a custom element is about to change (for example by change delay),
10726       // do not reset graphic animation when the custom element is moving
10727       if (game.graphics_engine_version < 4 &&
10728           !IS_MOVING(x, y))
10729       {
10730         ResetGfxAnimation(x, y);
10731         ResetRandomAnimationValue(x, y);
10732       }
10733
10734       if (change->pre_change_function)
10735         change->pre_change_function(x, y);
10736     }
10737   }
10738
10739   ChangeDelay[x][y]--;
10740
10741   if (ChangeDelay[x][y] != 0)           // continue element change
10742   {
10743     if (change->can_change)
10744     {
10745       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10746
10747       if (IS_ANIMATED(graphic))
10748         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10749
10750       if (change->change_function)
10751         change->change_function(x, y);
10752     }
10753   }
10754   else                                  // finish element change
10755   {
10756     if (ChangePage[x][y] != -1)         // remember page from delayed change
10757     {
10758       page = ChangePage[x][y];
10759       ChangePage[x][y] = -1;
10760
10761       change = &ei->change_page[page];
10762     }
10763
10764     if (IS_MOVING(x, y))                // never change a running system ;-)
10765     {
10766       ChangeDelay[x][y] = 1;            // try change after next move step
10767       ChangePage[x][y] = page;          // remember page to use for change
10768
10769       return;
10770     }
10771
10772     // special case: set new level random seed before changing element
10773     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10774       handle_action_before_change = TRUE;
10775
10776     if (change->has_action && handle_action_before_change)
10777       ExecuteCustomElementAction(x, y, element, page);
10778
10779     if (change->can_change)
10780     {
10781       if (ChangeElement(x, y, element, page))
10782       {
10783         if (change->post_change_function)
10784           change->post_change_function(x, y);
10785       }
10786     }
10787
10788     if (change->has_action && !handle_action_before_change)
10789       ExecuteCustomElementAction(x, y, element, page);
10790   }
10791 }
10792
10793 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10794                                               int trigger_element,
10795                                               int trigger_event,
10796                                               int trigger_player,
10797                                               int trigger_side,
10798                                               int trigger_page)
10799 {
10800   boolean change_done_any = FALSE;
10801   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10802   int i;
10803
10804   if (!(trigger_events[trigger_element][trigger_event]))
10805     return FALSE;
10806
10807   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10808
10809   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10810   {
10811     int element = EL_CUSTOM_START + i;
10812     boolean change_done = FALSE;
10813     int p;
10814
10815     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10816         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10817       continue;
10818
10819     for (p = 0; p < element_info[element].num_change_pages; p++)
10820     {
10821       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10822
10823       if (change->can_change_or_has_action &&
10824           change->has_event[trigger_event] &&
10825           change->trigger_side & trigger_side &&
10826           change->trigger_player & trigger_player &&
10827           change->trigger_page & trigger_page_bits &&
10828           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10829       {
10830         change->actual_trigger_element = trigger_element;
10831         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10832         change->actual_trigger_player_bits = trigger_player;
10833         change->actual_trigger_side = trigger_side;
10834         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10835         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10836
10837         if ((change->can_change && !change_done) || change->has_action)
10838         {
10839           int x, y;
10840
10841           SCAN_PLAYFIELD(x, y)
10842           {
10843             if (Feld[x][y] == element)
10844             {
10845               if (change->can_change && !change_done)
10846               {
10847                 // if element already changed in this frame, not only prevent
10848                 // another element change (checked in ChangeElement()), but
10849                 // also prevent additional element actions for this element
10850
10851                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10852                     !level.use_action_after_change_bug)
10853                   continue;
10854
10855                 ChangeDelay[x][y] = 1;
10856                 ChangeEvent[x][y] = trigger_event;
10857
10858                 HandleElementChange(x, y, p);
10859               }
10860               else if (change->has_action)
10861               {
10862                 // if element already changed in this frame, not only prevent
10863                 // another element change (checked in ChangeElement()), but
10864                 // also prevent additional element actions for this element
10865
10866                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10867                     !level.use_action_after_change_bug)
10868                   continue;
10869
10870                 ExecuteCustomElementAction(x, y, element, p);
10871                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10872               }
10873             }
10874           }
10875
10876           if (change->can_change)
10877           {
10878             change_done = TRUE;
10879             change_done_any = TRUE;
10880           }
10881         }
10882       }
10883     }
10884   }
10885
10886   RECURSION_LOOP_DETECTION_END();
10887
10888   return change_done_any;
10889 }
10890
10891 static boolean CheckElementChangeExt(int x, int y,
10892                                      int element,
10893                                      int trigger_element,
10894                                      int trigger_event,
10895                                      int trigger_player,
10896                                      int trigger_side)
10897 {
10898   boolean change_done = FALSE;
10899   int p;
10900
10901   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10902       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10903     return FALSE;
10904
10905   if (Feld[x][y] == EL_BLOCKED)
10906   {
10907     Blocked2Moving(x, y, &x, &y);
10908     element = Feld[x][y];
10909   }
10910
10911   // check if element has already changed or is about to change after moving
10912   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10913        Feld[x][y] != element) ||
10914
10915       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10916        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10917         ChangePage[x][y] != -1)))
10918     return FALSE;
10919
10920   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10921
10922   for (p = 0; p < element_info[element].num_change_pages; p++)
10923   {
10924     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10925
10926     /* check trigger element for all events where the element that is checked
10927        for changing interacts with a directly adjacent element -- this is
10928        different to element changes that affect other elements to change on the
10929        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10930     boolean check_trigger_element =
10931       (trigger_event == CE_TOUCHING_X ||
10932        trigger_event == CE_HITTING_X ||
10933        trigger_event == CE_HIT_BY_X ||
10934        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10935
10936     if (change->can_change_or_has_action &&
10937         change->has_event[trigger_event] &&
10938         change->trigger_side & trigger_side &&
10939         change->trigger_player & trigger_player &&
10940         (!check_trigger_element ||
10941          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10942     {
10943       change->actual_trigger_element = trigger_element;
10944       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10945       change->actual_trigger_player_bits = trigger_player;
10946       change->actual_trigger_side = trigger_side;
10947       change->actual_trigger_ce_value = CustomValue[x][y];
10948       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10949
10950       // special case: trigger element not at (x,y) position for some events
10951       if (check_trigger_element)
10952       {
10953         static struct
10954         {
10955           int dx, dy;
10956         } move_xy[] =
10957           {
10958             {  0,  0 },
10959             { -1,  0 },
10960             { +1,  0 },
10961             {  0,  0 },
10962             {  0, -1 },
10963             {  0,  0 }, { 0, 0 }, { 0, 0 },
10964             {  0, +1 }
10965           };
10966
10967         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10968         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10969
10970         change->actual_trigger_ce_value = CustomValue[xx][yy];
10971         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10972       }
10973
10974       if (change->can_change && !change_done)
10975       {
10976         ChangeDelay[x][y] = 1;
10977         ChangeEvent[x][y] = trigger_event;
10978
10979         HandleElementChange(x, y, p);
10980
10981         change_done = TRUE;
10982       }
10983       else if (change->has_action)
10984       {
10985         ExecuteCustomElementAction(x, y, element, p);
10986         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10987       }
10988     }
10989   }
10990
10991   RECURSION_LOOP_DETECTION_END();
10992
10993   return change_done;
10994 }
10995
10996 static void PlayPlayerSound(struct PlayerInfo *player)
10997 {
10998   int jx = player->jx, jy = player->jy;
10999   int sound_element = player->artwork_element;
11000   int last_action = player->last_action_waiting;
11001   int action = player->action_waiting;
11002
11003   if (player->is_waiting)
11004   {
11005     if (action != last_action)
11006       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11007     else
11008       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11009   }
11010   else
11011   {
11012     if (action != last_action)
11013       StopSound(element_info[sound_element].sound[last_action]);
11014
11015     if (last_action == ACTION_SLEEPING)
11016       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11017   }
11018 }
11019
11020 static void PlayAllPlayersSound(void)
11021 {
11022   int i;
11023
11024   for (i = 0; i < MAX_PLAYERS; i++)
11025     if (stored_player[i].active)
11026       PlayPlayerSound(&stored_player[i]);
11027 }
11028
11029 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11030 {
11031   boolean last_waiting = player->is_waiting;
11032   int move_dir = player->MovDir;
11033
11034   player->dir_waiting = move_dir;
11035   player->last_action_waiting = player->action_waiting;
11036
11037   if (is_waiting)
11038   {
11039     if (!last_waiting)          // not waiting -> waiting
11040     {
11041       player->is_waiting = TRUE;
11042
11043       player->frame_counter_bored =
11044         FrameCounter +
11045         game.player_boring_delay_fixed +
11046         GetSimpleRandom(game.player_boring_delay_random);
11047       player->frame_counter_sleeping =
11048         FrameCounter +
11049         game.player_sleeping_delay_fixed +
11050         GetSimpleRandom(game.player_sleeping_delay_random);
11051
11052       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11053     }
11054
11055     if (game.player_sleeping_delay_fixed +
11056         game.player_sleeping_delay_random > 0 &&
11057         player->anim_delay_counter == 0 &&
11058         player->post_delay_counter == 0 &&
11059         FrameCounter >= player->frame_counter_sleeping)
11060       player->is_sleeping = TRUE;
11061     else if (game.player_boring_delay_fixed +
11062              game.player_boring_delay_random > 0 &&
11063              FrameCounter >= player->frame_counter_bored)
11064       player->is_bored = TRUE;
11065
11066     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11067                               player->is_bored ? ACTION_BORING :
11068                               ACTION_WAITING);
11069
11070     if (player->is_sleeping && player->use_murphy)
11071     {
11072       // special case for sleeping Murphy when leaning against non-free tile
11073
11074       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11075           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11076            !IS_MOVING(player->jx - 1, player->jy)))
11077         move_dir = MV_LEFT;
11078       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11079                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11080                 !IS_MOVING(player->jx + 1, player->jy)))
11081         move_dir = MV_RIGHT;
11082       else
11083         player->is_sleeping = FALSE;
11084
11085       player->dir_waiting = move_dir;
11086     }
11087
11088     if (player->is_sleeping)
11089     {
11090       if (player->num_special_action_sleeping > 0)
11091       {
11092         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11093         {
11094           int last_special_action = player->special_action_sleeping;
11095           int num_special_action = player->num_special_action_sleeping;
11096           int special_action =
11097             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11098              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11099              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11100              last_special_action + 1 : ACTION_SLEEPING);
11101           int special_graphic =
11102             el_act_dir2img(player->artwork_element, special_action, move_dir);
11103
11104           player->anim_delay_counter =
11105             graphic_info[special_graphic].anim_delay_fixed +
11106             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11107           player->post_delay_counter =
11108             graphic_info[special_graphic].post_delay_fixed +
11109             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11110
11111           player->special_action_sleeping = special_action;
11112         }
11113
11114         if (player->anim_delay_counter > 0)
11115         {
11116           player->action_waiting = player->special_action_sleeping;
11117           player->anim_delay_counter--;
11118         }
11119         else if (player->post_delay_counter > 0)
11120         {
11121           player->post_delay_counter--;
11122         }
11123       }
11124     }
11125     else if (player->is_bored)
11126     {
11127       if (player->num_special_action_bored > 0)
11128       {
11129         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11130         {
11131           int special_action =
11132             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11133           int special_graphic =
11134             el_act_dir2img(player->artwork_element, special_action, move_dir);
11135
11136           player->anim_delay_counter =
11137             graphic_info[special_graphic].anim_delay_fixed +
11138             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11139           player->post_delay_counter =
11140             graphic_info[special_graphic].post_delay_fixed +
11141             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11142
11143           player->special_action_bored = special_action;
11144         }
11145
11146         if (player->anim_delay_counter > 0)
11147         {
11148           player->action_waiting = player->special_action_bored;
11149           player->anim_delay_counter--;
11150         }
11151         else if (player->post_delay_counter > 0)
11152         {
11153           player->post_delay_counter--;
11154         }
11155       }
11156     }
11157   }
11158   else if (last_waiting)        // waiting -> not waiting
11159   {
11160     player->is_waiting = FALSE;
11161     player->is_bored = FALSE;
11162     player->is_sleeping = FALSE;
11163
11164     player->frame_counter_bored = -1;
11165     player->frame_counter_sleeping = -1;
11166
11167     player->anim_delay_counter = 0;
11168     player->post_delay_counter = 0;
11169
11170     player->dir_waiting = player->MovDir;
11171     player->action_waiting = ACTION_DEFAULT;
11172
11173     player->special_action_bored = ACTION_DEFAULT;
11174     player->special_action_sleeping = ACTION_DEFAULT;
11175   }
11176 }
11177
11178 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11179 {
11180   if ((!player->is_moving  && player->was_moving) ||
11181       (player->MovPos == 0 && player->was_moving) ||
11182       (player->is_snapping && !player->was_snapping) ||
11183       (player->is_dropping && !player->was_dropping))
11184   {
11185     if (!CheckSaveEngineSnapshotToList())
11186       return;
11187
11188     player->was_moving = FALSE;
11189     player->was_snapping = TRUE;
11190     player->was_dropping = TRUE;
11191   }
11192   else
11193   {
11194     if (player->is_moving)
11195       player->was_moving = TRUE;
11196
11197     if (!player->is_snapping)
11198       player->was_snapping = FALSE;
11199
11200     if (!player->is_dropping)
11201       player->was_dropping = FALSE;
11202   }
11203 }
11204
11205 static void CheckSingleStepMode(struct PlayerInfo *player)
11206 {
11207   if (tape.single_step && tape.recording && !tape.pausing)
11208   {
11209     /* as it is called "single step mode", just return to pause mode when the
11210        player stopped moving after one tile (or never starts moving at all) */
11211     if (!player->is_moving &&
11212         !player->is_pushing &&
11213         !player->is_dropping_pressed)
11214       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11215   }
11216
11217   CheckSaveEngineSnapshot(player);
11218 }
11219
11220 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11221 {
11222   int left      = player_action & JOY_LEFT;
11223   int right     = player_action & JOY_RIGHT;
11224   int up        = player_action & JOY_UP;
11225   int down      = player_action & JOY_DOWN;
11226   int button1   = player_action & JOY_BUTTON_1;
11227   int button2   = player_action & JOY_BUTTON_2;
11228   int dx        = (left ? -1 : right ? 1 : 0);
11229   int dy        = (up   ? -1 : down  ? 1 : 0);
11230
11231   if (!player->active || tape.pausing)
11232     return 0;
11233
11234   if (player_action)
11235   {
11236     if (button1)
11237       SnapField(player, dx, dy);
11238     else
11239     {
11240       if (button2)
11241         DropElement(player);
11242
11243       MovePlayer(player, dx, dy);
11244     }
11245
11246     CheckSingleStepMode(player);
11247
11248     SetPlayerWaiting(player, FALSE);
11249
11250     return player_action;
11251   }
11252   else
11253   {
11254     // no actions for this player (no input at player's configured device)
11255
11256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11257     SnapField(player, 0, 0);
11258     CheckGravityMovementWhenNotMoving(player);
11259
11260     if (player->MovPos == 0)
11261       SetPlayerWaiting(player, TRUE);
11262
11263     if (player->MovPos == 0)    // needed for tape.playing
11264       player->is_moving = FALSE;
11265
11266     player->is_dropping = FALSE;
11267     player->is_dropping_pressed = FALSE;
11268     player->drop_pressed_delay = 0;
11269
11270     CheckSingleStepMode(player);
11271
11272     return 0;
11273   }
11274 }
11275
11276 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11277                                          byte *tape_action)
11278 {
11279   if (!tape.use_mouse_actions)
11280     return;
11281
11282   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11283   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11284   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11285 }
11286
11287 static void SetTapeActionFromMouseAction(byte *tape_action,
11288                                          struct MouseActionInfo *mouse_action)
11289 {
11290   if (!tape.use_mouse_actions)
11291     return;
11292
11293   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11294   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11295   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11296 }
11297
11298 static void CheckLevelSolved(void)
11299 {
11300   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11301   {
11302     if (game_em.level_solved &&
11303         !game_em.game_over)                             // game won
11304     {
11305       LevelSolved();
11306
11307       game_em.game_over = TRUE;
11308
11309       game.all_players_gone = TRUE;
11310     }
11311
11312     if (game_em.game_over)                              // game lost
11313       game.all_players_gone = TRUE;
11314   }
11315   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11316   {
11317     if (game_sp.level_solved &&
11318         !game_sp.game_over)                             // game won
11319     {
11320       LevelSolved();
11321
11322       game_sp.game_over = TRUE;
11323
11324       game.all_players_gone = TRUE;
11325     }
11326
11327     if (game_sp.game_over)                              // game lost
11328       game.all_players_gone = TRUE;
11329   }
11330   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11331   {
11332     if (game_mm.level_solved &&
11333         !game_mm.game_over)                             // game won
11334     {
11335       LevelSolved();
11336
11337       game_mm.game_over = TRUE;
11338
11339       game.all_players_gone = TRUE;
11340     }
11341
11342     if (game_mm.game_over)                              // game lost
11343       game.all_players_gone = TRUE;
11344   }
11345 }
11346
11347 static void CheckLevelTime(void)
11348 {
11349   int i;
11350
11351   if (TimeFrames >= FRAMES_PER_SECOND)
11352   {
11353     TimeFrames = 0;
11354     TapeTime++;
11355
11356     for (i = 0; i < MAX_PLAYERS; i++)
11357     {
11358       struct PlayerInfo *player = &stored_player[i];
11359
11360       if (SHIELD_ON(player))
11361       {
11362         player->shield_normal_time_left--;
11363
11364         if (player->shield_deadly_time_left > 0)
11365           player->shield_deadly_time_left--;
11366       }
11367     }
11368
11369     if (!game.LevelSolved && !level.use_step_counter)
11370     {
11371       TimePlayed++;
11372
11373       if (TimeLeft > 0)
11374       {
11375         TimeLeft--;
11376
11377         if (TimeLeft <= 10 && setup.time_limit)
11378           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11379
11380         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11381            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11382
11383         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11384
11385         if (!TimeLeft && setup.time_limit)
11386         {
11387           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11388             game_em.lev->killed_out_of_time = TRUE;
11389           else
11390             for (i = 0; i < MAX_PLAYERS; i++)
11391               KillPlayer(&stored_player[i]);
11392         }
11393       }
11394       else if (game.no_time_limit && !game.all_players_gone)
11395       {
11396         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11397       }
11398
11399       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11400     }
11401
11402     if (tape.recording || tape.playing)
11403       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11404   }
11405
11406   if (tape.recording || tape.playing)
11407     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11408
11409   UpdateAndDisplayGameControlValues();
11410 }
11411
11412 void AdvanceFrameAndPlayerCounters(int player_nr)
11413 {
11414   int i;
11415
11416   // advance frame counters (global frame counter and time frame counter)
11417   FrameCounter++;
11418   TimeFrames++;
11419
11420   // advance player counters (counters for move delay, move animation etc.)
11421   for (i = 0; i < MAX_PLAYERS; i++)
11422   {
11423     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11424     int move_delay_value = stored_player[i].move_delay_value;
11425     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11426
11427     if (!advance_player_counters)       // not all players may be affected
11428       continue;
11429
11430     if (move_frames == 0)       // less than one move per game frame
11431     {
11432       int stepsize = TILEX / move_delay_value;
11433       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11434       int count = (stored_player[i].is_moving ?
11435                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11436
11437       if (count % delay == 0)
11438         move_frames = 1;
11439     }
11440
11441     stored_player[i].Frame += move_frames;
11442
11443     if (stored_player[i].MovPos != 0)
11444       stored_player[i].StepFrame += move_frames;
11445
11446     if (stored_player[i].move_delay > 0)
11447       stored_player[i].move_delay--;
11448
11449     // due to bugs in previous versions, counter must count up, not down
11450     if (stored_player[i].push_delay != -1)
11451       stored_player[i].push_delay++;
11452
11453     if (stored_player[i].drop_delay > 0)
11454       stored_player[i].drop_delay--;
11455
11456     if (stored_player[i].is_dropping_pressed)
11457       stored_player[i].drop_pressed_delay++;
11458   }
11459 }
11460
11461 void StartGameActions(boolean init_network_game, boolean record_tape,
11462                       int random_seed)
11463 {
11464   unsigned int new_random_seed = InitRND(random_seed);
11465
11466   if (record_tape)
11467     TapeStartRecording(new_random_seed);
11468
11469   if (init_network_game)
11470   {
11471     SendToServer_LevelFile();
11472     SendToServer_StartPlaying();
11473
11474     return;
11475   }
11476
11477   InitGame();
11478 }
11479
11480 static void GameActionsExt(void)
11481 {
11482 #if 0
11483   static unsigned int game_frame_delay = 0;
11484 #endif
11485   unsigned int game_frame_delay_value;
11486   byte *recorded_player_action;
11487   byte summarized_player_action = 0;
11488   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11489   int i;
11490
11491   // detect endless loops, caused by custom element programming
11492   if (recursion_loop_detected && recursion_loop_depth == 0)
11493   {
11494     char *message = getStringCat3("Internal Error! Element ",
11495                                   EL_NAME(recursion_loop_element),
11496                                   " caused endless loop! Quit the game?");
11497
11498     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11499           EL_NAME(recursion_loop_element));
11500
11501     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11502
11503     recursion_loop_detected = FALSE;    // if game should be continued
11504
11505     free(message);
11506
11507     return;
11508   }
11509
11510   if (game.restart_level)
11511     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11512
11513   CheckLevelSolved();
11514
11515   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11516     GameWon();
11517
11518   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11519     TapeStop();
11520
11521   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11522     return;
11523
11524   game_frame_delay_value =
11525     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11526
11527   if (tape.playing && tape.warp_forward && !tape.pausing)
11528     game_frame_delay_value = 0;
11529
11530   SetVideoFrameDelay(game_frame_delay_value);
11531
11532   // (de)activate virtual buttons depending on current game status
11533   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11534   {
11535     if (game.all_players_gone)  // if no players there to be controlled anymore
11536       SetOverlayActive(FALSE);
11537     else if (!tape.playing)     // if game continues after tape stopped playing
11538       SetOverlayActive(TRUE);
11539   }
11540
11541 #if 0
11542 #if 0
11543   // ---------- main game synchronization point ----------
11544
11545   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11546
11547   printf("::: skip == %d\n", skip);
11548
11549 #else
11550   // ---------- main game synchronization point ----------
11551
11552   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11553 #endif
11554 #endif
11555
11556   if (network_playing && !network_player_action_received)
11557   {
11558     // try to get network player actions in time
11559
11560     // last chance to get network player actions without main loop delay
11561     HandleNetworking();
11562
11563     // game was quit by network peer
11564     if (game_status != GAME_MODE_PLAYING)
11565       return;
11566
11567     // check if network player actions still missing and game still running
11568     if (!network_player_action_received && !checkGameEnded())
11569       return;           // failed to get network player actions in time
11570
11571     // do not yet reset "network_player_action_received" (for tape.pausing)
11572   }
11573
11574   if (tape.pausing)
11575     return;
11576
11577   // at this point we know that we really continue executing the game
11578
11579   network_player_action_received = FALSE;
11580
11581   // when playing tape, read previously recorded player input from tape data
11582   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11583
11584   local_player->effective_mouse_action = local_player->mouse_action;
11585
11586   if (recorded_player_action != NULL)
11587     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11588                                  recorded_player_action);
11589
11590   // TapePlayAction() may return NULL when toggling to "pause before death"
11591   if (tape.pausing)
11592     return;
11593
11594   if (tape.set_centered_player)
11595   {
11596     game.centered_player_nr_next = tape.centered_player_nr_next;
11597     game.set_centered_player = TRUE;
11598   }
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     summarized_player_action |= stored_player[i].action;
11603
11604     if (!network_playing && (game.team_mode || tape.playing))
11605       stored_player[i].effective_action = stored_player[i].action;
11606   }
11607
11608   if (network_playing && !checkGameEnded())
11609     SendToServer_MovePlayer(summarized_player_action);
11610
11611   // summarize all actions at local players mapped input device position
11612   // (this allows using different input devices in single player mode)
11613   if (!network.enabled && !game.team_mode)
11614     stored_player[map_player_action[local_player->index_nr]].effective_action =
11615       summarized_player_action;
11616
11617   // summarize all actions at centered player in local team mode
11618   if (tape.recording &&
11619       setup.team_mode && !network.enabled &&
11620       setup.input_on_focus &&
11621       game.centered_player_nr != -1)
11622   {
11623     for (i = 0; i < MAX_PLAYERS; i++)
11624       stored_player[map_player_action[i]].effective_action =
11625         (i == game.centered_player_nr ? summarized_player_action : 0);
11626   }
11627
11628   if (recorded_player_action != NULL)
11629     for (i = 0; i < MAX_PLAYERS; i++)
11630       stored_player[i].effective_action = recorded_player_action[i];
11631
11632   for (i = 0; i < MAX_PLAYERS; i++)
11633   {
11634     tape_action[i] = stored_player[i].effective_action;
11635
11636     /* (this may happen in the RND game engine if a player was not present on
11637        the playfield on level start, but appeared later from a custom element */
11638     if (setup.team_mode &&
11639         tape.recording &&
11640         tape_action[i] &&
11641         !tape.player_participates[i])
11642       tape.player_participates[i] = TRUE;
11643   }
11644
11645   SetTapeActionFromMouseAction(tape_action,
11646                                &local_player->effective_mouse_action);
11647
11648   // only record actions from input devices, but not programmed actions
11649   if (tape.recording)
11650     TapeRecordAction(tape_action);
11651
11652   // remember if game was played (especially after tape stopped playing)
11653   if (!tape.playing && summarized_player_action)
11654     game.GamePlayed = TRUE;
11655
11656 #if USE_NEW_PLAYER_ASSIGNMENTS
11657   // !!! also map player actions in single player mode !!!
11658   // if (game.team_mode)
11659   if (1)
11660   {
11661     byte mapped_action[MAX_PLAYERS];
11662
11663 #if DEBUG_PLAYER_ACTIONS
11664     printf(":::");
11665     for (i = 0; i < MAX_PLAYERS; i++)
11666       printf(" %d, ", stored_player[i].effective_action);
11667 #endif
11668
11669     for (i = 0; i < MAX_PLAYERS; i++)
11670       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11671
11672     for (i = 0; i < MAX_PLAYERS; i++)
11673       stored_player[i].effective_action = mapped_action[i];
11674
11675 #if DEBUG_PLAYER_ACTIONS
11676     printf(" =>");
11677     for (i = 0; i < MAX_PLAYERS; i++)
11678       printf(" %d, ", stored_player[i].effective_action);
11679     printf("\n");
11680 #endif
11681   }
11682 #if DEBUG_PLAYER_ACTIONS
11683   else
11684   {
11685     printf(":::");
11686     for (i = 0; i < MAX_PLAYERS; i++)
11687       printf(" %d, ", stored_player[i].effective_action);
11688     printf("\n");
11689   }
11690 #endif
11691 #endif
11692
11693   for (i = 0; i < MAX_PLAYERS; i++)
11694   {
11695     // allow engine snapshot in case of changed movement attempt
11696     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11697         (stored_player[i].effective_action & KEY_MOTION))
11698       game.snapshot.changed_action = TRUE;
11699
11700     // allow engine snapshot in case of snapping/dropping attempt
11701     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11702         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11703       game.snapshot.changed_action = TRUE;
11704
11705     game.snapshot.last_action[i] = stored_player[i].effective_action;
11706   }
11707
11708   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11709   {
11710     GameActions_EM_Main();
11711   }
11712   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11713   {
11714     GameActions_SP_Main();
11715   }
11716   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11717   {
11718     GameActions_MM_Main();
11719   }
11720   else
11721   {
11722     GameActions_RND_Main();
11723   }
11724
11725   BlitScreenToBitmap(backbuffer);
11726
11727   CheckLevelSolved();
11728   CheckLevelTime();
11729
11730   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11731
11732   if (global.show_frames_per_second)
11733   {
11734     static unsigned int fps_counter = 0;
11735     static int fps_frames = 0;
11736     unsigned int fps_delay_ms = Counter() - fps_counter;
11737
11738     fps_frames++;
11739
11740     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11741     {
11742       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11743
11744       fps_frames = 0;
11745       fps_counter = Counter();
11746
11747       // always draw FPS to screen after FPS value was updated
11748       redraw_mask |= REDRAW_FPS;
11749     }
11750
11751     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11752     if (GetDrawDeactivationMask() == REDRAW_NONE)
11753       redraw_mask |= REDRAW_FPS;
11754   }
11755 }
11756
11757 static void GameActions_CheckSaveEngineSnapshot(void)
11758 {
11759   if (!game.snapshot.save_snapshot)
11760     return;
11761
11762   // clear flag for saving snapshot _before_ saving snapshot
11763   game.snapshot.save_snapshot = FALSE;
11764
11765   SaveEngineSnapshotToList();
11766 }
11767
11768 void GameActions(void)
11769 {
11770   GameActionsExt();
11771
11772   GameActions_CheckSaveEngineSnapshot();
11773 }
11774
11775 void GameActions_EM_Main(void)
11776 {
11777   byte effective_action[MAX_PLAYERS];
11778   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11779   int i;
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782     effective_action[i] = stored_player[i].effective_action;
11783
11784   GameActions_EM(effective_action, warp_mode);
11785 }
11786
11787 void GameActions_SP_Main(void)
11788 {
11789   byte effective_action[MAX_PLAYERS];
11790   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11791   int i;
11792
11793   for (i = 0; i < MAX_PLAYERS; i++)
11794     effective_action[i] = stored_player[i].effective_action;
11795
11796   GameActions_SP(effective_action, warp_mode);
11797
11798   for (i = 0; i < MAX_PLAYERS; i++)
11799   {
11800     if (stored_player[i].force_dropping)
11801       stored_player[i].action |= KEY_BUTTON_DROP;
11802
11803     stored_player[i].force_dropping = FALSE;
11804   }
11805 }
11806
11807 void GameActions_MM_Main(void)
11808 {
11809   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11810
11811   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11812 }
11813
11814 void GameActions_RND_Main(void)
11815 {
11816   GameActions_RND();
11817 }
11818
11819 void GameActions_RND(void)
11820 {
11821   static struct MouseActionInfo mouse_action_last = { 0 };
11822   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11823   int magic_wall_x = 0, magic_wall_y = 0;
11824   int i, x, y, element, graphic, last_gfx_frame;
11825
11826   InitPlayfieldScanModeVars();
11827
11828   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11829   {
11830     SCAN_PLAYFIELD(x, y)
11831     {
11832       ChangeCount[x][y] = 0;
11833       ChangeEvent[x][y] = -1;
11834     }
11835   }
11836
11837   if (game.set_centered_player)
11838   {
11839     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11840
11841     // switching to "all players" only possible if all players fit to screen
11842     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11843     {
11844       game.centered_player_nr_next = game.centered_player_nr;
11845       game.set_centered_player = FALSE;
11846     }
11847
11848     // do not switch focus to non-existing (or non-active) player
11849     if (game.centered_player_nr_next >= 0 &&
11850         !stored_player[game.centered_player_nr_next].active)
11851     {
11852       game.centered_player_nr_next = game.centered_player_nr;
11853       game.set_centered_player = FALSE;
11854     }
11855   }
11856
11857   if (game.set_centered_player &&
11858       ScreenMovPos == 0)        // screen currently aligned at tile position
11859   {
11860     int sx, sy;
11861
11862     if (game.centered_player_nr_next == -1)
11863     {
11864       setScreenCenteredToAllPlayers(&sx, &sy);
11865     }
11866     else
11867     {
11868       sx = stored_player[game.centered_player_nr_next].jx;
11869       sy = stored_player[game.centered_player_nr_next].jy;
11870     }
11871
11872     game.centered_player_nr = game.centered_player_nr_next;
11873     game.set_centered_player = FALSE;
11874
11875     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11876     DrawGameDoorValues();
11877   }
11878
11879   for (i = 0; i < MAX_PLAYERS; i++)
11880   {
11881     int actual_player_action = stored_player[i].effective_action;
11882
11883 #if 1
11884     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11885        - rnd_equinox_tetrachloride 048
11886        - rnd_equinox_tetrachloride_ii 096
11887        - rnd_emanuel_schmieg 002
11888        - doctor_sloan_ww 001, 020
11889     */
11890     if (stored_player[i].MovPos == 0)
11891       CheckGravityMovement(&stored_player[i]);
11892 #endif
11893
11894     // overwrite programmed action with tape action
11895     if (stored_player[i].programmed_action)
11896       actual_player_action = stored_player[i].programmed_action;
11897
11898     PlayerActions(&stored_player[i], actual_player_action);
11899
11900     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11901   }
11902
11903   ScrollScreen(NULL, SCROLL_GO_ON);
11904
11905   /* for backwards compatibility, the following code emulates a fixed bug that
11906      occured when pushing elements (causing elements that just made their last
11907      pushing step to already (if possible) make their first falling step in the
11908      same game frame, which is bad); this code is also needed to use the famous
11909      "spring push bug" which is used in older levels and might be wanted to be
11910      used also in newer levels, but in this case the buggy pushing code is only
11911      affecting the "spring" element and no other elements */
11912
11913   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11914   {
11915     for (i = 0; i < MAX_PLAYERS; i++)
11916     {
11917       struct PlayerInfo *player = &stored_player[i];
11918       int x = player->jx;
11919       int y = player->jy;
11920
11921       if (player->active && player->is_pushing && player->is_moving &&
11922           IS_MOVING(x, y) &&
11923           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11924            Feld[x][y] == EL_SPRING))
11925       {
11926         ContinueMoving(x, y);
11927
11928         // continue moving after pushing (this is actually a bug)
11929         if (!IS_MOVING(x, y))
11930           Stop[x][y] = FALSE;
11931       }
11932     }
11933   }
11934
11935   SCAN_PLAYFIELD(x, y)
11936   {
11937     Last[x][y] = Feld[x][y];
11938
11939     ChangeCount[x][y] = 0;
11940     ChangeEvent[x][y] = -1;
11941
11942     // this must be handled before main playfield loop
11943     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11944     {
11945       MovDelay[x][y]--;
11946       if (MovDelay[x][y] <= 0)
11947         RemoveField(x, y);
11948     }
11949
11950     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11951     {
11952       MovDelay[x][y]--;
11953       if (MovDelay[x][y] <= 0)
11954       {
11955         RemoveField(x, y);
11956         TEST_DrawLevelField(x, y);
11957
11958         TestIfElementTouchesCustomElement(x, y);        // for empty space
11959       }
11960     }
11961
11962 #if DEBUG
11963     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11964     {
11965       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11966       printf("GameActions(): This should never happen!\n");
11967
11968       ChangePage[x][y] = -1;
11969     }
11970 #endif
11971
11972     Stop[x][y] = FALSE;
11973     if (WasJustMoving[x][y] > 0)
11974       WasJustMoving[x][y]--;
11975     if (WasJustFalling[x][y] > 0)
11976       WasJustFalling[x][y]--;
11977     if (CheckCollision[x][y] > 0)
11978       CheckCollision[x][y]--;
11979     if (CheckImpact[x][y] > 0)
11980       CheckImpact[x][y]--;
11981
11982     GfxFrame[x][y]++;
11983
11984     /* reset finished pushing action (not done in ContinueMoving() to allow
11985        continuous pushing animation for elements with zero push delay) */
11986     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11987     {
11988       ResetGfxAnimation(x, y);
11989       TEST_DrawLevelField(x, y);
11990     }
11991
11992 #if DEBUG
11993     if (IS_BLOCKED(x, y))
11994     {
11995       int oldx, oldy;
11996
11997       Blocked2Moving(x, y, &oldx, &oldy);
11998       if (!IS_MOVING(oldx, oldy))
11999       {
12000         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12001         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12002         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12003         printf("GameActions(): This should never happen!\n");
12004       }
12005     }
12006 #endif
12007   }
12008
12009   if (mouse_action.button)
12010   {
12011     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12012
12013     x = mouse_action.lx;
12014     y = mouse_action.ly;
12015     element = Feld[x][y];
12016
12017     if (new_button)
12018     {
12019       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12020       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12021     }
12022
12023     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12024     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12025   }
12026
12027   SCAN_PLAYFIELD(x, y)
12028   {
12029     element = Feld[x][y];
12030     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12031     last_gfx_frame = GfxFrame[x][y];
12032
12033     ResetGfxFrame(x, y);
12034
12035     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12036       DrawLevelGraphicAnimation(x, y, graphic);
12037
12038     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12039         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12040       ResetRandomAnimationValue(x, y);
12041
12042     SetRandomAnimationValue(x, y);
12043
12044     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12045
12046     if (IS_INACTIVE(element))
12047     {
12048       if (IS_ANIMATED(graphic))
12049         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12050
12051       continue;
12052     }
12053
12054     // this may take place after moving, so 'element' may have changed
12055     if (IS_CHANGING(x, y) &&
12056         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12057     {
12058       int page = element_info[element].event_page_nr[CE_DELAY];
12059
12060       HandleElementChange(x, y, page);
12061
12062       element = Feld[x][y];
12063       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12064     }
12065
12066     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12067     {
12068       StartMoving(x, y);
12069
12070       element = Feld[x][y];
12071       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12072
12073       if (IS_ANIMATED(graphic) &&
12074           !IS_MOVING(x, y) &&
12075           !Stop[x][y])
12076         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12077
12078       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12079         TEST_DrawTwinkleOnField(x, y);
12080     }
12081     else if (element == EL_ACID)
12082     {
12083       if (!Stop[x][y])
12084         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12085     }
12086     else if ((element == EL_EXIT_OPEN ||
12087               element == EL_EM_EXIT_OPEN ||
12088               element == EL_SP_EXIT_OPEN ||
12089               element == EL_STEEL_EXIT_OPEN ||
12090               element == EL_EM_STEEL_EXIT_OPEN ||
12091               element == EL_SP_TERMINAL ||
12092               element == EL_SP_TERMINAL_ACTIVE ||
12093               element == EL_EXTRA_TIME ||
12094               element == EL_SHIELD_NORMAL ||
12095               element == EL_SHIELD_DEADLY) &&
12096              IS_ANIMATED(graphic))
12097       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12098     else if (IS_MOVING(x, y))
12099       ContinueMoving(x, y);
12100     else if (IS_ACTIVE_BOMB(element))
12101       CheckDynamite(x, y);
12102     else if (element == EL_AMOEBA_GROWING)
12103       AmoebeWaechst(x, y);
12104     else if (element == EL_AMOEBA_SHRINKING)
12105       AmoebaDisappearing(x, y);
12106
12107 #if !USE_NEW_AMOEBA_CODE
12108     else if (IS_AMOEBALIVE(element))
12109       AmoebeAbleger(x, y);
12110 #endif
12111
12112     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12113       Life(x, y);
12114     else if (element == EL_EXIT_CLOSED)
12115       CheckExit(x, y);
12116     else if (element == EL_EM_EXIT_CLOSED)
12117       CheckExitEM(x, y);
12118     else if (element == EL_STEEL_EXIT_CLOSED)
12119       CheckExitSteel(x, y);
12120     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12121       CheckExitSteelEM(x, y);
12122     else if (element == EL_SP_EXIT_CLOSED)
12123       CheckExitSP(x, y);
12124     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12125              element == EL_EXPANDABLE_STEELWALL_GROWING)
12126       MauerWaechst(x, y);
12127     else if (element == EL_EXPANDABLE_WALL ||
12128              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12129              element == EL_EXPANDABLE_WALL_VERTICAL ||
12130              element == EL_EXPANDABLE_WALL_ANY ||
12131              element == EL_BD_EXPANDABLE_WALL)
12132       MauerAbleger(x, y);
12133     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12134              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12135              element == EL_EXPANDABLE_STEELWALL_ANY)
12136       MauerAblegerStahl(x, y);
12137     else if (element == EL_FLAMES)
12138       CheckForDragon(x, y);
12139     else if (element == EL_EXPLOSION)
12140       ; // drawing of correct explosion animation is handled separately
12141     else if (element == EL_ELEMENT_SNAPPING ||
12142              element == EL_DIAGONAL_SHRINKING ||
12143              element == EL_DIAGONAL_GROWING)
12144     {
12145       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12146
12147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12148     }
12149     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12150       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12151
12152     if (IS_BELT_ACTIVE(element))
12153       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12154
12155     if (game.magic_wall_active)
12156     {
12157       int jx = local_player->jx, jy = local_player->jy;
12158
12159       // play the element sound at the position nearest to the player
12160       if ((element == EL_MAGIC_WALL_FULL ||
12161            element == EL_MAGIC_WALL_ACTIVE ||
12162            element == EL_MAGIC_WALL_EMPTYING ||
12163            element == EL_BD_MAGIC_WALL_FULL ||
12164            element == EL_BD_MAGIC_WALL_ACTIVE ||
12165            element == EL_BD_MAGIC_WALL_EMPTYING ||
12166            element == EL_DC_MAGIC_WALL_FULL ||
12167            element == EL_DC_MAGIC_WALL_ACTIVE ||
12168            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12169           ABS(x - jx) + ABS(y - jy) <
12170           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12171       {
12172         magic_wall_x = x;
12173         magic_wall_y = y;
12174       }
12175     }
12176   }
12177
12178 #if USE_NEW_AMOEBA_CODE
12179   // new experimental amoeba growth stuff
12180   if (!(FrameCounter % 8))
12181   {
12182     static unsigned int random = 1684108901;
12183
12184     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12185     {
12186       x = RND(lev_fieldx);
12187       y = RND(lev_fieldy);
12188       element = Feld[x][y];
12189
12190       if (!IS_PLAYER(x,y) &&
12191           (element == EL_EMPTY ||
12192            CAN_GROW_INTO(element) ||
12193            element == EL_QUICKSAND_EMPTY ||
12194            element == EL_QUICKSAND_FAST_EMPTY ||
12195            element == EL_ACID_SPLASH_LEFT ||
12196            element == EL_ACID_SPLASH_RIGHT))
12197       {
12198         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12199             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12200             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12201             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12202           Feld[x][y] = EL_AMOEBA_DROP;
12203       }
12204
12205       random = random * 129 + 1;
12206     }
12207   }
12208 #endif
12209
12210   game.explosions_delayed = FALSE;
12211
12212   SCAN_PLAYFIELD(x, y)
12213   {
12214     element = Feld[x][y];
12215
12216     if (ExplodeField[x][y])
12217       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12218     else if (element == EL_EXPLOSION)
12219       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12220
12221     ExplodeField[x][y] = EX_TYPE_NONE;
12222   }
12223
12224   game.explosions_delayed = TRUE;
12225
12226   if (game.magic_wall_active)
12227   {
12228     if (!(game.magic_wall_time_left % 4))
12229     {
12230       int element = Feld[magic_wall_x][magic_wall_y];
12231
12232       if (element == EL_BD_MAGIC_WALL_FULL ||
12233           element == EL_BD_MAGIC_WALL_ACTIVE ||
12234           element == EL_BD_MAGIC_WALL_EMPTYING)
12235         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12236       else if (element == EL_DC_MAGIC_WALL_FULL ||
12237                element == EL_DC_MAGIC_WALL_ACTIVE ||
12238                element == EL_DC_MAGIC_WALL_EMPTYING)
12239         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12240       else
12241         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12242     }
12243
12244     if (game.magic_wall_time_left > 0)
12245     {
12246       game.magic_wall_time_left--;
12247
12248       if (!game.magic_wall_time_left)
12249       {
12250         SCAN_PLAYFIELD(x, y)
12251         {
12252           element = Feld[x][y];
12253
12254           if (element == EL_MAGIC_WALL_ACTIVE ||
12255               element == EL_MAGIC_WALL_FULL)
12256           {
12257             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12258             TEST_DrawLevelField(x, y);
12259           }
12260           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12261                    element == EL_BD_MAGIC_WALL_FULL)
12262           {
12263             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12264             TEST_DrawLevelField(x, y);
12265           }
12266           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12267                    element == EL_DC_MAGIC_WALL_FULL)
12268           {
12269             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12270             TEST_DrawLevelField(x, y);
12271           }
12272         }
12273
12274         game.magic_wall_active = FALSE;
12275       }
12276     }
12277   }
12278
12279   if (game.light_time_left > 0)
12280   {
12281     game.light_time_left--;
12282
12283     if (game.light_time_left == 0)
12284       RedrawAllLightSwitchesAndInvisibleElements();
12285   }
12286
12287   if (game.timegate_time_left > 0)
12288   {
12289     game.timegate_time_left--;
12290
12291     if (game.timegate_time_left == 0)
12292       CloseAllOpenTimegates();
12293   }
12294
12295   if (game.lenses_time_left > 0)
12296   {
12297     game.lenses_time_left--;
12298
12299     if (game.lenses_time_left == 0)
12300       RedrawAllInvisibleElementsForLenses();
12301   }
12302
12303   if (game.magnify_time_left > 0)
12304   {
12305     game.magnify_time_left--;
12306
12307     if (game.magnify_time_left == 0)
12308       RedrawAllInvisibleElementsForMagnifier();
12309   }
12310
12311   for (i = 0; i < MAX_PLAYERS; i++)
12312   {
12313     struct PlayerInfo *player = &stored_player[i];
12314
12315     if (SHIELD_ON(player))
12316     {
12317       if (player->shield_deadly_time_left)
12318         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12319       else if (player->shield_normal_time_left)
12320         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12321     }
12322   }
12323
12324 #if USE_DELAYED_GFX_REDRAW
12325   SCAN_PLAYFIELD(x, y)
12326   {
12327     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12328     {
12329       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12330          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12331
12332       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12333         DrawLevelField(x, y);
12334
12335       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12336         DrawLevelFieldCrumbled(x, y);
12337
12338       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12339         DrawLevelFieldCrumbledNeighbours(x, y);
12340
12341       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12342         DrawTwinkleOnField(x, y);
12343     }
12344
12345     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12346   }
12347 #endif
12348
12349   DrawAllPlayers();
12350   PlayAllPlayersSound();
12351
12352   for (i = 0; i < MAX_PLAYERS; i++)
12353   {
12354     struct PlayerInfo *player = &stored_player[i];
12355
12356     if (player->show_envelope != 0 && (!player->active ||
12357                                        player->MovPos == 0))
12358     {
12359       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12360
12361       player->show_envelope = 0;
12362     }
12363   }
12364
12365   // use random number generator in every frame to make it less predictable
12366   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12367     RND(1);
12368
12369   mouse_action_last = mouse_action;
12370 }
12371
12372 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12373 {
12374   int min_x = x, min_y = y, max_x = x, max_y = y;
12375   int i;
12376
12377   for (i = 0; i < MAX_PLAYERS; i++)
12378   {
12379     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12380
12381     if (!stored_player[i].active || &stored_player[i] == player)
12382       continue;
12383
12384     min_x = MIN(min_x, jx);
12385     min_y = MIN(min_y, jy);
12386     max_x = MAX(max_x, jx);
12387     max_y = MAX(max_y, jy);
12388   }
12389
12390   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12391 }
12392
12393 static boolean AllPlayersInVisibleScreen(void)
12394 {
12395   int i;
12396
12397   for (i = 0; i < MAX_PLAYERS; i++)
12398   {
12399     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12400
12401     if (!stored_player[i].active)
12402       continue;
12403
12404     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12405       return FALSE;
12406   }
12407
12408   return TRUE;
12409 }
12410
12411 void ScrollLevel(int dx, int dy)
12412 {
12413   int scroll_offset = 2 * TILEX_VAR;
12414   int x, y;
12415
12416   BlitBitmap(drawto_field, drawto_field,
12417              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12418              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12419              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12420              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12421              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12422              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12423
12424   if (dx != 0)
12425   {
12426     x = (dx == 1 ? BX1 : BX2);
12427     for (y = BY1; y <= BY2; y++)
12428       DrawScreenField(x, y);
12429   }
12430
12431   if (dy != 0)
12432   {
12433     y = (dy == 1 ? BY1 : BY2);
12434     for (x = BX1; x <= BX2; x++)
12435       DrawScreenField(x, y);
12436   }
12437
12438   redraw_mask |= REDRAW_FIELD;
12439 }
12440
12441 static boolean canFallDown(struct PlayerInfo *player)
12442 {
12443   int jx = player->jx, jy = player->jy;
12444
12445   return (IN_LEV_FIELD(jx, jy + 1) &&
12446           (IS_FREE(jx, jy + 1) ||
12447            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12448           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12449           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12450 }
12451
12452 static boolean canPassField(int x, int y, int move_dir)
12453 {
12454   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12455   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12456   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12457   int nextx = x + dx;
12458   int nexty = y + dy;
12459   int element = Feld[x][y];
12460
12461   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12462           !CAN_MOVE(element) &&
12463           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12464           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12465           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12466 }
12467
12468 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12469 {
12470   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12471   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12472   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12473   int newx = x + dx;
12474   int newy = y + dy;
12475
12476   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12477           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12478           (IS_DIGGABLE(Feld[newx][newy]) ||
12479            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12480            canPassField(newx, newy, move_dir)));
12481 }
12482
12483 static void CheckGravityMovement(struct PlayerInfo *player)
12484 {
12485   if (player->gravity && !player->programmed_action)
12486   {
12487     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12488     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12489     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12490     int jx = player->jx, jy = player->jy;
12491     boolean player_is_moving_to_valid_field =
12492       (!player_is_snapping &&
12493        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12494         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12495     boolean player_can_fall_down = canFallDown(player);
12496
12497     if (player_can_fall_down &&
12498         !player_is_moving_to_valid_field)
12499       player->programmed_action = MV_DOWN;
12500   }
12501 }
12502
12503 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12504 {
12505   return CheckGravityMovement(player);
12506
12507   if (player->gravity && !player->programmed_action)
12508   {
12509     int jx = player->jx, jy = player->jy;
12510     boolean field_under_player_is_free =
12511       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12512     boolean player_is_standing_on_valid_field =
12513       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12514        (IS_WALKABLE(Feld[jx][jy]) &&
12515         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12516
12517     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12518       player->programmed_action = MV_DOWN;
12519   }
12520 }
12521
12522 /*
12523   MovePlayerOneStep()
12524   -----------------------------------------------------------------------------
12525   dx, dy:               direction (non-diagonal) to try to move the player to
12526   real_dx, real_dy:     direction as read from input device (can be diagonal)
12527 */
12528
12529 boolean MovePlayerOneStep(struct PlayerInfo *player,
12530                           int dx, int dy, int real_dx, int real_dy)
12531 {
12532   int jx = player->jx, jy = player->jy;
12533   int new_jx = jx + dx, new_jy = jy + dy;
12534   int can_move;
12535   boolean player_can_move = !player->cannot_move;
12536
12537   if (!player->active || (!dx && !dy))
12538     return MP_NO_ACTION;
12539
12540   player->MovDir = (dx < 0 ? MV_LEFT :
12541                     dx > 0 ? MV_RIGHT :
12542                     dy < 0 ? MV_UP :
12543                     dy > 0 ? MV_DOWN :  MV_NONE);
12544
12545   if (!IN_LEV_FIELD(new_jx, new_jy))
12546     return MP_NO_ACTION;
12547
12548   if (!player_can_move)
12549   {
12550     if (player->MovPos == 0)
12551     {
12552       player->is_moving = FALSE;
12553       player->is_digging = FALSE;
12554       player->is_collecting = FALSE;
12555       player->is_snapping = FALSE;
12556       player->is_pushing = FALSE;
12557     }
12558   }
12559
12560   if (!network.enabled && game.centered_player_nr == -1 &&
12561       !AllPlayersInSight(player, new_jx, new_jy))
12562     return MP_NO_ACTION;
12563
12564   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12565   if (can_move != MP_MOVING)
12566     return can_move;
12567
12568   // check if DigField() has caused relocation of the player
12569   if (player->jx != jx || player->jy != jy)
12570     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12571
12572   StorePlayer[jx][jy] = 0;
12573   player->last_jx = jx;
12574   player->last_jy = jy;
12575   player->jx = new_jx;
12576   player->jy = new_jy;
12577   StorePlayer[new_jx][new_jy] = player->element_nr;
12578
12579   if (player->move_delay_value_next != -1)
12580   {
12581     player->move_delay_value = player->move_delay_value_next;
12582     player->move_delay_value_next = -1;
12583   }
12584
12585   player->MovPos =
12586     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12587
12588   player->step_counter++;
12589
12590   PlayerVisit[jx][jy] = FrameCounter;
12591
12592   player->is_moving = TRUE;
12593
12594 #if 1
12595   // should better be called in MovePlayer(), but this breaks some tapes
12596   ScrollPlayer(player, SCROLL_INIT);
12597 #endif
12598
12599   return MP_MOVING;
12600 }
12601
12602 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12603 {
12604   int jx = player->jx, jy = player->jy;
12605   int old_jx = jx, old_jy = jy;
12606   int moved = MP_NO_ACTION;
12607
12608   if (!player->active)
12609     return FALSE;
12610
12611   if (!dx && !dy)
12612   {
12613     if (player->MovPos == 0)
12614     {
12615       player->is_moving = FALSE;
12616       player->is_digging = FALSE;
12617       player->is_collecting = FALSE;
12618       player->is_snapping = FALSE;
12619       player->is_pushing = FALSE;
12620     }
12621
12622     return FALSE;
12623   }
12624
12625   if (player->move_delay > 0)
12626     return FALSE;
12627
12628   player->move_delay = -1;              // set to "uninitialized" value
12629
12630   // store if player is automatically moved to next field
12631   player->is_auto_moving = (player->programmed_action != MV_NONE);
12632
12633   // remove the last programmed player action
12634   player->programmed_action = 0;
12635
12636   if (player->MovPos)
12637   {
12638     // should only happen if pre-1.2 tape recordings are played
12639     // this is only for backward compatibility
12640
12641     int original_move_delay_value = player->move_delay_value;
12642
12643 #if DEBUG
12644     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12645            tape.counter);
12646 #endif
12647
12648     // scroll remaining steps with finest movement resolution
12649     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12650
12651     while (player->MovPos)
12652     {
12653       ScrollPlayer(player, SCROLL_GO_ON);
12654       ScrollScreen(NULL, SCROLL_GO_ON);
12655
12656       AdvanceFrameAndPlayerCounters(player->index_nr);
12657
12658       DrawAllPlayers();
12659       BackToFront_WithFrameDelay(0);
12660     }
12661
12662     player->move_delay_value = original_move_delay_value;
12663   }
12664
12665   player->is_active = FALSE;
12666
12667   if (player->last_move_dir & MV_HORIZONTAL)
12668   {
12669     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12670       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12671   }
12672   else
12673   {
12674     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12675       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12676   }
12677
12678   if (!moved && !player->is_active)
12679   {
12680     player->is_moving = FALSE;
12681     player->is_digging = FALSE;
12682     player->is_collecting = FALSE;
12683     player->is_snapping = FALSE;
12684     player->is_pushing = FALSE;
12685   }
12686
12687   jx = player->jx;
12688   jy = player->jy;
12689
12690   if (moved & MP_MOVING && !ScreenMovPos &&
12691       (player->index_nr == game.centered_player_nr ||
12692        game.centered_player_nr == -1))
12693   {
12694     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12695
12696     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12697     {
12698       // actual player has left the screen -- scroll in that direction
12699       if (jx != old_jx)         // player has moved horizontally
12700         scroll_x += (jx - old_jx);
12701       else                      // player has moved vertically
12702         scroll_y += (jy - old_jy);
12703     }
12704     else
12705     {
12706       int offset_raw = game.scroll_delay_value;
12707
12708       if (jx != old_jx)         // player has moved horizontally
12709       {
12710         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12711         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12712         int new_scroll_x = jx - MIDPOSX + offset_x;
12713
12714         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12715             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12716           scroll_x = new_scroll_x;
12717
12718         // don't scroll over playfield boundaries
12719         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12720
12721         // don't scroll more than one field at a time
12722         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12723
12724         // don't scroll against the player's moving direction
12725         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12726             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12727           scroll_x = old_scroll_x;
12728       }
12729       else                      // player has moved vertically
12730       {
12731         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12732         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12733         int new_scroll_y = jy - MIDPOSY + offset_y;
12734
12735         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12736             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12737           scroll_y = new_scroll_y;
12738
12739         // don't scroll over playfield boundaries
12740         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12741
12742         // don't scroll more than one field at a time
12743         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12744
12745         // don't scroll against the player's moving direction
12746         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12747             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12748           scroll_y = old_scroll_y;
12749       }
12750     }
12751
12752     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12753     {
12754       if (!network.enabled && game.centered_player_nr == -1 &&
12755           !AllPlayersInVisibleScreen())
12756       {
12757         scroll_x = old_scroll_x;
12758         scroll_y = old_scroll_y;
12759       }
12760       else
12761       {
12762         ScrollScreen(player, SCROLL_INIT);
12763         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12764       }
12765     }
12766   }
12767
12768   player->StepFrame = 0;
12769
12770   if (moved & MP_MOVING)
12771   {
12772     if (old_jx != jx && old_jy == jy)
12773       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12774     else if (old_jx == jx && old_jy != jy)
12775       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12776
12777     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12778
12779     player->last_move_dir = player->MovDir;
12780     player->is_moving = TRUE;
12781     player->is_snapping = FALSE;
12782     player->is_switching = FALSE;
12783     player->is_dropping = FALSE;
12784     player->is_dropping_pressed = FALSE;
12785     player->drop_pressed_delay = 0;
12786
12787 #if 0
12788     // should better be called here than above, but this breaks some tapes
12789     ScrollPlayer(player, SCROLL_INIT);
12790 #endif
12791   }
12792   else
12793   {
12794     CheckGravityMovementWhenNotMoving(player);
12795
12796     player->is_moving = FALSE;
12797
12798     /* at this point, the player is allowed to move, but cannot move right now
12799        (e.g. because of something blocking the way) -- ensure that the player
12800        is also allowed to move in the next frame (in old versions before 3.1.1,
12801        the player was forced to wait again for eight frames before next try) */
12802
12803     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12804       player->move_delay = 0;   // allow direct movement in the next frame
12805   }
12806
12807   if (player->move_delay == -1)         // not yet initialized by DigField()
12808     player->move_delay = player->move_delay_value;
12809
12810   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12811   {
12812     TestIfPlayerTouchesBadThing(jx, jy);
12813     TestIfPlayerTouchesCustomElement(jx, jy);
12814   }
12815
12816   if (!player->active)
12817     RemovePlayer(player);
12818
12819   return moved;
12820 }
12821
12822 void ScrollPlayer(struct PlayerInfo *player, int mode)
12823 {
12824   int jx = player->jx, jy = player->jy;
12825   int last_jx = player->last_jx, last_jy = player->last_jy;
12826   int move_stepsize = TILEX / player->move_delay_value;
12827
12828   if (!player->active)
12829     return;
12830
12831   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12832     return;
12833
12834   if (mode == SCROLL_INIT)
12835   {
12836     player->actual_frame_counter = FrameCounter;
12837     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12838
12839     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12840         Feld[last_jx][last_jy] == EL_EMPTY)
12841     {
12842       int last_field_block_delay = 0;   // start with no blocking at all
12843       int block_delay_adjustment = player->block_delay_adjustment;
12844
12845       // if player blocks last field, add delay for exactly one move
12846       if (player->block_last_field)
12847       {
12848         last_field_block_delay += player->move_delay_value;
12849
12850         // when blocking enabled, prevent moving up despite gravity
12851         if (player->gravity && player->MovDir == MV_UP)
12852           block_delay_adjustment = -1;
12853       }
12854
12855       // add block delay adjustment (also possible when not blocking)
12856       last_field_block_delay += block_delay_adjustment;
12857
12858       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12859       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12860     }
12861
12862     if (player->MovPos != 0)    // player has not yet reached destination
12863       return;
12864   }
12865   else if (!FrameReached(&player->actual_frame_counter, 1))
12866     return;
12867
12868   if (player->MovPos != 0)
12869   {
12870     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12871     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12872
12873     // before DrawPlayer() to draw correct player graphic for this case
12874     if (player->MovPos == 0)
12875       CheckGravityMovement(player);
12876   }
12877
12878   if (player->MovPos == 0)      // player reached destination field
12879   {
12880     if (player->move_delay_reset_counter > 0)
12881     {
12882       player->move_delay_reset_counter--;
12883
12884       if (player->move_delay_reset_counter == 0)
12885       {
12886         // continue with normal speed after quickly moving through gate
12887         HALVE_PLAYER_SPEED(player);
12888
12889         // be able to make the next move without delay
12890         player->move_delay = 0;
12891       }
12892     }
12893
12894     player->last_jx = jx;
12895     player->last_jy = jy;
12896
12897     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12898         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12899         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12900         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12901         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12902         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12903         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12904         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12905     {
12906       ExitPlayer(player);
12907
12908       if (game.players_still_needed == 0 &&
12909           (game.friends_still_needed == 0 ||
12910            IS_SP_ELEMENT(Feld[jx][jy])))
12911         LevelSolved();
12912     }
12913
12914     // this breaks one level: "machine", level 000
12915     {
12916       int move_direction = player->MovDir;
12917       int enter_side = MV_DIR_OPPOSITE(move_direction);
12918       int leave_side = move_direction;
12919       int old_jx = last_jx;
12920       int old_jy = last_jy;
12921       int old_element = Feld[old_jx][old_jy];
12922       int new_element = Feld[jx][jy];
12923
12924       if (IS_CUSTOM_ELEMENT(old_element))
12925         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12926                                    CE_LEFT_BY_PLAYER,
12927                                    player->index_bit, leave_side);
12928
12929       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12930                                           CE_PLAYER_LEAVES_X,
12931                                           player->index_bit, leave_side);
12932
12933       if (IS_CUSTOM_ELEMENT(new_element))
12934         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12935                                    player->index_bit, enter_side);
12936
12937       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12938                                           CE_PLAYER_ENTERS_X,
12939                                           player->index_bit, enter_side);
12940
12941       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12942                                         CE_MOVE_OF_X, move_direction);
12943     }
12944
12945     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12946     {
12947       TestIfPlayerTouchesBadThing(jx, jy);
12948       TestIfPlayerTouchesCustomElement(jx, jy);
12949
12950       /* needed because pushed element has not yet reached its destination,
12951          so it would trigger a change event at its previous field location */
12952       if (!player->is_pushing)
12953         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12954
12955       if (!player->active)
12956         RemovePlayer(player);
12957     }
12958
12959     if (!game.LevelSolved && level.use_step_counter)
12960     {
12961       int i;
12962
12963       TimePlayed++;
12964
12965       if (TimeLeft > 0)
12966       {
12967         TimeLeft--;
12968
12969         if (TimeLeft <= 10 && setup.time_limit)
12970           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12971
12972         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12973
12974         DisplayGameControlValues();
12975
12976         if (!TimeLeft && setup.time_limit)
12977           for (i = 0; i < MAX_PLAYERS; i++)
12978             KillPlayer(&stored_player[i]);
12979       }
12980       else if (game.no_time_limit && !game.all_players_gone)
12981       {
12982         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12983
12984         DisplayGameControlValues();
12985       }
12986     }
12987
12988     if (tape.single_step && tape.recording && !tape.pausing &&
12989         !player->programmed_action)
12990       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12991
12992     if (!player->programmed_action)
12993       CheckSaveEngineSnapshot(player);
12994   }
12995 }
12996
12997 void ScrollScreen(struct PlayerInfo *player, int mode)
12998 {
12999   static unsigned int screen_frame_counter = 0;
13000
13001   if (mode == SCROLL_INIT)
13002   {
13003     // set scrolling step size according to actual player's moving speed
13004     ScrollStepSize = TILEX / player->move_delay_value;
13005
13006     screen_frame_counter = FrameCounter;
13007     ScreenMovDir = player->MovDir;
13008     ScreenMovPos = player->MovPos;
13009     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13010     return;
13011   }
13012   else if (!FrameReached(&screen_frame_counter, 1))
13013     return;
13014
13015   if (ScreenMovPos)
13016   {
13017     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13018     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13019     redraw_mask |= REDRAW_FIELD;
13020   }
13021   else
13022     ScreenMovDir = MV_NONE;
13023 }
13024
13025 void TestIfPlayerTouchesCustomElement(int x, int y)
13026 {
13027   static int xy[4][2] =
13028   {
13029     { 0, -1 },
13030     { -1, 0 },
13031     { +1, 0 },
13032     { 0, +1 }
13033   };
13034   static int trigger_sides[4][2] =
13035   {
13036     // center side       border side
13037     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13038     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13039     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13040     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13041   };
13042   static int touch_dir[4] =
13043   {
13044     MV_LEFT | MV_RIGHT,
13045     MV_UP   | MV_DOWN,
13046     MV_UP   | MV_DOWN,
13047     MV_LEFT | MV_RIGHT
13048   };
13049   int center_element = Feld[x][y];      // should always be non-moving!
13050   int i;
13051
13052   for (i = 0; i < NUM_DIRECTIONS; i++)
13053   {
13054     int xx = x + xy[i][0];
13055     int yy = y + xy[i][1];
13056     int center_side = trigger_sides[i][0];
13057     int border_side = trigger_sides[i][1];
13058     int border_element;
13059
13060     if (!IN_LEV_FIELD(xx, yy))
13061       continue;
13062
13063     if (IS_PLAYER(x, y))                // player found at center element
13064     {
13065       struct PlayerInfo *player = PLAYERINFO(x, y);
13066
13067       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13068         border_element = Feld[xx][yy];          // may be moving!
13069       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13070         border_element = Feld[xx][yy];
13071       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13072         border_element = MovingOrBlocked2Element(xx, yy);
13073       else
13074         continue;               // center and border element do not touch
13075
13076       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13077                                  player->index_bit, border_side);
13078       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13079                                           CE_PLAYER_TOUCHES_X,
13080                                           player->index_bit, border_side);
13081
13082       {
13083         /* use player element that is initially defined in the level playfield,
13084            not the player element that corresponds to the runtime player number
13085            (example: a level that contains EL_PLAYER_3 as the only player would
13086            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13087         int player_element = PLAYERINFO(x, y)->initial_element;
13088
13089         CheckElementChangeBySide(xx, yy, border_element, player_element,
13090                                  CE_TOUCHING_X, border_side);
13091       }
13092     }
13093     else if (IS_PLAYER(xx, yy))         // player found at border element
13094     {
13095       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13096
13097       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13098       {
13099         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13100           continue;             // center and border element do not touch
13101       }
13102
13103       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13104                                  player->index_bit, center_side);
13105       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13106                                           CE_PLAYER_TOUCHES_X,
13107                                           player->index_bit, center_side);
13108
13109       {
13110         /* use player element that is initially defined in the level playfield,
13111            not the player element that corresponds to the runtime player number
13112            (example: a level that contains EL_PLAYER_3 as the only player would
13113            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13114         int player_element = PLAYERINFO(xx, yy)->initial_element;
13115
13116         CheckElementChangeBySide(x, y, center_element, player_element,
13117                                  CE_TOUCHING_X, center_side);
13118       }
13119
13120       break;
13121     }
13122   }
13123 }
13124
13125 void TestIfElementTouchesCustomElement(int x, int y)
13126 {
13127   static int xy[4][2] =
13128   {
13129     { 0, -1 },
13130     { -1, 0 },
13131     { +1, 0 },
13132     { 0, +1 }
13133   };
13134   static int trigger_sides[4][2] =
13135   {
13136     // center side      border side
13137     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13138     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13139     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13140     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13141   };
13142   static int touch_dir[4] =
13143   {
13144     MV_LEFT | MV_RIGHT,
13145     MV_UP   | MV_DOWN,
13146     MV_UP   | MV_DOWN,
13147     MV_LEFT | MV_RIGHT
13148   };
13149   boolean change_center_element = FALSE;
13150   int center_element = Feld[x][y];      // should always be non-moving!
13151   int border_element_old[NUM_DIRECTIONS];
13152   int i;
13153
13154   for (i = 0; i < NUM_DIRECTIONS; i++)
13155   {
13156     int xx = x + xy[i][0];
13157     int yy = y + xy[i][1];
13158     int border_element;
13159
13160     border_element_old[i] = -1;
13161
13162     if (!IN_LEV_FIELD(xx, yy))
13163       continue;
13164
13165     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13166       border_element = Feld[xx][yy];    // may be moving!
13167     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13168       border_element = Feld[xx][yy];
13169     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13170       border_element = MovingOrBlocked2Element(xx, yy);
13171     else
13172       continue;                 // center and border element do not touch
13173
13174     border_element_old[i] = border_element;
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 center_side = trigger_sides[i][0];
13182     int border_element = border_element_old[i];
13183
13184     if (border_element == -1)
13185       continue;
13186
13187     // check for change of border element
13188     CheckElementChangeBySide(xx, yy, border_element, center_element,
13189                              CE_TOUCHING_X, center_side);
13190
13191     // (center element cannot be player, so we dont have to check this here)
13192   }
13193
13194   for (i = 0; i < NUM_DIRECTIONS; i++)
13195   {
13196     int xx = x + xy[i][0];
13197     int yy = y + xy[i][1];
13198     int border_side = trigger_sides[i][1];
13199     int border_element = border_element_old[i];
13200
13201     if (border_element == -1)
13202       continue;
13203
13204     // check for change of center element (but change it only once)
13205     if (!change_center_element)
13206       change_center_element =
13207         CheckElementChangeBySide(x, y, center_element, border_element,
13208                                  CE_TOUCHING_X, border_side);
13209
13210     if (IS_PLAYER(xx, yy))
13211     {
13212       /* use player element that is initially defined in the level playfield,
13213          not the player element that corresponds to the runtime player number
13214          (example: a level that contains EL_PLAYER_3 as the only player would
13215          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13216       int player_element = PLAYERINFO(xx, yy)->initial_element;
13217
13218       CheckElementChangeBySide(x, y, center_element, player_element,
13219                                CE_TOUCHING_X, border_side);
13220     }
13221   }
13222 }
13223
13224 void TestIfElementHitsCustomElement(int x, int y, int direction)
13225 {
13226   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13227   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13228   int hitx = x + dx, hity = y + dy;
13229   int hitting_element = Feld[x][y];
13230   int touched_element;
13231
13232   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13233     return;
13234
13235   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13236                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13237
13238   if (IN_LEV_FIELD(hitx, hity))
13239   {
13240     int opposite_direction = MV_DIR_OPPOSITE(direction);
13241     int hitting_side = direction;
13242     int touched_side = opposite_direction;
13243     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13244                           MovDir[hitx][hity] != direction ||
13245                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13246
13247     object_hit = TRUE;
13248
13249     if (object_hit)
13250     {
13251       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13252                                CE_HITTING_X, touched_side);
13253
13254       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13255                                CE_HIT_BY_X, hitting_side);
13256
13257       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13258                                CE_HIT_BY_SOMETHING, opposite_direction);
13259
13260       if (IS_PLAYER(hitx, hity))
13261       {
13262         /* use player element that is initially defined in the level playfield,
13263            not the player element that corresponds to the runtime player number
13264            (example: a level that contains EL_PLAYER_3 as the only player would
13265            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13266         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13267
13268         CheckElementChangeBySide(x, y, hitting_element, player_element,
13269                                  CE_HITTING_X, touched_side);
13270       }
13271     }
13272   }
13273
13274   // "hitting something" is also true when hitting the playfield border
13275   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13276                            CE_HITTING_SOMETHING, direction);
13277 }
13278
13279 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13280 {
13281   int i, kill_x = -1, kill_y = -1;
13282
13283   int bad_element = -1;
13284   static int test_xy[4][2] =
13285   {
13286     { 0, -1 },
13287     { -1, 0 },
13288     { +1, 0 },
13289     { 0, +1 }
13290   };
13291   static int test_dir[4] =
13292   {
13293     MV_UP,
13294     MV_LEFT,
13295     MV_RIGHT,
13296     MV_DOWN
13297   };
13298
13299   for (i = 0; i < NUM_DIRECTIONS; i++)
13300   {
13301     int test_x, test_y, test_move_dir, test_element;
13302
13303     test_x = good_x + test_xy[i][0];
13304     test_y = good_y + test_xy[i][1];
13305
13306     if (!IN_LEV_FIELD(test_x, test_y))
13307       continue;
13308
13309     test_move_dir =
13310       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13311
13312     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13313
13314     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13315        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13316     */
13317     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13318         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13319     {
13320       kill_x = test_x;
13321       kill_y = test_y;
13322       bad_element = test_element;
13323
13324       break;
13325     }
13326   }
13327
13328   if (kill_x != -1 || kill_y != -1)
13329   {
13330     if (IS_PLAYER(good_x, good_y))
13331     {
13332       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13333
13334       if (player->shield_deadly_time_left > 0 &&
13335           !IS_INDESTRUCTIBLE(bad_element))
13336         Bang(kill_x, kill_y);
13337       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13338         KillPlayer(player);
13339     }
13340     else
13341       Bang(good_x, good_y);
13342   }
13343 }
13344
13345 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13346 {
13347   int i, kill_x = -1, kill_y = -1;
13348   int bad_element = Feld[bad_x][bad_y];
13349   static int test_xy[4][2] =
13350   {
13351     { 0, -1 },
13352     { -1, 0 },
13353     { +1, 0 },
13354     { 0, +1 }
13355   };
13356   static int touch_dir[4] =
13357   {
13358     MV_LEFT | MV_RIGHT,
13359     MV_UP   | MV_DOWN,
13360     MV_UP   | MV_DOWN,
13361     MV_LEFT | MV_RIGHT
13362   };
13363   static int test_dir[4] =
13364   {
13365     MV_UP,
13366     MV_LEFT,
13367     MV_RIGHT,
13368     MV_DOWN
13369   };
13370
13371   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13372     return;
13373
13374   for (i = 0; i < NUM_DIRECTIONS; i++)
13375   {
13376     int test_x, test_y, test_move_dir, test_element;
13377
13378     test_x = bad_x + test_xy[i][0];
13379     test_y = bad_y + test_xy[i][1];
13380
13381     if (!IN_LEV_FIELD(test_x, test_y))
13382       continue;
13383
13384     test_move_dir =
13385       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13386
13387     test_element = Feld[test_x][test_y];
13388
13389     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13390        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13391     */
13392     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13393         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13394     {
13395       // good thing is player or penguin that does not move away
13396       if (IS_PLAYER(test_x, test_y))
13397       {
13398         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13399
13400         if (bad_element == EL_ROBOT && player->is_moving)
13401           continue;     // robot does not kill player if he is moving
13402
13403         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13404         {
13405           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13406             continue;           // center and border element do not touch
13407         }
13408
13409         kill_x = test_x;
13410         kill_y = test_y;
13411
13412         break;
13413       }
13414       else if (test_element == EL_PENGUIN)
13415       {
13416         kill_x = test_x;
13417         kill_y = test_y;
13418
13419         break;
13420       }
13421     }
13422   }
13423
13424   if (kill_x != -1 || kill_y != -1)
13425   {
13426     if (IS_PLAYER(kill_x, kill_y))
13427     {
13428       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13429
13430       if (player->shield_deadly_time_left > 0 &&
13431           !IS_INDESTRUCTIBLE(bad_element))
13432         Bang(bad_x, bad_y);
13433       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13434         KillPlayer(player);
13435     }
13436     else
13437       Bang(kill_x, kill_y);
13438   }
13439 }
13440
13441 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13442 {
13443   int bad_element = Feld[bad_x][bad_y];
13444   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13445   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13446   int test_x = bad_x + dx, test_y = bad_y + dy;
13447   int test_move_dir, test_element;
13448   int kill_x = -1, kill_y = -1;
13449
13450   if (!IN_LEV_FIELD(test_x, test_y))
13451     return;
13452
13453   test_move_dir =
13454     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13455
13456   test_element = Feld[test_x][test_y];
13457
13458   if (test_move_dir != bad_move_dir)
13459   {
13460     // good thing can be player or penguin that does not move away
13461     if (IS_PLAYER(test_x, test_y))
13462     {
13463       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13464
13465       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13466          player as being hit when he is moving towards the bad thing, because
13467          the "get hit by" condition would be lost after the player stops) */
13468       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13469         return;         // player moves away from bad thing
13470
13471       kill_x = test_x;
13472       kill_y = test_y;
13473     }
13474     else if (test_element == EL_PENGUIN)
13475     {
13476       kill_x = test_x;
13477       kill_y = test_y;
13478     }
13479   }
13480
13481   if (kill_x != -1 || kill_y != -1)
13482   {
13483     if (IS_PLAYER(kill_x, kill_y))
13484     {
13485       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13486
13487       if (player->shield_deadly_time_left > 0 &&
13488           !IS_INDESTRUCTIBLE(bad_element))
13489         Bang(bad_x, bad_y);
13490       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13491         KillPlayer(player);
13492     }
13493     else
13494       Bang(kill_x, kill_y);
13495   }
13496 }
13497
13498 void TestIfPlayerTouchesBadThing(int x, int y)
13499 {
13500   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13501 }
13502
13503 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13504 {
13505   TestIfGoodThingHitsBadThing(x, y, move_dir);
13506 }
13507
13508 void TestIfBadThingTouchesPlayer(int x, int y)
13509 {
13510   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13511 }
13512
13513 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13514 {
13515   TestIfBadThingHitsGoodThing(x, y, move_dir);
13516 }
13517
13518 void TestIfFriendTouchesBadThing(int x, int y)
13519 {
13520   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13521 }
13522
13523 void TestIfBadThingTouchesFriend(int x, int y)
13524 {
13525   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13526 }
13527
13528 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13529 {
13530   int i, kill_x = bad_x, kill_y = bad_y;
13531   static int xy[4][2] =
13532   {
13533     { 0, -1 },
13534     { -1, 0 },
13535     { +1, 0 },
13536     { 0, +1 }
13537   };
13538
13539   for (i = 0; i < NUM_DIRECTIONS; i++)
13540   {
13541     int x, y, element;
13542
13543     x = bad_x + xy[i][0];
13544     y = bad_y + xy[i][1];
13545     if (!IN_LEV_FIELD(x, y))
13546       continue;
13547
13548     element = Feld[x][y];
13549     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13550         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13551     {
13552       kill_x = x;
13553       kill_y = y;
13554       break;
13555     }
13556   }
13557
13558   if (kill_x != bad_x || kill_y != bad_y)
13559     Bang(bad_x, bad_y);
13560 }
13561
13562 void KillPlayer(struct PlayerInfo *player)
13563 {
13564   int jx = player->jx, jy = player->jy;
13565
13566   if (!player->active)
13567     return;
13568
13569 #if 0
13570   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13571          player->killed, player->active, player->reanimated);
13572 #endif
13573
13574   /* the following code was introduced to prevent an infinite loop when calling
13575      -> Bang()
13576      -> CheckTriggeredElementChangeExt()
13577      -> ExecuteCustomElementAction()
13578      -> KillPlayer()
13579      -> (infinitely repeating the above sequence of function calls)
13580      which occurs when killing the player while having a CE with the setting
13581      "kill player X when explosion of <player X>"; the solution using a new
13582      field "player->killed" was chosen for backwards compatibility, although
13583      clever use of the fields "player->active" etc. would probably also work */
13584 #if 1
13585   if (player->killed)
13586     return;
13587 #endif
13588
13589   player->killed = TRUE;
13590
13591   // remove accessible field at the player's position
13592   Feld[jx][jy] = EL_EMPTY;
13593
13594   // deactivate shield (else Bang()/Explode() would not work right)
13595   player->shield_normal_time_left = 0;
13596   player->shield_deadly_time_left = 0;
13597
13598 #if 0
13599   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13600          player->killed, player->active, player->reanimated);
13601 #endif
13602
13603   Bang(jx, jy);
13604
13605 #if 0
13606   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13607          player->killed, player->active, player->reanimated);
13608 #endif
13609
13610   if (player->reanimated)       // killed player may have been reanimated
13611     player->killed = player->reanimated = FALSE;
13612   else
13613     BuryPlayer(player);
13614 }
13615
13616 static void KillPlayerUnlessEnemyProtected(int x, int y)
13617 {
13618   if (!PLAYER_ENEMY_PROTECTED(x, y))
13619     KillPlayer(PLAYERINFO(x, y));
13620 }
13621
13622 static void KillPlayerUnlessExplosionProtected(int x, int y)
13623 {
13624   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13625     KillPlayer(PLAYERINFO(x, y));
13626 }
13627
13628 void BuryPlayer(struct PlayerInfo *player)
13629 {
13630   int jx = player->jx, jy = player->jy;
13631
13632   if (!player->active)
13633     return;
13634
13635   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13636   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13637
13638   RemovePlayer(player);
13639
13640   player->buried = TRUE;
13641
13642   if (game.all_players_gone)
13643     game.GameOver = TRUE;
13644 }
13645
13646 void RemovePlayer(struct PlayerInfo *player)
13647 {
13648   int jx = player->jx, jy = player->jy;
13649   int i, found = FALSE;
13650
13651   player->present = FALSE;
13652   player->active = FALSE;
13653
13654   // required for some CE actions (even if the player is not active anymore)
13655   player->MovPos = 0;
13656
13657   if (!ExplodeField[jx][jy])
13658     StorePlayer[jx][jy] = 0;
13659
13660   if (player->is_moving)
13661     TEST_DrawLevelField(player->last_jx, player->last_jy);
13662
13663   for (i = 0; i < MAX_PLAYERS; i++)
13664     if (stored_player[i].active)
13665       found = TRUE;
13666
13667   if (!found)
13668   {
13669     game.all_players_gone = TRUE;
13670     game.GameOver = TRUE;
13671   }
13672
13673   game.exit_x = game.robot_wheel_x = jx;
13674   game.exit_y = game.robot_wheel_y = jy;
13675 }
13676
13677 void ExitPlayer(struct PlayerInfo *player)
13678 {
13679   DrawPlayer(player);   // needed here only to cleanup last field
13680   RemovePlayer(player);
13681
13682   if (game.players_still_needed > 0)
13683     game.players_still_needed--;
13684 }
13685
13686 static void setFieldForSnapping(int x, int y, int element, int direction)
13687 {
13688   struct ElementInfo *ei = &element_info[element];
13689   int direction_bit = MV_DIR_TO_BIT(direction);
13690   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13691   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13692                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13693
13694   Feld[x][y] = EL_ELEMENT_SNAPPING;
13695   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13696
13697   ResetGfxAnimation(x, y);
13698
13699   GfxElement[x][y] = element;
13700   GfxAction[x][y] = action;
13701   GfxDir[x][y] = direction;
13702   GfxFrame[x][y] = -1;
13703 }
13704
13705 /*
13706   =============================================================================
13707   checkDiagonalPushing()
13708   -----------------------------------------------------------------------------
13709   check if diagonal input device direction results in pushing of object
13710   (by checking if the alternative direction is walkable, diggable, ...)
13711   =============================================================================
13712 */
13713
13714 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13715                                     int x, int y, int real_dx, int real_dy)
13716 {
13717   int jx, jy, dx, dy, xx, yy;
13718
13719   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13720     return TRUE;
13721
13722   // diagonal direction: check alternative direction
13723   jx = player->jx;
13724   jy = player->jy;
13725   dx = x - jx;
13726   dy = y - jy;
13727   xx = jx + (dx == 0 ? real_dx : 0);
13728   yy = jy + (dy == 0 ? real_dy : 0);
13729
13730   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13731 }
13732
13733 /*
13734   =============================================================================
13735   DigField()
13736   -----------------------------------------------------------------------------
13737   x, y:                 field next to player (non-diagonal) to try to dig to
13738   real_dx, real_dy:     direction as read from input device (can be diagonal)
13739   =============================================================================
13740 */
13741
13742 static int DigField(struct PlayerInfo *player,
13743                     int oldx, int oldy, int x, int y,
13744                     int real_dx, int real_dy, int mode)
13745 {
13746   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13747   boolean player_was_pushing = player->is_pushing;
13748   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13749   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13750   int jx = oldx, jy = oldy;
13751   int dx = x - jx, dy = y - jy;
13752   int nextx = x + dx, nexty = y + dy;
13753   int move_direction = (dx == -1 ? MV_LEFT  :
13754                         dx == +1 ? MV_RIGHT :
13755                         dy == -1 ? MV_UP    :
13756                         dy == +1 ? MV_DOWN  : MV_NONE);
13757   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13758   int dig_side = MV_DIR_OPPOSITE(move_direction);
13759   int old_element = Feld[jx][jy];
13760   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13761   int collect_count;
13762
13763   if (is_player)                // function can also be called by EL_PENGUIN
13764   {
13765     if (player->MovPos == 0)
13766     {
13767       player->is_digging = FALSE;
13768       player->is_collecting = FALSE;
13769     }
13770
13771     if (player->MovPos == 0)    // last pushing move finished
13772       player->is_pushing = FALSE;
13773
13774     if (mode == DF_NO_PUSH)     // player just stopped pushing
13775     {
13776       player->is_switching = FALSE;
13777       player->push_delay = -1;
13778
13779       return MP_NO_ACTION;
13780     }
13781   }
13782
13783   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13784     old_element = Back[jx][jy];
13785
13786   // in case of element dropped at player position, check background
13787   else if (Back[jx][jy] != EL_EMPTY &&
13788            game.engine_version >= VERSION_IDENT(2,2,0,0))
13789     old_element = Back[jx][jy];
13790
13791   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13792     return MP_NO_ACTION;        // field has no opening in this direction
13793
13794   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13795     return MP_NO_ACTION;        // field has no opening in this direction
13796
13797   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13798   {
13799     SplashAcid(x, y);
13800
13801     Feld[jx][jy] = player->artwork_element;
13802     InitMovingField(jx, jy, MV_DOWN);
13803     Store[jx][jy] = EL_ACID;
13804     ContinueMoving(jx, jy);
13805     BuryPlayer(player);
13806
13807     return MP_DONT_RUN_INTO;
13808   }
13809
13810   if (player_can_move && DONT_RUN_INTO(element))
13811   {
13812     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13813
13814     return MP_DONT_RUN_INTO;
13815   }
13816
13817   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13818     return MP_NO_ACTION;
13819
13820   collect_count = element_info[element].collect_count_initial;
13821
13822   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13823     return MP_NO_ACTION;
13824
13825   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13826     player_can_move = player_can_move_or_snap;
13827
13828   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13829       game.engine_version >= VERSION_IDENT(2,2,0,0))
13830   {
13831     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13832                                player->index_bit, dig_side);
13833     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13834                                         player->index_bit, dig_side);
13835
13836     if (element == EL_DC_LANDMINE)
13837       Bang(x, y);
13838
13839     if (Feld[x][y] != element)          // field changed by snapping
13840       return MP_ACTION;
13841
13842     return MP_NO_ACTION;
13843   }
13844
13845   if (player->gravity && is_player && !player->is_auto_moving &&
13846       canFallDown(player) && move_direction != MV_DOWN &&
13847       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13848     return MP_NO_ACTION;        // player cannot walk here due to gravity
13849
13850   if (player_can_move &&
13851       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13852   {
13853     int sound_element = SND_ELEMENT(element);
13854     int sound_action = ACTION_WALKING;
13855
13856     if (IS_RND_GATE(element))
13857     {
13858       if (!player->key[RND_GATE_NR(element)])
13859         return MP_NO_ACTION;
13860     }
13861     else if (IS_RND_GATE_GRAY(element))
13862     {
13863       if (!player->key[RND_GATE_GRAY_NR(element)])
13864         return MP_NO_ACTION;
13865     }
13866     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13867     {
13868       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13869         return MP_NO_ACTION;
13870     }
13871     else if (element == EL_EXIT_OPEN ||
13872              element == EL_EM_EXIT_OPEN ||
13873              element == EL_EM_EXIT_OPENING ||
13874              element == EL_STEEL_EXIT_OPEN ||
13875              element == EL_EM_STEEL_EXIT_OPEN ||
13876              element == EL_EM_STEEL_EXIT_OPENING ||
13877              element == EL_SP_EXIT_OPEN ||
13878              element == EL_SP_EXIT_OPENING)
13879     {
13880       sound_action = ACTION_PASSING;    // player is passing exit
13881     }
13882     else if (element == EL_EMPTY)
13883     {
13884       sound_action = ACTION_MOVING;             // nothing to walk on
13885     }
13886
13887     // play sound from background or player, whatever is available
13888     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13889       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13890     else
13891       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13892   }
13893   else if (player_can_move &&
13894            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13895   {
13896     if (!ACCESS_FROM(element, opposite_direction))
13897       return MP_NO_ACTION;      // field not accessible from this direction
13898
13899     if (CAN_MOVE(element))      // only fixed elements can be passed!
13900       return MP_NO_ACTION;
13901
13902     if (IS_EM_GATE(element))
13903     {
13904       if (!player->key[EM_GATE_NR(element)])
13905         return MP_NO_ACTION;
13906     }
13907     else if (IS_EM_GATE_GRAY(element))
13908     {
13909       if (!player->key[EM_GATE_GRAY_NR(element)])
13910         return MP_NO_ACTION;
13911     }
13912     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13913     {
13914       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13915         return MP_NO_ACTION;
13916     }
13917     else if (IS_EMC_GATE(element))
13918     {
13919       if (!player->key[EMC_GATE_NR(element)])
13920         return MP_NO_ACTION;
13921     }
13922     else if (IS_EMC_GATE_GRAY(element))
13923     {
13924       if (!player->key[EMC_GATE_GRAY_NR(element)])
13925         return MP_NO_ACTION;
13926     }
13927     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13928     {
13929       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13930         return MP_NO_ACTION;
13931     }
13932     else if (element == EL_DC_GATE_WHITE ||
13933              element == EL_DC_GATE_WHITE_GRAY ||
13934              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13935     {
13936       if (player->num_white_keys == 0)
13937         return MP_NO_ACTION;
13938
13939       player->num_white_keys--;
13940     }
13941     else if (IS_SP_PORT(element))
13942     {
13943       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13944           element == EL_SP_GRAVITY_PORT_RIGHT ||
13945           element == EL_SP_GRAVITY_PORT_UP ||
13946           element == EL_SP_GRAVITY_PORT_DOWN)
13947         player->gravity = !player->gravity;
13948       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13949                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13950                element == EL_SP_GRAVITY_ON_PORT_UP ||
13951                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13952         player->gravity = TRUE;
13953       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13954                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13955                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13956                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13957         player->gravity = FALSE;
13958     }
13959
13960     // automatically move to the next field with double speed
13961     player->programmed_action = move_direction;
13962
13963     if (player->move_delay_reset_counter == 0)
13964     {
13965       player->move_delay_reset_counter = 2;     // two double speed steps
13966
13967       DOUBLE_PLAYER_SPEED(player);
13968     }
13969
13970     PlayLevelSoundAction(x, y, ACTION_PASSING);
13971   }
13972   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13973   {
13974     RemoveField(x, y);
13975
13976     if (mode != DF_SNAP)
13977     {
13978       GfxElement[x][y] = GFX_ELEMENT(element);
13979       player->is_digging = TRUE;
13980     }
13981
13982     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13983
13984     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13985                                         player->index_bit, dig_side);
13986
13987     if (mode == DF_SNAP)
13988     {
13989       if (level.block_snap_field)
13990         setFieldForSnapping(x, y, element, move_direction);
13991       else
13992         TestIfElementTouchesCustomElement(x, y);        // for empty space
13993
13994       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13995                                           player->index_bit, dig_side);
13996     }
13997   }
13998   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13999   {
14000     RemoveField(x, y);
14001
14002     if (is_player && mode != DF_SNAP)
14003     {
14004       GfxElement[x][y] = element;
14005       player->is_collecting = TRUE;
14006     }
14007
14008     if (element == EL_SPEED_PILL)
14009     {
14010       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14011     }
14012     else if (element == EL_EXTRA_TIME && level.time > 0)
14013     {
14014       TimeLeft += level.extra_time;
14015
14016       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14017
14018       DisplayGameControlValues();
14019     }
14020     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14021     {
14022       player->shield_normal_time_left += level.shield_normal_time;
14023       if (element == EL_SHIELD_DEADLY)
14024         player->shield_deadly_time_left += level.shield_deadly_time;
14025     }
14026     else if (element == EL_DYNAMITE ||
14027              element == EL_EM_DYNAMITE ||
14028              element == EL_SP_DISK_RED)
14029     {
14030       if (player->inventory_size < MAX_INVENTORY_SIZE)
14031         player->inventory_element[player->inventory_size++] = element;
14032
14033       DrawGameDoorValues();
14034     }
14035     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14036     {
14037       player->dynabomb_count++;
14038       player->dynabombs_left++;
14039     }
14040     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14041     {
14042       player->dynabomb_size++;
14043     }
14044     else if (element == EL_DYNABOMB_INCREASE_POWER)
14045     {
14046       player->dynabomb_xl = TRUE;
14047     }
14048     else if (IS_KEY(element))
14049     {
14050       player->key[KEY_NR(element)] = TRUE;
14051
14052       DrawGameDoorValues();
14053     }
14054     else if (element == EL_DC_KEY_WHITE)
14055     {
14056       player->num_white_keys++;
14057
14058       // display white keys?
14059       // DrawGameDoorValues();
14060     }
14061     else if (IS_ENVELOPE(element))
14062     {
14063       player->show_envelope = element;
14064     }
14065     else if (element == EL_EMC_LENSES)
14066     {
14067       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14068
14069       RedrawAllInvisibleElementsForLenses();
14070     }
14071     else if (element == EL_EMC_MAGNIFIER)
14072     {
14073       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14074
14075       RedrawAllInvisibleElementsForMagnifier();
14076     }
14077     else if (IS_DROPPABLE(element) ||
14078              IS_THROWABLE(element))     // can be collected and dropped
14079     {
14080       int i;
14081
14082       if (collect_count == 0)
14083         player->inventory_infinite_element = element;
14084       else
14085         for (i = 0; i < collect_count; i++)
14086           if (player->inventory_size < MAX_INVENTORY_SIZE)
14087             player->inventory_element[player->inventory_size++] = element;
14088
14089       DrawGameDoorValues();
14090     }
14091     else if (collect_count > 0)
14092     {
14093       game.gems_still_needed -= collect_count;
14094       if (game.gems_still_needed < 0)
14095         game.gems_still_needed = 0;
14096
14097       game.snapshot.collected_item = TRUE;
14098
14099       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14100
14101       DisplayGameControlValues();
14102     }
14103
14104     RaiseScoreElement(element);
14105     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14106
14107     if (is_player)
14108       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14109                                           player->index_bit, dig_side);
14110
14111     if (mode == DF_SNAP)
14112     {
14113       if (level.block_snap_field)
14114         setFieldForSnapping(x, y, element, move_direction);
14115       else
14116         TestIfElementTouchesCustomElement(x, y);        // for empty space
14117
14118       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14119                                           player->index_bit, dig_side);
14120     }
14121   }
14122   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14123   {
14124     if (mode == DF_SNAP && element != EL_BD_ROCK)
14125       return MP_NO_ACTION;
14126
14127     if (CAN_FALL(element) && dy)
14128       return MP_NO_ACTION;
14129
14130     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14131         !(element == EL_SPRING && level.use_spring_bug))
14132       return MP_NO_ACTION;
14133
14134     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14135         ((move_direction & MV_VERTICAL &&
14136           ((element_info[element].move_pattern & MV_LEFT &&
14137             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14138            (element_info[element].move_pattern & MV_RIGHT &&
14139             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14140          (move_direction & MV_HORIZONTAL &&
14141           ((element_info[element].move_pattern & MV_UP &&
14142             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14143            (element_info[element].move_pattern & MV_DOWN &&
14144             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14145       return MP_NO_ACTION;
14146
14147     // do not push elements already moving away faster than player
14148     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14149         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14150       return MP_NO_ACTION;
14151
14152     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14153     {
14154       if (player->push_delay_value == -1 || !player_was_pushing)
14155         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14156     }
14157     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14158     {
14159       if (player->push_delay_value == -1)
14160         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14161     }
14162     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14163     {
14164       if (!player->is_pushing)
14165         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14166     }
14167
14168     player->is_pushing = TRUE;
14169     player->is_active = TRUE;
14170
14171     if (!(IN_LEV_FIELD(nextx, nexty) &&
14172           (IS_FREE(nextx, nexty) ||
14173            (IS_SB_ELEMENT(element) &&
14174             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14175            (IS_CUSTOM_ELEMENT(element) &&
14176             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14177       return MP_NO_ACTION;
14178
14179     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14180       return MP_NO_ACTION;
14181
14182     if (player->push_delay == -1)       // new pushing; restart delay
14183       player->push_delay = 0;
14184
14185     if (player->push_delay < player->push_delay_value &&
14186         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14187         element != EL_SPRING && element != EL_BALLOON)
14188     {
14189       // make sure that there is no move delay before next try to push
14190       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14191         player->move_delay = 0;
14192
14193       return MP_NO_ACTION;
14194     }
14195
14196     if (IS_CUSTOM_ELEMENT(element) &&
14197         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14198     {
14199       if (!DigFieldByCE(nextx, nexty, element))
14200         return MP_NO_ACTION;
14201     }
14202
14203     if (IS_SB_ELEMENT(element))
14204     {
14205       boolean sokoban_task_solved = FALSE;
14206
14207       if (element == EL_SOKOBAN_FIELD_FULL)
14208       {
14209         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14210
14211         IncrementSokobanFieldsNeeded();
14212         IncrementSokobanObjectsNeeded();
14213       }
14214
14215       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14216       {
14217         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14218
14219         DecrementSokobanFieldsNeeded();
14220         DecrementSokobanObjectsNeeded();
14221
14222         // sokoban object was pushed from empty field to sokoban field
14223         if (Back[x][y] == EL_EMPTY)
14224           sokoban_task_solved = TRUE;
14225       }
14226
14227       Feld[x][y] = EL_SOKOBAN_OBJECT;
14228
14229       if (Back[x][y] == Back[nextx][nexty])
14230         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14231       else if (Back[x][y] != 0)
14232         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14233                                     ACTION_EMPTYING);
14234       else
14235         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14236                                     ACTION_FILLING);
14237
14238       if (sokoban_task_solved &&
14239           game.sokoban_fields_still_needed == 0 &&
14240           game.sokoban_objects_still_needed == 0 &&
14241           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14242       {
14243         game.players_still_needed = 0;
14244
14245         LevelSolved();
14246
14247         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14248       }
14249     }
14250     else
14251       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14252
14253     InitMovingField(x, y, move_direction);
14254     GfxAction[x][y] = ACTION_PUSHING;
14255
14256     if (mode == DF_SNAP)
14257       ContinueMoving(x, y);
14258     else
14259       MovPos[x][y] = (dx != 0 ? dx : dy);
14260
14261     Pushed[x][y] = TRUE;
14262     Pushed[nextx][nexty] = TRUE;
14263
14264     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14265       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14266     else
14267       player->push_delay_value = -1;    // get new value later
14268
14269     // check for element change _after_ element has been pushed
14270     if (game.use_change_when_pushing_bug)
14271     {
14272       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14273                                  player->index_bit, dig_side);
14274       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14275                                           player->index_bit, dig_side);
14276     }
14277   }
14278   else if (IS_SWITCHABLE(element))
14279   {
14280     if (PLAYER_SWITCHING(player, x, y))
14281     {
14282       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14283                                           player->index_bit, dig_side);
14284
14285       return MP_ACTION;
14286     }
14287
14288     player->is_switching = TRUE;
14289     player->switch_x = x;
14290     player->switch_y = y;
14291
14292     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14293
14294     if (element == EL_ROBOT_WHEEL)
14295     {
14296       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14297
14298       game.robot_wheel_x = x;
14299       game.robot_wheel_y = y;
14300       game.robot_wheel_active = TRUE;
14301
14302       TEST_DrawLevelField(x, y);
14303     }
14304     else if (element == EL_SP_TERMINAL)
14305     {
14306       int xx, yy;
14307
14308       SCAN_PLAYFIELD(xx, yy)
14309       {
14310         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14311         {
14312           Bang(xx, yy);
14313         }
14314         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14315         {
14316           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14317
14318           ResetGfxAnimation(xx, yy);
14319           TEST_DrawLevelField(xx, yy);
14320         }
14321       }
14322     }
14323     else if (IS_BELT_SWITCH(element))
14324     {
14325       ToggleBeltSwitch(x, y);
14326     }
14327     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14328              element == EL_SWITCHGATE_SWITCH_DOWN ||
14329              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14330              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14331     {
14332       ToggleSwitchgateSwitch(x, y);
14333     }
14334     else if (element == EL_LIGHT_SWITCH ||
14335              element == EL_LIGHT_SWITCH_ACTIVE)
14336     {
14337       ToggleLightSwitch(x, y);
14338     }
14339     else if (element == EL_TIMEGATE_SWITCH ||
14340              element == EL_DC_TIMEGATE_SWITCH)
14341     {
14342       ActivateTimegateSwitch(x, y);
14343     }
14344     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14345              element == EL_BALLOON_SWITCH_RIGHT ||
14346              element == EL_BALLOON_SWITCH_UP    ||
14347              element == EL_BALLOON_SWITCH_DOWN  ||
14348              element == EL_BALLOON_SWITCH_NONE  ||
14349              element == EL_BALLOON_SWITCH_ANY)
14350     {
14351       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14352                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14353                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14354                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14355                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14356                              move_direction);
14357     }
14358     else if (element == EL_LAMP)
14359     {
14360       Feld[x][y] = EL_LAMP_ACTIVE;
14361       game.lights_still_needed--;
14362
14363       ResetGfxAnimation(x, y);
14364       TEST_DrawLevelField(x, y);
14365     }
14366     else if (element == EL_TIME_ORB_FULL)
14367     {
14368       Feld[x][y] = EL_TIME_ORB_EMPTY;
14369
14370       if (level.time > 0 || level.use_time_orb_bug)
14371       {
14372         TimeLeft += level.time_orb_time;
14373         game.no_time_limit = FALSE;
14374
14375         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14376
14377         DisplayGameControlValues();
14378       }
14379
14380       ResetGfxAnimation(x, y);
14381       TEST_DrawLevelField(x, y);
14382     }
14383     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14384              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14385     {
14386       int xx, yy;
14387
14388       game.ball_active = !game.ball_active;
14389
14390       SCAN_PLAYFIELD(xx, yy)
14391       {
14392         int e = Feld[xx][yy];
14393
14394         if (game.ball_active)
14395         {
14396           if (e == EL_EMC_MAGIC_BALL)
14397             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14398           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14399             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14400         }
14401         else
14402         {
14403           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14404             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14405           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14406             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14407         }
14408       }
14409     }
14410
14411     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14412                                         player->index_bit, dig_side);
14413
14414     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14415                                         player->index_bit, dig_side);
14416
14417     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14418                                         player->index_bit, dig_side);
14419
14420     return MP_ACTION;
14421   }
14422   else
14423   {
14424     if (!PLAYER_SWITCHING(player, x, y))
14425     {
14426       player->is_switching = TRUE;
14427       player->switch_x = x;
14428       player->switch_y = y;
14429
14430       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14431                                  player->index_bit, dig_side);
14432       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14433                                           player->index_bit, dig_side);
14434
14435       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14436                                  player->index_bit, dig_side);
14437       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14438                                           player->index_bit, dig_side);
14439     }
14440
14441     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14442                                player->index_bit, dig_side);
14443     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14444                                         player->index_bit, dig_side);
14445
14446     return MP_NO_ACTION;
14447   }
14448
14449   player->push_delay = -1;
14450
14451   if (is_player)                // function can also be called by EL_PENGUIN
14452   {
14453     if (Feld[x][y] != element)          // really digged/collected something
14454     {
14455       player->is_collecting = !player->is_digging;
14456       player->is_active = TRUE;
14457     }
14458   }
14459
14460   return MP_MOVING;
14461 }
14462
14463 static boolean DigFieldByCE(int x, int y, int digging_element)
14464 {
14465   int element = Feld[x][y];
14466
14467   if (!IS_FREE(x, y))
14468   {
14469     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14470                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14471                   ACTION_BREAKING);
14472
14473     // no element can dig solid indestructible elements
14474     if (IS_INDESTRUCTIBLE(element) &&
14475         !IS_DIGGABLE(element) &&
14476         !IS_COLLECTIBLE(element))
14477       return FALSE;
14478
14479     if (AmoebaNr[x][y] &&
14480         (element == EL_AMOEBA_FULL ||
14481          element == EL_BD_AMOEBA ||
14482          element == EL_AMOEBA_GROWING))
14483     {
14484       AmoebaCnt[AmoebaNr[x][y]]--;
14485       AmoebaCnt2[AmoebaNr[x][y]]--;
14486     }
14487
14488     if (IS_MOVING(x, y))
14489       RemoveMovingField(x, y);
14490     else
14491     {
14492       RemoveField(x, y);
14493       TEST_DrawLevelField(x, y);
14494     }
14495
14496     // if digged element was about to explode, prevent the explosion
14497     ExplodeField[x][y] = EX_TYPE_NONE;
14498
14499     PlayLevelSoundAction(x, y, action);
14500   }
14501
14502   Store[x][y] = EL_EMPTY;
14503
14504   // this makes it possible to leave the removed element again
14505   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14506     Store[x][y] = element;
14507
14508   return TRUE;
14509 }
14510
14511 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14512 {
14513   int jx = player->jx, jy = player->jy;
14514   int x = jx + dx, y = jy + dy;
14515   int snap_direction = (dx == -1 ? MV_LEFT  :
14516                         dx == +1 ? MV_RIGHT :
14517                         dy == -1 ? MV_UP    :
14518                         dy == +1 ? MV_DOWN  : MV_NONE);
14519   boolean can_continue_snapping = (level.continuous_snapping &&
14520                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14521
14522   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14523     return FALSE;
14524
14525   if (!player->active || !IN_LEV_FIELD(x, y))
14526     return FALSE;
14527
14528   if (dx && dy)
14529     return FALSE;
14530
14531   if (!dx && !dy)
14532   {
14533     if (player->MovPos == 0)
14534       player->is_pushing = FALSE;
14535
14536     player->is_snapping = FALSE;
14537
14538     if (player->MovPos == 0)
14539     {
14540       player->is_moving = FALSE;
14541       player->is_digging = FALSE;
14542       player->is_collecting = FALSE;
14543     }
14544
14545     return FALSE;
14546   }
14547
14548   // prevent snapping with already pressed snap key when not allowed
14549   if (player->is_snapping && !can_continue_snapping)
14550     return FALSE;
14551
14552   player->MovDir = snap_direction;
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   player->is_dropping = FALSE;
14562   player->is_dropping_pressed = FALSE;
14563   player->drop_pressed_delay = 0;
14564
14565   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14566     return FALSE;
14567
14568   player->is_snapping = TRUE;
14569   player->is_active = TRUE;
14570
14571   if (player->MovPos == 0)
14572   {
14573     player->is_moving = FALSE;
14574     player->is_digging = FALSE;
14575     player->is_collecting = FALSE;
14576   }
14577
14578   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14579     TEST_DrawLevelField(player->last_jx, player->last_jy);
14580
14581   TEST_DrawLevelField(x, y);
14582
14583   return TRUE;
14584 }
14585
14586 static boolean DropElement(struct PlayerInfo *player)
14587 {
14588   int old_element, new_element;
14589   int dropx = player->jx, dropy = player->jy;
14590   int drop_direction = player->MovDir;
14591   int drop_side = drop_direction;
14592   int drop_element = get_next_dropped_element(player);
14593
14594   /* do not drop an element on top of another element; when holding drop key
14595      pressed without moving, dropped element must move away before the next
14596      element can be dropped (this is especially important if the next element
14597      is dynamite, which can be placed on background for historical reasons) */
14598   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14599     return MP_ACTION;
14600
14601   if (IS_THROWABLE(drop_element))
14602   {
14603     dropx += GET_DX_FROM_DIR(drop_direction);
14604     dropy += GET_DY_FROM_DIR(drop_direction);
14605
14606     if (!IN_LEV_FIELD(dropx, dropy))
14607       return FALSE;
14608   }
14609
14610   old_element = Feld[dropx][dropy];     // old element at dropping position
14611   new_element = drop_element;           // default: no change when dropping
14612
14613   // check if player is active, not moving and ready to drop
14614   if (!player->active || player->MovPos || player->drop_delay > 0)
14615     return FALSE;
14616
14617   // check if player has anything that can be dropped
14618   if (new_element == EL_UNDEFINED)
14619     return FALSE;
14620
14621   // only set if player has anything that can be dropped
14622   player->is_dropping_pressed = TRUE;
14623
14624   // check if drop key was pressed long enough for EM style dynamite
14625   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14626     return FALSE;
14627
14628   // check if anything can be dropped at the current position
14629   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14630     return FALSE;
14631
14632   // collected custom elements can only be dropped on empty fields
14633   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14634     return FALSE;
14635
14636   if (old_element != EL_EMPTY)
14637     Back[dropx][dropy] = old_element;   // store old element on this field
14638
14639   ResetGfxAnimation(dropx, dropy);
14640   ResetRandomAnimationValue(dropx, dropy);
14641
14642   if (player->inventory_size > 0 ||
14643       player->inventory_infinite_element != EL_UNDEFINED)
14644   {
14645     if (player->inventory_size > 0)
14646     {
14647       player->inventory_size--;
14648
14649       DrawGameDoorValues();
14650
14651       if (new_element == EL_DYNAMITE)
14652         new_element = EL_DYNAMITE_ACTIVE;
14653       else if (new_element == EL_EM_DYNAMITE)
14654         new_element = EL_EM_DYNAMITE_ACTIVE;
14655       else if (new_element == EL_SP_DISK_RED)
14656         new_element = EL_SP_DISK_RED_ACTIVE;
14657     }
14658
14659     Feld[dropx][dropy] = new_element;
14660
14661     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14662       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14663                           el2img(Feld[dropx][dropy]), 0);
14664
14665     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14666
14667     // needed if previous element just changed to "empty" in the last frame
14668     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14669
14670     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14671                                player->index_bit, drop_side);
14672     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14673                                         CE_PLAYER_DROPS_X,
14674                                         player->index_bit, drop_side);
14675
14676     TestIfElementTouchesCustomElement(dropx, dropy);
14677   }
14678   else          // player is dropping a dyna bomb
14679   {
14680     player->dynabombs_left--;
14681
14682     Feld[dropx][dropy] = new_element;
14683
14684     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14685       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14686                           el2img(Feld[dropx][dropy]), 0);
14687
14688     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14689   }
14690
14691   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14692     InitField_WithBug1(dropx, dropy, FALSE);
14693
14694   new_element = Feld[dropx][dropy];     // element might have changed
14695
14696   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14697       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14698   {
14699     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14700       MovDir[dropx][dropy] = drop_direction;
14701
14702     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14703
14704     // do not cause impact style collision by dropping elements that can fall
14705     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14706   }
14707
14708   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14709   player->is_dropping = TRUE;
14710
14711   player->drop_pressed_delay = 0;
14712   player->is_dropping_pressed = FALSE;
14713
14714   player->drop_x = dropx;
14715   player->drop_y = dropy;
14716
14717   return TRUE;
14718 }
14719
14720 // ----------------------------------------------------------------------------
14721 // game sound playing functions
14722 // ----------------------------------------------------------------------------
14723
14724 static int *loop_sound_frame = NULL;
14725 static int *loop_sound_volume = NULL;
14726
14727 void InitPlayLevelSound(void)
14728 {
14729   int num_sounds = getSoundListSize();
14730
14731   checked_free(loop_sound_frame);
14732   checked_free(loop_sound_volume);
14733
14734   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14735   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14736 }
14737
14738 static void PlayLevelSound(int x, int y, int nr)
14739 {
14740   int sx = SCREENX(x), sy = SCREENY(y);
14741   int volume, stereo_position;
14742   int max_distance = 8;
14743   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14744
14745   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14746       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14747     return;
14748
14749   if (!IN_LEV_FIELD(x, y) ||
14750       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14751       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14752     return;
14753
14754   volume = SOUND_MAX_VOLUME;
14755
14756   if (!IN_SCR_FIELD(sx, sy))
14757   {
14758     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14759     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14760
14761     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14762   }
14763
14764   stereo_position = (SOUND_MAX_LEFT +
14765                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14766                      (SCR_FIELDX + 2 * max_distance));
14767
14768   if (IS_LOOP_SOUND(nr))
14769   {
14770     /* This assures that quieter loop sounds do not overwrite louder ones,
14771        while restarting sound volume comparison with each new game frame. */
14772
14773     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14774       return;
14775
14776     loop_sound_volume[nr] = volume;
14777     loop_sound_frame[nr] = FrameCounter;
14778   }
14779
14780   PlaySoundExt(nr, volume, stereo_position, type);
14781 }
14782
14783 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14784 {
14785   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14786                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14787                  y < LEVELY(BY1) ? LEVELY(BY1) :
14788                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14789                  sound_action);
14790 }
14791
14792 static void PlayLevelSoundAction(int x, int y, int action)
14793 {
14794   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14795 }
14796
14797 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14798 {
14799   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14800
14801   if (sound_effect != SND_UNDEFINED)
14802     PlayLevelSound(x, y, sound_effect);
14803 }
14804
14805 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14806                                               int action)
14807 {
14808   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14809
14810   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14811     PlayLevelSound(x, y, sound_effect);
14812 }
14813
14814 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14815 {
14816   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14817
14818   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14819     PlayLevelSound(x, y, sound_effect);
14820 }
14821
14822 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14823 {
14824   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14825
14826   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14827     StopSound(sound_effect);
14828 }
14829
14830 static int getLevelMusicNr(void)
14831 {
14832   if (levelset.music[level_nr] != MUS_UNDEFINED)
14833     return levelset.music[level_nr];            // from config file
14834   else
14835     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14836 }
14837
14838 static void FadeLevelSounds(void)
14839 {
14840   FadeSounds();
14841 }
14842
14843 static void FadeLevelMusic(void)
14844 {
14845   int music_nr = getLevelMusicNr();
14846   char *curr_music = getCurrentlyPlayingMusicFilename();
14847   char *next_music = getMusicInfoEntryFilename(music_nr);
14848
14849   if (!strEqual(curr_music, next_music))
14850     FadeMusic();
14851 }
14852
14853 void FadeLevelSoundsAndMusic(void)
14854 {
14855   FadeLevelSounds();
14856   FadeLevelMusic();
14857 }
14858
14859 static void PlayLevelMusic(void)
14860 {
14861   int music_nr = getLevelMusicNr();
14862   char *curr_music = getCurrentlyPlayingMusicFilename();
14863   char *next_music = getMusicInfoEntryFilename(music_nr);
14864
14865   if (!strEqual(curr_music, next_music))
14866     PlayMusicLoop(music_nr);
14867 }
14868
14869 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14870 {
14871   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14872   int offset = 0;
14873   int x = xx - offset;
14874   int y = yy - offset;
14875
14876   switch (sample)
14877   {
14878     case SOUND_blank:
14879       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14880       break;
14881
14882     case SOUND_roll:
14883       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14884       break;
14885
14886     case SOUND_stone:
14887       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14888       break;
14889
14890     case SOUND_nut:
14891       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14892       break;
14893
14894     case SOUND_crack:
14895       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14896       break;
14897
14898     case SOUND_bug:
14899       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14900       break;
14901
14902     case SOUND_tank:
14903       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14904       break;
14905
14906     case SOUND_android_clone:
14907       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14908       break;
14909
14910     case SOUND_android_move:
14911       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14912       break;
14913
14914     case SOUND_spring:
14915       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14916       break;
14917
14918     case SOUND_slurp:
14919       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14920       break;
14921
14922     case SOUND_eater:
14923       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14924       break;
14925
14926     case SOUND_eater_eat:
14927       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14928       break;
14929
14930     case SOUND_alien:
14931       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14932       break;
14933
14934     case SOUND_collect:
14935       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14936       break;
14937
14938     case SOUND_diamond:
14939       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14940       break;
14941
14942     case SOUND_squash:
14943       // !!! CHECK THIS !!!
14944 #if 1
14945       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14946 #else
14947       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14948 #endif
14949       break;
14950
14951     case SOUND_wonderfall:
14952       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14953       break;
14954
14955     case SOUND_drip:
14956       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14957       break;
14958
14959     case SOUND_push:
14960       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14961       break;
14962
14963     case SOUND_dirt:
14964       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14965       break;
14966
14967     case SOUND_acid:
14968       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14969       break;
14970
14971     case SOUND_ball:
14972       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14973       break;
14974
14975     case SOUND_slide:
14976       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14977       break;
14978
14979     case SOUND_wonder:
14980       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14981       break;
14982
14983     case SOUND_door:
14984       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14985       break;
14986
14987     case SOUND_exit_open:
14988       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14989       break;
14990
14991     case SOUND_exit_leave:
14992       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14993       break;
14994
14995     case SOUND_dynamite:
14996       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14997       break;
14998
14999     case SOUND_tick:
15000       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15001       break;
15002
15003     case SOUND_press:
15004       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15005       break;
15006
15007     case SOUND_wheel:
15008       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15009       break;
15010
15011     case SOUND_boom:
15012       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15013       break;
15014
15015     case SOUND_die:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15017       break;
15018
15019     case SOUND_time:
15020       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15021       break;
15022
15023     default:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15025       break;
15026   }
15027 }
15028
15029 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15030 {
15031   int element = map_element_SP_to_RND(element_sp);
15032   int action = map_action_SP_to_RND(action_sp);
15033   int offset = (setup.sp_show_border_elements ? 0 : 1);
15034   int x = xx - offset;
15035   int y = yy - offset;
15036
15037   PlayLevelSoundElementAction(x, y, element, action);
15038 }
15039
15040 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15041 {
15042   int element = map_element_MM_to_RND(element_mm);
15043   int action = map_action_MM_to_RND(action_mm);
15044   int offset = 0;
15045   int x = xx - offset;
15046   int y = yy - offset;
15047
15048   if (!IS_MM_ELEMENT(element))
15049     element = EL_MM_DEFAULT;
15050
15051   PlayLevelSoundElementAction(x, y, element, action);
15052 }
15053
15054 void PlaySound_MM(int sound_mm)
15055 {
15056   int sound = map_sound_MM_to_RND(sound_mm);
15057
15058   if (sound == SND_UNDEFINED)
15059     return;
15060
15061   PlaySound(sound);
15062 }
15063
15064 void PlaySoundLoop_MM(int sound_mm)
15065 {
15066   int sound = map_sound_MM_to_RND(sound_mm);
15067
15068   if (sound == SND_UNDEFINED)
15069     return;
15070
15071   PlaySoundLoop(sound);
15072 }
15073
15074 void StopSound_MM(int sound_mm)
15075 {
15076   int sound = map_sound_MM_to_RND(sound_mm);
15077
15078   if (sound == SND_UNDEFINED)
15079     return;
15080
15081   StopSound(sound);
15082 }
15083
15084 void RaiseScore(int value)
15085 {
15086   game.score += value;
15087
15088   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15089
15090   DisplayGameControlValues();
15091 }
15092
15093 void RaiseScoreElement(int element)
15094 {
15095   switch (element)
15096   {
15097     case EL_EMERALD:
15098     case EL_BD_DIAMOND:
15099     case EL_EMERALD_YELLOW:
15100     case EL_EMERALD_RED:
15101     case EL_EMERALD_PURPLE:
15102     case EL_SP_INFOTRON:
15103       RaiseScore(level.score[SC_EMERALD]);
15104       break;
15105     case EL_DIAMOND:
15106       RaiseScore(level.score[SC_DIAMOND]);
15107       break;
15108     case EL_CRYSTAL:
15109       RaiseScore(level.score[SC_CRYSTAL]);
15110       break;
15111     case EL_PEARL:
15112       RaiseScore(level.score[SC_PEARL]);
15113       break;
15114     case EL_BUG:
15115     case EL_BD_BUTTERFLY:
15116     case EL_SP_ELECTRON:
15117       RaiseScore(level.score[SC_BUG]);
15118       break;
15119     case EL_SPACESHIP:
15120     case EL_BD_FIREFLY:
15121     case EL_SP_SNIKSNAK:
15122       RaiseScore(level.score[SC_SPACESHIP]);
15123       break;
15124     case EL_YAMYAM:
15125     case EL_DARK_YAMYAM:
15126       RaiseScore(level.score[SC_YAMYAM]);
15127       break;
15128     case EL_ROBOT:
15129       RaiseScore(level.score[SC_ROBOT]);
15130       break;
15131     case EL_PACMAN:
15132       RaiseScore(level.score[SC_PACMAN]);
15133       break;
15134     case EL_NUT:
15135       RaiseScore(level.score[SC_NUT]);
15136       break;
15137     case EL_DYNAMITE:
15138     case EL_EM_DYNAMITE:
15139     case EL_SP_DISK_RED:
15140     case EL_DYNABOMB_INCREASE_NUMBER:
15141     case EL_DYNABOMB_INCREASE_SIZE:
15142     case EL_DYNABOMB_INCREASE_POWER:
15143       RaiseScore(level.score[SC_DYNAMITE]);
15144       break;
15145     case EL_SHIELD_NORMAL:
15146     case EL_SHIELD_DEADLY:
15147       RaiseScore(level.score[SC_SHIELD]);
15148       break;
15149     case EL_EXTRA_TIME:
15150       RaiseScore(level.extra_time_score);
15151       break;
15152     case EL_KEY_1:
15153     case EL_KEY_2:
15154     case EL_KEY_3:
15155     case EL_KEY_4:
15156     case EL_EM_KEY_1:
15157     case EL_EM_KEY_2:
15158     case EL_EM_KEY_3:
15159     case EL_EM_KEY_4:
15160     case EL_EMC_KEY_5:
15161     case EL_EMC_KEY_6:
15162     case EL_EMC_KEY_7:
15163     case EL_EMC_KEY_8:
15164     case EL_DC_KEY_WHITE:
15165       RaiseScore(level.score[SC_KEY]);
15166       break;
15167     default:
15168       RaiseScore(element_info[element].collect_score);
15169       break;
15170   }
15171 }
15172
15173 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15174 {
15175   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15176   {
15177     // closing door required in case of envelope style request dialogs
15178     if (!skip_request)
15179     {
15180       // prevent short reactivation of overlay buttons while closing door
15181       SetOverlayActive(FALSE);
15182
15183       CloseDoor(DOOR_CLOSE_1);
15184     }
15185
15186     if (network.enabled)
15187       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15188     else
15189     {
15190       if (quick_quit)
15191         FadeSkipNextFadeIn();
15192
15193       SetGameStatus(GAME_MODE_MAIN);
15194
15195       DrawMainMenu();
15196     }
15197   }
15198   else          // continue playing the game
15199   {
15200     if (tape.playing && tape.deactivate_display)
15201       TapeDeactivateDisplayOff(TRUE);
15202
15203     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15204
15205     if (tape.playing && tape.deactivate_display)
15206       TapeDeactivateDisplayOn();
15207   }
15208 }
15209
15210 void RequestQuitGame(boolean ask_if_really_quit)
15211 {
15212   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15213   boolean skip_request = game.all_players_gone || quick_quit;
15214
15215   RequestQuitGameExt(skip_request, quick_quit,
15216                      "Do you really want to quit the game?");
15217 }
15218
15219 void RequestRestartGame(char *message)
15220 {
15221   game.restart_game_message = NULL;
15222
15223   boolean has_started_game = hasStartedNetworkGame();
15224   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15225
15226   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15227   {
15228     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15229   }
15230   else
15231   {
15232     SetGameStatus(GAME_MODE_MAIN);
15233
15234     DrawMainMenu();
15235   }
15236 }
15237
15238 void CheckGameOver(void)
15239 {
15240   static boolean last_game_over = FALSE;
15241   static int game_over_delay = 0;
15242   int game_over_delay_value = 50;
15243   boolean game_over = checkGameFailed();
15244
15245   // do not handle game over if request dialog is already active
15246   if (game.request_active)
15247     return;
15248
15249   // do not ask to play again if game was never actually played
15250   if (!game.GamePlayed)
15251     return;
15252
15253   if (!game_over)
15254   {
15255     last_game_over = FALSE;
15256     game_over_delay = game_over_delay_value;
15257
15258     return;
15259   }
15260
15261   if (game_over_delay > 0)
15262   {
15263     game_over_delay--;
15264
15265     return;
15266   }
15267
15268   if (last_game_over != game_over)
15269     game.restart_game_message = (hasStartedNetworkGame() ?
15270                                  "Game over! Play it again?" :
15271                                  "Game over!");
15272
15273   last_game_over = game_over;
15274 }
15275
15276 boolean checkGameSolved(void)
15277 {
15278   // set for all game engines if level was solved
15279   return game.LevelSolved_GameEnd;
15280 }
15281
15282 boolean checkGameFailed(void)
15283 {
15284   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15285     return (game_em.game_over && !game_em.level_solved);
15286   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15287     return (game_sp.game_over && !game_sp.level_solved);
15288   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15289     return (game_mm.game_over && !game_mm.level_solved);
15290   else                          // GAME_ENGINE_TYPE_RND
15291     return (game.GameOver && !game.LevelSolved);
15292 }
15293
15294 boolean checkGameEnded(void)
15295 {
15296   return (checkGameSolved() || checkGameFailed());
15297 }
15298
15299
15300 // ----------------------------------------------------------------------------
15301 // random generator functions
15302 // ----------------------------------------------------------------------------
15303
15304 unsigned int InitEngineRandom_RND(int seed)
15305 {
15306   game.num_random_calls = 0;
15307
15308   return InitEngineRandom(seed);
15309 }
15310
15311 unsigned int RND(int max)
15312 {
15313   if (max > 0)
15314   {
15315     game.num_random_calls++;
15316
15317     return GetEngineRandom(max);
15318   }
15319
15320   return 0;
15321 }
15322
15323
15324 // ----------------------------------------------------------------------------
15325 // game engine snapshot handling functions
15326 // ----------------------------------------------------------------------------
15327
15328 struct EngineSnapshotInfo
15329 {
15330   // runtime values for custom element collect score
15331   int collect_score[NUM_CUSTOM_ELEMENTS];
15332
15333   // runtime values for group element choice position
15334   int choice_pos[NUM_GROUP_ELEMENTS];
15335
15336   // runtime values for belt position animations
15337   int belt_graphic[4][NUM_BELT_PARTS];
15338   int belt_anim_mode[4][NUM_BELT_PARTS];
15339 };
15340
15341 static struct EngineSnapshotInfo engine_snapshot_rnd;
15342 static char *snapshot_level_identifier = NULL;
15343 static int snapshot_level_nr = -1;
15344
15345 static void SaveEngineSnapshotValues_RND(void)
15346 {
15347   static int belt_base_active_element[4] =
15348   {
15349     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15350     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15351     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15352     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15353   };
15354   int i, j;
15355
15356   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15357   {
15358     int element = EL_CUSTOM_START + i;
15359
15360     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15361   }
15362
15363   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15364   {
15365     int element = EL_GROUP_START + i;
15366
15367     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15368   }
15369
15370   for (i = 0; i < 4; i++)
15371   {
15372     for (j = 0; j < NUM_BELT_PARTS; j++)
15373     {
15374       int element = belt_base_active_element[i] + j;
15375       int graphic = el2img(element);
15376       int anim_mode = graphic_info[graphic].anim_mode;
15377
15378       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15379       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15380     }
15381   }
15382 }
15383
15384 static void LoadEngineSnapshotValues_RND(void)
15385 {
15386   unsigned int num_random_calls = game.num_random_calls;
15387   int i, j;
15388
15389   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15390   {
15391     int element = EL_CUSTOM_START + i;
15392
15393     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15394   }
15395
15396   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15397   {
15398     int element = EL_GROUP_START + i;
15399
15400     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15401   }
15402
15403   for (i = 0; i < 4; i++)
15404   {
15405     for (j = 0; j < NUM_BELT_PARTS; j++)
15406     {
15407       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15408       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15409
15410       graphic_info[graphic].anim_mode = anim_mode;
15411     }
15412   }
15413
15414   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15415   {
15416     InitRND(tape.random_seed);
15417     for (i = 0; i < num_random_calls; i++)
15418       RND(1);
15419   }
15420
15421   if (game.num_random_calls != num_random_calls)
15422   {
15423     Error(ERR_INFO, "number of random calls out of sync");
15424     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15425     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15426     Error(ERR_EXIT, "this should not happen -- please debug");
15427   }
15428 }
15429
15430 void FreeEngineSnapshotSingle(void)
15431 {
15432   FreeSnapshotSingle();
15433
15434   setString(&snapshot_level_identifier, NULL);
15435   snapshot_level_nr = -1;
15436 }
15437
15438 void FreeEngineSnapshotList(void)
15439 {
15440   FreeSnapshotList();
15441 }
15442
15443 static ListNode *SaveEngineSnapshotBuffers(void)
15444 {
15445   ListNode *buffers = NULL;
15446
15447   // copy some special values to a structure better suited for the snapshot
15448
15449   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15450     SaveEngineSnapshotValues_RND();
15451   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15452     SaveEngineSnapshotValues_EM();
15453   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15454     SaveEngineSnapshotValues_SP(&buffers);
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15456     SaveEngineSnapshotValues_MM(&buffers);
15457
15458   // save values stored in special snapshot structure
15459
15460   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15461     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15462   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15463     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15464   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15465     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15466   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15467     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15468
15469   // save further RND engine values
15470
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15472   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15474
15475   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15480
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15484
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15486
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15489
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15508
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15511
15512   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15515
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15518
15519   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15521   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15522   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15523   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15524
15525   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15526   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15527
15528 #if 0
15529   ListNode *node = engine_snapshot_list_rnd;
15530   int num_bytes = 0;
15531
15532   while (node != NULL)
15533   {
15534     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15535
15536     node = node->next;
15537   }
15538
15539   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15540 #endif
15541
15542   return buffers;
15543 }
15544
15545 void SaveEngineSnapshotSingle(void)
15546 {
15547   ListNode *buffers = SaveEngineSnapshotBuffers();
15548
15549   // finally save all snapshot buffers to single snapshot
15550   SaveSnapshotSingle(buffers);
15551
15552   // save level identification information
15553   setString(&snapshot_level_identifier, leveldir_current->identifier);
15554   snapshot_level_nr = level_nr;
15555 }
15556
15557 boolean CheckSaveEngineSnapshotToList(void)
15558 {
15559   boolean save_snapshot =
15560     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15561      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15562       game.snapshot.changed_action) ||
15563      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15564       game.snapshot.collected_item));
15565
15566   game.snapshot.changed_action = FALSE;
15567   game.snapshot.collected_item = FALSE;
15568   game.snapshot.save_snapshot = save_snapshot;
15569
15570   return save_snapshot;
15571 }
15572
15573 void SaveEngineSnapshotToList(void)
15574 {
15575   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15576       tape.quick_resume)
15577     return;
15578
15579   ListNode *buffers = SaveEngineSnapshotBuffers();
15580
15581   // finally save all snapshot buffers to snapshot list
15582   SaveSnapshotToList(buffers);
15583 }
15584
15585 void SaveEngineSnapshotToListInitial(void)
15586 {
15587   FreeEngineSnapshotList();
15588
15589   SaveEngineSnapshotToList();
15590 }
15591
15592 static void LoadEngineSnapshotValues(void)
15593 {
15594   // restore special values from snapshot structure
15595
15596   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15597     LoadEngineSnapshotValues_RND();
15598   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15599     LoadEngineSnapshotValues_EM();
15600   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15601     LoadEngineSnapshotValues_SP();
15602   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15603     LoadEngineSnapshotValues_MM();
15604 }
15605
15606 void LoadEngineSnapshotSingle(void)
15607 {
15608   LoadSnapshotSingle();
15609
15610   LoadEngineSnapshotValues();
15611 }
15612
15613 static void LoadEngineSnapshot_Undo(int steps)
15614 {
15615   LoadSnapshotFromList_Older(steps);
15616
15617   LoadEngineSnapshotValues();
15618 }
15619
15620 static void LoadEngineSnapshot_Redo(int steps)
15621 {
15622   LoadSnapshotFromList_Newer(steps);
15623
15624   LoadEngineSnapshotValues();
15625 }
15626
15627 boolean CheckEngineSnapshotSingle(void)
15628 {
15629   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15630           snapshot_level_nr == level_nr);
15631 }
15632
15633 boolean CheckEngineSnapshotList(void)
15634 {
15635   return CheckSnapshotList();
15636 }
15637
15638
15639 // ---------- new game button stuff -------------------------------------------
15640
15641 static struct
15642 {
15643   int graphic;
15644   struct XY *pos;
15645   int gadget_id;
15646   boolean *setup_value;
15647   boolean allowed_on_tape;
15648   boolean is_touch_button;
15649   char *infotext;
15650 } gamebutton_info[NUM_GAME_BUTTONS] =
15651 {
15652   {
15653     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15654     GAME_CTRL_ID_STOP,                          NULL,
15655     TRUE, FALSE,                                "stop game"
15656   },
15657   {
15658     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15659     GAME_CTRL_ID_PAUSE,                         NULL,
15660     TRUE, FALSE,                                "pause game"
15661   },
15662   {
15663     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15664     GAME_CTRL_ID_PLAY,                          NULL,
15665     TRUE, FALSE,                                "play game"
15666   },
15667   {
15668     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15669     GAME_CTRL_ID_UNDO,                          NULL,
15670     TRUE, FALSE,                                "undo step"
15671   },
15672   {
15673     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15674     GAME_CTRL_ID_REDO,                          NULL,
15675     TRUE, FALSE,                                "redo step"
15676   },
15677   {
15678     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15679     GAME_CTRL_ID_SAVE,                          NULL,
15680     TRUE, FALSE,                                "save game"
15681   },
15682   {
15683     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15684     GAME_CTRL_ID_PAUSE2,                        NULL,
15685     TRUE, FALSE,                                "pause game"
15686   },
15687   {
15688     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15689     GAME_CTRL_ID_LOAD,                          NULL,
15690     TRUE, FALSE,                                "load game"
15691   },
15692   {
15693     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15694     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15695     FALSE, FALSE,                               "stop game"
15696   },
15697   {
15698     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15699     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15700     FALSE, FALSE,                               "pause game"
15701   },
15702   {
15703     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15704     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15705     FALSE, FALSE,                               "play game"
15706   },
15707   {
15708     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15709     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15710     FALSE, TRUE,                                "stop game"
15711   },
15712   {
15713     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15714     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15715     FALSE, TRUE,                                "pause game"
15716   },
15717   {
15718     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15719     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15720     TRUE, FALSE,                                "background music on/off"
15721   },
15722   {
15723     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15724     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15725     TRUE, FALSE,                                "sound loops on/off"
15726   },
15727   {
15728     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15729     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15730     TRUE, FALSE,                                "normal sounds on/off"
15731   },
15732   {
15733     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15734     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15735     FALSE, FALSE,                               "background music on/off"
15736   },
15737   {
15738     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15739     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15740     FALSE, FALSE,                               "sound loops on/off"
15741   },
15742   {
15743     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15744     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15745     FALSE, FALSE,                               "normal sounds on/off"
15746   }
15747 };
15748
15749 void CreateGameButtons(void)
15750 {
15751   int i;
15752
15753   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15754   {
15755     int graphic = gamebutton_info[i].graphic;
15756     struct GraphicInfo *gfx = &graphic_info[graphic];
15757     struct XY *pos = gamebutton_info[i].pos;
15758     struct GadgetInfo *gi;
15759     int button_type;
15760     boolean checked;
15761     unsigned int event_mask;
15762     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15763     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15764     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15765     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15766     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15767     int gd_x   = gfx->src_x;
15768     int gd_y   = gfx->src_y;
15769     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15770     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15771     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15772     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15773     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15774     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15775     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15776     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15777     int id = i;
15778
15779     if (gfx->bitmap == NULL)
15780     {
15781       game_gadget[id] = NULL;
15782
15783       continue;
15784     }
15785
15786     if (id == GAME_CTRL_ID_STOP ||
15787         id == GAME_CTRL_ID_PANEL_STOP ||
15788         id == GAME_CTRL_ID_TOUCH_STOP ||
15789         id == GAME_CTRL_ID_PLAY ||
15790         id == GAME_CTRL_ID_PANEL_PLAY ||
15791         id == GAME_CTRL_ID_SAVE ||
15792         id == GAME_CTRL_ID_LOAD)
15793     {
15794       button_type = GD_TYPE_NORMAL_BUTTON;
15795       checked = FALSE;
15796       event_mask = GD_EVENT_RELEASED;
15797     }
15798     else if (id == GAME_CTRL_ID_UNDO ||
15799              id == GAME_CTRL_ID_REDO)
15800     {
15801       button_type = GD_TYPE_NORMAL_BUTTON;
15802       checked = FALSE;
15803       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15804     }
15805     else
15806     {
15807       button_type = GD_TYPE_CHECK_BUTTON;
15808       checked = (gamebutton_info[i].setup_value != NULL ?
15809                  *gamebutton_info[i].setup_value : FALSE);
15810       event_mask = GD_EVENT_PRESSED;
15811     }
15812
15813     gi = CreateGadget(GDI_CUSTOM_ID, id,
15814                       GDI_IMAGE_ID, graphic,
15815                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15816                       GDI_X, base_x + x,
15817                       GDI_Y, base_y + y,
15818                       GDI_WIDTH, gfx->width,
15819                       GDI_HEIGHT, gfx->height,
15820                       GDI_TYPE, button_type,
15821                       GDI_STATE, GD_BUTTON_UNPRESSED,
15822                       GDI_CHECKED, checked,
15823                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15824                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15825                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15826                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15827                       GDI_DIRECT_DRAW, FALSE,
15828                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15829                       GDI_EVENT_MASK, event_mask,
15830                       GDI_CALLBACK_ACTION, HandleGameButtons,
15831                       GDI_END);
15832
15833     if (gi == NULL)
15834       Error(ERR_EXIT, "cannot create gadget");
15835
15836     game_gadget[id] = gi;
15837   }
15838 }
15839
15840 void FreeGameButtons(void)
15841 {
15842   int i;
15843
15844   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15845     FreeGadget(game_gadget[i]);
15846 }
15847
15848 static void UnmapGameButtonsAtSamePosition(int id)
15849 {
15850   int i;
15851
15852   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15853     if (i != id &&
15854         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15855         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15856       UnmapGadget(game_gadget[i]);
15857 }
15858
15859 static void UnmapGameButtonsAtSamePosition_All(void)
15860 {
15861   if (setup.show_snapshot_buttons)
15862   {
15863     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15864     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15865     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15866   }
15867   else
15868   {
15869     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15870     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15871     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15872
15873     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15874     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15875     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15876   }
15877 }
15878
15879 static void MapGameButtonsAtSamePosition(int id)
15880 {
15881   int i;
15882
15883   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15884     if (i != id &&
15885         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15886         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15887       MapGadget(game_gadget[i]);
15888
15889   UnmapGameButtonsAtSamePosition_All();
15890 }
15891
15892 void MapUndoRedoButtons(void)
15893 {
15894   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15895   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15896
15897   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15898   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15899 }
15900
15901 void UnmapUndoRedoButtons(void)
15902 {
15903   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15904   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15905
15906   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15907   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15908 }
15909
15910 void ModifyPauseButtons(void)
15911 {
15912   static int ids[] =
15913   {
15914     GAME_CTRL_ID_PAUSE,
15915     GAME_CTRL_ID_PAUSE2,
15916     GAME_CTRL_ID_PANEL_PAUSE,
15917     GAME_CTRL_ID_TOUCH_PAUSE,
15918     -1
15919   };
15920   int i;
15921
15922   for (i = 0; ids[i] > -1; i++)
15923     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15924 }
15925
15926 static void MapGameButtonsExt(boolean on_tape)
15927 {
15928   int i;
15929
15930   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15931     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15932         i != GAME_CTRL_ID_UNDO &&
15933         i != GAME_CTRL_ID_REDO)
15934       MapGadget(game_gadget[i]);
15935
15936   UnmapGameButtonsAtSamePosition_All();
15937
15938   RedrawGameButtons();
15939 }
15940
15941 static void UnmapGameButtonsExt(boolean on_tape)
15942 {
15943   int i;
15944
15945   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15946     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15947       UnmapGadget(game_gadget[i]);
15948 }
15949
15950 static void RedrawGameButtonsExt(boolean on_tape)
15951 {
15952   int i;
15953
15954   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15955     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15956       RedrawGadget(game_gadget[i]);
15957 }
15958
15959 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15960 {
15961   if (gi == NULL)
15962     return;
15963
15964   gi->checked = state;
15965 }
15966
15967 static void RedrawSoundButtonGadget(int id)
15968 {
15969   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15970              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15971              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15972              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15973              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15974              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15975              id);
15976
15977   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15978   RedrawGadget(game_gadget[id2]);
15979 }
15980
15981 void MapGameButtons(void)
15982 {
15983   MapGameButtonsExt(FALSE);
15984 }
15985
15986 void UnmapGameButtons(void)
15987 {
15988   UnmapGameButtonsExt(FALSE);
15989 }
15990
15991 void RedrawGameButtons(void)
15992 {
15993   RedrawGameButtonsExt(FALSE);
15994 }
15995
15996 void MapGameButtonsOnTape(void)
15997 {
15998   MapGameButtonsExt(TRUE);
15999 }
16000
16001 void UnmapGameButtonsOnTape(void)
16002 {
16003   UnmapGameButtonsExt(TRUE);
16004 }
16005
16006 void RedrawGameButtonsOnTape(void)
16007 {
16008   RedrawGameButtonsExt(TRUE);
16009 }
16010
16011 static void GameUndoRedoExt(void)
16012 {
16013   ClearPlayerAction();
16014
16015   tape.pausing = TRUE;
16016
16017   RedrawPlayfield();
16018   UpdateAndDisplayGameControlValues();
16019
16020   DrawCompleteVideoDisplay();
16021   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16022   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16023   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16024
16025   BackToFront();
16026 }
16027
16028 static void GameUndo(int steps)
16029 {
16030   if (!CheckEngineSnapshotList())
16031     return;
16032
16033   LoadEngineSnapshot_Undo(steps);
16034
16035   GameUndoRedoExt();
16036 }
16037
16038 static void GameRedo(int steps)
16039 {
16040   if (!CheckEngineSnapshotList())
16041     return;
16042
16043   LoadEngineSnapshot_Redo(steps);
16044
16045   GameUndoRedoExt();
16046 }
16047
16048 static void HandleGameButtonsExt(int id, int button)
16049 {
16050   static boolean game_undo_executed = FALSE;
16051   int steps = BUTTON_STEPSIZE(button);
16052   boolean handle_game_buttons =
16053     (game_status == GAME_MODE_PLAYING ||
16054      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16055
16056   if (!handle_game_buttons)
16057     return;
16058
16059   switch (id)
16060   {
16061     case GAME_CTRL_ID_STOP:
16062     case GAME_CTRL_ID_PANEL_STOP:
16063     case GAME_CTRL_ID_TOUCH_STOP:
16064       if (game_status == GAME_MODE_MAIN)
16065         break;
16066
16067       if (tape.playing)
16068         TapeStop();
16069       else
16070         RequestQuitGame(TRUE);
16071
16072       break;
16073
16074     case GAME_CTRL_ID_PAUSE:
16075     case GAME_CTRL_ID_PAUSE2:
16076     case GAME_CTRL_ID_PANEL_PAUSE:
16077     case GAME_CTRL_ID_TOUCH_PAUSE:
16078       if (network.enabled && game_status == GAME_MODE_PLAYING)
16079       {
16080         if (tape.pausing)
16081           SendToServer_ContinuePlaying();
16082         else
16083           SendToServer_PausePlaying();
16084       }
16085       else
16086         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16087
16088       game_undo_executed = FALSE;
16089
16090       break;
16091
16092     case GAME_CTRL_ID_PLAY:
16093     case GAME_CTRL_ID_PANEL_PLAY:
16094       if (game_status == GAME_MODE_MAIN)
16095       {
16096         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16097       }
16098       else if (tape.pausing)
16099       {
16100         if (network.enabled)
16101           SendToServer_ContinuePlaying();
16102         else
16103           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16104       }
16105       break;
16106
16107     case GAME_CTRL_ID_UNDO:
16108       // Important: When using "save snapshot when collecting an item" mode,
16109       // load last (current) snapshot for first "undo" after pressing "pause"
16110       // (else the last-but-one snapshot would be loaded, because the snapshot
16111       // pointer already points to the last snapshot when pressing "pause",
16112       // which is fine for "every step/move" mode, but not for "every collect")
16113       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16114           !game_undo_executed)
16115         steps--;
16116
16117       game_undo_executed = TRUE;
16118
16119       GameUndo(steps);
16120       break;
16121
16122     case GAME_CTRL_ID_REDO:
16123       GameRedo(steps);
16124       break;
16125
16126     case GAME_CTRL_ID_SAVE:
16127       TapeQuickSave();
16128       break;
16129
16130     case GAME_CTRL_ID_LOAD:
16131       TapeQuickLoad();
16132       break;
16133
16134     case SOUND_CTRL_ID_MUSIC:
16135     case SOUND_CTRL_ID_PANEL_MUSIC:
16136       if (setup.sound_music)
16137       { 
16138         setup.sound_music = FALSE;
16139
16140         FadeMusic();
16141       }
16142       else if (audio.music_available)
16143       { 
16144         setup.sound = setup.sound_music = TRUE;
16145
16146         SetAudioMode(setup.sound);
16147
16148         if (game_status == GAME_MODE_PLAYING)
16149           PlayLevelMusic();
16150       }
16151
16152       RedrawSoundButtonGadget(id);
16153
16154       break;
16155
16156     case SOUND_CTRL_ID_LOOPS:
16157     case SOUND_CTRL_ID_PANEL_LOOPS:
16158       if (setup.sound_loops)
16159         setup.sound_loops = FALSE;
16160       else if (audio.loops_available)
16161       {
16162         setup.sound = setup.sound_loops = TRUE;
16163
16164         SetAudioMode(setup.sound);
16165       }
16166
16167       RedrawSoundButtonGadget(id);
16168
16169       break;
16170
16171     case SOUND_CTRL_ID_SIMPLE:
16172     case SOUND_CTRL_ID_PANEL_SIMPLE:
16173       if (setup.sound_simple)
16174         setup.sound_simple = FALSE;
16175       else if (audio.sound_available)
16176       {
16177         setup.sound = setup.sound_simple = TRUE;
16178
16179         SetAudioMode(setup.sound);
16180       }
16181
16182       RedrawSoundButtonGadget(id);
16183
16184       break;
16185
16186     default:
16187       break;
16188   }
16189 }
16190
16191 static void HandleGameButtons(struct GadgetInfo *gi)
16192 {
16193   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16194 }
16195
16196 void HandleSoundButtonKeys(Key key)
16197 {
16198   if (key == setup.shortcut.sound_simple)
16199     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16200   else if (key == setup.shortcut.sound_loops)
16201     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16202   else if (key == setup.shortcut.sound_music)
16203     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16204 }