fixed bug with not updating game panel when leaving invisible warp mode
[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                                          Tile[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                                          Tile[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                                          Tile[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, Tile[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(Tile[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, Tile[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(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[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(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[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) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[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(Tile[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 AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(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 void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[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 = Tile[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 = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[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 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[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         Tile[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(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[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           Tile[x][y] = Tile[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         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[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       Tile[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(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[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(Tile[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 = Tile[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("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           nr_new = GAME_PANEL_KEY_1 + k;
2376
2377           if (game_panel_controls[nr_new].value == EL_EMPTY)
2378           {
2379             game_panel_controls[nr_new].value = gpc->value;
2380
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 void UpdateGameDoorValues(void)
2873 {
2874   UpdateGameControlValues();
2875 }
2876
2877 void DrawGameDoorValues(void)
2878 {
2879   DisplayGameControlValues();
2880 }
2881
2882
2883 // ============================================================================
2884 // InitGameEngine()
2885 // ----------------------------------------------------------------------------
2886 // initialize game engine due to level / tape version number
2887 // ============================================================================
2888
2889 static void InitGameEngine(void)
2890 {
2891   int i, j, k, l, x, y;
2892
2893   // set game engine from tape file when re-playing, else from level file
2894   game.engine_version = (tape.playing ? tape.engine_version :
2895                          level.game_version);
2896
2897   // set single or multi-player game mode (needed for re-playing tapes)
2898   game.team_mode = setup.team_mode;
2899
2900   if (tape.playing)
2901   {
2902     int num_players = 0;
2903
2904     for (i = 0; i < MAX_PLAYERS; i++)
2905       if (tape.player_participates[i])
2906         num_players++;
2907
2908     // multi-player tapes contain input data for more than one player
2909     game.team_mode = (num_players > 1);
2910   }
2911
2912 #if 0
2913   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2914         level.game_version);
2915   Debug("game:init:level", "          tape.file_version   == %06d",
2916         tape.file_version);
2917   Debug("game:init:level", "          tape.game_version   == %06d",
2918         tape.game_version);
2919   Debug("game:init:level", "          tape.engine_version == %06d",
2920         tape.engine_version);
2921   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2922         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2923 #endif
2924
2925   // --------------------------------------------------------------------------
2926   // set flags for bugs and changes according to active game engine version
2927   // --------------------------------------------------------------------------
2928
2929   /*
2930     Summary of bugfix:
2931     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2932
2933     Bug was introduced in version:
2934     2.0.1
2935
2936     Bug was fixed in version:
2937     4.2.0.0
2938
2939     Description:
2940     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2941     but the property "can fall" was missing, which caused some levels to be
2942     unsolvable. This was fixed in version 4.2.0.0.
2943
2944     Affected levels/tapes:
2945     An example for a tape that was fixed by this bugfix is tape 029 from the
2946     level set "rnd_sam_bateman".
2947     The wrong behaviour will still be used for all levels or tapes that were
2948     created/recorded with it. An example for this is tape 023 from the level
2949     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2950   */
2951
2952   boolean use_amoeba_dropping_cannot_fall_bug =
2953     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2954       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2955      (tape.playing &&
2956       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2957       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed move speed of elements entering or leaving magic wall.
2962
2963     Fixed/changed in version:
2964     2.0.1
2965
2966     Description:
2967     Before 2.0.1, move speed of elements entering or leaving magic wall was
2968     twice as fast as it is now.
2969     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2970
2971     Affected levels/tapes:
2972     The first condition is generally needed for all levels/tapes before version
2973     2.0.1, which might use the old behaviour before it was changed; known tapes
2974     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2975     The second condition is an exception from the above case and is needed for
2976     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2977     above, but before it was known that this change would break tapes like the
2978     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2979     although the engine version while recording maybe was before 2.0.1. There
2980     are a lot of tapes that are affected by this exception, like tape 006 from
2981     the level set "rnd_conor_mancone".
2982   */
2983
2984   boolean use_old_move_stepsize_for_magic_wall =
2985     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2986      !(tape.playing &&
2987        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2988        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2989
2990   /*
2991     Summary of bugfix/change:
2992     Fixed handling for custom elements that change when pushed by the player.
2993
2994     Fixed/changed in version:
2995     3.1.0
2996
2997     Description:
2998     Before 3.1.0, custom elements that "change when pushing" changed directly
2999     after the player started pushing them (until then handled in "DigField()").
3000     Since 3.1.0, these custom elements are not changed until the "pushing"
3001     move of the element is finished (now handled in "ContinueMoving()").
3002
3003     Affected levels/tapes:
3004     The first condition is generally needed for all levels/tapes before version
3005     3.1.0, which might use the old behaviour before it was changed; known tapes
3006     that are affected are some tapes from the level set "Walpurgis Gardens" by
3007     Jamie Cullen.
3008     The second condition is an exception from the above case and is needed for
3009     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3010     above (including some development versions of 3.1.0), but before it was
3011     known that this change would break tapes like the above and was fixed in
3012     3.1.1, so that the changed behaviour was active although the engine version
3013     while recording maybe was before 3.1.0. There is at least one tape that is
3014     affected by this exception, which is the tape for the one-level set "Bug
3015     Machine" by Juergen Bonhagen.
3016   */
3017
3018   game.use_change_when_pushing_bug =
3019     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3020      !(tape.playing &&
3021        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3022        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3023
3024   /*
3025     Summary of bugfix/change:
3026     Fixed handling for blocking the field the player leaves when moving.
3027
3028     Fixed/changed in version:
3029     3.1.1
3030
3031     Description:
3032     Before 3.1.1, when "block last field when moving" was enabled, the field
3033     the player is leaving when moving was blocked for the time of the move,
3034     and was directly unblocked afterwards. This resulted in the last field
3035     being blocked for exactly one less than the number of frames of one player
3036     move. Additionally, even when blocking was disabled, the last field was
3037     blocked for exactly one frame.
3038     Since 3.1.1, due to changes in player movement handling, the last field
3039     is not blocked at all when blocking is disabled. When blocking is enabled,
3040     the last field is blocked for exactly the number of frames of one player
3041     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3042     last field is blocked for exactly one more than the number of frames of
3043     one player move.
3044
3045     Affected levels/tapes:
3046     (!!! yet to be determined -- probably many !!!)
3047   */
3048
3049   game.use_block_last_field_bug =
3050     (game.engine_version < VERSION_IDENT(3,1,1,0));
3051
3052   /* various special flags and settings for native Emerald Mine game engine */
3053
3054   game_em.use_single_button =
3055     (game.engine_version > VERSION_IDENT(4,0,0,2));
3056
3057   game_em.use_snap_key_bug =
3058     (game.engine_version < VERSION_IDENT(4,0,1,0));
3059
3060   game_em.use_random_bug =
3061     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3062
3063   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3064
3065   game_em.use_old_explosions            = use_old_em_engine;
3066   game_em.use_old_android               = use_old_em_engine;
3067   game_em.use_old_push_elements         = use_old_em_engine;
3068   game_em.use_old_push_into_acid        = use_old_em_engine;
3069
3070   game_em.use_wrap_around               = !use_old_em_engine;
3071
3072   // --------------------------------------------------------------------------
3073
3074   // set maximal allowed number of custom element changes per game frame
3075   game.max_num_changes_per_frame = 1;
3076
3077   // default scan direction: scan playfield from top/left to bottom/right
3078   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3079
3080   // dynamically adjust element properties according to game engine version
3081   InitElementPropertiesEngine(game.engine_version);
3082
3083   // ---------- initialize special element properties -------------------------
3084
3085   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3086   if (use_amoeba_dropping_cannot_fall_bug)
3087     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3088
3089   // ---------- initialize player's initial move delay ------------------------
3090
3091   // dynamically adjust player properties according to level information
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay_value[i] =
3094       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3095
3096   // dynamically adjust player properties according to game engine version
3097   for (i = 0; i < MAX_PLAYERS; i++)
3098     game.initial_move_delay[i] =
3099       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3100        game.initial_move_delay_value[i] : 0);
3101
3102   // ---------- initialize player's initial push delay ------------------------
3103
3104   // dynamically adjust player properties according to game engine version
3105   game.initial_push_delay_value =
3106     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3107
3108   // ---------- initialize changing elements ----------------------------------
3109
3110   // initialize changing elements information
3111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3112   {
3113     struct ElementInfo *ei = &element_info[i];
3114
3115     // this pointer might have been changed in the level editor
3116     ei->change = &ei->change_page[0];
3117
3118     if (!IS_CUSTOM_ELEMENT(i))
3119     {
3120       ei->change->target_element = EL_EMPTY_SPACE;
3121       ei->change->delay_fixed = 0;
3122       ei->change->delay_random = 0;
3123       ei->change->delay_frames = 1;
3124     }
3125
3126     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3127     {
3128       ei->has_change_event[j] = FALSE;
3129
3130       ei->event_page_nr[j] = 0;
3131       ei->event_page[j] = &ei->change_page[0];
3132     }
3133   }
3134
3135   // add changing elements from pre-defined list
3136   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3137   {
3138     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3139     struct ElementInfo *ei = &element_info[ch_delay->element];
3140
3141     ei->change->target_element       = ch_delay->target_element;
3142     ei->change->delay_fixed          = ch_delay->change_delay;
3143
3144     ei->change->pre_change_function  = ch_delay->pre_change_function;
3145     ei->change->change_function      = ch_delay->change_function;
3146     ei->change->post_change_function = ch_delay->post_change_function;
3147
3148     ei->change->can_change = TRUE;
3149     ei->change->can_change_or_has_action = TRUE;
3150
3151     ei->has_change_event[CE_DELAY] = TRUE;
3152
3153     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3154     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3155   }
3156
3157   // ---------- initialize internal run-time variables ------------------------
3158
3159   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3160   {
3161     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3162
3163     for (j = 0; j < ei->num_change_pages; j++)
3164     {
3165       ei->change_page[j].can_change_or_has_action =
3166         (ei->change_page[j].can_change |
3167          ei->change_page[j].has_action);
3168     }
3169   }
3170
3171   // add change events from custom element configuration
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + 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       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3182       {
3183         // only add event page for the first page found with this event
3184         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3185         {
3186           ei->has_change_event[k] = TRUE;
3187
3188           ei->event_page_nr[k] = j;
3189           ei->event_page[k] = &ei->change_page[j];
3190         }
3191       }
3192     }
3193   }
3194
3195   // ---------- initialize reference elements in change conditions ------------
3196
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200     struct ElementInfo *ei = &element_info[element];
3201
3202     for (j = 0; j < ei->num_change_pages; j++)
3203     {
3204       int trigger_element = ei->change_page[j].initial_trigger_element;
3205
3206       if (trigger_element >= EL_PREV_CE_8 &&
3207           trigger_element <= EL_NEXT_CE_8)
3208         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3209
3210       ei->change_page[j].trigger_element = trigger_element;
3211     }
3212   }
3213
3214   // ---------- initialize run-time trigger player and element ----------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3219
3220     for (j = 0; j < ei->num_change_pages; j++)
3221     {
3222       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3223       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3224       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3225       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3226       ei->change_page[j].actual_trigger_ce_value = 0;
3227       ei->change_page[j].actual_trigger_ce_score = 0;
3228     }
3229   }
3230
3231   // ---------- initialize trigger events -------------------------------------
3232
3233   // initialize trigger events information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3236       trigger_events[i][j] = FALSE;
3237
3238   // add trigger events from element change event properties
3239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240   {
3241     struct ElementInfo *ei = &element_info[i];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       if (!ei->change_page[j].can_change_or_has_action)
3246         continue;
3247
3248       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3249       {
3250         int trigger_element = ei->change_page[j].trigger_element;
3251
3252         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3253         {
3254           if (ei->change_page[j].has_event[k])
3255           {
3256             if (IS_GROUP_ELEMENT(trigger_element))
3257             {
3258               struct ElementGroupInfo *group =
3259                 element_info[trigger_element].group;
3260
3261               for (l = 0; l < group->num_elements_resolved; l++)
3262                 trigger_events[group->element_resolved[l]][k] = TRUE;
3263             }
3264             else if (trigger_element == EL_ANY_ELEMENT)
3265               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3266                 trigger_events[l][k] = TRUE;
3267             else
3268               trigger_events[trigger_element][k] = TRUE;
3269           }
3270         }
3271       }
3272     }
3273   }
3274
3275   // ---------- initialize push delay -----------------------------------------
3276
3277   // initialize push delay values to default
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279   {
3280     if (!IS_CUSTOM_ELEMENT(i))
3281     {
3282       // set default push delay values (corrected since version 3.0.7-1)
3283       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3284       {
3285         element_info[i].push_delay_fixed = 2;
3286         element_info[i].push_delay_random = 8;
3287       }
3288       else
3289       {
3290         element_info[i].push_delay_fixed = 8;
3291         element_info[i].push_delay_random = 8;
3292       }
3293     }
3294   }
3295
3296   // set push delay value for certain elements from pre-defined list
3297   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3298   {
3299     int e = push_delay_list[i].element;
3300
3301     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3302     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3303   }
3304
3305   // set push delay value for Supaplex elements for newer engine versions
3306   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3307   {
3308     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     {
3310       if (IS_SP_ELEMENT(i))
3311       {
3312         // set SP push delay to just enough to push under a falling zonk
3313         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3314
3315         element_info[i].push_delay_fixed  = delay;
3316         element_info[i].push_delay_random = 0;
3317       }
3318     }
3319   }
3320
3321   // ---------- initialize move stepsize --------------------------------------
3322
3323   // initialize move stepsize values to default
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (!IS_CUSTOM_ELEMENT(i))
3326       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3327
3328   // set move stepsize value for certain elements from pre-defined list
3329   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3330   {
3331     int e = move_stepsize_list[i].element;
3332
3333     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3334
3335     // set move stepsize value for certain elements for older engine versions
3336     if (use_old_move_stepsize_for_magic_wall)
3337     {
3338       if (e == EL_MAGIC_WALL_FILLING ||
3339           e == EL_MAGIC_WALL_EMPTYING ||
3340           e == EL_BD_MAGIC_WALL_FILLING ||
3341           e == EL_BD_MAGIC_WALL_EMPTYING)
3342         element_info[e].move_stepsize *= 2;
3343     }
3344   }
3345
3346   // ---------- initialize collect score --------------------------------------
3347
3348   // initialize collect score values for custom elements from initial value
3349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     if (IS_CUSTOM_ELEMENT(i))
3351       element_info[i].collect_score = element_info[i].collect_score_initial;
3352
3353   // ---------- initialize collect count --------------------------------------
3354
3355   // initialize collect count values for non-custom elements
3356   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357     if (!IS_CUSTOM_ELEMENT(i))
3358       element_info[i].collect_count_initial = 0;
3359
3360   // add collect count values for all elements from pre-defined list
3361   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3362     element_info[collect_count_list[i].element].collect_count_initial =
3363       collect_count_list[i].count;
3364
3365   // ---------- initialize access direction -----------------------------------
3366
3367   // initialize access direction values to default (access from every side)
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (!IS_CUSTOM_ELEMENT(i))
3370       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3371
3372   // set access direction value for certain elements from pre-defined list
3373   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3374     element_info[access_direction_list[i].element].access_direction =
3375       access_direction_list[i].direction;
3376
3377   // ---------- initialize explosion content ----------------------------------
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (IS_CUSTOM_ELEMENT(i))
3381       continue;
3382
3383     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3384     {
3385       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3386
3387       element_info[i].content.e[x][y] =
3388         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3389          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3390          i == EL_PLAYER_3 ? EL_EMERALD :
3391          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3392          i == EL_MOLE ? EL_EMERALD_RED :
3393          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3394          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3395          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3396          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3397          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3398          i == EL_WALL_EMERALD ? EL_EMERALD :
3399          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3400          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3401          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3402          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3403          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3404          i == EL_WALL_PEARL ? EL_PEARL :
3405          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3406          EL_EMPTY);
3407     }
3408   }
3409
3410   // ---------- initialize recursion detection --------------------------------
3411   recursion_loop_depth = 0;
3412   recursion_loop_detected = FALSE;
3413   recursion_loop_element = EL_UNDEFINED;
3414
3415   // ---------- initialize graphics engine ------------------------------------
3416   game.scroll_delay_value =
3417     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3418      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3419      !setup.forced_scroll_delay           ? 0 :
3420      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3421   game.scroll_delay_value =
3422     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3423
3424   // ---------- initialize game engine snapshots ------------------------------
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426     game.snapshot.last_action[i] = 0;
3427   game.snapshot.changed_action = FALSE;
3428   game.snapshot.collected_item = FALSE;
3429   game.snapshot.mode =
3430     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3431      SNAPSHOT_MODE_EVERY_STEP :
3432      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3433      SNAPSHOT_MODE_EVERY_MOVE :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3435      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3436   game.snapshot.save_snapshot = FALSE;
3437
3438   // ---------- initialize level time for Supaplex engine ---------------------
3439   // Supaplex levels with time limit currently unsupported -- should be added
3440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3441     level.time = 0;
3442
3443   // ---------- initialize flags for handling game actions --------------------
3444
3445   // set flags for game actions to default values
3446   game.use_key_actions = TRUE;
3447   game.use_mouse_actions = FALSE;
3448
3449   // when using Mirror Magic game engine, handle mouse events only
3450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3451   {
3452     game.use_key_actions = FALSE;
3453     game.use_mouse_actions = TRUE;
3454   }
3455
3456   // check for custom elements with mouse click events
3457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3458   {
3459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3460     {
3461       int element = EL_CUSTOM_START + i;
3462
3463       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3464           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3465           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3466           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3467         game.use_mouse_actions = TRUE;
3468     }
3469   }
3470 }
3471
3472 static int get_num_special_action(int element, int action_first,
3473                                   int action_last)
3474 {
3475   int num_special_action = 0;
3476   int i, j;
3477
3478   for (i = action_first; i <= action_last; i++)
3479   {
3480     boolean found = FALSE;
3481
3482     for (j = 0; j < NUM_DIRECTIONS; j++)
3483       if (el_act_dir2img(element, i, j) !=
3484           el_act_dir2img(element, ACTION_DEFAULT, j))
3485         found = TRUE;
3486
3487     if (found)
3488       num_special_action++;
3489     else
3490       break;
3491   }
3492
3493   return num_special_action;
3494 }
3495
3496
3497 // ============================================================================
3498 // InitGame()
3499 // ----------------------------------------------------------------------------
3500 // initialize and start new game
3501 // ============================================================================
3502
3503 #if DEBUG_INIT_PLAYER
3504 static void DebugPrintPlayerStatus(char *message)
3505 {
3506   int i;
3507
3508   if (!options.debug)
3509     return;
3510
3511   Debug("game:init:player", "%s:", message);
3512
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     Debug("game:init:player",
3518           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3519           i + 1,
3520           player->present,
3521           player->connected,
3522           player->connected_locally,
3523           player->connected_network,
3524           player->active,
3525           (local_player == player ? " (local player)" : ""));
3526   }
3527 }
3528 #endif
3529
3530 void InitGame(void)
3531 {
3532   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3533   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3534   int fade_mask = REDRAW_FIELD;
3535
3536   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3537   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3538   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3539   int initial_move_dir = MV_DOWN;
3540   int i, j, x, y;
3541
3542   // required here to update video display before fading (FIX THIS)
3543   DrawMaskedBorder(REDRAW_DOOR_2);
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548   SetGameStatus(GAME_MODE_PLAYING);
3549
3550   if (level_editor_test_game)
3551     FadeSkipNextFadeOut();
3552   else
3553     FadeSetEnterScreen();
3554
3555   if (CheckFadeAll())
3556     fade_mask = REDRAW_ALL;
3557
3558   FadeLevelSoundsAndMusic();
3559
3560   ExpireSoundLoops(TRUE);
3561
3562   FadeOut(fade_mask);
3563
3564   if (level_editor_test_game)
3565     FadeSkipNextFadeIn();
3566
3567   // needed if different viewport properties defined for playing
3568   ChangeViewportPropertiesIfNeeded();
3569
3570   ClearField();
3571
3572   DrawCompleteVideoDisplay();
3573
3574   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3575
3576   InitGameEngine();
3577   InitGameControlValues();
3578
3579   if (tape.recording)
3580   {
3581     // initialize tape actions from game when recording tape
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584
3585     // initialize visible playfield size when recording tape (for team mode)
3586     tape.scr_fieldx = SCR_FIELDX;
3587     tape.scr_fieldy = SCR_FIELDY;
3588   }
3589
3590   // don't play tapes over network
3591   network_playing = (network.enabled && !tape.playing);
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     struct PlayerInfo *player = &stored_player[i];
3596
3597     player->index_nr = i;
3598     player->index_bit = (1 << i);
3599     player->element_nr = EL_PLAYER_1 + i;
3600
3601     player->present = FALSE;
3602     player->active = FALSE;
3603     player->mapped = FALSE;
3604
3605     player->killed = FALSE;
3606     player->reanimated = FALSE;
3607     player->buried = FALSE;
3608
3609     player->action = 0;
3610     player->effective_action = 0;
3611     player->programmed_action = 0;
3612     player->snap_action = 0;
3613
3614     player->mouse_action.lx = 0;
3615     player->mouse_action.ly = 0;
3616     player->mouse_action.button = 0;
3617     player->mouse_action.button_hint = 0;
3618
3619     player->effective_mouse_action.lx = 0;
3620     player->effective_mouse_action.ly = 0;
3621     player->effective_mouse_action.button = 0;
3622     player->effective_mouse_action.button_hint = 0;
3623
3624     for (j = 0; j < MAX_NUM_KEYS; j++)
3625       player->key[j] = FALSE;
3626
3627     player->num_white_keys = 0;
3628
3629     player->dynabomb_count = 0;
3630     player->dynabomb_size = 1;
3631     player->dynabombs_left = 0;
3632     player->dynabomb_xl = FALSE;
3633
3634     player->MovDir = initial_move_dir;
3635     player->MovPos = 0;
3636     player->GfxPos = 0;
3637     player->GfxDir = initial_move_dir;
3638     player->GfxAction = ACTION_DEFAULT;
3639     player->Frame = 0;
3640     player->StepFrame = 0;
3641
3642     player->initial_element = player->element_nr;
3643     player->artwork_element =
3644       (level.use_artwork_element[i] ? level.artwork_element[i] :
3645        player->element_nr);
3646     player->use_murphy = FALSE;
3647
3648     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3649     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3650
3651     player->gravity = level.initial_player_gravity[i];
3652
3653     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3654
3655     player->actual_frame_counter = 0;
3656
3657     player->step_counter = 0;
3658
3659     player->last_move_dir = initial_move_dir;
3660
3661     player->is_active = FALSE;
3662
3663     player->is_waiting = FALSE;
3664     player->is_moving = FALSE;
3665     player->is_auto_moving = FALSE;
3666     player->is_digging = FALSE;
3667     player->is_snapping = FALSE;
3668     player->is_collecting = FALSE;
3669     player->is_pushing = FALSE;
3670     player->is_switching = FALSE;
3671     player->is_dropping = FALSE;
3672     player->is_dropping_pressed = FALSE;
3673
3674     player->is_bored = FALSE;
3675     player->is_sleeping = FALSE;
3676
3677     player->was_waiting = TRUE;
3678     player->was_moving = FALSE;
3679     player->was_snapping = FALSE;
3680     player->was_dropping = FALSE;
3681
3682     player->force_dropping = FALSE;
3683
3684     player->frame_counter_bored = -1;
3685     player->frame_counter_sleeping = -1;
3686
3687     player->anim_delay_counter = 0;
3688     player->post_delay_counter = 0;
3689
3690     player->dir_waiting = initial_move_dir;
3691     player->action_waiting = ACTION_DEFAULT;
3692     player->last_action_waiting = ACTION_DEFAULT;
3693     player->special_action_bored = ACTION_DEFAULT;
3694     player->special_action_sleeping = ACTION_DEFAULT;
3695
3696     player->switch_x = -1;
3697     player->switch_y = -1;
3698
3699     player->drop_x = -1;
3700     player->drop_y = -1;
3701
3702     player->show_envelope = 0;
3703
3704     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3705
3706     player->push_delay       = -1;      // initialized when pushing starts
3707     player->push_delay_value = game.initial_push_delay_value;
3708
3709     player->drop_delay = 0;
3710     player->drop_pressed_delay = 0;
3711
3712     player->last_jx = -1;
3713     player->last_jy = -1;
3714     player->jx = -1;
3715     player->jy = -1;
3716
3717     player->shield_normal_time_left = 0;
3718     player->shield_deadly_time_left = 0;
3719
3720     player->last_removed_element = EL_UNDEFINED;
3721
3722     player->inventory_infinite_element = EL_UNDEFINED;
3723     player->inventory_size = 0;
3724
3725     if (level.use_initial_inventory[i])
3726     {
3727       for (j = 0; j < level.initial_inventory_size[i]; j++)
3728       {
3729         int element = level.initial_inventory_content[i][j];
3730         int collect_count = element_info[element].collect_count_initial;
3731         int k;
3732
3733         if (!IS_CUSTOM_ELEMENT(element))
3734           collect_count = 1;
3735
3736         if (collect_count == 0)
3737           player->inventory_infinite_element = element;
3738         else
3739           for (k = 0; k < collect_count; k++)
3740             if (player->inventory_size < MAX_INVENTORY_SIZE)
3741               player->inventory_element[player->inventory_size++] = element;
3742       }
3743     }
3744
3745     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3746     SnapField(player, 0, 0);
3747
3748     map_player_action[i] = i;
3749   }
3750
3751   network_player_action_received = FALSE;
3752
3753   // initial null action
3754   if (network_playing)
3755     SendToServer_MovePlayer(MV_NONE);
3756
3757   FrameCounter = 0;
3758   TimeFrames = 0;
3759   TimePlayed = 0;
3760   TimeLeft = level.time;
3761   TapeTime = 0;
3762
3763   ScreenMovDir = MV_NONE;
3764   ScreenMovPos = 0;
3765   ScreenGfxPos = 0;
3766
3767   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3768
3769   game.robot_wheel_x = -1;
3770   game.robot_wheel_y = -1;
3771
3772   game.exit_x = -1;
3773   game.exit_y = -1;
3774
3775   game.all_players_gone = FALSE;
3776
3777   game.LevelSolved = FALSE;
3778   game.GameOver = FALSE;
3779
3780   game.GamePlayed = !tape.playing;
3781
3782   game.LevelSolved_GameWon = FALSE;
3783   game.LevelSolved_GameEnd = FALSE;
3784   game.LevelSolved_SaveTape = FALSE;
3785   game.LevelSolved_SaveScore = FALSE;
3786
3787   game.LevelSolved_CountingTime = 0;
3788   game.LevelSolved_CountingScore = 0;
3789   game.LevelSolved_CountingHealth = 0;
3790
3791   game.panel.active = TRUE;
3792
3793   game.no_time_limit = (level.time == 0);
3794
3795   game.yamyam_content_nr = 0;
3796   game.robot_wheel_active = FALSE;
3797   game.magic_wall_active = FALSE;
3798   game.magic_wall_time_left = 0;
3799   game.light_time_left = 0;
3800   game.timegate_time_left = 0;
3801   game.switchgate_pos = 0;
3802   game.wind_direction = level.wind_direction_initial;
3803
3804   game.score = 0;
3805   game.score_final = 0;
3806
3807   game.health = MAX_HEALTH;
3808   game.health_final = MAX_HEALTH;
3809
3810   game.gems_still_needed = level.gems_needed;
3811   game.sokoban_fields_still_needed = 0;
3812   game.sokoban_objects_still_needed = 0;
3813   game.lights_still_needed = 0;
3814   game.players_still_needed = 0;
3815   game.friends_still_needed = 0;
3816
3817   game.lenses_time_left = 0;
3818   game.magnify_time_left = 0;
3819
3820   game.ball_active = level.ball_active_initial;
3821   game.ball_content_nr = 0;
3822
3823   game.explosions_delayed = TRUE;
3824
3825   game.envelope_active = FALSE;
3826
3827   for (i = 0; i < NUM_BELTS; i++)
3828   {
3829     game.belt_dir[i] = MV_NONE;
3830     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3831   }
3832
3833   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3834     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3835
3836 #if DEBUG_INIT_PLAYER
3837   DebugPrintPlayerStatus("Player status at level initialization");
3838 #endif
3839
3840   SCAN_PLAYFIELD(x, y)
3841   {
3842     Tile[x][y] = Last[x][y] = level.field[x][y];
3843     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3844     ChangeDelay[x][y] = 0;
3845     ChangePage[x][y] = -1;
3846     CustomValue[x][y] = 0;              // initialized in InitField()
3847     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3848     AmoebaNr[x][y] = 0;
3849     WasJustMoving[x][y] = 0;
3850     WasJustFalling[x][y] = 0;
3851     CheckCollision[x][y] = 0;
3852     CheckImpact[x][y] = 0;
3853     Stop[x][y] = FALSE;
3854     Pushed[x][y] = FALSE;
3855
3856     ChangeCount[x][y] = 0;
3857     ChangeEvent[x][y] = -1;
3858
3859     ExplodePhase[x][y] = 0;
3860     ExplodeDelay[x][y] = 0;
3861     ExplodeField[x][y] = EX_TYPE_NONE;
3862
3863     RunnerVisit[x][y] = 0;
3864     PlayerVisit[x][y] = 0;
3865
3866     GfxFrame[x][y] = 0;
3867     GfxRandom[x][y] = INIT_GFX_RANDOM();
3868     GfxElement[x][y] = EL_UNDEFINED;
3869     GfxAction[x][y] = ACTION_DEFAULT;
3870     GfxDir[x][y] = MV_NONE;
3871     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3872   }
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3877       emulate_bd = FALSE;
3878     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3879       emulate_sb = FALSE;
3880     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3881       emulate_sp = FALSE;
3882
3883     InitField(x, y, TRUE);
3884
3885     ResetGfxAnimation(x, y);
3886   }
3887
3888   InitBeltMovement();
3889
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     struct PlayerInfo *player = &stored_player[i];
3893
3894     // set number of special actions for bored and sleeping animation
3895     player->num_special_action_bored =
3896       get_num_special_action(player->artwork_element,
3897                              ACTION_BORING_1, ACTION_BORING_LAST);
3898     player->num_special_action_sleeping =
3899       get_num_special_action(player->artwork_element,
3900                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3901   }
3902
3903   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3904                     emulate_sb ? EMU_SOKOBAN :
3905                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3906
3907   // initialize type of slippery elements
3908   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3909   {
3910     if (!IS_CUSTOM_ELEMENT(i))
3911     {
3912       // default: elements slip down either to the left or right randomly
3913       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3914
3915       // SP style elements prefer to slip down on the left side
3916       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3917         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3918
3919       // BD style elements prefer to slip down on the left side
3920       if (game.emulation == EMU_BOULDERDASH)
3921         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3922     }
3923   }
3924
3925   // initialize explosion and ignition delay
3926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3927   {
3928     if (!IS_CUSTOM_ELEMENT(i))
3929     {
3930       int num_phase = 8;
3931       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3932                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3933                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3934       int last_phase = (num_phase + 1) * delay;
3935       int half_phase = (num_phase / 2) * delay;
3936
3937       element_info[i].explosion_delay = last_phase - 1;
3938       element_info[i].ignition_delay = half_phase;
3939
3940       if (i == EL_BLACK_ORB)
3941         element_info[i].ignition_delay = 1;
3942     }
3943   }
3944
3945   // correct non-moving belts to start moving left
3946   for (i = 0; i < NUM_BELTS; i++)
3947     if (game.belt_dir[i] == MV_NONE)
3948       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3949
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951   // use preferred player also in local single-player mode
3952   if (!network.enabled && !game.team_mode)
3953   {
3954     int new_index_nr = setup.network_player_nr;
3955
3956     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3957     {
3958       for (i = 0; i < MAX_PLAYERS; i++)
3959         stored_player[i].connected_locally = FALSE;
3960
3961       stored_player[new_index_nr].connected_locally = TRUE;
3962     }
3963   }
3964
3965   for (i = 0; i < MAX_PLAYERS; i++)
3966   {
3967     stored_player[i].connected = FALSE;
3968
3969     // in network game mode, the local player might not be the first player
3970     if (stored_player[i].connected_locally)
3971       local_player = &stored_player[i];
3972   }
3973
3974   if (!network.enabled)
3975     local_player->connected = TRUE;
3976
3977   if (tape.playing)
3978   {
3979     for (i = 0; i < MAX_PLAYERS; i++)
3980       stored_player[i].connected = tape.player_participates[i];
3981   }
3982   else if (network.enabled)
3983   {
3984     // add team mode players connected over the network (needed for correct
3985     // assignment of player figures from level to locally playing players)
3986
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988       if (stored_player[i].connected_network)
3989         stored_player[i].connected = TRUE;
3990   }
3991   else if (game.team_mode)
3992   {
3993     // try to guess locally connected team mode players (needed for correct
3994     // assignment of player figures from level to locally playing players)
3995
3996     for (i = 0; i < MAX_PLAYERS; i++)
3997       if (setup.input[i].use_joystick ||
3998           setup.input[i].key.left != KSYM_UNDEFINED)
3999         stored_player[i].connected = TRUE;
4000   }
4001
4002 #if DEBUG_INIT_PLAYER
4003   DebugPrintPlayerStatus("Player status after level initialization");
4004 #endif
4005
4006 #if DEBUG_INIT_PLAYER
4007   Debug("game:init:player", "Reassigning players ...");
4008 #endif
4009
4010   // check if any connected player was not found in playfield
4011   for (i = 0; i < MAX_PLAYERS; i++)
4012   {
4013     struct PlayerInfo *player = &stored_player[i];
4014
4015     if (player->connected && !player->present)
4016     {
4017       struct PlayerInfo *field_player = NULL;
4018
4019 #if DEBUG_INIT_PLAYER
4020       Debug("game:init:player",
4021             "- looking for field player for player %d ...", i + 1);
4022 #endif
4023
4024       // assign first free player found that is present in the playfield
4025
4026       // first try: look for unmapped playfield player that is not connected
4027       for (j = 0; j < MAX_PLAYERS; j++)
4028         if (field_player == NULL &&
4029             stored_player[j].present &&
4030             !stored_player[j].mapped &&
4031             !stored_player[j].connected)
4032           field_player = &stored_player[j];
4033
4034       // second try: look for *any* unmapped playfield player
4035       for (j = 0; j < MAX_PLAYERS; j++)
4036         if (field_player == NULL &&
4037             stored_player[j].present &&
4038             !stored_player[j].mapped)
4039           field_player = &stored_player[j];
4040
4041       if (field_player != NULL)
4042       {
4043         int jx = field_player->jx, jy = field_player->jy;
4044
4045 #if DEBUG_INIT_PLAYER
4046         Debug("game:init:player", "- found player %d",
4047               field_player->index_nr + 1);
4048 #endif
4049
4050         player->present = FALSE;
4051         player->active = FALSE;
4052
4053         field_player->present = TRUE;
4054         field_player->active = TRUE;
4055
4056         /*
4057         player->initial_element = field_player->initial_element;
4058         player->artwork_element = field_player->artwork_element;
4059
4060         player->block_last_field       = field_player->block_last_field;
4061         player->block_delay_adjustment = field_player->block_delay_adjustment;
4062         */
4063
4064         StorePlayer[jx][jy] = field_player->element_nr;
4065
4066         field_player->jx = field_player->last_jx = jx;
4067         field_player->jy = field_player->last_jy = jy;
4068
4069         if (local_player == player)
4070           local_player = field_player;
4071
4072         map_player_action[field_player->index_nr] = i;
4073
4074         field_player->mapped = TRUE;
4075
4076 #if DEBUG_INIT_PLAYER
4077         Debug("game:init:player", "- map_player_action[%d] == %d",
4078               field_player->index_nr + 1, i + 1);
4079 #endif
4080       }
4081     }
4082
4083     if (player->connected && player->present)
4084       player->mapped = TRUE;
4085   }
4086
4087 #if DEBUG_INIT_PLAYER
4088   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4089 #endif
4090
4091 #else
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       for (j = 0; j < MAX_PLAYERS; j++)
4101       {
4102         struct PlayerInfo *field_player = &stored_player[j];
4103         int jx = field_player->jx, jy = field_player->jy;
4104
4105         // assign first free player found that is present in the playfield
4106         if (field_player->present && !field_player->connected)
4107         {
4108           player->present = TRUE;
4109           player->active = TRUE;
4110
4111           field_player->present = FALSE;
4112           field_player->active = FALSE;
4113
4114           player->initial_element = field_player->initial_element;
4115           player->artwork_element = field_player->artwork_element;
4116
4117           player->block_last_field       = field_player->block_last_field;
4118           player->block_delay_adjustment = field_player->block_delay_adjustment;
4119
4120           StorePlayer[jx][jy] = player->element_nr;
4121
4122           player->jx = player->last_jx = jx;
4123           player->jy = player->last_jy = jy;
4124
4125           break;
4126         }
4127       }
4128     }
4129   }
4130 #endif
4131
4132 #if 0
4133   Debug("game:init:player", "local_player->present == %d",
4134         local_player->present);
4135 #endif
4136
4137   // set focus to local player for network games, else to all players
4138   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4139   game.centered_player_nr_next = game.centered_player_nr;
4140   game.set_centered_player = FALSE;
4141   game.set_centered_player_wrap = FALSE;
4142
4143   if (network_playing && tape.recording)
4144   {
4145     // store client dependent player focus when recording network games
4146     tape.centered_player_nr_next = game.centered_player_nr_next;
4147     tape.set_centered_player = TRUE;
4148   }
4149
4150   if (tape.playing)
4151   {
4152     // when playing a tape, eliminate all players who do not participate
4153
4154 #if USE_NEW_PLAYER_ASSIGNMENTS
4155
4156     if (!game.team_mode)
4157     {
4158       for (i = 0; i < MAX_PLAYERS; i++)
4159       {
4160         if (stored_player[i].active &&
4161             !tape.player_participates[map_player_action[i]])
4162         {
4163           struct PlayerInfo *player = &stored_player[i];
4164           int jx = player->jx, jy = player->jy;
4165
4166 #if DEBUG_INIT_PLAYER
4167           Debug("game:init:player", "Removing player %d at (%d, %d)",
4168                 i + 1, jx, jy);
4169 #endif
4170
4171           player->active = FALSE;
4172           StorePlayer[jx][jy] = 0;
4173           Tile[jx][jy] = EL_EMPTY;
4174         }
4175       }
4176     }
4177
4178 #else
4179
4180     for (i = 0; i < MAX_PLAYERS; i++)
4181     {
4182       if (stored_player[i].active &&
4183           !tape.player_participates[i])
4184       {
4185         struct PlayerInfo *player = &stored_player[i];
4186         int jx = player->jx, jy = player->jy;
4187
4188         player->active = FALSE;
4189         StorePlayer[jx][jy] = 0;
4190         Tile[jx][jy] = EL_EMPTY;
4191       }
4192     }
4193 #endif
4194   }
4195   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4196   {
4197     // when in single player mode, eliminate all but the local player
4198
4199     for (i = 0; i < MAX_PLAYERS; i++)
4200     {
4201       struct PlayerInfo *player = &stored_player[i];
4202
4203       if (player->active && player != local_player)
4204       {
4205         int jx = player->jx, jy = player->jy;
4206
4207         player->active = FALSE;
4208         player->present = FALSE;
4209
4210         StorePlayer[jx][jy] = 0;
4211         Tile[jx][jy] = EL_EMPTY;
4212       }
4213     }
4214   }
4215
4216   for (i = 0; i < MAX_PLAYERS; i++)
4217     if (stored_player[i].active)
4218       game.players_still_needed++;
4219
4220   if (level.solved_by_one_player)
4221     game.players_still_needed = 1;
4222
4223   // when recording the game, store which players take part in the game
4224   if (tape.recording)
4225   {
4226 #if USE_NEW_PLAYER_ASSIGNMENTS
4227     for (i = 0; i < MAX_PLAYERS; i++)
4228       if (stored_player[i].connected)
4229         tape.player_participates[i] = TRUE;
4230 #else
4231     for (i = 0; i < MAX_PLAYERS; i++)
4232       if (stored_player[i].active)
4233         tape.player_participates[i] = TRUE;
4234 #endif
4235   }
4236
4237 #if DEBUG_INIT_PLAYER
4238   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4239 #endif
4240
4241   if (BorderElement == EL_EMPTY)
4242   {
4243     SBX_Left = 0;
4244     SBX_Right = lev_fieldx - SCR_FIELDX;
4245     SBY_Upper = 0;
4246     SBY_Lower = lev_fieldy - SCR_FIELDY;
4247   }
4248   else
4249   {
4250     SBX_Left = -1;
4251     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4252     SBY_Upper = -1;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4254   }
4255
4256   if (full_lev_fieldx <= SCR_FIELDX)
4257     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4258   if (full_lev_fieldy <= SCR_FIELDY)
4259     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4260
4261   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4262     SBX_Left--;
4263   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4264     SBY_Upper--;
4265
4266   // if local player not found, look for custom element that might create
4267   // the player (make some assumptions about the right custom element)
4268   if (!local_player->present)
4269   {
4270     int start_x = 0, start_y = 0;
4271     int found_rating = 0;
4272     int found_element = EL_UNDEFINED;
4273     int player_nr = local_player->index_nr;
4274
4275     SCAN_PLAYFIELD(x, y)
4276     {
4277       int element = Tile[x][y];
4278       int content;
4279       int xx, yy;
4280       boolean is_player;
4281
4282       if (level.use_start_element[player_nr] &&
4283           level.start_element[player_nr] == element &&
4284           found_rating < 4)
4285       {
4286         start_x = x;
4287         start_y = y;
4288
4289         found_rating = 4;
4290         found_element = element;
4291       }
4292
4293       if (!IS_CUSTOM_ELEMENT(element))
4294         continue;
4295
4296       if (CAN_CHANGE(element))
4297       {
4298         for (i = 0; i < element_info[element].num_change_pages; i++)
4299         {
4300           // check for player created from custom element as single target
4301           content = element_info[element].change_page[i].target_element;
4302           is_player = ELEM_IS_PLAYER(content);
4303
4304           if (is_player && (found_rating < 3 ||
4305                             (found_rating == 3 && element < found_element)))
4306           {
4307             start_x = x;
4308             start_y = y;
4309
4310             found_rating = 3;
4311             found_element = element;
4312           }
4313         }
4314       }
4315
4316       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4317       {
4318         // check for player created from custom element as explosion content
4319         content = element_info[element].content.e[xx][yy];
4320         is_player = ELEM_IS_PLAYER(content);
4321
4322         if (is_player && (found_rating < 2 ||
4323                           (found_rating == 2 && element < found_element)))
4324         {
4325           start_x = x + xx - 1;
4326           start_y = y + yy - 1;
4327
4328           found_rating = 2;
4329           found_element = element;
4330         }
4331
4332         if (!CAN_CHANGE(element))
4333           continue;
4334
4335         for (i = 0; i < element_info[element].num_change_pages; i++)
4336         {
4337           // check for player created from custom element as extended target
4338           content =
4339             element_info[element].change_page[i].target_content.e[xx][yy];
4340
4341           is_player = ELEM_IS_PLAYER(content);
4342
4343           if (is_player && (found_rating < 1 ||
4344                             (found_rating == 1 && element < found_element)))
4345           {
4346             start_x = x + xx - 1;
4347             start_y = y + yy - 1;
4348
4349             found_rating = 1;
4350             found_element = element;
4351           }
4352         }
4353       }
4354     }
4355
4356     scroll_x = SCROLL_POSITION_X(start_x);
4357     scroll_y = SCROLL_POSITION_Y(start_y);
4358   }
4359   else
4360   {
4361     scroll_x = SCROLL_POSITION_X(local_player->jx);
4362     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4363   }
4364
4365   // !!! FIX THIS (START) !!!
4366   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4367   {
4368     InitGameEngine_EM();
4369   }
4370   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4371   {
4372     InitGameEngine_SP();
4373   }
4374   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4375   {
4376     InitGameEngine_MM();
4377   }
4378   else
4379   {
4380     DrawLevel(REDRAW_FIELD);
4381     DrawAllPlayers();
4382
4383     // after drawing the level, correct some elements
4384     if (game.timegate_time_left == 0)
4385       CloseAllOpenTimegates();
4386   }
4387
4388   // blit playfield from scroll buffer to normal back buffer for fading in
4389   BlitScreenToBitmap(backbuffer);
4390   // !!! FIX THIS (END) !!!
4391
4392   DrawMaskedBorder(fade_mask);
4393
4394   FadeIn(fade_mask);
4395
4396 #if 1
4397   // full screen redraw is required at this point in the following cases:
4398   // - special editor door undrawn when game was started from level editor
4399   // - drawing area (playfield) was changed and has to be removed completely
4400   redraw_mask = REDRAW_ALL;
4401   BackToFront();
4402 #endif
4403
4404   if (!game.restart_level)
4405   {
4406     // copy default game door content to main double buffer
4407
4408     // !!! CHECK AGAIN !!!
4409     SetPanelBackground();
4410     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4411     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4412   }
4413
4414   SetPanelBackground();
4415   SetDrawBackgroundMask(REDRAW_DOOR_1);
4416
4417   UpdateAndDisplayGameControlValues();
4418
4419   if (!game.restart_level)
4420   {
4421     UnmapGameButtons();
4422     UnmapTapeButtons();
4423
4424     FreeGameButtons();
4425     CreateGameButtons();
4426
4427     MapGameButtons();
4428     MapTapeButtons();
4429
4430     // copy actual game door content to door double buffer for OpenDoor()
4431     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4432
4433     OpenDoor(DOOR_OPEN_ALL);
4434
4435     KeyboardAutoRepeatOffUnlessAutoplay();
4436
4437 #if DEBUG_INIT_PLAYER
4438     DebugPrintPlayerStatus("Player status (final)");
4439 #endif
4440   }
4441
4442   UnmapAllGadgets();
4443
4444   MapGameButtons();
4445   MapTapeButtons();
4446
4447   if (!game.restart_level && !tape.playing)
4448   {
4449     LevelStats_incPlayed(level_nr);
4450
4451     SaveLevelSetup_SeriesInfo();
4452   }
4453
4454   game.restart_level = FALSE;
4455   game.restart_game_message = NULL;
4456
4457   game.request_active = FALSE;
4458   game.request_active_or_moving = FALSE;
4459
4460   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4461     InitGameActions_MM();
4462
4463   SaveEngineSnapshotToListInitial();
4464
4465   if (!game.restart_level)
4466   {
4467     PlaySound(SND_GAME_STARTING);
4468
4469     if (setup.sound_music)
4470       PlayLevelMusic();
4471   }
4472 }
4473
4474 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4475                         int actual_player_x, int actual_player_y)
4476 {
4477   // this is used for non-R'n'D game engines to update certain engine values
4478
4479   // needed to determine if sounds are played within the visible screen area
4480   scroll_x = actual_scroll_x;
4481   scroll_y = actual_scroll_y;
4482
4483   // needed to get player position for "follow finger" playing input method
4484   local_player->jx = actual_player_x;
4485   local_player->jy = actual_player_y;
4486 }
4487
4488 void InitMovDir(int x, int y)
4489 {
4490   int i, element = Tile[x][y];
4491   static int xy[4][2] =
4492   {
4493     {  0, +1 },
4494     { +1,  0 },
4495     {  0, -1 },
4496     { -1,  0 }
4497   };
4498   static int direction[3][4] =
4499   {
4500     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4501     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4502     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4503   };
4504
4505   switch (element)
4506   {
4507     case EL_BUG_RIGHT:
4508     case EL_BUG_UP:
4509     case EL_BUG_LEFT:
4510     case EL_BUG_DOWN:
4511       Tile[x][y] = EL_BUG;
4512       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4513       break;
4514
4515     case EL_SPACESHIP_RIGHT:
4516     case EL_SPACESHIP_UP:
4517     case EL_SPACESHIP_LEFT:
4518     case EL_SPACESHIP_DOWN:
4519       Tile[x][y] = EL_SPACESHIP;
4520       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4521       break;
4522
4523     case EL_BD_BUTTERFLY_RIGHT:
4524     case EL_BD_BUTTERFLY_UP:
4525     case EL_BD_BUTTERFLY_LEFT:
4526     case EL_BD_BUTTERFLY_DOWN:
4527       Tile[x][y] = EL_BD_BUTTERFLY;
4528       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4529       break;
4530
4531     case EL_BD_FIREFLY_RIGHT:
4532     case EL_BD_FIREFLY_UP:
4533     case EL_BD_FIREFLY_LEFT:
4534     case EL_BD_FIREFLY_DOWN:
4535       Tile[x][y] = EL_BD_FIREFLY;
4536       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4537       break;
4538
4539     case EL_PACMAN_RIGHT:
4540     case EL_PACMAN_UP:
4541     case EL_PACMAN_LEFT:
4542     case EL_PACMAN_DOWN:
4543       Tile[x][y] = EL_PACMAN;
4544       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4545       break;
4546
4547     case EL_YAMYAM_LEFT:
4548     case EL_YAMYAM_RIGHT:
4549     case EL_YAMYAM_UP:
4550     case EL_YAMYAM_DOWN:
4551       Tile[x][y] = EL_YAMYAM;
4552       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4553       break;
4554
4555     case EL_SP_SNIKSNAK:
4556       MovDir[x][y] = MV_UP;
4557       break;
4558
4559     case EL_SP_ELECTRON:
4560       MovDir[x][y] = MV_LEFT;
4561       break;
4562
4563     case EL_MOLE_LEFT:
4564     case EL_MOLE_RIGHT:
4565     case EL_MOLE_UP:
4566     case EL_MOLE_DOWN:
4567       Tile[x][y] = EL_MOLE;
4568       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4569       break;
4570
4571     case EL_SPRING_LEFT:
4572     case EL_SPRING_RIGHT:
4573       Tile[x][y] = EL_SPRING;
4574       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4575       break;
4576
4577     default:
4578       if (IS_CUSTOM_ELEMENT(element))
4579       {
4580         struct ElementInfo *ei = &element_info[element];
4581         int move_direction_initial = ei->move_direction_initial;
4582         int move_pattern = ei->move_pattern;
4583
4584         if (move_direction_initial == MV_START_PREVIOUS)
4585         {
4586           if (MovDir[x][y] != MV_NONE)
4587             return;
4588
4589           move_direction_initial = MV_START_AUTOMATIC;
4590         }
4591
4592         if (move_direction_initial == MV_START_RANDOM)
4593           MovDir[x][y] = 1 << RND(4);
4594         else if (move_direction_initial & MV_ANY_DIRECTION)
4595           MovDir[x][y] = move_direction_initial;
4596         else if (move_pattern == MV_ALL_DIRECTIONS ||
4597                  move_pattern == MV_TURNING_LEFT ||
4598                  move_pattern == MV_TURNING_RIGHT ||
4599                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4600                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4601                  move_pattern == MV_TURNING_RANDOM)
4602           MovDir[x][y] = 1 << RND(4);
4603         else if (move_pattern == MV_HORIZONTAL)
4604           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4605         else if (move_pattern == MV_VERTICAL)
4606           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4607         else if (move_pattern & MV_ANY_DIRECTION)
4608           MovDir[x][y] = element_info[element].move_pattern;
4609         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4610                  move_pattern == MV_ALONG_RIGHT_SIDE)
4611         {
4612           // use random direction as default start direction
4613           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4614             MovDir[x][y] = 1 << RND(4);
4615
4616           for (i = 0; i < NUM_DIRECTIONS; i++)
4617           {
4618             int x1 = x + xy[i][0];
4619             int y1 = y + xy[i][1];
4620
4621             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4622             {
4623               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4624                 MovDir[x][y] = direction[0][i];
4625               else
4626                 MovDir[x][y] = direction[1][i];
4627
4628               break;
4629             }
4630           }
4631         }                
4632       }
4633       else
4634       {
4635         MovDir[x][y] = 1 << RND(4);
4636
4637         if (element != EL_BUG &&
4638             element != EL_SPACESHIP &&
4639             element != EL_BD_BUTTERFLY &&
4640             element != EL_BD_FIREFLY)
4641           break;
4642
4643         for (i = 0; i < NUM_DIRECTIONS; i++)
4644         {
4645           int x1 = x + xy[i][0];
4646           int y1 = y + xy[i][1];
4647
4648           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4649           {
4650             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4651             {
4652               MovDir[x][y] = direction[0][i];
4653               break;
4654             }
4655             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4656                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4657             {
4658               MovDir[x][y] = direction[1][i];
4659               break;
4660             }
4661           }
4662         }
4663       }
4664       break;
4665   }
4666
4667   GfxDir[x][y] = MovDir[x][y];
4668 }
4669
4670 void InitAmoebaNr(int x, int y)
4671 {
4672   int i;
4673   int group_nr = AmoebaNeighbourNr(x, y);
4674
4675   if (group_nr == 0)
4676   {
4677     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4678     {
4679       if (AmoebaCnt[i] == 0)
4680       {
4681         group_nr = i;
4682         break;
4683       }
4684     }
4685   }
4686
4687   AmoebaNr[x][y] = group_nr;
4688   AmoebaCnt[group_nr]++;
4689   AmoebaCnt2[group_nr]++;
4690 }
4691
4692 static void LevelSolved(void)
4693 {
4694   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4695       game.players_still_needed > 0)
4696     return;
4697
4698   game.LevelSolved = TRUE;
4699   game.GameOver = TRUE;
4700
4701   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4702                       game_em.lev->score :
4703                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4704                       game_mm.score :
4705                       game.score);
4706   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4707                        MM_HEALTH(game_mm.laser_overload_value) :
4708                        game.health);
4709
4710   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4711   game.LevelSolved_CountingScore = game.score_final;
4712   game.LevelSolved_CountingHealth = game.health_final;
4713 }
4714
4715 void GameWon(void)
4716 {
4717   static int time_count_steps;
4718   static int time, time_final;
4719   static float score, score_final; // needed for time score < 10 for 10 seconds
4720   static int health, health_final;
4721   static int game_over_delay_1 = 0;
4722   static int game_over_delay_2 = 0;
4723   static int game_over_delay_3 = 0;
4724   int game_over_delay_value_1 = 50;
4725   int game_over_delay_value_2 = 25;
4726   int game_over_delay_value_3 = 50;
4727   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4728   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4729
4730   if (!game.LevelSolved_GameWon)
4731   {
4732     int i;
4733
4734     // do not start end game actions before the player stops moving (to exit)
4735     if (local_player->active && local_player->MovPos)
4736       return;
4737
4738     game.LevelSolved_GameWon = TRUE;
4739     game.LevelSolved_SaveTape = tape.recording;
4740     game.LevelSolved_SaveScore = !tape.playing;
4741
4742     if (!tape.playing)
4743     {
4744       LevelStats_incSolved(level_nr);
4745
4746       SaveLevelSetup_SeriesInfo();
4747     }
4748
4749     if (tape.auto_play)         // tape might already be stopped here
4750       tape.auto_play_level_solved = TRUE;
4751
4752     TapeStop();
4753
4754     game_over_delay_1 = 0;
4755     game_over_delay_2 = 0;
4756     game_over_delay_3 = game_over_delay_value_3;
4757
4758     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4759     score = score_final = game.score_final;
4760     health = health_final = game.health_final;
4761
4762     if (time_score > 0)
4763     {
4764       int time_frames = 0;
4765
4766       if (TimeLeft > 0)
4767       {
4768         time_final = 0;
4769         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4770       }
4771       else if (game.no_time_limit && TimePlayed < 999)
4772       {
4773         time_final = 999;
4774         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4775       }
4776
4777       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4778
4779       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4780
4781       game_over_delay_1 = game_over_delay_value_1;
4782
4783       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4784       {
4785         health_final = 0;
4786         score_final += health * time_score;
4787
4788         game_over_delay_2 = game_over_delay_value_2;
4789       }
4790
4791       game.score_final = score_final;
4792       game.health_final = health_final;
4793     }
4794
4795     if (level_editor_test_game)
4796     {
4797       time = time_final;
4798       score = score_final;
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
4809     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4810     {
4811       // check if last player has left the level
4812       if (game.exit_x >= 0 &&
4813           game.exit_y >= 0)
4814       {
4815         int x = game.exit_x;
4816         int y = game.exit_y;
4817         int element = Tile[x][y];
4818
4819         // close exit door after last player
4820         if ((game.all_players_gone &&
4821              (element == EL_EXIT_OPEN ||
4822               element == EL_SP_EXIT_OPEN ||
4823               element == EL_STEEL_EXIT_OPEN)) ||
4824             element == EL_EM_EXIT_OPEN ||
4825             element == EL_EM_STEEL_EXIT_OPEN)
4826         {
4827
4828           Tile[x][y] =
4829             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4830              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4831              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4832              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4833              EL_EM_STEEL_EXIT_CLOSING);
4834
4835           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4836         }
4837
4838         // player disappears
4839         DrawLevelField(x, y);
4840       }
4841
4842       for (i = 0; i < MAX_PLAYERS; i++)
4843       {
4844         struct PlayerInfo *player = &stored_player[i];
4845
4846         if (player->present)
4847         {
4848           RemovePlayer(player);
4849
4850           // player disappears
4851           DrawLevelField(player->jx, player->jy);
4852         }
4853       }
4854     }
4855
4856     PlaySound(SND_GAME_WINNING);
4857   }
4858
4859   if (game_over_delay_1 > 0)
4860   {
4861     game_over_delay_1--;
4862
4863     return;
4864   }
4865
4866   if (time != time_final)
4867   {
4868     int time_to_go = ABS(time_final - time);
4869     int time_count_dir = (time < time_final ? +1 : -1);
4870
4871     if (time_to_go < time_count_steps)
4872       time_count_steps = 1;
4873
4874     time  += time_count_steps * time_count_dir;
4875     score += time_count_steps * time_score;
4876
4877     // set final score to correct rounding differences after counting score
4878     if (time == time_final)
4879       score = score_final;
4880
4881     game.LevelSolved_CountingTime = time;
4882     game.LevelSolved_CountingScore = score;
4883
4884     game_panel_controls[GAME_PANEL_TIME].value = time;
4885     game_panel_controls[GAME_PANEL_SCORE].value = score;
4886
4887     DisplayGameControlValues();
4888
4889     if (time == time_final)
4890       StopSound(SND_GAME_LEVELTIME_BONUS);
4891     else if (setup.sound_loops)
4892       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4893     else
4894       PlaySound(SND_GAME_LEVELTIME_BONUS);
4895
4896     return;
4897   }
4898
4899   if (game_over_delay_2 > 0)
4900   {
4901     game_over_delay_2--;
4902
4903     return;
4904   }
4905
4906   if (health != health_final)
4907   {
4908     int health_count_dir = (health < health_final ? +1 : -1);
4909
4910     health += health_count_dir;
4911     score  += time_score;
4912
4913     game.LevelSolved_CountingHealth = health;
4914     game.LevelSolved_CountingScore = score;
4915
4916     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4917     game_panel_controls[GAME_PANEL_SCORE].value = score;
4918
4919     DisplayGameControlValues();
4920
4921     if (health == health_final)
4922       StopSound(SND_GAME_LEVELTIME_BONUS);
4923     else if (setup.sound_loops)
4924       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4925     else
4926       PlaySound(SND_GAME_LEVELTIME_BONUS);
4927
4928     return;
4929   }
4930
4931   game.panel.active = FALSE;
4932
4933   if (game_over_delay_3 > 0)
4934   {
4935     game_over_delay_3--;
4936
4937     return;
4938   }
4939
4940   GameEnd();
4941 }
4942
4943 void GameEnd(void)
4944 {
4945   // used instead of "level_nr" (needed for network games)
4946   int last_level_nr = levelset.level_nr;
4947   int hi_pos;
4948
4949   game.LevelSolved_GameEnd = TRUE;
4950
4951   if (game.LevelSolved_SaveTape)
4952   {
4953     // make sure that request dialog to save tape does not open door again
4954     if (!global.use_envelope_request)
4955       CloseDoor(DOOR_CLOSE_1);
4956
4957     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4958   }
4959
4960   // if no tape is to be saved, close both doors simultaneously
4961   CloseDoor(DOOR_CLOSE_ALL);
4962
4963   if (level_editor_test_game)
4964   {
4965     SetGameStatus(GAME_MODE_MAIN);
4966
4967     DrawMainMenu();
4968
4969     return;
4970   }
4971
4972   if (!game.LevelSolved_SaveScore)
4973   {
4974     SetGameStatus(GAME_MODE_MAIN);
4975
4976     DrawMainMenu();
4977
4978     return;
4979   }
4980
4981   if (level_nr == leveldir_current->handicap_level)
4982   {
4983     leveldir_current->handicap_level++;
4984
4985     SaveLevelSetup_SeriesInfo();
4986   }
4987
4988   if (setup.increment_levels &&
4989       level_nr < leveldir_current->last_level &&
4990       !network_playing)
4991   {
4992     level_nr++;         // advance to next level
4993     TapeErase();        // start with empty tape
4994
4995     if (setup.auto_play_next_level)
4996     {
4997       LoadLevel(level_nr);
4998
4999       SaveLevelSetup_SeriesInfo();
5000     }
5001   }
5002
5003   hi_pos = NewHiScore(last_level_nr);
5004
5005   if (hi_pos >= 0 && !setup.skip_scores_after_game)
5006   {
5007     SetGameStatus(GAME_MODE_SCORES);
5008
5009     DrawHallOfFame(last_level_nr, hi_pos);
5010   }
5011   else if (setup.auto_play_next_level && setup.increment_levels &&
5012            last_level_nr < leveldir_current->last_level &&
5013            !network_playing)
5014   {
5015     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5016   }
5017   else
5018   {
5019     SetGameStatus(GAME_MODE_MAIN);
5020
5021     DrawMainMenu();
5022   }
5023 }
5024
5025 int NewHiScore(int level_nr)
5026 {
5027   int k, l;
5028   int position = -1;
5029   boolean one_score_entry_per_name = !program.many_scores_per_name;
5030
5031   LoadScore(level_nr);
5032
5033   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5034       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5035     return -1;
5036
5037   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5038   {
5039     if (game.score_final > highscore[k].Score)
5040     {
5041       // player has made it to the hall of fame
5042
5043       if (k < MAX_SCORE_ENTRIES - 1)
5044       {
5045         int m = MAX_SCORE_ENTRIES - 1;
5046
5047         if (one_score_entry_per_name)
5048         {
5049           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5050             if (strEqual(setup.player_name, highscore[l].Name))
5051               m = l;
5052
5053           if (m == k)   // player's new highscore overwrites his old one
5054             goto put_into_list;
5055         }
5056
5057         for (l = m; l > k; l--)
5058         {
5059           strcpy(highscore[l].Name, highscore[l - 1].Name);
5060           highscore[l].Score = highscore[l - 1].Score;
5061         }
5062       }
5063
5064       put_into_list:
5065
5066       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5067       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5068       highscore[k].Score = game.score_final;
5069       position = k;
5070
5071       break;
5072     }
5073     else if (one_score_entry_per_name &&
5074              !strncmp(setup.player_name, highscore[k].Name,
5075                       MAX_PLAYER_NAME_LEN))
5076       break;    // player already there with a higher score
5077   }
5078
5079   if (position >= 0) 
5080     SaveScore(level_nr);
5081
5082   return position;
5083 }
5084
5085 static int getElementMoveStepsizeExt(int x, int y, int direction)
5086 {
5087   int element = Tile[x][y];
5088   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5089   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5090   int horiz_move = (dx != 0);
5091   int sign = (horiz_move ? dx : dy);
5092   int step = sign * element_info[element].move_stepsize;
5093
5094   // special values for move stepsize for spring and things on conveyor belt
5095   if (horiz_move)
5096   {
5097     if (CAN_FALL(element) &&
5098         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5099       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5100     else if (element == EL_SPRING)
5101       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5102   }
5103
5104   return step;
5105 }
5106
5107 static int getElementMoveStepsize(int x, int y)
5108 {
5109   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5110 }
5111
5112 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5113 {
5114   if (player->GfxAction != action || player->GfxDir != dir)
5115   {
5116     player->GfxAction = action;
5117     player->GfxDir = dir;
5118     player->Frame = 0;
5119     player->StepFrame = 0;
5120   }
5121 }
5122
5123 static void ResetGfxFrame(int x, int y)
5124 {
5125   // profiling showed that "autotest" spends 10~20% of its time in this function
5126   if (DrawingDeactivatedField())
5127     return;
5128
5129   int element = Tile[x][y];
5130   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5131
5132   if (graphic_info[graphic].anim_global_sync)
5133     GfxFrame[x][y] = FrameCounter;
5134   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5135     GfxFrame[x][y] = CustomValue[x][y];
5136   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5137     GfxFrame[x][y] = element_info[element].collect_score;
5138   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5139     GfxFrame[x][y] = ChangeDelay[x][y];
5140 }
5141
5142 static void ResetGfxAnimation(int x, int y)
5143 {
5144   GfxAction[x][y] = ACTION_DEFAULT;
5145   GfxDir[x][y] = MovDir[x][y];
5146   GfxFrame[x][y] = 0;
5147
5148   ResetGfxFrame(x, y);
5149 }
5150
5151 static void ResetRandomAnimationValue(int x, int y)
5152 {
5153   GfxRandom[x][y] = INIT_GFX_RANDOM();
5154 }
5155
5156 static void InitMovingField(int x, int y, int direction)
5157 {
5158   int element = Tile[x][y];
5159   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5160   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5161   int newx = x + dx;
5162   int newy = y + dy;
5163   boolean is_moving_before, is_moving_after;
5164
5165   // check if element was/is moving or being moved before/after mode change
5166   is_moving_before = (WasJustMoving[x][y] != 0);
5167   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5168
5169   // reset animation only for moving elements which change direction of moving
5170   // or which just started or stopped moving
5171   // (else CEs with property "can move" / "not moving" are reset each frame)
5172   if (is_moving_before != is_moving_after ||
5173       direction != MovDir[x][y])
5174     ResetGfxAnimation(x, y);
5175
5176   MovDir[x][y] = direction;
5177   GfxDir[x][y] = direction;
5178
5179   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5180                      direction == MV_DOWN && CAN_FALL(element) ?
5181                      ACTION_FALLING : ACTION_MOVING);
5182
5183   // this is needed for CEs with property "can move" / "not moving"
5184
5185   if (is_moving_after)
5186   {
5187     if (Tile[newx][newy] == EL_EMPTY)
5188       Tile[newx][newy] = EL_BLOCKED;
5189
5190     MovDir[newx][newy] = MovDir[x][y];
5191
5192     CustomValue[newx][newy] = CustomValue[x][y];
5193
5194     GfxFrame[newx][newy] = GfxFrame[x][y];
5195     GfxRandom[newx][newy] = GfxRandom[x][y];
5196     GfxAction[newx][newy] = GfxAction[x][y];
5197     GfxDir[newx][newy] = GfxDir[x][y];
5198   }
5199 }
5200
5201 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5202 {
5203   int direction = MovDir[x][y];
5204   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5205   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5206
5207   *goes_to_x = newx;
5208   *goes_to_y = newy;
5209 }
5210
5211 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5212 {
5213   int oldx = x, oldy = y;
5214   int direction = MovDir[x][y];
5215
5216   if (direction == MV_LEFT)
5217     oldx++;
5218   else if (direction == MV_RIGHT)
5219     oldx--;
5220   else if (direction == MV_UP)
5221     oldy++;
5222   else if (direction == MV_DOWN)
5223     oldy--;
5224
5225   *comes_from_x = oldx;
5226   *comes_from_y = oldy;
5227 }
5228
5229 static int MovingOrBlocked2Element(int x, int y)
5230 {
5231   int element = Tile[x][y];
5232
5233   if (element == EL_BLOCKED)
5234   {
5235     int oldx, oldy;
5236
5237     Blocked2Moving(x, y, &oldx, &oldy);
5238     return Tile[oldx][oldy];
5239   }
5240   else
5241     return element;
5242 }
5243
5244 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5245 {
5246   // like MovingOrBlocked2Element(), but if element is moving
5247   // and (x,y) is the field the moving element is just leaving,
5248   // return EL_BLOCKED instead of the element value
5249   int element = Tile[x][y];
5250
5251   if (IS_MOVING(x, y))
5252   {
5253     if (element == EL_BLOCKED)
5254     {
5255       int oldx, oldy;
5256
5257       Blocked2Moving(x, y, &oldx, &oldy);
5258       return Tile[oldx][oldy];
5259     }
5260     else
5261       return EL_BLOCKED;
5262   }
5263   else
5264     return element;
5265 }
5266
5267 static void RemoveField(int x, int y)
5268 {
5269   Tile[x][y] = EL_EMPTY;
5270
5271   MovPos[x][y] = 0;
5272   MovDir[x][y] = 0;
5273   MovDelay[x][y] = 0;
5274
5275   CustomValue[x][y] = 0;
5276
5277   AmoebaNr[x][y] = 0;
5278   ChangeDelay[x][y] = 0;
5279   ChangePage[x][y] = -1;
5280   Pushed[x][y] = FALSE;
5281
5282   GfxElement[x][y] = EL_UNDEFINED;
5283   GfxAction[x][y] = ACTION_DEFAULT;
5284   GfxDir[x][y] = MV_NONE;
5285 }
5286
5287 static void RemoveMovingField(int x, int y)
5288 {
5289   int oldx = x, oldy = y, newx = x, newy = y;
5290   int element = Tile[x][y];
5291   int next_element = EL_UNDEFINED;
5292
5293   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5294     return;
5295
5296   if (IS_MOVING(x, y))
5297   {
5298     Moving2Blocked(x, y, &newx, &newy);
5299
5300     if (Tile[newx][newy] != EL_BLOCKED)
5301     {
5302       // element is moving, but target field is not free (blocked), but
5303       // already occupied by something different (example: acid pool);
5304       // in this case, only remove the moving field, but not the target
5305
5306       RemoveField(oldx, oldy);
5307
5308       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5309
5310       TEST_DrawLevelField(oldx, oldy);
5311
5312       return;
5313     }
5314   }
5315   else if (element == EL_BLOCKED)
5316   {
5317     Blocked2Moving(x, y, &oldx, &oldy);
5318     if (!IS_MOVING(oldx, oldy))
5319       return;
5320   }
5321
5322   if (element == EL_BLOCKED &&
5323       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5324        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5325        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5326        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5327        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5328        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5329     next_element = get_next_element(Tile[oldx][oldy]);
5330
5331   RemoveField(oldx, oldy);
5332   RemoveField(newx, newy);
5333
5334   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5335
5336   if (next_element != EL_UNDEFINED)
5337     Tile[oldx][oldy] = next_element;
5338
5339   TEST_DrawLevelField(oldx, oldy);
5340   TEST_DrawLevelField(newx, newy);
5341 }
5342
5343 void DrawDynamite(int x, int y)
5344 {
5345   int sx = SCREENX(x), sy = SCREENY(y);
5346   int graphic = el2img(Tile[x][y]);
5347   int frame;
5348
5349   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5350     return;
5351
5352   if (IS_WALKABLE_INSIDE(Back[x][y]))
5353     return;
5354
5355   if (Back[x][y])
5356     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5357   else if (Store[x][y])
5358     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5359
5360   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5361
5362   if (Back[x][y] || Store[x][y])
5363     DrawGraphicThruMask(sx, sy, graphic, frame);
5364   else
5365     DrawGraphic(sx, sy, graphic, frame);
5366 }
5367
5368 static void CheckDynamite(int x, int y)
5369 {
5370   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5371   {
5372     MovDelay[x][y]--;
5373
5374     if (MovDelay[x][y] != 0)
5375     {
5376       DrawDynamite(x, y);
5377       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5378
5379       return;
5380     }
5381   }
5382
5383   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5384
5385   Bang(x, y);
5386 }
5387
5388 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5389 {
5390   boolean num_checked_players = 0;
5391   int i;
5392
5393   for (i = 0; i < MAX_PLAYERS; i++)
5394   {
5395     if (stored_player[i].active)
5396     {
5397       int sx = stored_player[i].jx;
5398       int sy = stored_player[i].jy;
5399
5400       if (num_checked_players == 0)
5401       {
5402         *sx1 = *sx2 = sx;
5403         *sy1 = *sy2 = sy;
5404       }
5405       else
5406       {
5407         *sx1 = MIN(*sx1, sx);
5408         *sy1 = MIN(*sy1, sy);
5409         *sx2 = MAX(*sx2, sx);
5410         *sy2 = MAX(*sy2, sy);
5411       }
5412
5413       num_checked_players++;
5414     }
5415   }
5416 }
5417
5418 static boolean checkIfAllPlayersFitToScreen_RND(void)
5419 {
5420   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5421
5422   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5423
5424   return (sx2 - sx1 < SCR_FIELDX &&
5425           sy2 - sy1 < SCR_FIELDY);
5426 }
5427
5428 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5429 {
5430   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5431
5432   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5433
5434   *sx = (sx1 + sx2) / 2;
5435   *sy = (sy1 + sy2) / 2;
5436 }
5437
5438 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5439                                boolean center_screen, boolean quick_relocation)
5440 {
5441   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5442   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5443   boolean no_delay = (tape.warp_forward);
5444   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5445   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5446   int new_scroll_x, new_scroll_y;
5447
5448   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5449   {
5450     // case 1: quick relocation inside visible screen (without scrolling)
5451
5452     RedrawPlayfield();
5453
5454     return;
5455   }
5456
5457   if (!level.shifted_relocation || center_screen)
5458   {
5459     // relocation _with_ centering of screen
5460
5461     new_scroll_x = SCROLL_POSITION_X(x);
5462     new_scroll_y = SCROLL_POSITION_Y(y);
5463   }
5464   else
5465   {
5466     // relocation _without_ centering of screen
5467
5468     int center_scroll_x = SCROLL_POSITION_X(old_x);
5469     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5470     int offset_x = x + (scroll_x - center_scroll_x);
5471     int offset_y = y + (scroll_y - center_scroll_y);
5472
5473     // for new screen position, apply previous offset to center position
5474     new_scroll_x = SCROLL_POSITION_X(offset_x);
5475     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5476   }
5477
5478   if (quick_relocation)
5479   {
5480     // case 2: quick relocation (redraw without visible scrolling)
5481
5482     scroll_x = new_scroll_x;
5483     scroll_y = new_scroll_y;
5484
5485     RedrawPlayfield();
5486
5487     return;
5488   }
5489
5490   // case 3: visible relocation (with scrolling to new position)
5491
5492   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5493
5494   SetVideoFrameDelay(wait_delay_value);
5495
5496   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5497   {
5498     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5499     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5500
5501     if (dx == 0 && dy == 0)             // no scrolling needed at all
5502       break;
5503
5504     scroll_x -= dx;
5505     scroll_y -= dy;
5506
5507     // set values for horizontal/vertical screen scrolling (half tile size)
5508     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5509     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5510     int pos_x = dx * TILEX / 2;
5511     int pos_y = dy * TILEY / 2;
5512     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5513     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5514
5515     ScrollLevel(dx, dy);
5516     DrawAllPlayers();
5517
5518     // scroll in two steps of half tile size to make things smoother
5519     BlitScreenToBitmapExt_RND(window, fx, fy);
5520
5521     // scroll second step to align at full tile size
5522     BlitScreenToBitmap(window);
5523   }
5524
5525   DrawAllPlayers();
5526   BackToFront();
5527
5528   SetVideoFrameDelay(frame_delay_value_old);
5529 }
5530
5531 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5532 {
5533   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5534   int player_nr = GET_PLAYER_NR(el_player);
5535   struct PlayerInfo *player = &stored_player[player_nr];
5536   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5537   boolean no_delay = (tape.warp_forward);
5538   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5539   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5540   int old_jx = player->jx;
5541   int old_jy = player->jy;
5542   int old_element = Tile[old_jx][old_jy];
5543   int element = Tile[jx][jy];
5544   boolean player_relocated = (old_jx != jx || old_jy != jy);
5545
5546   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5547   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5548   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5549   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5550   int leave_side_horiz = move_dir_horiz;
5551   int leave_side_vert  = move_dir_vert;
5552   int enter_side = enter_side_horiz | enter_side_vert;
5553   int leave_side = leave_side_horiz | leave_side_vert;
5554
5555   if (player->buried)           // do not reanimate dead player
5556     return;
5557
5558   if (!player_relocated)        // no need to relocate the player
5559     return;
5560
5561   if (IS_PLAYER(jx, jy))        // player already placed at new position
5562   {
5563     RemoveField(jx, jy);        // temporarily remove newly placed player
5564     DrawLevelField(jx, jy);
5565   }
5566
5567   if (player->present)
5568   {
5569     while (player->MovPos)
5570     {
5571       ScrollPlayer(player, SCROLL_GO_ON);
5572       ScrollScreen(NULL, SCROLL_GO_ON);
5573
5574       AdvanceFrameAndPlayerCounters(player->index_nr);
5575
5576       DrawPlayer(player);
5577
5578       BackToFront_WithFrameDelay(wait_delay_value);
5579     }
5580
5581     DrawPlayer(player);         // needed here only to cleanup last field
5582     DrawLevelField(player->jx, player->jy);     // remove player graphic
5583
5584     player->is_moving = FALSE;
5585   }
5586
5587   if (IS_CUSTOM_ELEMENT(old_element))
5588     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5589                                CE_LEFT_BY_PLAYER,
5590                                player->index_bit, leave_side);
5591
5592   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5593                                       CE_PLAYER_LEAVES_X,
5594                                       player->index_bit, leave_side);
5595
5596   Tile[jx][jy] = el_player;
5597   InitPlayerField(jx, jy, el_player, TRUE);
5598
5599   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5600      possible that the relocation target field did not contain a player element,
5601      but a walkable element, to which the new player was relocated -- in this
5602      case, restore that (already initialized!) element on the player field */
5603   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5604   {
5605     Tile[jx][jy] = element;     // restore previously existing element
5606   }
5607
5608   // only visually relocate centered player
5609   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5610                      FALSE, level.instant_relocation);
5611
5612   TestIfPlayerTouchesBadThing(jx, jy);
5613   TestIfPlayerTouchesCustomElement(jx, jy);
5614
5615   if (IS_CUSTOM_ELEMENT(element))
5616     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5617                                player->index_bit, enter_side);
5618
5619   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5620                                       player->index_bit, enter_side);
5621
5622   if (player->is_switching)
5623   {
5624     /* ensure that relocation while still switching an element does not cause
5625        a new element to be treated as also switched directly after relocation
5626        (this is important for teleporter switches that teleport the player to
5627        a place where another teleporter switch is in the same direction, which
5628        would then incorrectly be treated as immediately switched before the
5629        direction key that caused the switch was released) */
5630
5631     player->switch_x += jx - old_jx;
5632     player->switch_y += jy - old_jy;
5633   }
5634 }
5635
5636 static void Explode(int ex, int ey, int phase, int mode)
5637 {
5638   int x, y;
5639   int last_phase;
5640   int border_element;
5641
5642   // !!! eliminate this variable !!!
5643   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5644
5645   if (game.explosions_delayed)
5646   {
5647     ExplodeField[ex][ey] = mode;
5648     return;
5649   }
5650
5651   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5652   {
5653     int center_element = Tile[ex][ey];
5654     int artwork_element, explosion_element;     // set these values later
5655
5656     // remove things displayed in background while burning dynamite
5657     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5658       Back[ex][ey] = 0;
5659
5660     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5661     {
5662       // put moving element to center field (and let it explode there)
5663       center_element = MovingOrBlocked2Element(ex, ey);
5664       RemoveMovingField(ex, ey);
5665       Tile[ex][ey] = center_element;
5666     }
5667
5668     // now "center_element" is finally determined -- set related values now
5669     artwork_element = center_element;           // for custom player artwork
5670     explosion_element = center_element;         // for custom player artwork
5671
5672     if (IS_PLAYER(ex, ey))
5673     {
5674       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5675
5676       artwork_element = stored_player[player_nr].artwork_element;
5677
5678       if (level.use_explosion_element[player_nr])
5679       {
5680         explosion_element = level.explosion_element[player_nr];
5681         artwork_element = explosion_element;
5682       }
5683     }
5684
5685     if (mode == EX_TYPE_NORMAL ||
5686         mode == EX_TYPE_CENTER ||
5687         mode == EX_TYPE_CROSS)
5688       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5689
5690     last_phase = element_info[explosion_element].explosion_delay + 1;
5691
5692     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5693     {
5694       int xx = x - ex + 1;
5695       int yy = y - ey + 1;
5696       int element;
5697
5698       if (!IN_LEV_FIELD(x, y) ||
5699           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5700           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5701         continue;
5702
5703       element = Tile[x][y];
5704
5705       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5706       {
5707         element = MovingOrBlocked2Element(x, y);
5708
5709         if (!IS_EXPLOSION_PROOF(element))
5710           RemoveMovingField(x, y);
5711       }
5712
5713       // indestructible elements can only explode in center (but not flames)
5714       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5715                                            mode == EX_TYPE_BORDER)) ||
5716           element == EL_FLAMES)
5717         continue;
5718
5719       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5720          behaviour, for example when touching a yamyam that explodes to rocks
5721          with active deadly shield, a rock is created under the player !!! */
5722       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5723 #if 0
5724       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5725           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5726            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5727 #else
5728       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5729 #endif
5730       {
5731         if (IS_ACTIVE_BOMB(element))
5732         {
5733           // re-activate things under the bomb like gate or penguin
5734           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5735           Back[x][y] = 0;
5736         }
5737
5738         continue;
5739       }
5740
5741       // save walkable background elements while explosion on same tile
5742       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5743           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5744         Back[x][y] = element;
5745
5746       // ignite explodable elements reached by other explosion
5747       if (element == EL_EXPLOSION)
5748         element = Store2[x][y];
5749
5750       if (AmoebaNr[x][y] &&
5751           (element == EL_AMOEBA_FULL ||
5752            element == EL_BD_AMOEBA ||
5753            element == EL_AMOEBA_GROWING))
5754       {
5755         AmoebaCnt[AmoebaNr[x][y]]--;
5756         AmoebaCnt2[AmoebaNr[x][y]]--;
5757       }
5758
5759       RemoveField(x, y);
5760
5761       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5762       {
5763         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5764
5765         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5766
5767         if (PLAYERINFO(ex, ey)->use_murphy)
5768           Store[x][y] = EL_EMPTY;
5769       }
5770
5771       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5772       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5773       else if (ELEM_IS_PLAYER(center_element))
5774         Store[x][y] = EL_EMPTY;
5775       else if (center_element == EL_YAMYAM)
5776         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5777       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5778         Store[x][y] = element_info[center_element].content.e[xx][yy];
5779 #if 1
5780       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5781       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5782       // otherwise) -- FIX THIS !!!
5783       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5784         Store[x][y] = element_info[element].content.e[1][1];
5785 #else
5786       else if (!CAN_EXPLODE(element))
5787         Store[x][y] = element_info[element].content.e[1][1];
5788 #endif
5789       else
5790         Store[x][y] = EL_EMPTY;
5791
5792       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5793           center_element == EL_AMOEBA_TO_DIAMOND)
5794         Store2[x][y] = element;
5795
5796       Tile[x][y] = EL_EXPLOSION;
5797       GfxElement[x][y] = artwork_element;
5798
5799       ExplodePhase[x][y] = 1;
5800       ExplodeDelay[x][y] = last_phase;
5801
5802       Stop[x][y] = TRUE;
5803     }
5804
5805     if (center_element == EL_YAMYAM)
5806       game.yamyam_content_nr =
5807         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5808
5809     return;
5810   }
5811
5812   if (Stop[ex][ey])
5813     return;
5814
5815   x = ex;
5816   y = ey;
5817
5818   if (phase == 1)
5819     GfxFrame[x][y] = 0;         // restart explosion animation
5820
5821   last_phase = ExplodeDelay[x][y];
5822
5823   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5824
5825   // this can happen if the player leaves an explosion just in time
5826   if (GfxElement[x][y] == EL_UNDEFINED)
5827     GfxElement[x][y] = EL_EMPTY;
5828
5829   border_element = Store2[x][y];
5830   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5831     border_element = StorePlayer[x][y];
5832
5833   if (phase == element_info[border_element].ignition_delay ||
5834       phase == last_phase)
5835   {
5836     boolean border_explosion = FALSE;
5837
5838     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5839         !PLAYER_EXPLOSION_PROTECTED(x, y))
5840     {
5841       KillPlayerUnlessExplosionProtected(x, y);
5842       border_explosion = TRUE;
5843     }
5844     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5845     {
5846       Tile[x][y] = Store2[x][y];
5847       Store2[x][y] = 0;
5848       Bang(x, y);
5849       border_explosion = TRUE;
5850     }
5851     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5852     {
5853       AmoebaToDiamond(x, y);
5854       Store2[x][y] = 0;
5855       border_explosion = TRUE;
5856     }
5857
5858     // if an element just explodes due to another explosion (chain-reaction),
5859     // do not immediately end the new explosion when it was the last frame of
5860     // the explosion (as it would be done in the following "if"-statement!)
5861     if (border_explosion && phase == last_phase)
5862       return;
5863   }
5864
5865   if (phase == last_phase)
5866   {
5867     int element;
5868
5869     element = Tile[x][y] = Store[x][y];
5870     Store[x][y] = Store2[x][y] = 0;
5871     GfxElement[x][y] = EL_UNDEFINED;
5872
5873     // player can escape from explosions and might therefore be still alive
5874     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5875         element <= EL_PLAYER_IS_EXPLODING_4)
5876     {
5877       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5878       int explosion_element = EL_PLAYER_1 + player_nr;
5879       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5880       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5881
5882       if (level.use_explosion_element[player_nr])
5883         explosion_element = level.explosion_element[player_nr];
5884
5885       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5886                     element_info[explosion_element].content.e[xx][yy]);
5887     }
5888
5889     // restore probably existing indestructible background element
5890     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5891       element = Tile[x][y] = Back[x][y];
5892     Back[x][y] = 0;
5893
5894     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5895     GfxDir[x][y] = MV_NONE;
5896     ChangeDelay[x][y] = 0;
5897     ChangePage[x][y] = -1;
5898
5899     CustomValue[x][y] = 0;
5900
5901     InitField_WithBug2(x, y, FALSE);
5902
5903     TEST_DrawLevelField(x, y);
5904
5905     TestIfElementTouchesCustomElement(x, y);
5906
5907     if (GFX_CRUMBLED(element))
5908       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5909
5910     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5911       StorePlayer[x][y] = 0;
5912
5913     if (ELEM_IS_PLAYER(element))
5914       RelocatePlayer(x, y, element);
5915   }
5916   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5917   {
5918     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5919     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5920
5921     if (phase == delay)
5922       TEST_DrawLevelFieldCrumbled(x, y);
5923
5924     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5925     {
5926       DrawLevelElement(x, y, Back[x][y]);
5927       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5928     }
5929     else if (IS_WALKABLE_UNDER(Back[x][y]))
5930     {
5931       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5932       DrawLevelElementThruMask(x, y, Back[x][y]);
5933     }
5934     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5935       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5936   }
5937 }
5938
5939 static void DynaExplode(int ex, int ey)
5940 {
5941   int i, j;
5942   int dynabomb_element = Tile[ex][ey];
5943   int dynabomb_size = 1;
5944   boolean dynabomb_xl = FALSE;
5945   struct PlayerInfo *player;
5946   static int xy[4][2] =
5947   {
5948     { 0, -1 },
5949     { -1, 0 },
5950     { +1, 0 },
5951     { 0, +1 }
5952   };
5953
5954   if (IS_ACTIVE_BOMB(dynabomb_element))
5955   {
5956     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5957     dynabomb_size = player->dynabomb_size;
5958     dynabomb_xl = player->dynabomb_xl;
5959     player->dynabombs_left++;
5960   }
5961
5962   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5963
5964   for (i = 0; i < NUM_DIRECTIONS; i++)
5965   {
5966     for (j = 1; j <= dynabomb_size; j++)
5967     {
5968       int x = ex + j * xy[i][0];
5969       int y = ey + j * xy[i][1];
5970       int element;
5971
5972       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5973         break;
5974
5975       element = Tile[x][y];
5976
5977       // do not restart explosions of fields with active bombs
5978       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5979         continue;
5980
5981       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5982
5983       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5984           !IS_DIGGABLE(element) && !dynabomb_xl)
5985         break;
5986     }
5987   }
5988 }
5989
5990 void Bang(int x, int y)
5991 {
5992   int element = MovingOrBlocked2Element(x, y);
5993   int explosion_type = EX_TYPE_NORMAL;
5994
5995   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5996   {
5997     struct PlayerInfo *player = PLAYERINFO(x, y);
5998
5999     element = Tile[x][y] = player->initial_element;
6000
6001     if (level.use_explosion_element[player->index_nr])
6002     {
6003       int explosion_element = level.explosion_element[player->index_nr];
6004
6005       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6006         explosion_type = EX_TYPE_CROSS;
6007       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6008         explosion_type = EX_TYPE_CENTER;
6009     }
6010   }
6011
6012   switch (element)
6013   {
6014     case EL_BUG:
6015     case EL_SPACESHIP:
6016     case EL_BD_BUTTERFLY:
6017     case EL_BD_FIREFLY:
6018     case EL_YAMYAM:
6019     case EL_DARK_YAMYAM:
6020     case EL_ROBOT:
6021     case EL_PACMAN:
6022     case EL_MOLE:
6023       RaiseScoreElement(element);
6024       break;
6025
6026     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6027     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6028     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6029     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6030     case EL_DYNABOMB_INCREASE_NUMBER:
6031     case EL_DYNABOMB_INCREASE_SIZE:
6032     case EL_DYNABOMB_INCREASE_POWER:
6033       explosion_type = EX_TYPE_DYNA;
6034       break;
6035
6036     case EL_DC_LANDMINE:
6037       explosion_type = EX_TYPE_CENTER;
6038       break;
6039
6040     case EL_PENGUIN:
6041     case EL_LAMP:
6042     case EL_LAMP_ACTIVE:
6043     case EL_AMOEBA_TO_DIAMOND:
6044       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6045         explosion_type = EX_TYPE_CENTER;
6046       break;
6047
6048     default:
6049       if (element_info[element].explosion_type == EXPLODES_CROSS)
6050         explosion_type = EX_TYPE_CROSS;
6051       else if (element_info[element].explosion_type == EXPLODES_1X1)
6052         explosion_type = EX_TYPE_CENTER;
6053       break;
6054   }
6055
6056   if (explosion_type == EX_TYPE_DYNA)
6057     DynaExplode(x, y);
6058   else
6059     Explode(x, y, EX_PHASE_START, explosion_type);
6060
6061   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6062 }
6063
6064 static void SplashAcid(int x, int y)
6065 {
6066   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6067       (!IN_LEV_FIELD(x - 1, y - 2) ||
6068        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6069     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6070
6071   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6072       (!IN_LEV_FIELD(x + 1, y - 2) ||
6073        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6074     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6075
6076   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6077 }
6078
6079 static void InitBeltMovement(void)
6080 {
6081   static int belt_base_element[4] =
6082   {
6083     EL_CONVEYOR_BELT_1_LEFT,
6084     EL_CONVEYOR_BELT_2_LEFT,
6085     EL_CONVEYOR_BELT_3_LEFT,
6086     EL_CONVEYOR_BELT_4_LEFT
6087   };
6088   static int belt_base_active_element[4] =
6089   {
6090     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6091     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6092     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6093     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6094   };
6095
6096   int x, y, i, j;
6097
6098   // set frame order for belt animation graphic according to belt direction
6099   for (i = 0; i < NUM_BELTS; i++)
6100   {
6101     int belt_nr = i;
6102
6103     for (j = 0; j < NUM_BELT_PARTS; j++)
6104     {
6105       int element = belt_base_active_element[belt_nr] + j;
6106       int graphic_1 = el2img(element);
6107       int graphic_2 = el2panelimg(element);
6108
6109       if (game.belt_dir[i] == MV_LEFT)
6110       {
6111         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6112         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6113       }
6114       else
6115       {
6116         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6117         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6118       }
6119     }
6120   }
6121
6122   SCAN_PLAYFIELD(x, y)
6123   {
6124     int element = Tile[x][y];
6125
6126     for (i = 0; i < NUM_BELTS; i++)
6127     {
6128       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6129       {
6130         int e_belt_nr = getBeltNrFromBeltElement(element);
6131         int belt_nr = i;
6132
6133         if (e_belt_nr == belt_nr)
6134         {
6135           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6136
6137           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6138         }
6139       }
6140     }
6141   }
6142 }
6143
6144 static void ToggleBeltSwitch(int x, int y)
6145 {
6146   static int belt_base_element[4] =
6147   {
6148     EL_CONVEYOR_BELT_1_LEFT,
6149     EL_CONVEYOR_BELT_2_LEFT,
6150     EL_CONVEYOR_BELT_3_LEFT,
6151     EL_CONVEYOR_BELT_4_LEFT
6152   };
6153   static int belt_base_active_element[4] =
6154   {
6155     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6156     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6157     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6158     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6159   };
6160   static int belt_base_switch_element[4] =
6161   {
6162     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6163     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6164     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6165     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6166   };
6167   static int belt_move_dir[4] =
6168   {
6169     MV_LEFT,
6170     MV_NONE,
6171     MV_RIGHT,
6172     MV_NONE,
6173   };
6174
6175   int element = Tile[x][y];
6176   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6177   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6178   int belt_dir = belt_move_dir[belt_dir_nr];
6179   int xx, yy, i;
6180
6181   if (!IS_BELT_SWITCH(element))
6182     return;
6183
6184   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6185   game.belt_dir[belt_nr] = belt_dir;
6186
6187   if (belt_dir_nr == 3)
6188     belt_dir_nr = 1;
6189
6190   // set frame order for belt animation graphic according to belt direction
6191   for (i = 0; i < NUM_BELT_PARTS; i++)
6192   {
6193     int element = belt_base_active_element[belt_nr] + i;
6194     int graphic_1 = el2img(element);
6195     int graphic_2 = el2panelimg(element);
6196
6197     if (belt_dir == MV_LEFT)
6198     {
6199       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6200       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6201     }
6202     else
6203     {
6204       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6205       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6206     }
6207   }
6208
6209   SCAN_PLAYFIELD(xx, yy)
6210   {
6211     int element = Tile[xx][yy];
6212
6213     if (IS_BELT_SWITCH(element))
6214     {
6215       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6216
6217       if (e_belt_nr == belt_nr)
6218       {
6219         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6220         TEST_DrawLevelField(xx, yy);
6221       }
6222     }
6223     else if (IS_BELT(element) && belt_dir != MV_NONE)
6224     {
6225       int e_belt_nr = getBeltNrFromBeltElement(element);
6226
6227       if (e_belt_nr == belt_nr)
6228       {
6229         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6230
6231         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6232         TEST_DrawLevelField(xx, yy);
6233       }
6234     }
6235     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6236     {
6237       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6238
6239       if (e_belt_nr == belt_nr)
6240       {
6241         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6242
6243         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6244         TEST_DrawLevelField(xx, yy);
6245       }
6246     }
6247   }
6248 }
6249
6250 static void ToggleSwitchgateSwitch(int x, int y)
6251 {
6252   int xx, yy;
6253
6254   game.switchgate_pos = !game.switchgate_pos;
6255
6256   SCAN_PLAYFIELD(xx, yy)
6257   {
6258     int element = Tile[xx][yy];
6259
6260     if (element == EL_SWITCHGATE_SWITCH_UP)
6261     {
6262       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6263       TEST_DrawLevelField(xx, yy);
6264     }
6265     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6266     {
6267       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6268       TEST_DrawLevelField(xx, yy);
6269     }
6270     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6271     {
6272       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6273       TEST_DrawLevelField(xx, yy);
6274     }
6275     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6276     {
6277       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6278       TEST_DrawLevelField(xx, yy);
6279     }
6280     else if (element == EL_SWITCHGATE_OPEN ||
6281              element == EL_SWITCHGATE_OPENING)
6282     {
6283       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6284
6285       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6286     }
6287     else if (element == EL_SWITCHGATE_CLOSED ||
6288              element == EL_SWITCHGATE_CLOSING)
6289     {
6290       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6291
6292       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6293     }
6294   }
6295 }
6296
6297 static int getInvisibleActiveFromInvisibleElement(int element)
6298 {
6299   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6300           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6301           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6302           element);
6303 }
6304
6305 static int getInvisibleFromInvisibleActiveElement(int element)
6306 {
6307   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6308           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6309           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6310           element);
6311 }
6312
6313 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6314 {
6315   int x, y;
6316
6317   SCAN_PLAYFIELD(x, y)
6318   {
6319     int element = Tile[x][y];
6320
6321     if (element == EL_LIGHT_SWITCH &&
6322         game.light_time_left > 0)
6323     {
6324       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6325       TEST_DrawLevelField(x, y);
6326     }
6327     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6328              game.light_time_left == 0)
6329     {
6330       Tile[x][y] = EL_LIGHT_SWITCH;
6331       TEST_DrawLevelField(x, y);
6332     }
6333     else if (element == EL_EMC_DRIPPER &&
6334              game.light_time_left > 0)
6335     {
6336       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6337       TEST_DrawLevelField(x, y);
6338     }
6339     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6340              game.light_time_left == 0)
6341     {
6342       Tile[x][y] = EL_EMC_DRIPPER;
6343       TEST_DrawLevelField(x, y);
6344     }
6345     else if (element == EL_INVISIBLE_STEELWALL ||
6346              element == EL_INVISIBLE_WALL ||
6347              element == EL_INVISIBLE_SAND)
6348     {
6349       if (game.light_time_left > 0)
6350         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6351
6352       TEST_DrawLevelField(x, y);
6353
6354       // uncrumble neighbour fields, if needed
6355       if (element == EL_INVISIBLE_SAND)
6356         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6357     }
6358     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6359              element == EL_INVISIBLE_WALL_ACTIVE ||
6360              element == EL_INVISIBLE_SAND_ACTIVE)
6361     {
6362       if (game.light_time_left == 0)
6363         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6364
6365       TEST_DrawLevelField(x, y);
6366
6367       // re-crumble neighbour fields, if needed
6368       if (element == EL_INVISIBLE_SAND)
6369         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6370     }
6371   }
6372 }
6373
6374 static void RedrawAllInvisibleElementsForLenses(void)
6375 {
6376   int x, y;
6377
6378   SCAN_PLAYFIELD(x, y)
6379   {
6380     int element = Tile[x][y];
6381
6382     if (element == EL_EMC_DRIPPER &&
6383         game.lenses_time_left > 0)
6384     {
6385       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6386       TEST_DrawLevelField(x, y);
6387     }
6388     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6389              game.lenses_time_left == 0)
6390     {
6391       Tile[x][y] = EL_EMC_DRIPPER;
6392       TEST_DrawLevelField(x, y);
6393     }
6394     else if (element == EL_INVISIBLE_STEELWALL ||
6395              element == EL_INVISIBLE_WALL ||
6396              element == EL_INVISIBLE_SAND)
6397     {
6398       if (game.lenses_time_left > 0)
6399         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6400
6401       TEST_DrawLevelField(x, y);
6402
6403       // uncrumble neighbour fields, if needed
6404       if (element == EL_INVISIBLE_SAND)
6405         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6406     }
6407     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6408              element == EL_INVISIBLE_WALL_ACTIVE ||
6409              element == EL_INVISIBLE_SAND_ACTIVE)
6410     {
6411       if (game.lenses_time_left == 0)
6412         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6413
6414       TEST_DrawLevelField(x, y);
6415
6416       // re-crumble neighbour fields, if needed
6417       if (element == EL_INVISIBLE_SAND)
6418         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6419     }
6420   }
6421 }
6422
6423 static void RedrawAllInvisibleElementsForMagnifier(void)
6424 {
6425   int x, y;
6426
6427   SCAN_PLAYFIELD(x, y)
6428   {
6429     int element = Tile[x][y];
6430
6431     if (element == EL_EMC_FAKE_GRASS &&
6432         game.magnify_time_left > 0)
6433     {
6434       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6435       TEST_DrawLevelField(x, y);
6436     }
6437     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6438              game.magnify_time_left == 0)
6439     {
6440       Tile[x][y] = EL_EMC_FAKE_GRASS;
6441       TEST_DrawLevelField(x, y);
6442     }
6443     else if (IS_GATE_GRAY(element) &&
6444              game.magnify_time_left > 0)
6445     {
6446       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6447                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6448                     IS_EM_GATE_GRAY(element) ?
6449                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6450                     IS_EMC_GATE_GRAY(element) ?
6451                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6452                     IS_DC_GATE_GRAY(element) ?
6453                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6454                     element);
6455       TEST_DrawLevelField(x, y);
6456     }
6457     else if (IS_GATE_GRAY_ACTIVE(element) &&
6458              game.magnify_time_left == 0)
6459     {
6460       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6461                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6462                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6463                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6464                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6465                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6466                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6467                     EL_DC_GATE_WHITE_GRAY :
6468                     element);
6469       TEST_DrawLevelField(x, y);
6470     }
6471   }
6472 }
6473
6474 static void ToggleLightSwitch(int x, int y)
6475 {
6476   int element = Tile[x][y];
6477
6478   game.light_time_left =
6479     (element == EL_LIGHT_SWITCH ?
6480      level.time_light * FRAMES_PER_SECOND : 0);
6481
6482   RedrawAllLightSwitchesAndInvisibleElements();
6483 }
6484
6485 static void ActivateTimegateSwitch(int x, int y)
6486 {
6487   int xx, yy;
6488
6489   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6490
6491   SCAN_PLAYFIELD(xx, yy)
6492   {
6493     int element = Tile[xx][yy];
6494
6495     if (element == EL_TIMEGATE_CLOSED ||
6496         element == EL_TIMEGATE_CLOSING)
6497     {
6498       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6499       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6500     }
6501
6502     /*
6503     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6504     {
6505       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6506       TEST_DrawLevelField(xx, yy);
6507     }
6508     */
6509
6510   }
6511
6512   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6513                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6514 }
6515
6516 static void Impact(int x, int y)
6517 {
6518   boolean last_line = (y == lev_fieldy - 1);
6519   boolean object_hit = FALSE;
6520   boolean impact = (last_line || object_hit);
6521   int element = Tile[x][y];
6522   int smashed = EL_STEELWALL;
6523
6524   if (!last_line)       // check if element below was hit
6525   {
6526     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6527       return;
6528
6529     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6530                                          MovDir[x][y + 1] != MV_DOWN ||
6531                                          MovPos[x][y + 1] <= TILEY / 2));
6532
6533     // do not smash moving elements that left the smashed field in time
6534     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6535         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6536       object_hit = FALSE;
6537
6538 #if USE_QUICKSAND_IMPACT_BUGFIX
6539     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6540     {
6541       RemoveMovingField(x, y + 1);
6542       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6543       Tile[x][y + 2] = EL_ROCK;
6544       TEST_DrawLevelField(x, y + 2);
6545
6546       object_hit = TRUE;
6547     }
6548
6549     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6550     {
6551       RemoveMovingField(x, y + 1);
6552       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6553       Tile[x][y + 2] = EL_ROCK;
6554       TEST_DrawLevelField(x, y + 2);
6555
6556       object_hit = TRUE;
6557     }
6558 #endif
6559
6560     if (object_hit)
6561       smashed = MovingOrBlocked2Element(x, y + 1);
6562
6563     impact = (last_line || object_hit);
6564   }
6565
6566   if (!last_line && smashed == EL_ACID) // element falls into acid
6567   {
6568     SplashAcid(x, y + 1);
6569     return;
6570   }
6571
6572   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6573   // only reset graphic animation if graphic really changes after impact
6574   if (impact &&
6575       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6576   {
6577     ResetGfxAnimation(x, y);
6578     TEST_DrawLevelField(x, y);
6579   }
6580
6581   if (impact && CAN_EXPLODE_IMPACT(element))
6582   {
6583     Bang(x, y);
6584     return;
6585   }
6586   else if (impact && element == EL_PEARL &&
6587            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6588   {
6589     ResetGfxAnimation(x, y);
6590
6591     Tile[x][y] = EL_PEARL_BREAKING;
6592     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6593     return;
6594   }
6595   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6596   {
6597     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6598
6599     return;
6600   }
6601
6602   if (impact && element == EL_AMOEBA_DROP)
6603   {
6604     if (object_hit && IS_PLAYER(x, y + 1))
6605       KillPlayerUnlessEnemyProtected(x, y + 1);
6606     else if (object_hit && smashed == EL_PENGUIN)
6607       Bang(x, y + 1);
6608     else
6609     {
6610       Tile[x][y] = EL_AMOEBA_GROWING;
6611       Store[x][y] = EL_AMOEBA_WET;
6612
6613       ResetRandomAnimationValue(x, y);
6614     }
6615     return;
6616   }
6617
6618   if (object_hit)               // check which object was hit
6619   {
6620     if ((CAN_PASS_MAGIC_WALL(element) && 
6621          (smashed == EL_MAGIC_WALL ||
6622           smashed == EL_BD_MAGIC_WALL)) ||
6623         (CAN_PASS_DC_MAGIC_WALL(element) &&
6624          smashed == EL_DC_MAGIC_WALL))
6625     {
6626       int xx, yy;
6627       int activated_magic_wall =
6628         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6629          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6630          EL_DC_MAGIC_WALL_ACTIVE);
6631
6632       // activate magic wall / mill
6633       SCAN_PLAYFIELD(xx, yy)
6634       {
6635         if (Tile[xx][yy] == smashed)
6636           Tile[xx][yy] = activated_magic_wall;
6637       }
6638
6639       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6640       game.magic_wall_active = TRUE;
6641
6642       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6643                             SND_MAGIC_WALL_ACTIVATING :
6644                             smashed == EL_BD_MAGIC_WALL ?
6645                             SND_BD_MAGIC_WALL_ACTIVATING :
6646                             SND_DC_MAGIC_WALL_ACTIVATING));
6647     }
6648
6649     if (IS_PLAYER(x, y + 1))
6650     {
6651       if (CAN_SMASH_PLAYER(element))
6652       {
6653         KillPlayerUnlessEnemyProtected(x, y + 1);
6654         return;
6655       }
6656     }
6657     else if (smashed == EL_PENGUIN)
6658     {
6659       if (CAN_SMASH_PLAYER(element))
6660       {
6661         Bang(x, y + 1);
6662         return;
6663       }
6664     }
6665     else if (element == EL_BD_DIAMOND)
6666     {
6667       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6668       {
6669         Bang(x, y + 1);
6670         return;
6671       }
6672     }
6673     else if (((element == EL_SP_INFOTRON ||
6674                element == EL_SP_ZONK) &&
6675               (smashed == EL_SP_SNIKSNAK ||
6676                smashed == EL_SP_ELECTRON ||
6677                smashed == EL_SP_DISK_ORANGE)) ||
6678              (element == EL_SP_INFOTRON &&
6679               smashed == EL_SP_DISK_YELLOW))
6680     {
6681       Bang(x, y + 1);
6682       return;
6683     }
6684     else if (CAN_SMASH_EVERYTHING(element))
6685     {
6686       if (IS_CLASSIC_ENEMY(smashed) ||
6687           CAN_EXPLODE_SMASHED(smashed))
6688       {
6689         Bang(x, y + 1);
6690         return;
6691       }
6692       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6693       {
6694         if (smashed == EL_LAMP ||
6695             smashed == EL_LAMP_ACTIVE)
6696         {
6697           Bang(x, y + 1);
6698           return;
6699         }
6700         else if (smashed == EL_NUT)
6701         {
6702           Tile[x][y + 1] = EL_NUT_BREAKING;
6703           PlayLevelSound(x, y, SND_NUT_BREAKING);
6704           RaiseScoreElement(EL_NUT);
6705           return;
6706         }
6707         else if (smashed == EL_PEARL)
6708         {
6709           ResetGfxAnimation(x, y);
6710
6711           Tile[x][y + 1] = EL_PEARL_BREAKING;
6712           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6713           return;
6714         }
6715         else if (smashed == EL_DIAMOND)
6716         {
6717           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6718           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6719           return;
6720         }
6721         else if (IS_BELT_SWITCH(smashed))
6722         {
6723           ToggleBeltSwitch(x, y + 1);
6724         }
6725         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6726                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6727                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6728                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6729         {
6730           ToggleSwitchgateSwitch(x, y + 1);
6731         }
6732         else if (smashed == EL_LIGHT_SWITCH ||
6733                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6734         {
6735           ToggleLightSwitch(x, y + 1);
6736         }
6737         else
6738         {
6739           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6740
6741           CheckElementChangeBySide(x, y + 1, smashed, element,
6742                                    CE_SWITCHED, CH_SIDE_TOP);
6743           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6744                                             CH_SIDE_TOP);
6745         }
6746       }
6747       else
6748       {
6749         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6750       }
6751     }
6752   }
6753
6754   // play sound of magic wall / mill
6755   if (!last_line &&
6756       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6757        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6758        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6759   {
6760     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6761       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6762     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6763       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6764     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6765       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6766
6767     return;
6768   }
6769
6770   // play sound of object that hits the ground
6771   if (last_line || object_hit)
6772     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6773 }
6774
6775 static void TurnRoundExt(int x, int y)
6776 {
6777   static struct
6778   {
6779     int dx, dy;
6780   } move_xy[] =
6781   {
6782     {  0,  0 },
6783     { -1,  0 },
6784     { +1,  0 },
6785     {  0,  0 },
6786     {  0, -1 },
6787     {  0,  0 }, { 0, 0 }, { 0, 0 },
6788     {  0, +1 }
6789   };
6790   static struct
6791   {
6792     int left, right, back;
6793   } turn[] =
6794   {
6795     { 0,        0,              0        },
6796     { MV_DOWN,  MV_UP,          MV_RIGHT },
6797     { MV_UP,    MV_DOWN,        MV_LEFT  },
6798     { 0,        0,              0        },
6799     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6800     { 0,        0,              0        },
6801     { 0,        0,              0        },
6802     { 0,        0,              0        },
6803     { MV_RIGHT, MV_LEFT,        MV_UP    }
6804   };
6805
6806   int element = Tile[x][y];
6807   int move_pattern = element_info[element].move_pattern;
6808
6809   int old_move_dir = MovDir[x][y];
6810   int left_dir  = turn[old_move_dir].left;
6811   int right_dir = turn[old_move_dir].right;
6812   int back_dir  = turn[old_move_dir].back;
6813
6814   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6815   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6816   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6817   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6818
6819   int left_x  = x + left_dx,  left_y  = y + left_dy;
6820   int right_x = x + right_dx, right_y = y + right_dy;
6821   int move_x  = x + move_dx,  move_y  = y + move_dy;
6822
6823   int xx, yy;
6824
6825   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6826   {
6827     TestIfBadThingTouchesOtherBadThing(x, y);
6828
6829     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6830       MovDir[x][y] = right_dir;
6831     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6832       MovDir[x][y] = left_dir;
6833
6834     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6835       MovDelay[x][y] = 9;
6836     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6837       MovDelay[x][y] = 1;
6838   }
6839   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6840   {
6841     TestIfBadThingTouchesOtherBadThing(x, y);
6842
6843     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6844       MovDir[x][y] = left_dir;
6845     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6846       MovDir[x][y] = right_dir;
6847
6848     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6849       MovDelay[x][y] = 9;
6850     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6851       MovDelay[x][y] = 1;
6852   }
6853   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6854   {
6855     TestIfBadThingTouchesOtherBadThing(x, y);
6856
6857     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6858       MovDir[x][y] = left_dir;
6859     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6860       MovDir[x][y] = right_dir;
6861
6862     if (MovDir[x][y] != old_move_dir)
6863       MovDelay[x][y] = 9;
6864   }
6865   else if (element == EL_YAMYAM)
6866   {
6867     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6868     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6869
6870     if (can_turn_left && can_turn_right)
6871       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6872     else if (can_turn_left)
6873       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6874     else if (can_turn_right)
6875       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6876     else
6877       MovDir[x][y] = back_dir;
6878
6879     MovDelay[x][y] = 16 + 16 * RND(3);
6880   }
6881   else if (element == EL_DARK_YAMYAM)
6882   {
6883     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6884                                                          left_x, left_y);
6885     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6886                                                          right_x, right_y);
6887
6888     if (can_turn_left && can_turn_right)
6889       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6890     else if (can_turn_left)
6891       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6892     else if (can_turn_right)
6893       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6894     else
6895       MovDir[x][y] = back_dir;
6896
6897     MovDelay[x][y] = 16 + 16 * RND(3);
6898   }
6899   else if (element == EL_PACMAN)
6900   {
6901     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6902     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6903
6904     if (can_turn_left && can_turn_right)
6905       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6906     else if (can_turn_left)
6907       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6908     else if (can_turn_right)
6909       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6910     else
6911       MovDir[x][y] = back_dir;
6912
6913     MovDelay[x][y] = 6 + RND(40);
6914   }
6915   else if (element == EL_PIG)
6916   {
6917     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6918     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6919     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6920     boolean should_turn_left, should_turn_right, should_move_on;
6921     int rnd_value = 24;
6922     int rnd = RND(rnd_value);
6923
6924     should_turn_left = (can_turn_left &&
6925                         (!can_move_on ||
6926                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6927                                                    y + back_dy + left_dy)));
6928     should_turn_right = (can_turn_right &&
6929                          (!can_move_on ||
6930                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6931                                                     y + back_dy + right_dy)));
6932     should_move_on = (can_move_on &&
6933                       (!can_turn_left ||
6934                        !can_turn_right ||
6935                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6936                                                  y + move_dy + left_dy) ||
6937                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6938                                                  y + move_dy + right_dy)));
6939
6940     if (should_turn_left || should_turn_right || should_move_on)
6941     {
6942       if (should_turn_left && should_turn_right && should_move_on)
6943         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6944                         rnd < 2 * rnd_value / 3 ? right_dir :
6945                         old_move_dir);
6946       else if (should_turn_left && should_turn_right)
6947         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6948       else if (should_turn_left && should_move_on)
6949         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6950       else if (should_turn_right && should_move_on)
6951         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6952       else if (should_turn_left)
6953         MovDir[x][y] = left_dir;
6954       else if (should_turn_right)
6955         MovDir[x][y] = right_dir;
6956       else if (should_move_on)
6957         MovDir[x][y] = old_move_dir;
6958     }
6959     else if (can_move_on && rnd > rnd_value / 8)
6960       MovDir[x][y] = old_move_dir;
6961     else if (can_turn_left && can_turn_right)
6962       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6963     else if (can_turn_left && rnd > rnd_value / 8)
6964       MovDir[x][y] = left_dir;
6965     else if (can_turn_right && rnd > rnd_value/8)
6966       MovDir[x][y] = right_dir;
6967     else
6968       MovDir[x][y] = back_dir;
6969
6970     xx = x + move_xy[MovDir[x][y]].dx;
6971     yy = y + move_xy[MovDir[x][y]].dy;
6972
6973     if (!IN_LEV_FIELD(xx, yy) ||
6974         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6975       MovDir[x][y] = old_move_dir;
6976
6977     MovDelay[x][y] = 0;
6978   }
6979   else if (element == EL_DRAGON)
6980   {
6981     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6982     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6983     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6984     int rnd_value = 24;
6985     int rnd = RND(rnd_value);
6986
6987     if (can_move_on && rnd > rnd_value / 8)
6988       MovDir[x][y] = old_move_dir;
6989     else if (can_turn_left && can_turn_right)
6990       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6991     else if (can_turn_left && rnd > rnd_value / 8)
6992       MovDir[x][y] = left_dir;
6993     else if (can_turn_right && rnd > rnd_value / 8)
6994       MovDir[x][y] = right_dir;
6995     else
6996       MovDir[x][y] = back_dir;
6997
6998     xx = x + move_xy[MovDir[x][y]].dx;
6999     yy = y + move_xy[MovDir[x][y]].dy;
7000
7001     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7002       MovDir[x][y] = old_move_dir;
7003
7004     MovDelay[x][y] = 0;
7005   }
7006   else if (element == EL_MOLE)
7007   {
7008     boolean can_move_on =
7009       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7010                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7011                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7012     if (!can_move_on)
7013     {
7014       boolean can_turn_left =
7015         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7016                               IS_AMOEBOID(Tile[left_x][left_y])));
7017
7018       boolean can_turn_right =
7019         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7020                               IS_AMOEBOID(Tile[right_x][right_y])));
7021
7022       if (can_turn_left && can_turn_right)
7023         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7024       else if (can_turn_left)
7025         MovDir[x][y] = left_dir;
7026       else
7027         MovDir[x][y] = right_dir;
7028     }
7029
7030     if (MovDir[x][y] != old_move_dir)
7031       MovDelay[x][y] = 9;
7032   }
7033   else if (element == EL_BALLOON)
7034   {
7035     MovDir[x][y] = game.wind_direction;
7036     MovDelay[x][y] = 0;
7037   }
7038   else if (element == EL_SPRING)
7039   {
7040     if (MovDir[x][y] & MV_HORIZONTAL)
7041     {
7042       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7043           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7044       {
7045         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7046         ResetGfxAnimation(move_x, move_y);
7047         TEST_DrawLevelField(move_x, move_y);
7048
7049         MovDir[x][y] = back_dir;
7050       }
7051       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7052                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7053         MovDir[x][y] = MV_NONE;
7054     }
7055
7056     MovDelay[x][y] = 0;
7057   }
7058   else if (element == EL_ROBOT ||
7059            element == EL_SATELLITE ||
7060            element == EL_PENGUIN ||
7061            element == EL_EMC_ANDROID)
7062   {
7063     int attr_x = -1, attr_y = -1;
7064
7065     if (game.all_players_gone)
7066     {
7067       attr_x = game.exit_x;
7068       attr_y = game.exit_y;
7069     }
7070     else
7071     {
7072       int i;
7073
7074       for (i = 0; i < MAX_PLAYERS; i++)
7075       {
7076         struct PlayerInfo *player = &stored_player[i];
7077         int jx = player->jx, jy = player->jy;
7078
7079         if (!player->active)
7080           continue;
7081
7082         if (attr_x == -1 ||
7083             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7084         {
7085           attr_x = jx;
7086           attr_y = jy;
7087         }
7088       }
7089     }
7090
7091     if (element == EL_ROBOT &&
7092         game.robot_wheel_x >= 0 &&
7093         game.robot_wheel_y >= 0 &&
7094         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7095          game.engine_version < VERSION_IDENT(3,1,0,0)))
7096     {
7097       attr_x = game.robot_wheel_x;
7098       attr_y = game.robot_wheel_y;
7099     }
7100
7101     if (element == EL_PENGUIN)
7102     {
7103       int i;
7104       static int xy[4][2] =
7105       {
7106         { 0, -1 },
7107         { -1, 0 },
7108         { +1, 0 },
7109         { 0, +1 }
7110       };
7111
7112       for (i = 0; i < NUM_DIRECTIONS; i++)
7113       {
7114         int ex = x + xy[i][0];
7115         int ey = y + xy[i][1];
7116
7117         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7118                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7119                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7120                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7121         {
7122           attr_x = ex;
7123           attr_y = ey;
7124           break;
7125         }
7126       }
7127     }
7128
7129     MovDir[x][y] = MV_NONE;
7130     if (attr_x < x)
7131       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7132     else if (attr_x > x)
7133       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7134     if (attr_y < y)
7135       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7136     else if (attr_y > y)
7137       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7138
7139     if (element == EL_ROBOT)
7140     {
7141       int newx, newy;
7142
7143       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7144         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7145       Moving2Blocked(x, y, &newx, &newy);
7146
7147       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7148         MovDelay[x][y] = 8 + 8 * !RND(3);
7149       else
7150         MovDelay[x][y] = 16;
7151     }
7152     else if (element == EL_PENGUIN)
7153     {
7154       int newx, newy;
7155
7156       MovDelay[x][y] = 1;
7157
7158       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7159       {
7160         boolean first_horiz = RND(2);
7161         int new_move_dir = MovDir[x][y];
7162
7163         MovDir[x][y] =
7164           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7165         Moving2Blocked(x, y, &newx, &newy);
7166
7167         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7168           return;
7169
7170         MovDir[x][y] =
7171           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7172         Moving2Blocked(x, y, &newx, &newy);
7173
7174         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7175           return;
7176
7177         MovDir[x][y] = old_move_dir;
7178         return;
7179       }
7180     }
7181     else if (element == EL_SATELLITE)
7182     {
7183       int newx, newy;
7184
7185       MovDelay[x][y] = 1;
7186
7187       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7188       {
7189         boolean first_horiz = RND(2);
7190         int new_move_dir = MovDir[x][y];
7191
7192         MovDir[x][y] =
7193           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7194         Moving2Blocked(x, y, &newx, &newy);
7195
7196         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7197           return;
7198
7199         MovDir[x][y] =
7200           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7201         Moving2Blocked(x, y, &newx, &newy);
7202
7203         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7204           return;
7205
7206         MovDir[x][y] = old_move_dir;
7207         return;
7208       }
7209     }
7210     else if (element == EL_EMC_ANDROID)
7211     {
7212       static int check_pos[16] =
7213       {
7214         -1,             //  0 => (invalid)
7215         7,              //  1 => MV_LEFT
7216         3,              //  2 => MV_RIGHT
7217         -1,             //  3 => (invalid)
7218         1,              //  4 =>            MV_UP
7219         0,              //  5 => MV_LEFT  | MV_UP
7220         2,              //  6 => MV_RIGHT | MV_UP
7221         -1,             //  7 => (invalid)
7222         5,              //  8 =>            MV_DOWN
7223         6,              //  9 => MV_LEFT  | MV_DOWN
7224         4,              // 10 => MV_RIGHT | MV_DOWN
7225         -1,             // 11 => (invalid)
7226         -1,             // 12 => (invalid)
7227         -1,             // 13 => (invalid)
7228         -1,             // 14 => (invalid)
7229         -1,             // 15 => (invalid)
7230       };
7231       static struct
7232       {
7233         int dx, dy;
7234         int dir;
7235       } check_xy[8] =
7236       {
7237         { -1, -1,       MV_LEFT  | MV_UP   },
7238         {  0, -1,                  MV_UP   },
7239         { +1, -1,       MV_RIGHT | MV_UP   },
7240         { +1,  0,       MV_RIGHT           },
7241         { +1, +1,       MV_RIGHT | MV_DOWN },
7242         {  0, +1,                  MV_DOWN },
7243         { -1, +1,       MV_LEFT  | MV_DOWN },
7244         { -1,  0,       MV_LEFT            },
7245       };
7246       int start_pos, check_order;
7247       boolean can_clone = FALSE;
7248       int i;
7249
7250       // check if there is any free field around current position
7251       for (i = 0; i < 8; i++)
7252       {
7253         int newx = x + check_xy[i].dx;
7254         int newy = y + check_xy[i].dy;
7255
7256         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7257         {
7258           can_clone = TRUE;
7259
7260           break;
7261         }
7262       }
7263
7264       if (can_clone)            // randomly find an element to clone
7265       {
7266         can_clone = FALSE;
7267
7268         start_pos = check_pos[RND(8)];
7269         check_order = (RND(2) ? -1 : +1);
7270
7271         for (i = 0; i < 8; i++)
7272         {
7273           int pos_raw = start_pos + i * check_order;
7274           int pos = (pos_raw + 8) % 8;
7275           int newx = x + check_xy[pos].dx;
7276           int newy = y + check_xy[pos].dy;
7277
7278           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7279           {
7280             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7281             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7282
7283             Store[x][y] = Tile[newx][newy];
7284
7285             can_clone = TRUE;
7286
7287             break;
7288           }
7289         }
7290       }
7291
7292       if (can_clone)            // randomly find a direction to move
7293       {
7294         can_clone = FALSE;
7295
7296         start_pos = check_pos[RND(8)];
7297         check_order = (RND(2) ? -1 : +1);
7298
7299         for (i = 0; i < 8; i++)
7300         {
7301           int pos_raw = start_pos + i * check_order;
7302           int pos = (pos_raw + 8) % 8;
7303           int newx = x + check_xy[pos].dx;
7304           int newy = y + check_xy[pos].dy;
7305           int new_move_dir = check_xy[pos].dir;
7306
7307           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7308           {
7309             MovDir[x][y] = new_move_dir;
7310             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7311
7312             can_clone = TRUE;
7313
7314             break;
7315           }
7316         }
7317       }
7318
7319       if (can_clone)            // cloning and moving successful
7320         return;
7321
7322       // cannot clone -- try to move towards player
7323
7324       start_pos = check_pos[MovDir[x][y] & 0x0f];
7325       check_order = (RND(2) ? -1 : +1);
7326
7327       for (i = 0; i < 3; i++)
7328       {
7329         // first check start_pos, then previous/next or (next/previous) pos
7330         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7331         int pos = (pos_raw + 8) % 8;
7332         int newx = x + check_xy[pos].dx;
7333         int newy = y + check_xy[pos].dy;
7334         int new_move_dir = check_xy[pos].dir;
7335
7336         if (IS_PLAYER(newx, newy))
7337           break;
7338
7339         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7340         {
7341           MovDir[x][y] = new_move_dir;
7342           MovDelay[x][y] = level.android_move_time * 8 + 1;
7343
7344           break;
7345         }
7346       }
7347     }
7348   }
7349   else if (move_pattern == MV_TURNING_LEFT ||
7350            move_pattern == MV_TURNING_RIGHT ||
7351            move_pattern == MV_TURNING_LEFT_RIGHT ||
7352            move_pattern == MV_TURNING_RIGHT_LEFT ||
7353            move_pattern == MV_TURNING_RANDOM ||
7354            move_pattern == MV_ALL_DIRECTIONS)
7355   {
7356     boolean can_turn_left =
7357       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7358     boolean can_turn_right =
7359       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7360
7361     if (element_info[element].move_stepsize == 0)       // "not moving"
7362       return;
7363
7364     if (move_pattern == MV_TURNING_LEFT)
7365       MovDir[x][y] = left_dir;
7366     else if (move_pattern == MV_TURNING_RIGHT)
7367       MovDir[x][y] = right_dir;
7368     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7369       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7370     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7371       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7372     else if (move_pattern == MV_TURNING_RANDOM)
7373       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7374                       can_turn_right && !can_turn_left ? right_dir :
7375                       RND(2) ? left_dir : right_dir);
7376     else if (can_turn_left && can_turn_right)
7377       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7378     else if (can_turn_left)
7379       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7380     else if (can_turn_right)
7381       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7382     else
7383       MovDir[x][y] = back_dir;
7384
7385     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386   }
7387   else if (move_pattern == MV_HORIZONTAL ||
7388            move_pattern == MV_VERTICAL)
7389   {
7390     if (move_pattern & old_move_dir)
7391       MovDir[x][y] = back_dir;
7392     else if (move_pattern == MV_HORIZONTAL)
7393       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7394     else if (move_pattern == MV_VERTICAL)
7395       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7396
7397     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7398   }
7399   else if (move_pattern & MV_ANY_DIRECTION)
7400   {
7401     MovDir[x][y] = move_pattern;
7402     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7403   }
7404   else if (move_pattern & MV_WIND_DIRECTION)
7405   {
7406     MovDir[x][y] = game.wind_direction;
7407     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7408   }
7409   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7410   {
7411     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7412       MovDir[x][y] = left_dir;
7413     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7414       MovDir[x][y] = right_dir;
7415
7416     if (MovDir[x][y] != old_move_dir)
7417       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7418   }
7419   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7420   {
7421     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7422       MovDir[x][y] = right_dir;
7423     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7424       MovDir[x][y] = left_dir;
7425
7426     if (MovDir[x][y] != old_move_dir)
7427       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7428   }
7429   else if (move_pattern == MV_TOWARDS_PLAYER ||
7430            move_pattern == MV_AWAY_FROM_PLAYER)
7431   {
7432     int attr_x = -1, attr_y = -1;
7433     int newx, newy;
7434     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7435
7436     if (game.all_players_gone)
7437     {
7438       attr_x = game.exit_x;
7439       attr_y = game.exit_y;
7440     }
7441     else
7442     {
7443       int i;
7444
7445       for (i = 0; i < MAX_PLAYERS; i++)
7446       {
7447         struct PlayerInfo *player = &stored_player[i];
7448         int jx = player->jx, jy = player->jy;
7449
7450         if (!player->active)
7451           continue;
7452
7453         if (attr_x == -1 ||
7454             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7455         {
7456           attr_x = jx;
7457           attr_y = jy;
7458         }
7459       }
7460     }
7461
7462     MovDir[x][y] = MV_NONE;
7463     if (attr_x < x)
7464       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7465     else if (attr_x > x)
7466       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7467     if (attr_y < y)
7468       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7469     else if (attr_y > y)
7470       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7471
7472     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7473
7474     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7475     {
7476       boolean first_horiz = RND(2);
7477       int new_move_dir = MovDir[x][y];
7478
7479       if (element_info[element].move_stepsize == 0)     // "not moving"
7480       {
7481         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7482         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7483
7484         return;
7485       }
7486
7487       MovDir[x][y] =
7488         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489       Moving2Blocked(x, y, &newx, &newy);
7490
7491       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7492         return;
7493
7494       MovDir[x][y] =
7495         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7496       Moving2Blocked(x, y, &newx, &newy);
7497
7498       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7499         return;
7500
7501       MovDir[x][y] = old_move_dir;
7502     }
7503   }
7504   else if (move_pattern == MV_WHEN_PUSHED ||
7505            move_pattern == MV_WHEN_DROPPED)
7506   {
7507     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7508       MovDir[x][y] = MV_NONE;
7509
7510     MovDelay[x][y] = 0;
7511   }
7512   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7513   {
7514     static int test_xy[7][2] =
7515     {
7516       { 0, -1 },
7517       { -1, 0 },
7518       { +1, 0 },
7519       { 0, +1 },
7520       { 0, -1 },
7521       { -1, 0 },
7522       { +1, 0 },
7523     };
7524     static int test_dir[7] =
7525     {
7526       MV_UP,
7527       MV_LEFT,
7528       MV_RIGHT,
7529       MV_DOWN,
7530       MV_UP,
7531       MV_LEFT,
7532       MV_RIGHT,
7533     };
7534     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7535     int move_preference = -1000000;     // start with very low preference
7536     int new_move_dir = MV_NONE;
7537     int start_test = RND(4);
7538     int i;
7539
7540     for (i = 0; i < NUM_DIRECTIONS; i++)
7541     {
7542       int move_dir = test_dir[start_test + i];
7543       int move_dir_preference;
7544
7545       xx = x + test_xy[start_test + i][0];
7546       yy = y + test_xy[start_test + i][1];
7547
7548       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7549           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7550       {
7551         new_move_dir = move_dir;
7552
7553         break;
7554       }
7555
7556       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7557         continue;
7558
7559       move_dir_preference = -1 * RunnerVisit[xx][yy];
7560       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7561         move_dir_preference = PlayerVisit[xx][yy];
7562
7563       if (move_dir_preference > move_preference)
7564       {
7565         // prefer field that has not been visited for the longest time
7566         move_preference = move_dir_preference;
7567         new_move_dir = move_dir;
7568       }
7569       else if (move_dir_preference == move_preference &&
7570                move_dir == old_move_dir)
7571       {
7572         // prefer last direction when all directions are preferred equally
7573         move_preference = move_dir_preference;
7574         new_move_dir = move_dir;
7575       }
7576     }
7577
7578     MovDir[x][y] = new_move_dir;
7579     if (old_move_dir != new_move_dir)
7580       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7581   }
7582 }
7583
7584 static void TurnRound(int x, int y)
7585 {
7586   int direction = MovDir[x][y];
7587
7588   TurnRoundExt(x, y);
7589
7590   GfxDir[x][y] = MovDir[x][y];
7591
7592   if (direction != MovDir[x][y])
7593     GfxFrame[x][y] = 0;
7594
7595   if (MovDelay[x][y])
7596     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7597
7598   ResetGfxFrame(x, y);
7599 }
7600
7601 static boolean JustBeingPushed(int x, int y)
7602 {
7603   int i;
7604
7605   for (i = 0; i < MAX_PLAYERS; i++)
7606   {
7607     struct PlayerInfo *player = &stored_player[i];
7608
7609     if (player->active && player->is_pushing && player->MovPos)
7610     {
7611       int next_jx = player->jx + (player->jx - player->last_jx);
7612       int next_jy = player->jy + (player->jy - player->last_jy);
7613
7614       if (x == next_jx && y == next_jy)
7615         return TRUE;
7616     }
7617   }
7618
7619   return FALSE;
7620 }
7621
7622 static void StartMoving(int x, int y)
7623 {
7624   boolean started_moving = FALSE;       // some elements can fall _and_ move
7625   int element = Tile[x][y];
7626
7627   if (Stop[x][y])
7628     return;
7629
7630   if (MovDelay[x][y] == 0)
7631     GfxAction[x][y] = ACTION_DEFAULT;
7632
7633   if (CAN_FALL(element) && y < lev_fieldy - 1)
7634   {
7635     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7636         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7637       if (JustBeingPushed(x, y))
7638         return;
7639
7640     if (element == EL_QUICKSAND_FULL)
7641     {
7642       if (IS_FREE(x, y + 1))
7643       {
7644         InitMovingField(x, y, MV_DOWN);
7645         started_moving = TRUE;
7646
7647         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7648 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7649         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7650           Store[x][y] = EL_ROCK;
7651 #else
7652         Store[x][y] = EL_ROCK;
7653 #endif
7654
7655         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7656       }
7657       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7658       {
7659         if (!MovDelay[x][y])
7660         {
7661           MovDelay[x][y] = TILEY + 1;
7662
7663           ResetGfxAnimation(x, y);
7664           ResetGfxAnimation(x, y + 1);
7665         }
7666
7667         if (MovDelay[x][y])
7668         {
7669           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7670           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7671
7672           MovDelay[x][y]--;
7673           if (MovDelay[x][y])
7674             return;
7675         }
7676
7677         Tile[x][y] = EL_QUICKSAND_EMPTY;
7678         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7679         Store[x][y + 1] = Store[x][y];
7680         Store[x][y] = 0;
7681
7682         PlayLevelSoundAction(x, y, ACTION_FILLING);
7683       }
7684       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7685       {
7686         if (!MovDelay[x][y])
7687         {
7688           MovDelay[x][y] = TILEY + 1;
7689
7690           ResetGfxAnimation(x, y);
7691           ResetGfxAnimation(x, y + 1);
7692         }
7693
7694         if (MovDelay[x][y])
7695         {
7696           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7697           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7698
7699           MovDelay[x][y]--;
7700           if (MovDelay[x][y])
7701             return;
7702         }
7703
7704         Tile[x][y] = EL_QUICKSAND_EMPTY;
7705         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7706         Store[x][y + 1] = Store[x][y];
7707         Store[x][y] = 0;
7708
7709         PlayLevelSoundAction(x, y, ACTION_FILLING);
7710       }
7711     }
7712     else if (element == EL_QUICKSAND_FAST_FULL)
7713     {
7714       if (IS_FREE(x, y + 1))
7715       {
7716         InitMovingField(x, y, MV_DOWN);
7717         started_moving = TRUE;
7718
7719         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7720 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7721         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7722           Store[x][y] = EL_ROCK;
7723 #else
7724         Store[x][y] = EL_ROCK;
7725 #endif
7726
7727         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7728       }
7729       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7730       {
7731         if (!MovDelay[x][y])
7732         {
7733           MovDelay[x][y] = TILEY + 1;
7734
7735           ResetGfxAnimation(x, y);
7736           ResetGfxAnimation(x, y + 1);
7737         }
7738
7739         if (MovDelay[x][y])
7740         {
7741           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7742           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7743
7744           MovDelay[x][y]--;
7745           if (MovDelay[x][y])
7746             return;
7747         }
7748
7749         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7750         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7751         Store[x][y + 1] = Store[x][y];
7752         Store[x][y] = 0;
7753
7754         PlayLevelSoundAction(x, y, ACTION_FILLING);
7755       }
7756       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7757       {
7758         if (!MovDelay[x][y])
7759         {
7760           MovDelay[x][y] = TILEY + 1;
7761
7762           ResetGfxAnimation(x, y);
7763           ResetGfxAnimation(x, y + 1);
7764         }
7765
7766         if (MovDelay[x][y])
7767         {
7768           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7769           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7770
7771           MovDelay[x][y]--;
7772           if (MovDelay[x][y])
7773             return;
7774         }
7775
7776         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7777         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7778         Store[x][y + 1] = Store[x][y];
7779         Store[x][y] = 0;
7780
7781         PlayLevelSoundAction(x, y, ACTION_FILLING);
7782       }
7783     }
7784     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7785              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7786     {
7787       InitMovingField(x, y, MV_DOWN);
7788       started_moving = TRUE;
7789
7790       Tile[x][y] = EL_QUICKSAND_FILLING;
7791       Store[x][y] = element;
7792
7793       PlayLevelSoundAction(x, y, ACTION_FILLING);
7794     }
7795     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7796              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7797     {
7798       InitMovingField(x, y, MV_DOWN);
7799       started_moving = TRUE;
7800
7801       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7802       Store[x][y] = element;
7803
7804       PlayLevelSoundAction(x, y, ACTION_FILLING);
7805     }
7806     else if (element == EL_MAGIC_WALL_FULL)
7807     {
7808       if (IS_FREE(x, y + 1))
7809       {
7810         InitMovingField(x, y, MV_DOWN);
7811         started_moving = TRUE;
7812
7813         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7814         Store[x][y] = EL_CHANGED(Store[x][y]);
7815       }
7816       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7817       {
7818         if (!MovDelay[x][y])
7819           MovDelay[x][y] = TILEY / 4 + 1;
7820
7821         if (MovDelay[x][y])
7822         {
7823           MovDelay[x][y]--;
7824           if (MovDelay[x][y])
7825             return;
7826         }
7827
7828         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7829         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7830         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7831         Store[x][y] = 0;
7832       }
7833     }
7834     else if (element == EL_BD_MAGIC_WALL_FULL)
7835     {
7836       if (IS_FREE(x, y + 1))
7837       {
7838         InitMovingField(x, y, MV_DOWN);
7839         started_moving = TRUE;
7840
7841         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7842         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7843       }
7844       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7845       {
7846         if (!MovDelay[x][y])
7847           MovDelay[x][y] = TILEY / 4 + 1;
7848
7849         if (MovDelay[x][y])
7850         {
7851           MovDelay[x][y]--;
7852           if (MovDelay[x][y])
7853             return;
7854         }
7855
7856         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7857         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7858         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7859         Store[x][y] = 0;
7860       }
7861     }
7862     else if (element == EL_DC_MAGIC_WALL_FULL)
7863     {
7864       if (IS_FREE(x, y + 1))
7865       {
7866         InitMovingField(x, y, MV_DOWN);
7867         started_moving = TRUE;
7868
7869         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7870         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7871       }
7872       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7873       {
7874         if (!MovDelay[x][y])
7875           MovDelay[x][y] = TILEY / 4 + 1;
7876
7877         if (MovDelay[x][y])
7878         {
7879           MovDelay[x][y]--;
7880           if (MovDelay[x][y])
7881             return;
7882         }
7883
7884         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7885         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7886         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7887         Store[x][y] = 0;
7888       }
7889     }
7890     else if ((CAN_PASS_MAGIC_WALL(element) &&
7891               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7892                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7893              (CAN_PASS_DC_MAGIC_WALL(element) &&
7894               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7895
7896     {
7897       InitMovingField(x, y, MV_DOWN);
7898       started_moving = TRUE;
7899
7900       Tile[x][y] =
7901         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7902          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7903          EL_DC_MAGIC_WALL_FILLING);
7904       Store[x][y] = element;
7905     }
7906     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7907     {
7908       SplashAcid(x, y + 1);
7909
7910       InitMovingField(x, y, MV_DOWN);
7911       started_moving = TRUE;
7912
7913       Store[x][y] = EL_ACID;
7914     }
7915     else if (
7916              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7917               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7918              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7919               CAN_FALL(element) && WasJustFalling[x][y] &&
7920               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7921
7922              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7923               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7924               (Tile[x][y + 1] == EL_BLOCKED)))
7925     {
7926       /* this is needed for a special case not covered by calling "Impact()"
7927          from "ContinueMoving()": if an element moves to a tile directly below
7928          another element which was just falling on that tile (which was empty
7929          in the previous frame), the falling element above would just stop
7930          instead of smashing the element below (in previous version, the above
7931          element was just checked for "moving" instead of "falling", resulting
7932          in incorrect smashes caused by horizontal movement of the above
7933          element; also, the case of the player being the element to smash was
7934          simply not covered here... :-/ ) */
7935
7936       CheckCollision[x][y] = 0;
7937       CheckImpact[x][y] = 0;
7938
7939       Impact(x, y);
7940     }
7941     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7942     {
7943       if (MovDir[x][y] == MV_NONE)
7944       {
7945         InitMovingField(x, y, MV_DOWN);
7946         started_moving = TRUE;
7947       }
7948     }
7949     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7950     {
7951       if (WasJustFalling[x][y]) // prevent animation from being restarted
7952         MovDir[x][y] = MV_DOWN;
7953
7954       InitMovingField(x, y, MV_DOWN);
7955       started_moving = TRUE;
7956     }
7957     else if (element == EL_AMOEBA_DROP)
7958     {
7959       Tile[x][y] = EL_AMOEBA_GROWING;
7960       Store[x][y] = EL_AMOEBA_WET;
7961     }
7962     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7963               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7964              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7965              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7966     {
7967       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7968                                 (IS_FREE(x - 1, y + 1) ||
7969                                  Tile[x - 1][y + 1] == EL_ACID));
7970       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7971                                 (IS_FREE(x + 1, y + 1) ||
7972                                  Tile[x + 1][y + 1] == EL_ACID));
7973       boolean can_fall_any  = (can_fall_left || can_fall_right);
7974       boolean can_fall_both = (can_fall_left && can_fall_right);
7975       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7976
7977       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7978       {
7979         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7980           can_fall_right = FALSE;
7981         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7982           can_fall_left = FALSE;
7983         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7984           can_fall_right = FALSE;
7985         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7986           can_fall_left = FALSE;
7987
7988         can_fall_any  = (can_fall_left || can_fall_right);
7989         can_fall_both = FALSE;
7990       }
7991
7992       if (can_fall_both)
7993       {
7994         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7995           can_fall_right = FALSE;       // slip down on left side
7996         else
7997           can_fall_left = !(can_fall_right = RND(2));
7998
7999         can_fall_both = FALSE;
8000       }
8001
8002       if (can_fall_any)
8003       {
8004         // if not determined otherwise, prefer left side for slipping down
8005         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8006         started_moving = TRUE;
8007       }
8008     }
8009     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8010     {
8011       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8012       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8013       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8014       int belt_dir = game.belt_dir[belt_nr];
8015
8016       if ((belt_dir == MV_LEFT  && left_is_free) ||
8017           (belt_dir == MV_RIGHT && right_is_free))
8018       {
8019         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8020
8021         InitMovingField(x, y, belt_dir);
8022         started_moving = TRUE;
8023
8024         Pushed[x][y] = TRUE;
8025         Pushed[nextx][y] = TRUE;
8026
8027         GfxAction[x][y] = ACTION_DEFAULT;
8028       }
8029       else
8030       {
8031         MovDir[x][y] = 0;       // if element was moving, stop it
8032       }
8033     }
8034   }
8035
8036   // not "else if" because of elements that can fall and move (EL_SPRING)
8037   if (CAN_MOVE(element) && !started_moving)
8038   {
8039     int move_pattern = element_info[element].move_pattern;
8040     int newx, newy;
8041
8042     Moving2Blocked(x, y, &newx, &newy);
8043
8044     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8045       return;
8046
8047     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8048         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8049     {
8050       WasJustMoving[x][y] = 0;
8051       CheckCollision[x][y] = 0;
8052
8053       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8054
8055       if (Tile[x][y] != element)        // element has changed
8056         return;
8057     }
8058
8059     if (!MovDelay[x][y])        // start new movement phase
8060     {
8061       // all objects that can change their move direction after each step
8062       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8063
8064       if (element != EL_YAMYAM &&
8065           element != EL_DARK_YAMYAM &&
8066           element != EL_PACMAN &&
8067           !(move_pattern & MV_ANY_DIRECTION) &&
8068           move_pattern != MV_TURNING_LEFT &&
8069           move_pattern != MV_TURNING_RIGHT &&
8070           move_pattern != MV_TURNING_LEFT_RIGHT &&
8071           move_pattern != MV_TURNING_RIGHT_LEFT &&
8072           move_pattern != MV_TURNING_RANDOM)
8073       {
8074         TurnRound(x, y);
8075
8076         if (MovDelay[x][y] && (element == EL_BUG ||
8077                                element == EL_SPACESHIP ||
8078                                element == EL_SP_SNIKSNAK ||
8079                                element == EL_SP_ELECTRON ||
8080                                element == EL_MOLE))
8081           TEST_DrawLevelField(x, y);
8082       }
8083     }
8084
8085     if (MovDelay[x][y])         // wait some time before next movement
8086     {
8087       MovDelay[x][y]--;
8088
8089       if (element == EL_ROBOT ||
8090           element == EL_YAMYAM ||
8091           element == EL_DARK_YAMYAM)
8092       {
8093         DrawLevelElementAnimationIfNeeded(x, y, element);
8094         PlayLevelSoundAction(x, y, ACTION_WAITING);
8095       }
8096       else if (element == EL_SP_ELECTRON)
8097         DrawLevelElementAnimationIfNeeded(x, y, element);
8098       else if (element == EL_DRAGON)
8099       {
8100         int i;
8101         int dir = MovDir[x][y];
8102         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8103         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8104         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8105                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8106                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8107                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8108         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8109
8110         GfxAction[x][y] = ACTION_ATTACKING;
8111
8112         if (IS_PLAYER(x, y))
8113           DrawPlayerField(x, y);
8114         else
8115           TEST_DrawLevelField(x, y);
8116
8117         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8118
8119         for (i = 1; i <= 3; i++)
8120         {
8121           int xx = x + i * dx;
8122           int yy = y + i * dy;
8123           int sx = SCREENX(xx);
8124           int sy = SCREENY(yy);
8125           int flame_graphic = graphic + (i - 1);
8126
8127           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8128             break;
8129
8130           if (MovDelay[x][y])
8131           {
8132             int flamed = MovingOrBlocked2Element(xx, yy);
8133
8134             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8135               Bang(xx, yy);
8136             else
8137               RemoveMovingField(xx, yy);
8138
8139             ChangeDelay[xx][yy] = 0;
8140
8141             Tile[xx][yy] = EL_FLAMES;
8142
8143             if (IN_SCR_FIELD(sx, sy))
8144             {
8145               TEST_DrawLevelFieldCrumbled(xx, yy);
8146               DrawGraphic(sx, sy, flame_graphic, frame);
8147             }
8148           }
8149           else
8150           {
8151             if (Tile[xx][yy] == EL_FLAMES)
8152               Tile[xx][yy] = EL_EMPTY;
8153             TEST_DrawLevelField(xx, yy);
8154           }
8155         }
8156       }
8157
8158       if (MovDelay[x][y])       // element still has to wait some time
8159       {
8160         PlayLevelSoundAction(x, y, ACTION_WAITING);
8161
8162         return;
8163       }
8164     }
8165
8166     // now make next step
8167
8168     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8169
8170     if (DONT_COLLIDE_WITH(element) &&
8171         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8172         !PLAYER_ENEMY_PROTECTED(newx, newy))
8173     {
8174       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8175
8176       return;
8177     }
8178
8179     else if (CAN_MOVE_INTO_ACID(element) &&
8180              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8181              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8182              (MovDir[x][y] == MV_DOWN ||
8183               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8184     {
8185       SplashAcid(newx, newy);
8186       Store[x][y] = EL_ACID;
8187     }
8188     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8189     {
8190       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8191           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8192           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8193           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8194       {
8195         RemoveField(x, y);
8196         TEST_DrawLevelField(x, y);
8197
8198         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8199         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8200           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8201
8202         game.friends_still_needed--;
8203         if (!game.friends_still_needed &&
8204             !game.GameOver &&
8205             game.all_players_gone)
8206           LevelSolved();
8207
8208         return;
8209       }
8210       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8211       {
8212         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8213           TEST_DrawLevelField(newx, newy);
8214         else
8215           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8216       }
8217       else if (!IS_FREE(newx, newy))
8218       {
8219         GfxAction[x][y] = ACTION_WAITING;
8220
8221         if (IS_PLAYER(x, y))
8222           DrawPlayerField(x, y);
8223         else
8224           TEST_DrawLevelField(x, y);
8225
8226         return;
8227       }
8228     }
8229     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8230     {
8231       if (IS_FOOD_PIG(Tile[newx][newy]))
8232       {
8233         if (IS_MOVING(newx, newy))
8234           RemoveMovingField(newx, newy);
8235         else
8236         {
8237           Tile[newx][newy] = EL_EMPTY;
8238           TEST_DrawLevelField(newx, newy);
8239         }
8240
8241         PlayLevelSound(x, y, SND_PIG_DIGGING);
8242       }
8243       else if (!IS_FREE(newx, newy))
8244       {
8245         if (IS_PLAYER(x, y))
8246           DrawPlayerField(x, y);
8247         else
8248           TEST_DrawLevelField(x, y);
8249
8250         return;
8251       }
8252     }
8253     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8254     {
8255       if (Store[x][y] != EL_EMPTY)
8256       {
8257         boolean can_clone = FALSE;
8258         int xx, yy;
8259
8260         // check if element to clone is still there
8261         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8262         {
8263           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8264           {
8265             can_clone = TRUE;
8266
8267             break;
8268           }
8269         }
8270
8271         // cannot clone or target field not free anymore -- do not clone
8272         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8273           Store[x][y] = EL_EMPTY;
8274       }
8275
8276       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8277       {
8278         if (IS_MV_DIAGONAL(MovDir[x][y]))
8279         {
8280           int diagonal_move_dir = MovDir[x][y];
8281           int stored = Store[x][y];
8282           int change_delay = 8;
8283           int graphic;
8284
8285           // android is moving diagonally
8286
8287           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8288
8289           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8290           GfxElement[x][y] = EL_EMC_ANDROID;
8291           GfxAction[x][y] = ACTION_SHRINKING;
8292           GfxDir[x][y] = diagonal_move_dir;
8293           ChangeDelay[x][y] = change_delay;
8294
8295           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8296                                    GfxDir[x][y]);
8297
8298           DrawLevelGraphicAnimation(x, y, graphic);
8299           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8300
8301           if (Tile[newx][newy] == EL_ACID)
8302           {
8303             SplashAcid(newx, newy);
8304
8305             return;
8306           }
8307
8308           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8309
8310           Store[newx][newy] = EL_EMC_ANDROID;
8311           GfxElement[newx][newy] = EL_EMC_ANDROID;
8312           GfxAction[newx][newy] = ACTION_GROWING;
8313           GfxDir[newx][newy] = diagonal_move_dir;
8314           ChangeDelay[newx][newy] = change_delay;
8315
8316           graphic = el_act_dir2img(GfxElement[newx][newy],
8317                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8318
8319           DrawLevelGraphicAnimation(newx, newy, graphic);
8320           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8321
8322           return;
8323         }
8324         else
8325         {
8326           Tile[newx][newy] = EL_EMPTY;
8327           TEST_DrawLevelField(newx, newy);
8328
8329           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8330         }
8331       }
8332       else if (!IS_FREE(newx, newy))
8333       {
8334         return;
8335       }
8336     }
8337     else if (IS_CUSTOM_ELEMENT(element) &&
8338              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8339     {
8340       if (!DigFieldByCE(newx, newy, element))
8341         return;
8342
8343       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8344       {
8345         RunnerVisit[x][y] = FrameCounter;
8346         PlayerVisit[x][y] /= 8;         // expire player visit path
8347       }
8348     }
8349     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8350     {
8351       if (!IS_FREE(newx, newy))
8352       {
8353         if (IS_PLAYER(x, y))
8354           DrawPlayerField(x, y);
8355         else
8356           TEST_DrawLevelField(x, y);
8357
8358         return;
8359       }
8360       else
8361       {
8362         boolean wanna_flame = !RND(10);
8363         int dx = newx - x, dy = newy - y;
8364         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8365         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8366         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8367                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8368         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8369                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8370
8371         if ((wanna_flame ||
8372              IS_CLASSIC_ENEMY(element1) ||
8373              IS_CLASSIC_ENEMY(element2)) &&
8374             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8375             element1 != EL_FLAMES && element2 != EL_FLAMES)
8376         {
8377           ResetGfxAnimation(x, y);
8378           GfxAction[x][y] = ACTION_ATTACKING;
8379
8380           if (IS_PLAYER(x, y))
8381             DrawPlayerField(x, y);
8382           else
8383             TEST_DrawLevelField(x, y);
8384
8385           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8386
8387           MovDelay[x][y] = 50;
8388
8389           Tile[newx][newy] = EL_FLAMES;
8390           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8391             Tile[newx1][newy1] = EL_FLAMES;
8392           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8393             Tile[newx2][newy2] = EL_FLAMES;
8394
8395           return;
8396         }
8397       }
8398     }
8399     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8400              Tile[newx][newy] == EL_DIAMOND)
8401     {
8402       if (IS_MOVING(newx, newy))
8403         RemoveMovingField(newx, newy);
8404       else
8405       {
8406         Tile[newx][newy] = EL_EMPTY;
8407         TEST_DrawLevelField(newx, newy);
8408       }
8409
8410       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8411     }
8412     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8413              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8414     {
8415       if (AmoebaNr[newx][newy])
8416       {
8417         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8418         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8419             Tile[newx][newy] == EL_BD_AMOEBA)
8420           AmoebaCnt[AmoebaNr[newx][newy]]--;
8421       }
8422
8423       if (IS_MOVING(newx, newy))
8424       {
8425         RemoveMovingField(newx, newy);
8426       }
8427       else
8428       {
8429         Tile[newx][newy] = EL_EMPTY;
8430         TEST_DrawLevelField(newx, newy);
8431       }
8432
8433       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8434     }
8435     else if ((element == EL_PACMAN || element == EL_MOLE)
8436              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8437     {
8438       if (AmoebaNr[newx][newy])
8439       {
8440         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8441         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8442             Tile[newx][newy] == EL_BD_AMOEBA)
8443           AmoebaCnt[AmoebaNr[newx][newy]]--;
8444       }
8445
8446       if (element == EL_MOLE)
8447       {
8448         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8449         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8450
8451         ResetGfxAnimation(x, y);
8452         GfxAction[x][y] = ACTION_DIGGING;
8453         TEST_DrawLevelField(x, y);
8454
8455         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8456
8457         return;                         // wait for shrinking amoeba
8458       }
8459       else      // element == EL_PACMAN
8460       {
8461         Tile[newx][newy] = EL_EMPTY;
8462         TEST_DrawLevelField(newx, newy);
8463         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8464       }
8465     }
8466     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8467              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8468               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8469     {
8470       // wait for shrinking amoeba to completely disappear
8471       return;
8472     }
8473     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8474     {
8475       // object was running against a wall
8476
8477       TurnRound(x, y);
8478
8479       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8480         DrawLevelElementAnimation(x, y, element);
8481
8482       if (DONT_TOUCH(element))
8483         TestIfBadThingTouchesPlayer(x, y);
8484
8485       return;
8486     }
8487
8488     InitMovingField(x, y, MovDir[x][y]);
8489
8490     PlayLevelSoundAction(x, y, ACTION_MOVING);
8491   }
8492
8493   if (MovDir[x][y])
8494     ContinueMoving(x, y);
8495 }
8496
8497 void ContinueMoving(int x, int y)
8498 {
8499   int element = Tile[x][y];
8500   struct ElementInfo *ei = &element_info[element];
8501   int direction = MovDir[x][y];
8502   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8503   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8504   int newx = x + dx, newy = y + dy;
8505   int stored = Store[x][y];
8506   int stored_new = Store[newx][newy];
8507   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8508   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8509   boolean last_line = (newy == lev_fieldy - 1);
8510
8511   MovPos[x][y] += getElementMoveStepsize(x, y);
8512
8513   if (pushed_by_player) // special case: moving object pushed by player
8514     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8515
8516   if (ABS(MovPos[x][y]) < TILEX)
8517   {
8518     TEST_DrawLevelField(x, y);
8519
8520     return;     // element is still moving
8521   }
8522
8523   // element reached destination field
8524
8525   Tile[x][y] = EL_EMPTY;
8526   Tile[newx][newy] = element;
8527   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8528
8529   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8530   {
8531     element = Tile[newx][newy] = EL_ACID;
8532   }
8533   else if (element == EL_MOLE)
8534   {
8535     Tile[x][y] = EL_SAND;
8536
8537     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8538   }
8539   else if (element == EL_QUICKSAND_FILLING)
8540   {
8541     element = Tile[newx][newy] = get_next_element(element);
8542     Store[newx][newy] = Store[x][y];
8543   }
8544   else if (element == EL_QUICKSAND_EMPTYING)
8545   {
8546     Tile[x][y] = get_next_element(element);
8547     element = Tile[newx][newy] = Store[x][y];
8548   }
8549   else if (element == EL_QUICKSAND_FAST_FILLING)
8550   {
8551     element = Tile[newx][newy] = get_next_element(element);
8552     Store[newx][newy] = Store[x][y];
8553   }
8554   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8555   {
8556     Tile[x][y] = get_next_element(element);
8557     element = Tile[newx][newy] = Store[x][y];
8558   }
8559   else if (element == EL_MAGIC_WALL_FILLING)
8560   {
8561     element = Tile[newx][newy] = get_next_element(element);
8562     if (!game.magic_wall_active)
8563       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8564     Store[newx][newy] = Store[x][y];
8565   }
8566   else if (element == EL_MAGIC_WALL_EMPTYING)
8567   {
8568     Tile[x][y] = get_next_element(element);
8569     if (!game.magic_wall_active)
8570       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8571     element = Tile[newx][newy] = Store[x][y];
8572
8573     InitField(newx, newy, FALSE);
8574   }
8575   else if (element == EL_BD_MAGIC_WALL_FILLING)
8576   {
8577     element = Tile[newx][newy] = get_next_element(element);
8578     if (!game.magic_wall_active)
8579       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8580     Store[newx][newy] = Store[x][y];
8581   }
8582   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8583   {
8584     Tile[x][y] = get_next_element(element);
8585     if (!game.magic_wall_active)
8586       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8587     element = Tile[newx][newy] = Store[x][y];
8588
8589     InitField(newx, newy, FALSE);
8590   }
8591   else if (element == EL_DC_MAGIC_WALL_FILLING)
8592   {
8593     element = Tile[newx][newy] = get_next_element(element);
8594     if (!game.magic_wall_active)
8595       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8596     Store[newx][newy] = Store[x][y];
8597   }
8598   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8599   {
8600     Tile[x][y] = get_next_element(element);
8601     if (!game.magic_wall_active)
8602       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8603     element = Tile[newx][newy] = Store[x][y];
8604
8605     InitField(newx, newy, FALSE);
8606   }
8607   else if (element == EL_AMOEBA_DROPPING)
8608   {
8609     Tile[x][y] = get_next_element(element);
8610     element = Tile[newx][newy] = Store[x][y];
8611   }
8612   else if (element == EL_SOKOBAN_OBJECT)
8613   {
8614     if (Back[x][y])
8615       Tile[x][y] = Back[x][y];
8616
8617     if (Back[newx][newy])
8618       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8619
8620     Back[x][y] = Back[newx][newy] = 0;
8621   }
8622
8623   Store[x][y] = EL_EMPTY;
8624   MovPos[x][y] = 0;
8625   MovDir[x][y] = 0;
8626   MovDelay[x][y] = 0;
8627
8628   MovDelay[newx][newy] = 0;
8629
8630   if (CAN_CHANGE_OR_HAS_ACTION(element))
8631   {
8632     // copy element change control values to new field
8633     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8634     ChangePage[newx][newy]  = ChangePage[x][y];
8635     ChangeCount[newx][newy] = ChangeCount[x][y];
8636     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8637   }
8638
8639   CustomValue[newx][newy] = CustomValue[x][y];
8640
8641   ChangeDelay[x][y] = 0;
8642   ChangePage[x][y] = -1;
8643   ChangeCount[x][y] = 0;
8644   ChangeEvent[x][y] = -1;
8645
8646   CustomValue[x][y] = 0;
8647
8648   // copy animation control values to new field
8649   GfxFrame[newx][newy]  = GfxFrame[x][y];
8650   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8651   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8652   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8653
8654   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8655
8656   // some elements can leave other elements behind after moving
8657   if (ei->move_leave_element != EL_EMPTY &&
8658       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8659       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8660   {
8661     int move_leave_element = ei->move_leave_element;
8662
8663     // this makes it possible to leave the removed element again
8664     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8665       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8666
8667     Tile[x][y] = move_leave_element;
8668
8669     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8670       MovDir[x][y] = direction;
8671
8672     InitField(x, y, FALSE);
8673
8674     if (GFX_CRUMBLED(Tile[x][y]))
8675       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8676
8677     if (ELEM_IS_PLAYER(move_leave_element))
8678       RelocatePlayer(x, y, move_leave_element);
8679   }
8680
8681   // do this after checking for left-behind element
8682   ResetGfxAnimation(x, y);      // reset animation values for old field
8683
8684   if (!CAN_MOVE(element) ||
8685       (CAN_FALL(element) && direction == MV_DOWN &&
8686        (element == EL_SPRING ||
8687         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8688         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8689     GfxDir[x][y] = MovDir[newx][newy] = 0;
8690
8691   TEST_DrawLevelField(x, y);
8692   TEST_DrawLevelField(newx, newy);
8693
8694   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8695
8696   // prevent pushed element from moving on in pushed direction
8697   if (pushed_by_player && CAN_MOVE(element) &&
8698       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8699       !(element_info[element].move_pattern & direction))
8700     TurnRound(newx, newy);
8701
8702   // prevent elements on conveyor belt from moving on in last direction
8703   if (pushed_by_conveyor && CAN_FALL(element) &&
8704       direction & MV_HORIZONTAL)
8705     MovDir[newx][newy] = 0;
8706
8707   if (!pushed_by_player)
8708   {
8709     int nextx = newx + dx, nexty = newy + dy;
8710     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8711
8712     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8713
8714     if (CAN_FALL(element) && direction == MV_DOWN)
8715       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8716
8717     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8718       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8719
8720     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8721       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8722   }
8723
8724   if (DONT_TOUCH(element))      // object may be nasty to player or others
8725   {
8726     TestIfBadThingTouchesPlayer(newx, newy);
8727     TestIfBadThingTouchesFriend(newx, newy);
8728
8729     if (!IS_CUSTOM_ELEMENT(element))
8730       TestIfBadThingTouchesOtherBadThing(newx, newy);
8731   }
8732   else if (element == EL_PENGUIN)
8733     TestIfFriendTouchesBadThing(newx, newy);
8734
8735   if (DONT_GET_HIT_BY(element))
8736   {
8737     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8738   }
8739
8740   // give the player one last chance (one more frame) to move away
8741   if (CAN_FALL(element) && direction == MV_DOWN &&
8742       (last_line || (!IS_FREE(x, newy + 1) &&
8743                      (!IS_PLAYER(x, newy + 1) ||
8744                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8745     Impact(x, newy);
8746
8747   if (pushed_by_player && !game.use_change_when_pushing_bug)
8748   {
8749     int push_side = MV_DIR_OPPOSITE(direction);
8750     struct PlayerInfo *player = PLAYERINFO(x, y);
8751
8752     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8753                                player->index_bit, push_side);
8754     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8755                                         player->index_bit, push_side);
8756   }
8757
8758   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8759     MovDelay[newx][newy] = 1;
8760
8761   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8762
8763   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8764   TestIfElementHitsCustomElement(newx, newy, direction);
8765   TestIfPlayerTouchesCustomElement(newx, newy);
8766   TestIfElementTouchesCustomElement(newx, newy);
8767
8768   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8769       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8770     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8771                              MV_DIR_OPPOSITE(direction));
8772 }
8773
8774 int AmoebaNeighbourNr(int ax, int ay)
8775 {
8776   int i;
8777   int element = Tile[ax][ay];
8778   int group_nr = 0;
8779   static int xy[4][2] =
8780   {
8781     { 0, -1 },
8782     { -1, 0 },
8783     { +1, 0 },
8784     { 0, +1 }
8785   };
8786
8787   for (i = 0; i < NUM_DIRECTIONS; i++)
8788   {
8789     int x = ax + xy[i][0];
8790     int y = ay + xy[i][1];
8791
8792     if (!IN_LEV_FIELD(x, y))
8793       continue;
8794
8795     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8796       group_nr = AmoebaNr[x][y];
8797   }
8798
8799   return group_nr;
8800 }
8801
8802 static void AmoebaMerge(int ax, int ay)
8803 {
8804   int i, x, y, xx, yy;
8805   int new_group_nr = AmoebaNr[ax][ay];
8806   static int xy[4][2] =
8807   {
8808     { 0, -1 },
8809     { -1, 0 },
8810     { +1, 0 },
8811     { 0, +1 }
8812   };
8813
8814   if (new_group_nr == 0)
8815     return;
8816
8817   for (i = 0; i < NUM_DIRECTIONS; i++)
8818   {
8819     x = ax + xy[i][0];
8820     y = ay + xy[i][1];
8821
8822     if (!IN_LEV_FIELD(x, y))
8823       continue;
8824
8825     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8826          Tile[x][y] == EL_BD_AMOEBA ||
8827          Tile[x][y] == EL_AMOEBA_DEAD) &&
8828         AmoebaNr[x][y] != new_group_nr)
8829     {
8830       int old_group_nr = AmoebaNr[x][y];
8831
8832       if (old_group_nr == 0)
8833         return;
8834
8835       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8836       AmoebaCnt[old_group_nr] = 0;
8837       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8838       AmoebaCnt2[old_group_nr] = 0;
8839
8840       SCAN_PLAYFIELD(xx, yy)
8841       {
8842         if (AmoebaNr[xx][yy] == old_group_nr)
8843           AmoebaNr[xx][yy] = new_group_nr;
8844       }
8845     }
8846   }
8847 }
8848
8849 void AmoebaToDiamond(int ax, int ay)
8850 {
8851   int i, x, y;
8852
8853   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8854   {
8855     int group_nr = AmoebaNr[ax][ay];
8856
8857 #ifdef DEBUG
8858     if (group_nr == 0)
8859     {
8860       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8861       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8862
8863       return;
8864     }
8865 #endif
8866
8867     SCAN_PLAYFIELD(x, y)
8868     {
8869       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8870       {
8871         AmoebaNr[x][y] = 0;
8872         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8873       }
8874     }
8875
8876     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8877                             SND_AMOEBA_TURNING_TO_GEM :
8878                             SND_AMOEBA_TURNING_TO_ROCK));
8879     Bang(ax, ay);
8880   }
8881   else
8882   {
8883     static int xy[4][2] =
8884     {
8885       { 0, -1 },
8886       { -1, 0 },
8887       { +1, 0 },
8888       { 0, +1 }
8889     };
8890
8891     for (i = 0; i < NUM_DIRECTIONS; i++)
8892     {
8893       x = ax + xy[i][0];
8894       y = ay + xy[i][1];
8895
8896       if (!IN_LEV_FIELD(x, y))
8897         continue;
8898
8899       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8900       {
8901         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8902                               SND_AMOEBA_TURNING_TO_GEM :
8903                               SND_AMOEBA_TURNING_TO_ROCK));
8904         Bang(x, y);
8905       }
8906     }
8907   }
8908 }
8909
8910 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8911 {
8912   int x, y;
8913   int group_nr = AmoebaNr[ax][ay];
8914   boolean done = FALSE;
8915
8916 #ifdef DEBUG
8917   if (group_nr == 0)
8918   {
8919     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8920     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8921
8922     return;
8923   }
8924 #endif
8925
8926   SCAN_PLAYFIELD(x, y)
8927   {
8928     if (AmoebaNr[x][y] == group_nr &&
8929         (Tile[x][y] == EL_AMOEBA_DEAD ||
8930          Tile[x][y] == EL_BD_AMOEBA ||
8931          Tile[x][y] == EL_AMOEBA_GROWING))
8932     {
8933       AmoebaNr[x][y] = 0;
8934       Tile[x][y] = new_element;
8935       InitField(x, y, FALSE);
8936       TEST_DrawLevelField(x, y);
8937       done = TRUE;
8938     }
8939   }
8940
8941   if (done)
8942     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8943                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8944                             SND_BD_AMOEBA_TURNING_TO_GEM));
8945 }
8946
8947 static void AmoebaGrowing(int x, int y)
8948 {
8949   static unsigned int sound_delay = 0;
8950   static unsigned int sound_delay_value = 0;
8951
8952   if (!MovDelay[x][y])          // start new growing cycle
8953   {
8954     MovDelay[x][y] = 7;
8955
8956     if (DelayReached(&sound_delay, sound_delay_value))
8957     {
8958       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8959       sound_delay_value = 30;
8960     }
8961   }
8962
8963   if (MovDelay[x][y])           // wait some time before growing bigger
8964   {
8965     MovDelay[x][y]--;
8966     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8967     {
8968       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8969                                            6 - MovDelay[x][y]);
8970
8971       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8972     }
8973
8974     if (!MovDelay[x][y])
8975     {
8976       Tile[x][y] = Store[x][y];
8977       Store[x][y] = 0;
8978       TEST_DrawLevelField(x, y);
8979     }
8980   }
8981 }
8982
8983 static void AmoebaShrinking(int x, int y)
8984 {
8985   static unsigned int sound_delay = 0;
8986   static unsigned int sound_delay_value = 0;
8987
8988   if (!MovDelay[x][y])          // start new shrinking cycle
8989   {
8990     MovDelay[x][y] = 7;
8991
8992     if (DelayReached(&sound_delay, sound_delay_value))
8993       sound_delay_value = 30;
8994   }
8995
8996   if (MovDelay[x][y])           // wait some time before shrinking
8997   {
8998     MovDelay[x][y]--;
8999     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9000     {
9001       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9002                                            6 - MovDelay[x][y]);
9003
9004       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9005     }
9006
9007     if (!MovDelay[x][y])
9008     {
9009       Tile[x][y] = EL_EMPTY;
9010       TEST_DrawLevelField(x, y);
9011
9012       // don't let mole enter this field in this cycle;
9013       // (give priority to objects falling to this field from above)
9014       Stop[x][y] = TRUE;
9015     }
9016   }
9017 }
9018
9019 static void AmoebaReproduce(int ax, int ay)
9020 {
9021   int i;
9022   int element = Tile[ax][ay];
9023   int graphic = el2img(element);
9024   int newax = ax, neway = ay;
9025   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9026   static int xy[4][2] =
9027   {
9028     { 0, -1 },
9029     { -1, 0 },
9030     { +1, 0 },
9031     { 0, +1 }
9032   };
9033
9034   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9035   {
9036     Tile[ax][ay] = EL_AMOEBA_DEAD;
9037     TEST_DrawLevelField(ax, ay);
9038     return;
9039   }
9040
9041   if (IS_ANIMATED(graphic))
9042     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9043
9044   if (!MovDelay[ax][ay])        // start making new amoeba field
9045     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9046
9047   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9048   {
9049     MovDelay[ax][ay]--;
9050     if (MovDelay[ax][ay])
9051       return;
9052   }
9053
9054   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9055   {
9056     int start = RND(4);
9057     int x = ax + xy[start][0];
9058     int y = ay + xy[start][1];
9059
9060     if (!IN_LEV_FIELD(x, y))
9061       return;
9062
9063     if (IS_FREE(x, y) ||
9064         CAN_GROW_INTO(Tile[x][y]) ||
9065         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9066         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9067     {
9068       newax = x;
9069       neway = y;
9070     }
9071
9072     if (newax == ax && neway == ay)
9073       return;
9074   }
9075   else                          // normal or "filled" (BD style) amoeba
9076   {
9077     int start = RND(4);
9078     boolean waiting_for_player = FALSE;
9079
9080     for (i = 0; i < NUM_DIRECTIONS; i++)
9081     {
9082       int j = (start + i) % 4;
9083       int x = ax + xy[j][0];
9084       int y = ay + xy[j][1];
9085
9086       if (!IN_LEV_FIELD(x, y))
9087         continue;
9088
9089       if (IS_FREE(x, y) ||
9090           CAN_GROW_INTO(Tile[x][y]) ||
9091           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9092           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9093       {
9094         newax = x;
9095         neway = y;
9096         break;
9097       }
9098       else if (IS_PLAYER(x, y))
9099         waiting_for_player = TRUE;
9100     }
9101
9102     if (newax == ax && neway == ay)             // amoeba cannot grow
9103     {
9104       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9105       {
9106         Tile[ax][ay] = EL_AMOEBA_DEAD;
9107         TEST_DrawLevelField(ax, ay);
9108         AmoebaCnt[AmoebaNr[ax][ay]]--;
9109
9110         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9111         {
9112           if (element == EL_AMOEBA_FULL)
9113             AmoebaToDiamond(ax, ay);
9114           else if (element == EL_BD_AMOEBA)
9115             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9116         }
9117       }
9118       return;
9119     }
9120     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9121     {
9122       // amoeba gets larger by growing in some direction
9123
9124       int new_group_nr = AmoebaNr[ax][ay];
9125
9126 #ifdef DEBUG
9127   if (new_group_nr == 0)
9128   {
9129     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9130           newax, neway);
9131     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9132
9133     return;
9134   }
9135 #endif
9136
9137       AmoebaNr[newax][neway] = new_group_nr;
9138       AmoebaCnt[new_group_nr]++;
9139       AmoebaCnt2[new_group_nr]++;
9140
9141       // if amoeba touches other amoeba(s) after growing, unify them
9142       AmoebaMerge(newax, neway);
9143
9144       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9145       {
9146         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9147         return;
9148       }
9149     }
9150   }
9151
9152   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9153       (neway == lev_fieldy - 1 && newax != ax))
9154   {
9155     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9156     Store[newax][neway] = element;
9157   }
9158   else if (neway == ay || element == EL_EMC_DRIPPER)
9159   {
9160     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9161
9162     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9163   }
9164   else
9165   {
9166     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9167     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9168     Store[ax][ay] = EL_AMOEBA_DROP;
9169     ContinueMoving(ax, ay);
9170     return;
9171   }
9172
9173   TEST_DrawLevelField(newax, neway);
9174 }
9175
9176 static void Life(int ax, int ay)
9177 {
9178   int x1, y1, x2, y2;
9179   int life_time = 40;
9180   int element = Tile[ax][ay];
9181   int graphic = el2img(element);
9182   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9183                          level.biomaze);
9184   boolean changed = FALSE;
9185
9186   if (IS_ANIMATED(graphic))
9187     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9188
9189   if (Stop[ax][ay])
9190     return;
9191
9192   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9193     MovDelay[ax][ay] = life_time;
9194
9195   if (MovDelay[ax][ay])         // wait some time before next cycle
9196   {
9197     MovDelay[ax][ay]--;
9198     if (MovDelay[ax][ay])
9199       return;
9200   }
9201
9202   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9203   {
9204     int xx = ax+x1, yy = ay+y1;
9205     int old_element = Tile[xx][yy];
9206     int num_neighbours = 0;
9207
9208     if (!IN_LEV_FIELD(xx, yy))
9209       continue;
9210
9211     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9212     {
9213       int x = xx+x2, y = yy+y2;
9214
9215       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9216         continue;
9217
9218       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9219       boolean is_neighbour = FALSE;
9220
9221       if (level.use_life_bugs)
9222         is_neighbour =
9223           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9224            (IS_FREE(x, y)                             &&  Stop[x][y]));
9225       else
9226         is_neighbour =
9227           (Last[x][y] == element || is_player_cell);
9228
9229       if (is_neighbour)
9230         num_neighbours++;
9231     }
9232
9233     boolean is_free = FALSE;
9234
9235     if (level.use_life_bugs)
9236       is_free = (IS_FREE(xx, yy));
9237     else
9238       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9239
9240     if (xx == ax && yy == ay)           // field in the middle
9241     {
9242       if (num_neighbours < life_parameter[0] ||
9243           num_neighbours > life_parameter[1])
9244       {
9245         Tile[xx][yy] = EL_EMPTY;
9246         if (Tile[xx][yy] != old_element)
9247           TEST_DrawLevelField(xx, yy);
9248         Stop[xx][yy] = TRUE;
9249         changed = TRUE;
9250       }
9251     }
9252     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9253     {                                   // free border field
9254       if (num_neighbours >= life_parameter[2] &&
9255           num_neighbours <= life_parameter[3])
9256       {
9257         Tile[xx][yy] = element;
9258         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9259         if (Tile[xx][yy] != old_element)
9260           TEST_DrawLevelField(xx, yy);
9261         Stop[xx][yy] = TRUE;
9262         changed = TRUE;
9263       }
9264     }
9265   }
9266
9267   if (changed)
9268     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9269                    SND_GAME_OF_LIFE_GROWING);
9270 }
9271
9272 static void InitRobotWheel(int x, int y)
9273 {
9274   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9275 }
9276
9277 static void RunRobotWheel(int x, int y)
9278 {
9279   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9280 }
9281
9282 static void StopRobotWheel(int x, int y)
9283 {
9284   if (game.robot_wheel_x == x &&
9285       game.robot_wheel_y == y)
9286   {
9287     game.robot_wheel_x = -1;
9288     game.robot_wheel_y = -1;
9289     game.robot_wheel_active = FALSE;
9290   }
9291 }
9292
9293 static void InitTimegateWheel(int x, int y)
9294 {
9295   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9296 }
9297
9298 static void RunTimegateWheel(int x, int y)
9299 {
9300   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9301 }
9302
9303 static void InitMagicBallDelay(int x, int y)
9304 {
9305   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9306 }
9307
9308 static void ActivateMagicBall(int bx, int by)
9309 {
9310   int x, y;
9311
9312   if (level.ball_random)
9313   {
9314     int pos_border = RND(8);    // select one of the eight border elements
9315     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9316     int xx = pos_content % 3;
9317     int yy = pos_content / 3;
9318
9319     x = bx - 1 + xx;
9320     y = by - 1 + yy;
9321
9322     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9323       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9324   }
9325   else
9326   {
9327     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9328     {
9329       int xx = x - bx + 1;
9330       int yy = y - by + 1;
9331
9332       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9333         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9334     }
9335   }
9336
9337   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9338 }
9339
9340 static void CheckExit(int x, int y)
9341 {
9342   if (game.gems_still_needed > 0 ||
9343       game.sokoban_fields_still_needed > 0 ||
9344       game.sokoban_objects_still_needed > 0 ||
9345       game.lights_still_needed > 0)
9346   {
9347     int element = Tile[x][y];
9348     int graphic = el2img(element);
9349
9350     if (IS_ANIMATED(graphic))
9351       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9352
9353     return;
9354   }
9355
9356   // do not re-open exit door closed after last player
9357   if (game.all_players_gone)
9358     return;
9359
9360   Tile[x][y] = EL_EXIT_OPENING;
9361
9362   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9363 }
9364
9365 static void CheckExitEM(int x, int y)
9366 {
9367   if (game.gems_still_needed > 0 ||
9368       game.sokoban_fields_still_needed > 0 ||
9369       game.sokoban_objects_still_needed > 0 ||
9370       game.lights_still_needed > 0)
9371   {
9372     int element = Tile[x][y];
9373     int graphic = el2img(element);
9374
9375     if (IS_ANIMATED(graphic))
9376       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9377
9378     return;
9379   }
9380
9381   // do not re-open exit door closed after last player
9382   if (game.all_players_gone)
9383     return;
9384
9385   Tile[x][y] = EL_EM_EXIT_OPENING;
9386
9387   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9388 }
9389
9390 static void CheckExitSteel(int x, int y)
9391 {
9392   if (game.gems_still_needed > 0 ||
9393       game.sokoban_fields_still_needed > 0 ||
9394       game.sokoban_objects_still_needed > 0 ||
9395       game.lights_still_needed > 0)
9396   {
9397     int element = Tile[x][y];
9398     int graphic = el2img(element);
9399
9400     if (IS_ANIMATED(graphic))
9401       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9402
9403     return;
9404   }
9405
9406   // do not re-open exit door closed after last player
9407   if (game.all_players_gone)
9408     return;
9409
9410   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9411
9412   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9413 }
9414
9415 static void CheckExitSteelEM(int x, int y)
9416 {
9417   if (game.gems_still_needed > 0 ||
9418       game.sokoban_fields_still_needed > 0 ||
9419       game.sokoban_objects_still_needed > 0 ||
9420       game.lights_still_needed > 0)
9421   {
9422     int element = Tile[x][y];
9423     int graphic = el2img(element);
9424
9425     if (IS_ANIMATED(graphic))
9426       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9427
9428     return;
9429   }
9430
9431   // do not re-open exit door closed after last player
9432   if (game.all_players_gone)
9433     return;
9434
9435   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9436
9437   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9438 }
9439
9440 static void CheckExitSP(int x, int y)
9441 {
9442   if (game.gems_still_needed > 0)
9443   {
9444     int element = Tile[x][y];
9445     int graphic = el2img(element);
9446
9447     if (IS_ANIMATED(graphic))
9448       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9449
9450     return;
9451   }
9452
9453   // do not re-open exit door closed after last player
9454   if (game.all_players_gone)
9455     return;
9456
9457   Tile[x][y] = EL_SP_EXIT_OPENING;
9458
9459   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9460 }
9461
9462 static void CloseAllOpenTimegates(void)
9463 {
9464   int x, y;
9465
9466   SCAN_PLAYFIELD(x, y)
9467   {
9468     int element = Tile[x][y];
9469
9470     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9471     {
9472       Tile[x][y] = EL_TIMEGATE_CLOSING;
9473
9474       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9475     }
9476   }
9477 }
9478
9479 static void DrawTwinkleOnField(int x, int y)
9480 {
9481   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9482     return;
9483
9484   if (Tile[x][y] == EL_BD_DIAMOND)
9485     return;
9486
9487   if (MovDelay[x][y] == 0)      // next animation frame
9488     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9489
9490   if (MovDelay[x][y] != 0)      // wait some time before next frame
9491   {
9492     MovDelay[x][y]--;
9493
9494     DrawLevelElementAnimation(x, y, Tile[x][y]);
9495
9496     if (MovDelay[x][y] != 0)
9497     {
9498       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9499                                            10 - MovDelay[x][y]);
9500
9501       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9502     }
9503   }
9504 }
9505
9506 static void MauerWaechst(int x, int y)
9507 {
9508   int delay = 6;
9509
9510   if (!MovDelay[x][y])          // next animation frame
9511     MovDelay[x][y] = 3 * delay;
9512
9513   if (MovDelay[x][y])           // wait some time before next frame
9514   {
9515     MovDelay[x][y]--;
9516
9517     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9518     {
9519       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9520       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9521
9522       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9523     }
9524
9525     if (!MovDelay[x][y])
9526     {
9527       if (MovDir[x][y] == MV_LEFT)
9528       {
9529         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9530           TEST_DrawLevelField(x - 1, y);
9531       }
9532       else if (MovDir[x][y] == MV_RIGHT)
9533       {
9534         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9535           TEST_DrawLevelField(x + 1, y);
9536       }
9537       else if (MovDir[x][y] == MV_UP)
9538       {
9539         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9540           TEST_DrawLevelField(x, y - 1);
9541       }
9542       else
9543       {
9544         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9545           TEST_DrawLevelField(x, y + 1);
9546       }
9547
9548       Tile[x][y] = Store[x][y];
9549       Store[x][y] = 0;
9550       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9551       TEST_DrawLevelField(x, y);
9552     }
9553   }
9554 }
9555
9556 static void MauerAbleger(int ax, int ay)
9557 {
9558   int element = Tile[ax][ay];
9559   int graphic = el2img(element);
9560   boolean oben_frei = FALSE, unten_frei = FALSE;
9561   boolean links_frei = FALSE, rechts_frei = FALSE;
9562   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9563   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9564   boolean new_wall = FALSE;
9565
9566   if (IS_ANIMATED(graphic))
9567     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9568
9569   if (!MovDelay[ax][ay])        // start building new wall
9570     MovDelay[ax][ay] = 6;
9571
9572   if (MovDelay[ax][ay])         // wait some time before building new wall
9573   {
9574     MovDelay[ax][ay]--;
9575     if (MovDelay[ax][ay])
9576       return;
9577   }
9578
9579   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9580     oben_frei = TRUE;
9581   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9582     unten_frei = TRUE;
9583   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9584     links_frei = TRUE;
9585   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9586     rechts_frei = TRUE;
9587
9588   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9589       element == EL_EXPANDABLE_WALL_ANY)
9590   {
9591     if (oben_frei)
9592     {
9593       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9594       Store[ax][ay-1] = element;
9595       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9596       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9597         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9598                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9599       new_wall = TRUE;
9600     }
9601     if (unten_frei)
9602     {
9603       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9604       Store[ax][ay+1] = element;
9605       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9606       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9607         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9608                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9609       new_wall = TRUE;
9610     }
9611   }
9612
9613   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9614       element == EL_EXPANDABLE_WALL_ANY ||
9615       element == EL_EXPANDABLE_WALL ||
9616       element == EL_BD_EXPANDABLE_WALL)
9617   {
9618     if (links_frei)
9619     {
9620       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9621       Store[ax-1][ay] = element;
9622       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9623       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9624         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9625                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9626       new_wall = TRUE;
9627     }
9628
9629     if (rechts_frei)
9630     {
9631       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9632       Store[ax+1][ay] = element;
9633       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9634       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9635         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9636                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9637       new_wall = TRUE;
9638     }
9639   }
9640
9641   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9642     TEST_DrawLevelField(ax, ay);
9643
9644   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9645     oben_massiv = TRUE;
9646   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9647     unten_massiv = TRUE;
9648   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9649     links_massiv = TRUE;
9650   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9651     rechts_massiv = TRUE;
9652
9653   if (((oben_massiv && unten_massiv) ||
9654        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9655        element == EL_EXPANDABLE_WALL) &&
9656       ((links_massiv && rechts_massiv) ||
9657        element == EL_EXPANDABLE_WALL_VERTICAL))
9658     Tile[ax][ay] = EL_WALL;
9659
9660   if (new_wall)
9661     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9662 }
9663
9664 static void MauerAblegerStahl(int ax, int ay)
9665 {
9666   int element = Tile[ax][ay];
9667   int graphic = el2img(element);
9668   boolean oben_frei = FALSE, unten_frei = FALSE;
9669   boolean links_frei = FALSE, rechts_frei = FALSE;
9670   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9671   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9672   boolean new_wall = FALSE;
9673
9674   if (IS_ANIMATED(graphic))
9675     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9676
9677   if (!MovDelay[ax][ay])        // start building new wall
9678     MovDelay[ax][ay] = 6;
9679
9680   if (MovDelay[ax][ay])         // wait some time before building new wall
9681   {
9682     MovDelay[ax][ay]--;
9683     if (MovDelay[ax][ay])
9684       return;
9685   }
9686
9687   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9688     oben_frei = TRUE;
9689   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9690     unten_frei = TRUE;
9691   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9692     links_frei = TRUE;
9693   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9694     rechts_frei = TRUE;
9695
9696   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9697       element == EL_EXPANDABLE_STEELWALL_ANY)
9698   {
9699     if (oben_frei)
9700     {
9701       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9702       Store[ax][ay-1] = element;
9703       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9704       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9705         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9706                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9707       new_wall = TRUE;
9708     }
9709     if (unten_frei)
9710     {
9711       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9712       Store[ax][ay+1] = element;
9713       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9714       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9715         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9716                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9717       new_wall = TRUE;
9718     }
9719   }
9720
9721   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9722       element == EL_EXPANDABLE_STEELWALL_ANY)
9723   {
9724     if (links_frei)
9725     {
9726       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9727       Store[ax-1][ay] = element;
9728       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9729       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9730         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9731                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9732       new_wall = TRUE;
9733     }
9734
9735     if (rechts_frei)
9736     {
9737       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9738       Store[ax+1][ay] = element;
9739       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9740       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9741         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9742                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9743       new_wall = TRUE;
9744     }
9745   }
9746
9747   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9748     oben_massiv = TRUE;
9749   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9750     unten_massiv = TRUE;
9751   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9752     links_massiv = TRUE;
9753   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9754     rechts_massiv = TRUE;
9755
9756   if (((oben_massiv && unten_massiv) ||
9757        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9758       ((links_massiv && rechts_massiv) ||
9759        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9760     Tile[ax][ay] = EL_STEELWALL;
9761
9762   if (new_wall)
9763     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9764 }
9765
9766 static void CheckForDragon(int x, int y)
9767 {
9768   int i, j;
9769   boolean dragon_found = FALSE;
9770   static int xy[4][2] =
9771   {
9772     { 0, -1 },
9773     { -1, 0 },
9774     { +1, 0 },
9775     { 0, +1 }
9776   };
9777
9778   for (i = 0; i < NUM_DIRECTIONS; i++)
9779   {
9780     for (j = 0; j < 4; j++)
9781     {
9782       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9783
9784       if (IN_LEV_FIELD(xx, yy) &&
9785           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9786       {
9787         if (Tile[xx][yy] == EL_DRAGON)
9788           dragon_found = TRUE;
9789       }
9790       else
9791         break;
9792     }
9793   }
9794
9795   if (!dragon_found)
9796   {
9797     for (i = 0; i < NUM_DIRECTIONS; i++)
9798     {
9799       for (j = 0; j < 3; j++)
9800       {
9801         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9802   
9803         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9804         {
9805           Tile[xx][yy] = EL_EMPTY;
9806           TEST_DrawLevelField(xx, yy);
9807         }
9808         else
9809           break;
9810       }
9811     }
9812   }
9813 }
9814
9815 static void InitBuggyBase(int x, int y)
9816 {
9817   int element = Tile[x][y];
9818   int activating_delay = FRAMES_PER_SECOND / 4;
9819
9820   ChangeDelay[x][y] =
9821     (element == EL_SP_BUGGY_BASE ?
9822      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9823      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9824      activating_delay :
9825      element == EL_SP_BUGGY_BASE_ACTIVE ?
9826      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9827 }
9828
9829 static void WarnBuggyBase(int x, int y)
9830 {
9831   int i;
9832   static int xy[4][2] =
9833   {
9834     { 0, -1 },
9835     { -1, 0 },
9836     { +1, 0 },
9837     { 0, +1 }
9838   };
9839
9840   for (i = 0; i < NUM_DIRECTIONS; i++)
9841   {
9842     int xx = x + xy[i][0];
9843     int yy = y + xy[i][1];
9844
9845     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9846     {
9847       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9848
9849       break;
9850     }
9851   }
9852 }
9853
9854 static void InitTrap(int x, int y)
9855 {
9856   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9857 }
9858
9859 static void ActivateTrap(int x, int y)
9860 {
9861   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9862 }
9863
9864 static void ChangeActiveTrap(int x, int y)
9865 {
9866   int graphic = IMG_TRAP_ACTIVE;
9867
9868   // if new animation frame was drawn, correct crumbled sand border
9869   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9870     TEST_DrawLevelFieldCrumbled(x, y);
9871 }
9872
9873 static int getSpecialActionElement(int element, int number, int base_element)
9874 {
9875   return (element != EL_EMPTY ? element :
9876           number != -1 ? base_element + number - 1 :
9877           EL_EMPTY);
9878 }
9879
9880 static int getModifiedActionNumber(int value_old, int operator, int operand,
9881                                    int value_min, int value_max)
9882 {
9883   int value_new = (operator == CA_MODE_SET      ? operand :
9884                    operator == CA_MODE_ADD      ? value_old + operand :
9885                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9886                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9887                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9888                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9889                    value_old);
9890
9891   return (value_new < value_min ? value_min :
9892           value_new > value_max ? value_max :
9893           value_new);
9894 }
9895
9896 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9897 {
9898   struct ElementInfo *ei = &element_info[element];
9899   struct ElementChangeInfo *change = &ei->change_page[page];
9900   int target_element = change->target_element;
9901   int action_type = change->action_type;
9902   int action_mode = change->action_mode;
9903   int action_arg = change->action_arg;
9904   int action_element = change->action_element;
9905   int i;
9906
9907   if (!change->has_action)
9908     return;
9909
9910   // ---------- determine action paramater values -----------------------------
9911
9912   int level_time_value =
9913     (level.time > 0 ? TimeLeft :
9914      TimePlayed);
9915
9916   int action_arg_element_raw =
9917     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9918      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9919      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9920      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9921      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9922      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9923      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9924      EL_EMPTY);
9925   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9926
9927   int action_arg_direction =
9928     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9929      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9930      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9931      change->actual_trigger_side :
9932      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9933      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9934      MV_NONE);
9935
9936   int action_arg_number_min =
9937     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9938      CA_ARG_MIN);
9939
9940   int action_arg_number_max =
9941     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9942      action_type == CA_SET_LEVEL_GEMS ? 999 :
9943      action_type == CA_SET_LEVEL_TIME ? 9999 :
9944      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9945      action_type == CA_SET_CE_VALUE ? 9999 :
9946      action_type == CA_SET_CE_SCORE ? 9999 :
9947      CA_ARG_MAX);
9948
9949   int action_arg_number_reset =
9950     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9951      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9952      action_type == CA_SET_LEVEL_TIME ? level.time :
9953      action_type == CA_SET_LEVEL_SCORE ? 0 :
9954      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9955      action_type == CA_SET_CE_SCORE ? 0 :
9956      0);
9957
9958   int action_arg_number =
9959     (action_arg <= CA_ARG_MAX ? action_arg :
9960      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9961      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9962      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9963      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9964      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9965      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9966      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9967      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9968      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9969      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9970      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9971      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9972      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9973      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9974      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9975      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9976      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9977      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9978      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9979      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9980      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9981      -1);
9982
9983   int action_arg_number_old =
9984     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9985      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9986      action_type == CA_SET_LEVEL_SCORE ? game.score :
9987      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9988      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9989      0);
9990
9991   int action_arg_number_new =
9992     getModifiedActionNumber(action_arg_number_old,
9993                             action_mode, action_arg_number,
9994                             action_arg_number_min, action_arg_number_max);
9995
9996   int trigger_player_bits =
9997     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9998      change->actual_trigger_player_bits : change->trigger_player);
9999
10000   int action_arg_player_bits =
10001     (action_arg >= CA_ARG_PLAYER_1 &&
10002      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10003      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10004      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10005      PLAYER_BITS_ANY);
10006
10007   // ---------- execute action  -----------------------------------------------
10008
10009   switch (action_type)
10010   {
10011     case CA_NO_ACTION:
10012     {
10013       return;
10014     }
10015
10016     // ---------- level actions  ----------------------------------------------
10017
10018     case CA_RESTART_LEVEL:
10019     {
10020       game.restart_level = TRUE;
10021
10022       break;
10023     }
10024
10025     case CA_SHOW_ENVELOPE:
10026     {
10027       int element = getSpecialActionElement(action_arg_element,
10028                                             action_arg_number, EL_ENVELOPE_1);
10029
10030       if (IS_ENVELOPE(element))
10031         local_player->show_envelope = element;
10032
10033       break;
10034     }
10035
10036     case CA_SET_LEVEL_TIME:
10037     {
10038       if (level.time > 0)       // only modify limited time value
10039       {
10040         TimeLeft = action_arg_number_new;
10041
10042         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10043
10044         DisplayGameControlValues();
10045
10046         if (!TimeLeft && setup.time_limit)
10047           for (i = 0; i < MAX_PLAYERS; i++)
10048             KillPlayer(&stored_player[i]);
10049       }
10050
10051       break;
10052     }
10053
10054     case CA_SET_LEVEL_SCORE:
10055     {
10056       game.score = action_arg_number_new;
10057
10058       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10059
10060       DisplayGameControlValues();
10061
10062       break;
10063     }
10064
10065     case CA_SET_LEVEL_GEMS:
10066     {
10067       game.gems_still_needed = action_arg_number_new;
10068
10069       game.snapshot.collected_item = TRUE;
10070
10071       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10072
10073       DisplayGameControlValues();
10074
10075       break;
10076     }
10077
10078     case CA_SET_LEVEL_WIND:
10079     {
10080       game.wind_direction = action_arg_direction;
10081
10082       break;
10083     }
10084
10085     case CA_SET_LEVEL_RANDOM_SEED:
10086     {
10087       // ensure that setting a new random seed while playing is predictable
10088       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10089
10090       break;
10091     }
10092
10093     // ---------- player actions  ---------------------------------------------
10094
10095     case CA_MOVE_PLAYER:
10096     case CA_MOVE_PLAYER_NEW:
10097     {
10098       // automatically move to the next field in specified direction
10099       for (i = 0; i < MAX_PLAYERS; i++)
10100         if (trigger_player_bits & (1 << i))
10101           if (action_type == CA_MOVE_PLAYER ||
10102               stored_player[i].MovPos == 0)
10103             stored_player[i].programmed_action = action_arg_direction;
10104
10105       break;
10106     }
10107
10108     case CA_EXIT_PLAYER:
10109     {
10110       for (i = 0; i < MAX_PLAYERS; i++)
10111         if (action_arg_player_bits & (1 << i))
10112           ExitPlayer(&stored_player[i]);
10113
10114       if (game.players_still_needed == 0)
10115         LevelSolved();
10116
10117       break;
10118     }
10119
10120     case CA_KILL_PLAYER:
10121     {
10122       for (i = 0; i < MAX_PLAYERS; i++)
10123         if (action_arg_player_bits & (1 << i))
10124           KillPlayer(&stored_player[i]);
10125
10126       break;
10127     }
10128
10129     case CA_SET_PLAYER_KEYS:
10130     {
10131       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10132       int element = getSpecialActionElement(action_arg_element,
10133                                             action_arg_number, EL_KEY_1);
10134
10135       if (IS_KEY(element))
10136       {
10137         for (i = 0; i < MAX_PLAYERS; i++)
10138         {
10139           if (trigger_player_bits & (1 << i))
10140           {
10141             stored_player[i].key[KEY_NR(element)] = key_state;
10142
10143             DrawGameDoorValues();
10144           }
10145         }
10146       }
10147
10148       break;
10149     }
10150
10151     case CA_SET_PLAYER_SPEED:
10152     {
10153       for (i = 0; i < MAX_PLAYERS; i++)
10154       {
10155         if (trigger_player_bits & (1 << i))
10156         {
10157           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10158
10159           if (action_arg == CA_ARG_SPEED_FASTER &&
10160               stored_player[i].cannot_move)
10161           {
10162             action_arg_number = STEPSIZE_VERY_SLOW;
10163           }
10164           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10165                    action_arg == CA_ARG_SPEED_FASTER)
10166           {
10167             action_arg_number = 2;
10168             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10169                            CA_MODE_MULTIPLY);
10170           }
10171           else if (action_arg == CA_ARG_NUMBER_RESET)
10172           {
10173             action_arg_number = level.initial_player_stepsize[i];
10174           }
10175
10176           move_stepsize =
10177             getModifiedActionNumber(move_stepsize,
10178                                     action_mode,
10179                                     action_arg_number,
10180                                     action_arg_number_min,
10181                                     action_arg_number_max);
10182
10183           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10184         }
10185       }
10186
10187       break;
10188     }
10189
10190     case CA_SET_PLAYER_SHIELD:
10191     {
10192       for (i = 0; i < MAX_PLAYERS; i++)
10193       {
10194         if (trigger_player_bits & (1 << i))
10195         {
10196           if (action_arg == CA_ARG_SHIELD_OFF)
10197           {
10198             stored_player[i].shield_normal_time_left = 0;
10199             stored_player[i].shield_deadly_time_left = 0;
10200           }
10201           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10202           {
10203             stored_player[i].shield_normal_time_left = 999999;
10204           }
10205           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10206           {
10207             stored_player[i].shield_normal_time_left = 999999;
10208             stored_player[i].shield_deadly_time_left = 999999;
10209           }
10210         }
10211       }
10212
10213       break;
10214     }
10215
10216     case CA_SET_PLAYER_GRAVITY:
10217     {
10218       for (i = 0; i < MAX_PLAYERS; i++)
10219       {
10220         if (trigger_player_bits & (1 << i))
10221         {
10222           stored_player[i].gravity =
10223             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10224              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10225              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10226              stored_player[i].gravity);
10227         }
10228       }
10229
10230       break;
10231     }
10232
10233     case CA_SET_PLAYER_ARTWORK:
10234     {
10235       for (i = 0; i < MAX_PLAYERS; i++)
10236       {
10237         if (trigger_player_bits & (1 << i))
10238         {
10239           int artwork_element = action_arg_element;
10240
10241           if (action_arg == CA_ARG_ELEMENT_RESET)
10242             artwork_element =
10243               (level.use_artwork_element[i] ? level.artwork_element[i] :
10244                stored_player[i].element_nr);
10245
10246           if (stored_player[i].artwork_element != artwork_element)
10247             stored_player[i].Frame = 0;
10248
10249           stored_player[i].artwork_element = artwork_element;
10250
10251           SetPlayerWaiting(&stored_player[i], FALSE);
10252
10253           // set number of special actions for bored and sleeping animation
10254           stored_player[i].num_special_action_bored =
10255             get_num_special_action(artwork_element,
10256                                    ACTION_BORING_1, ACTION_BORING_LAST);
10257           stored_player[i].num_special_action_sleeping =
10258             get_num_special_action(artwork_element,
10259                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10260         }
10261       }
10262
10263       break;
10264     }
10265
10266     case CA_SET_PLAYER_INVENTORY:
10267     {
10268       for (i = 0; i < MAX_PLAYERS; i++)
10269       {
10270         struct PlayerInfo *player = &stored_player[i];
10271         int j, k;
10272
10273         if (trigger_player_bits & (1 << i))
10274         {
10275           int inventory_element = action_arg_element;
10276
10277           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10278               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10279               action_arg == CA_ARG_ELEMENT_ACTION)
10280           {
10281             int element = inventory_element;
10282             int collect_count = element_info[element].collect_count_initial;
10283
10284             if (!IS_CUSTOM_ELEMENT(element))
10285               collect_count = 1;
10286
10287             if (collect_count == 0)
10288               player->inventory_infinite_element = element;
10289             else
10290               for (k = 0; k < collect_count; k++)
10291                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10292                   player->inventory_element[player->inventory_size++] =
10293                     element;
10294           }
10295           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10296                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10297                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10298           {
10299             if (player->inventory_infinite_element != EL_UNDEFINED &&
10300                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10301                                      action_arg_element_raw))
10302               player->inventory_infinite_element = EL_UNDEFINED;
10303
10304             for (k = 0, j = 0; j < player->inventory_size; j++)
10305             {
10306               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10307                                         action_arg_element_raw))
10308                 player->inventory_element[k++] = player->inventory_element[j];
10309             }
10310
10311             player->inventory_size = k;
10312           }
10313           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10314           {
10315             if (player->inventory_size > 0)
10316             {
10317               for (j = 0; j < player->inventory_size - 1; j++)
10318                 player->inventory_element[j] = player->inventory_element[j + 1];
10319
10320               player->inventory_size--;
10321             }
10322           }
10323           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10324           {
10325             if (player->inventory_size > 0)
10326               player->inventory_size--;
10327           }
10328           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10329           {
10330             player->inventory_infinite_element = EL_UNDEFINED;
10331             player->inventory_size = 0;
10332           }
10333           else if (action_arg == CA_ARG_INVENTORY_RESET)
10334           {
10335             player->inventory_infinite_element = EL_UNDEFINED;
10336             player->inventory_size = 0;
10337
10338             if (level.use_initial_inventory[i])
10339             {
10340               for (j = 0; j < level.initial_inventory_size[i]; j++)
10341               {
10342                 int element = level.initial_inventory_content[i][j];
10343                 int collect_count = element_info[element].collect_count_initial;
10344
10345                 if (!IS_CUSTOM_ELEMENT(element))
10346                   collect_count = 1;
10347
10348                 if (collect_count == 0)
10349                   player->inventory_infinite_element = element;
10350                 else
10351                   for (k = 0; k < collect_count; k++)
10352                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10353                       player->inventory_element[player->inventory_size++] =
10354                         element;
10355               }
10356             }
10357           }
10358         }
10359       }
10360
10361       break;
10362     }
10363
10364     // ---------- CE actions  -------------------------------------------------
10365
10366     case CA_SET_CE_VALUE:
10367     {
10368       int last_ce_value = CustomValue[x][y];
10369
10370       CustomValue[x][y] = action_arg_number_new;
10371
10372       if (CustomValue[x][y] != last_ce_value)
10373       {
10374         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10375         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10376
10377         if (CustomValue[x][y] == 0)
10378         {
10379           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10380           ChangeCount[x][y] = 0;        // allow at least one more change
10381
10382           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10383           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10384         }
10385       }
10386
10387       break;
10388     }
10389
10390     case CA_SET_CE_SCORE:
10391     {
10392       int last_ce_score = ei->collect_score;
10393
10394       ei->collect_score = action_arg_number_new;
10395
10396       if (ei->collect_score != last_ce_score)
10397       {
10398         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10399         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10400
10401         if (ei->collect_score == 0)
10402         {
10403           int xx, yy;
10404
10405           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10406           ChangeCount[x][y] = 0;        // allow at least one more change
10407
10408           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10409           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10410
10411           /*
10412             This is a very special case that seems to be a mixture between
10413             CheckElementChange() and CheckTriggeredElementChange(): while
10414             the first one only affects single elements that are triggered
10415             directly, the second one affects multiple elements in the playfield
10416             that are triggered indirectly by another element. This is a third
10417             case: Changing the CE score always affects multiple identical CEs,
10418             so every affected CE must be checked, not only the single CE for
10419             which the CE score was changed in the first place (as every instance
10420             of that CE shares the same CE score, and therefore also can change)!
10421           */
10422           SCAN_PLAYFIELD(xx, yy)
10423           {
10424             if (Tile[xx][yy] == element)
10425               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10426                                  CE_SCORE_GETS_ZERO);
10427           }
10428         }
10429       }
10430
10431       break;
10432     }
10433
10434     case CA_SET_CE_ARTWORK:
10435     {
10436       int artwork_element = action_arg_element;
10437       boolean reset_frame = FALSE;
10438       int xx, yy;
10439
10440       if (action_arg == CA_ARG_ELEMENT_RESET)
10441         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10442                            element);
10443
10444       if (ei->gfx_element != artwork_element)
10445         reset_frame = TRUE;
10446
10447       ei->gfx_element = artwork_element;
10448
10449       SCAN_PLAYFIELD(xx, yy)
10450       {
10451         if (Tile[xx][yy] == element)
10452         {
10453           if (reset_frame)
10454           {
10455             ResetGfxAnimation(xx, yy);
10456             ResetRandomAnimationValue(xx, yy);
10457           }
10458
10459           TEST_DrawLevelField(xx, yy);
10460         }
10461       }
10462
10463       break;
10464     }
10465
10466     // ---------- engine actions  ---------------------------------------------
10467
10468     case CA_SET_ENGINE_SCAN_MODE:
10469     {
10470       InitPlayfieldScanMode(action_arg);
10471
10472       break;
10473     }
10474
10475     default:
10476       break;
10477   }
10478 }
10479
10480 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10481 {
10482   int old_element = Tile[x][y];
10483   int new_element = GetElementFromGroupElement(element);
10484   int previous_move_direction = MovDir[x][y];
10485   int last_ce_value = CustomValue[x][y];
10486   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10487   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10488   boolean add_player_onto_element = (new_element_is_player &&
10489                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10490                                      IS_WALKABLE(old_element));
10491
10492   if (!add_player_onto_element)
10493   {
10494     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10495       RemoveMovingField(x, y);
10496     else
10497       RemoveField(x, y);
10498
10499     Tile[x][y] = new_element;
10500
10501     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10502       MovDir[x][y] = previous_move_direction;
10503
10504     if (element_info[new_element].use_last_ce_value)
10505       CustomValue[x][y] = last_ce_value;
10506
10507     InitField_WithBug1(x, y, FALSE);
10508
10509     new_element = Tile[x][y];   // element may have changed
10510
10511     ResetGfxAnimation(x, y);
10512     ResetRandomAnimationValue(x, y);
10513
10514     TEST_DrawLevelField(x, y);
10515
10516     if (GFX_CRUMBLED(new_element))
10517       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10518   }
10519
10520   // check if element under the player changes from accessible to unaccessible
10521   // (needed for special case of dropping element which then changes)
10522   // (must be checked after creating new element for walkable group elements)
10523   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10524       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10525   {
10526     Bang(x, y);
10527
10528     return;
10529   }
10530
10531   // "ChangeCount" not set yet to allow "entered by player" change one time
10532   if (new_element_is_player)
10533     RelocatePlayer(x, y, new_element);
10534
10535   if (is_change)
10536     ChangeCount[x][y]++;        // count number of changes in the same frame
10537
10538   TestIfBadThingTouchesPlayer(x, y);
10539   TestIfPlayerTouchesCustomElement(x, y);
10540   TestIfElementTouchesCustomElement(x, y);
10541 }
10542
10543 static void CreateField(int x, int y, int element)
10544 {
10545   CreateFieldExt(x, y, element, FALSE);
10546 }
10547
10548 static void CreateElementFromChange(int x, int y, int element)
10549 {
10550   element = GET_VALID_RUNTIME_ELEMENT(element);
10551
10552   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10553   {
10554     int old_element = Tile[x][y];
10555
10556     // prevent changed element from moving in same engine frame
10557     // unless both old and new element can either fall or move
10558     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10559         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10560       Stop[x][y] = TRUE;
10561   }
10562
10563   CreateFieldExt(x, y, element, TRUE);
10564 }
10565
10566 static boolean ChangeElement(int x, int y, int element, int page)
10567 {
10568   struct ElementInfo *ei = &element_info[element];
10569   struct ElementChangeInfo *change = &ei->change_page[page];
10570   int ce_value = CustomValue[x][y];
10571   int ce_score = ei->collect_score;
10572   int target_element;
10573   int old_element = Tile[x][y];
10574
10575   // always use default change event to prevent running into a loop
10576   if (ChangeEvent[x][y] == -1)
10577     ChangeEvent[x][y] = CE_DELAY;
10578
10579   if (ChangeEvent[x][y] == CE_DELAY)
10580   {
10581     // reset actual trigger element, trigger player and action element
10582     change->actual_trigger_element = EL_EMPTY;
10583     change->actual_trigger_player = EL_EMPTY;
10584     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10585     change->actual_trigger_side = CH_SIDE_NONE;
10586     change->actual_trigger_ce_value = 0;
10587     change->actual_trigger_ce_score = 0;
10588   }
10589
10590   // do not change elements more than a specified maximum number of changes
10591   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10592     return FALSE;
10593
10594   ChangeCount[x][y]++;          // count number of changes in the same frame
10595
10596   if (change->explode)
10597   {
10598     Bang(x, y);
10599
10600     return TRUE;
10601   }
10602
10603   if (change->use_target_content)
10604   {
10605     boolean complete_replace = TRUE;
10606     boolean can_replace[3][3];
10607     int xx, yy;
10608
10609     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10610     {
10611       boolean is_empty;
10612       boolean is_walkable;
10613       boolean is_diggable;
10614       boolean is_collectible;
10615       boolean is_removable;
10616       boolean is_destructible;
10617       int ex = x + xx - 1;
10618       int ey = y + yy - 1;
10619       int content_element = change->target_content.e[xx][yy];
10620       int e;
10621
10622       can_replace[xx][yy] = TRUE;
10623
10624       if (ex == x && ey == y)   // do not check changing element itself
10625         continue;
10626
10627       if (content_element == EL_EMPTY_SPACE)
10628       {
10629         can_replace[xx][yy] = FALSE;    // do not replace border with space
10630
10631         continue;
10632       }
10633
10634       if (!IN_LEV_FIELD(ex, ey))
10635       {
10636         can_replace[xx][yy] = FALSE;
10637         complete_replace = FALSE;
10638
10639         continue;
10640       }
10641
10642       e = Tile[ex][ey];
10643
10644       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10645         e = MovingOrBlocked2Element(ex, ey);
10646
10647       is_empty = (IS_FREE(ex, ey) ||
10648                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10649
10650       is_walkable     = (is_empty || IS_WALKABLE(e));
10651       is_diggable     = (is_empty || IS_DIGGABLE(e));
10652       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10653       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10654       is_removable    = (is_diggable || is_collectible);
10655
10656       can_replace[xx][yy] =
10657         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10658           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10659           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10660           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10661           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10662           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10663          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10664
10665       if (!can_replace[xx][yy])
10666         complete_replace = FALSE;
10667     }
10668
10669     if (!change->only_if_complete || complete_replace)
10670     {
10671       boolean something_has_changed = FALSE;
10672
10673       if (change->only_if_complete && change->use_random_replace &&
10674           RND(100) < change->random_percentage)
10675         return FALSE;
10676
10677       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10678       {
10679         int ex = x + xx - 1;
10680         int ey = y + yy - 1;
10681         int content_element;
10682
10683         if (can_replace[xx][yy] && (!change->use_random_replace ||
10684                                     RND(100) < change->random_percentage))
10685         {
10686           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10687             RemoveMovingField(ex, ey);
10688
10689           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10690
10691           content_element = change->target_content.e[xx][yy];
10692           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10693                                               ce_value, ce_score);
10694
10695           CreateElementFromChange(ex, ey, target_element);
10696
10697           something_has_changed = TRUE;
10698
10699           // for symmetry reasons, freeze newly created border elements
10700           if (ex != x || ey != y)
10701             Stop[ex][ey] = TRUE;        // no more moving in this frame
10702         }
10703       }
10704
10705       if (something_has_changed)
10706       {
10707         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10708         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10709       }
10710     }
10711   }
10712   else
10713   {
10714     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10715                                         ce_value, ce_score);
10716
10717     if (element == EL_DIAGONAL_GROWING ||
10718         element == EL_DIAGONAL_SHRINKING)
10719     {
10720       target_element = Store[x][y];
10721
10722       Store[x][y] = EL_EMPTY;
10723     }
10724
10725     CreateElementFromChange(x, y, target_element);
10726
10727     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10728     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10729   }
10730
10731   // this uses direct change before indirect change
10732   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10733
10734   return TRUE;
10735 }
10736
10737 static void HandleElementChange(int x, int y, int page)
10738 {
10739   int element = MovingOrBlocked2Element(x, y);
10740   struct ElementInfo *ei = &element_info[element];
10741   struct ElementChangeInfo *change = &ei->change_page[page];
10742   boolean handle_action_before_change = FALSE;
10743
10744 #ifdef DEBUG
10745   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10746       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10747   {
10748     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10749           x, y, element, element_info[element].token_name);
10750     Debug("game:playing:HandleElementChange", "This should never happen!");
10751   }
10752 #endif
10753
10754   // this can happen with classic bombs on walkable, changing elements
10755   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10756   {
10757     return;
10758   }
10759
10760   if (ChangeDelay[x][y] == 0)           // initialize element change
10761   {
10762     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10763
10764     if (change->can_change)
10765     {
10766       // !!! not clear why graphic animation should be reset at all here !!!
10767       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10768       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10769
10770       /*
10771         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10772
10773         When using an animation frame delay of 1 (this only happens with
10774         "sp_zonk.moving.left/right" in the classic graphics), the default
10775         (non-moving) animation shows wrong animation frames (while the
10776         moving animation, like "sp_zonk.moving.left/right", is correct,
10777         so this graphical bug never shows up with the classic graphics).
10778         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10779         be drawn instead of the correct frames 0,1,2,3. This is caused by
10780         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10781         an element change: First when the change delay ("ChangeDelay[][]")
10782         counter has reached zero after decrementing, then a second time in
10783         the next frame (after "GfxFrame[][]" was already incremented) when
10784         "ChangeDelay[][]" is reset to the initial delay value again.
10785
10786         This causes frame 0 to be drawn twice, while the last frame won't
10787         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10788
10789         As some animations may already be cleverly designed around this bug
10790         (at least the "Snake Bite" snake tail animation does this), it cannot
10791         simply be fixed here without breaking such existing animations.
10792         Unfortunately, it cannot easily be detected if a graphics set was
10793         designed "before" or "after" the bug was fixed. As a workaround,
10794         a new graphics set option "game.graphics_engine_version" was added
10795         to be able to specify the game's major release version for which the
10796         graphics set was designed, which can then be used to decide if the
10797         bugfix should be used (version 4 and above) or not (version 3 or
10798         below, or if no version was specified at all, as with old sets).
10799
10800         (The wrong/fixed animation frames can be tested with the test level set
10801         "test_gfxframe" and level "000", which contains a specially prepared
10802         custom element at level position (x/y) == (11/9) which uses the zonk
10803         animation mentioned above. Using "game.graphics_engine_version: 4"
10804         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10805         This can also be seen from the debug output for this test element.)
10806       */
10807
10808       // when a custom element is about to change (for example by change delay),
10809       // do not reset graphic animation when the custom element is moving
10810       if (game.graphics_engine_version < 4 &&
10811           !IS_MOVING(x, y))
10812       {
10813         ResetGfxAnimation(x, y);
10814         ResetRandomAnimationValue(x, y);
10815       }
10816
10817       if (change->pre_change_function)
10818         change->pre_change_function(x, y);
10819     }
10820   }
10821
10822   ChangeDelay[x][y]--;
10823
10824   if (ChangeDelay[x][y] != 0)           // continue element change
10825   {
10826     if (change->can_change)
10827     {
10828       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10829
10830       if (IS_ANIMATED(graphic))
10831         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10832
10833       if (change->change_function)
10834         change->change_function(x, y);
10835     }
10836   }
10837   else                                  // finish element change
10838   {
10839     if (ChangePage[x][y] != -1)         // remember page from delayed change
10840     {
10841       page = ChangePage[x][y];
10842       ChangePage[x][y] = -1;
10843
10844       change = &ei->change_page[page];
10845     }
10846
10847     if (IS_MOVING(x, y))                // never change a running system ;-)
10848     {
10849       ChangeDelay[x][y] = 1;            // try change after next move step
10850       ChangePage[x][y] = page;          // remember page to use for change
10851
10852       return;
10853     }
10854
10855     // special case: set new level random seed before changing element
10856     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10857       handle_action_before_change = TRUE;
10858
10859     if (change->has_action && handle_action_before_change)
10860       ExecuteCustomElementAction(x, y, element, page);
10861
10862     if (change->can_change)
10863     {
10864       if (ChangeElement(x, y, element, page))
10865       {
10866         if (change->post_change_function)
10867           change->post_change_function(x, y);
10868       }
10869     }
10870
10871     if (change->has_action && !handle_action_before_change)
10872       ExecuteCustomElementAction(x, y, element, page);
10873   }
10874 }
10875
10876 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10877                                               int trigger_element,
10878                                               int trigger_event,
10879                                               int trigger_player,
10880                                               int trigger_side,
10881                                               int trigger_page)
10882 {
10883   boolean change_done_any = FALSE;
10884   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10885   int i;
10886
10887   if (!(trigger_events[trigger_element][trigger_event]))
10888     return FALSE;
10889
10890   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10891
10892   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10893   {
10894     int element = EL_CUSTOM_START + i;
10895     boolean change_done = FALSE;
10896     int p;
10897
10898     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10899         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10900       continue;
10901
10902     for (p = 0; p < element_info[element].num_change_pages; p++)
10903     {
10904       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10905
10906       if (change->can_change_or_has_action &&
10907           change->has_event[trigger_event] &&
10908           change->trigger_side & trigger_side &&
10909           change->trigger_player & trigger_player &&
10910           change->trigger_page & trigger_page_bits &&
10911           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10912       {
10913         change->actual_trigger_element = trigger_element;
10914         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10915         change->actual_trigger_player_bits = trigger_player;
10916         change->actual_trigger_side = trigger_side;
10917         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10918         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10919
10920         if ((change->can_change && !change_done) || change->has_action)
10921         {
10922           int x, y;
10923
10924           SCAN_PLAYFIELD(x, y)
10925           {
10926             if (Tile[x][y] == element)
10927             {
10928               if (change->can_change && !change_done)
10929               {
10930                 // if element already changed in this frame, not only prevent
10931                 // another element change (checked in ChangeElement()), but
10932                 // also prevent additional element actions for this element
10933
10934                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10935                     !level.use_action_after_change_bug)
10936                   continue;
10937
10938                 ChangeDelay[x][y] = 1;
10939                 ChangeEvent[x][y] = trigger_event;
10940
10941                 HandleElementChange(x, y, p);
10942               }
10943               else if (change->has_action)
10944               {
10945                 // if element already changed in this frame, not only prevent
10946                 // another element change (checked in ChangeElement()), but
10947                 // also prevent additional element actions for this element
10948
10949                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10950                     !level.use_action_after_change_bug)
10951                   continue;
10952
10953                 ExecuteCustomElementAction(x, y, element, p);
10954                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10955               }
10956             }
10957           }
10958
10959           if (change->can_change)
10960           {
10961             change_done = TRUE;
10962             change_done_any = TRUE;
10963           }
10964         }
10965       }
10966     }
10967   }
10968
10969   RECURSION_LOOP_DETECTION_END();
10970
10971   return change_done_any;
10972 }
10973
10974 static boolean CheckElementChangeExt(int x, int y,
10975                                      int element,
10976                                      int trigger_element,
10977                                      int trigger_event,
10978                                      int trigger_player,
10979                                      int trigger_side)
10980 {
10981   boolean change_done = FALSE;
10982   int p;
10983
10984   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10985       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10986     return FALSE;
10987
10988   if (Tile[x][y] == EL_BLOCKED)
10989   {
10990     Blocked2Moving(x, y, &x, &y);
10991     element = Tile[x][y];
10992   }
10993
10994   // check if element has already changed or is about to change after moving
10995   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10996        Tile[x][y] != element) ||
10997
10998       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10999        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11000         ChangePage[x][y] != -1)))
11001     return FALSE;
11002
11003   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11004
11005   for (p = 0; p < element_info[element].num_change_pages; p++)
11006   {
11007     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11008
11009     /* check trigger element for all events where the element that is checked
11010        for changing interacts with a directly adjacent element -- this is
11011        different to element changes that affect other elements to change on the
11012        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11013     boolean check_trigger_element =
11014       (trigger_event == CE_TOUCHING_X ||
11015        trigger_event == CE_HITTING_X ||
11016        trigger_event == CE_HIT_BY_X ||
11017        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11018
11019     if (change->can_change_or_has_action &&
11020         change->has_event[trigger_event] &&
11021         change->trigger_side & trigger_side &&
11022         change->trigger_player & trigger_player &&
11023         (!check_trigger_element ||
11024          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11025     {
11026       change->actual_trigger_element = trigger_element;
11027       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11028       change->actual_trigger_player_bits = trigger_player;
11029       change->actual_trigger_side = trigger_side;
11030       change->actual_trigger_ce_value = CustomValue[x][y];
11031       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11032
11033       // special case: trigger element not at (x,y) position for some events
11034       if (check_trigger_element)
11035       {
11036         static struct
11037         {
11038           int dx, dy;
11039         } move_xy[] =
11040           {
11041             {  0,  0 },
11042             { -1,  0 },
11043             { +1,  0 },
11044             {  0,  0 },
11045             {  0, -1 },
11046             {  0,  0 }, { 0, 0 }, { 0, 0 },
11047             {  0, +1 }
11048           };
11049
11050         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11051         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11052
11053         change->actual_trigger_ce_value = CustomValue[xx][yy];
11054         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11055       }
11056
11057       if (change->can_change && !change_done)
11058       {
11059         ChangeDelay[x][y] = 1;
11060         ChangeEvent[x][y] = trigger_event;
11061
11062         HandleElementChange(x, y, p);
11063
11064         change_done = TRUE;
11065       }
11066       else if (change->has_action)
11067       {
11068         ExecuteCustomElementAction(x, y, element, p);
11069         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11070       }
11071     }
11072   }
11073
11074   RECURSION_LOOP_DETECTION_END();
11075
11076   return change_done;
11077 }
11078
11079 static void PlayPlayerSound(struct PlayerInfo *player)
11080 {
11081   int jx = player->jx, jy = player->jy;
11082   int sound_element = player->artwork_element;
11083   int last_action = player->last_action_waiting;
11084   int action = player->action_waiting;
11085
11086   if (player->is_waiting)
11087   {
11088     if (action != last_action)
11089       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11090     else
11091       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11092   }
11093   else
11094   {
11095     if (action != last_action)
11096       StopSound(element_info[sound_element].sound[last_action]);
11097
11098     if (last_action == ACTION_SLEEPING)
11099       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11100   }
11101 }
11102
11103 static void PlayAllPlayersSound(void)
11104 {
11105   int i;
11106
11107   for (i = 0; i < MAX_PLAYERS; i++)
11108     if (stored_player[i].active)
11109       PlayPlayerSound(&stored_player[i]);
11110 }
11111
11112 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11113 {
11114   boolean last_waiting = player->is_waiting;
11115   int move_dir = player->MovDir;
11116
11117   player->dir_waiting = move_dir;
11118   player->last_action_waiting = player->action_waiting;
11119
11120   if (is_waiting)
11121   {
11122     if (!last_waiting)          // not waiting -> waiting
11123     {
11124       player->is_waiting = TRUE;
11125
11126       player->frame_counter_bored =
11127         FrameCounter +
11128         game.player_boring_delay_fixed +
11129         GetSimpleRandom(game.player_boring_delay_random);
11130       player->frame_counter_sleeping =
11131         FrameCounter +
11132         game.player_sleeping_delay_fixed +
11133         GetSimpleRandom(game.player_sleeping_delay_random);
11134
11135       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11136     }
11137
11138     if (game.player_sleeping_delay_fixed +
11139         game.player_sleeping_delay_random > 0 &&
11140         player->anim_delay_counter == 0 &&
11141         player->post_delay_counter == 0 &&
11142         FrameCounter >= player->frame_counter_sleeping)
11143       player->is_sleeping = TRUE;
11144     else if (game.player_boring_delay_fixed +
11145              game.player_boring_delay_random > 0 &&
11146              FrameCounter >= player->frame_counter_bored)
11147       player->is_bored = TRUE;
11148
11149     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11150                               player->is_bored ? ACTION_BORING :
11151                               ACTION_WAITING);
11152
11153     if (player->is_sleeping && player->use_murphy)
11154     {
11155       // special case for sleeping Murphy when leaning against non-free tile
11156
11157       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11158           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11159            !IS_MOVING(player->jx - 1, player->jy)))
11160         move_dir = MV_LEFT;
11161       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11162                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11163                 !IS_MOVING(player->jx + 1, player->jy)))
11164         move_dir = MV_RIGHT;
11165       else
11166         player->is_sleeping = FALSE;
11167
11168       player->dir_waiting = move_dir;
11169     }
11170
11171     if (player->is_sleeping)
11172     {
11173       if (player->num_special_action_sleeping > 0)
11174       {
11175         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11176         {
11177           int last_special_action = player->special_action_sleeping;
11178           int num_special_action = player->num_special_action_sleeping;
11179           int special_action =
11180             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11181              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11182              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11183              last_special_action + 1 : ACTION_SLEEPING);
11184           int special_graphic =
11185             el_act_dir2img(player->artwork_element, special_action, move_dir);
11186
11187           player->anim_delay_counter =
11188             graphic_info[special_graphic].anim_delay_fixed +
11189             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11190           player->post_delay_counter =
11191             graphic_info[special_graphic].post_delay_fixed +
11192             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11193
11194           player->special_action_sleeping = special_action;
11195         }
11196
11197         if (player->anim_delay_counter > 0)
11198         {
11199           player->action_waiting = player->special_action_sleeping;
11200           player->anim_delay_counter--;
11201         }
11202         else if (player->post_delay_counter > 0)
11203         {
11204           player->post_delay_counter--;
11205         }
11206       }
11207     }
11208     else if (player->is_bored)
11209     {
11210       if (player->num_special_action_bored > 0)
11211       {
11212         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11213         {
11214           int special_action =
11215             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11216           int special_graphic =
11217             el_act_dir2img(player->artwork_element, special_action, move_dir);
11218
11219           player->anim_delay_counter =
11220             graphic_info[special_graphic].anim_delay_fixed +
11221             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11222           player->post_delay_counter =
11223             graphic_info[special_graphic].post_delay_fixed +
11224             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11225
11226           player->special_action_bored = special_action;
11227         }
11228
11229         if (player->anim_delay_counter > 0)
11230         {
11231           player->action_waiting = player->special_action_bored;
11232           player->anim_delay_counter--;
11233         }
11234         else if (player->post_delay_counter > 0)
11235         {
11236           player->post_delay_counter--;
11237         }
11238       }
11239     }
11240   }
11241   else if (last_waiting)        // waiting -> not waiting
11242   {
11243     player->is_waiting = FALSE;
11244     player->is_bored = FALSE;
11245     player->is_sleeping = FALSE;
11246
11247     player->frame_counter_bored = -1;
11248     player->frame_counter_sleeping = -1;
11249
11250     player->anim_delay_counter = 0;
11251     player->post_delay_counter = 0;
11252
11253     player->dir_waiting = player->MovDir;
11254     player->action_waiting = ACTION_DEFAULT;
11255
11256     player->special_action_bored = ACTION_DEFAULT;
11257     player->special_action_sleeping = ACTION_DEFAULT;
11258   }
11259 }
11260
11261 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11262 {
11263   if ((!player->is_moving  && player->was_moving) ||
11264       (player->MovPos == 0 && player->was_moving) ||
11265       (player->is_snapping && !player->was_snapping) ||
11266       (player->is_dropping && !player->was_dropping))
11267   {
11268     if (!CheckSaveEngineSnapshotToList())
11269       return;
11270
11271     player->was_moving = FALSE;
11272     player->was_snapping = TRUE;
11273     player->was_dropping = TRUE;
11274   }
11275   else
11276   {
11277     if (player->is_moving)
11278       player->was_moving = TRUE;
11279
11280     if (!player->is_snapping)
11281       player->was_snapping = FALSE;
11282
11283     if (!player->is_dropping)
11284       player->was_dropping = FALSE;
11285   }
11286
11287   static struct MouseActionInfo mouse_action_last = { 0 };
11288   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11289   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11290
11291   if (new_released)
11292     CheckSaveEngineSnapshotToList();
11293
11294   mouse_action_last = mouse_action;
11295 }
11296
11297 static void CheckSingleStepMode(struct PlayerInfo *player)
11298 {
11299   if (tape.single_step && tape.recording && !tape.pausing)
11300   {
11301     // as it is called "single step mode", just return to pause mode when the
11302     // player stopped moving after one tile (or never starts moving at all)
11303     // (reverse logic needed here in case single step mode used in team mode)
11304     if (player->is_moving ||
11305         player->is_pushing ||
11306         player->is_dropping_pressed ||
11307         player->effective_mouse_action.button)
11308       game.enter_single_step_mode = FALSE;
11309   }
11310
11311   CheckSaveEngineSnapshot(player);
11312 }
11313
11314 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11315 {
11316   int left      = player_action & JOY_LEFT;
11317   int right     = player_action & JOY_RIGHT;
11318   int up        = player_action & JOY_UP;
11319   int down      = player_action & JOY_DOWN;
11320   int button1   = player_action & JOY_BUTTON_1;
11321   int button2   = player_action & JOY_BUTTON_2;
11322   int dx        = (left ? -1 : right ? 1 : 0);
11323   int dy        = (up   ? -1 : down  ? 1 : 0);
11324
11325   if (!player->active || tape.pausing)
11326     return 0;
11327
11328   if (player_action)
11329   {
11330     if (button1)
11331       SnapField(player, dx, dy);
11332     else
11333     {
11334       if (button2)
11335         DropElement(player);
11336
11337       MovePlayer(player, dx, dy);
11338     }
11339
11340     CheckSingleStepMode(player);
11341
11342     SetPlayerWaiting(player, FALSE);
11343
11344     return player_action;
11345   }
11346   else
11347   {
11348     // no actions for this player (no input at player's configured device)
11349
11350     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11351     SnapField(player, 0, 0);
11352     CheckGravityMovementWhenNotMoving(player);
11353
11354     if (player->MovPos == 0)
11355       SetPlayerWaiting(player, TRUE);
11356
11357     if (player->MovPos == 0)    // needed for tape.playing
11358       player->is_moving = FALSE;
11359
11360     player->is_dropping = FALSE;
11361     player->is_dropping_pressed = FALSE;
11362     player->drop_pressed_delay = 0;
11363
11364     CheckSingleStepMode(player);
11365
11366     return 0;
11367   }
11368 }
11369
11370 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11371                                          byte *tape_action)
11372 {
11373   if (!tape.use_mouse_actions)
11374     return;
11375
11376   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11377   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11378   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11379 }
11380
11381 static void SetTapeActionFromMouseAction(byte *tape_action,
11382                                          struct MouseActionInfo *mouse_action)
11383 {
11384   if (!tape.use_mouse_actions)
11385     return;
11386
11387   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11388   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11389   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11390 }
11391
11392 static void CheckLevelSolved(void)
11393 {
11394   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11395   {
11396     if (game_em.level_solved &&
11397         !game_em.game_over)                             // game won
11398     {
11399       LevelSolved();
11400
11401       game_em.game_over = TRUE;
11402
11403       game.all_players_gone = TRUE;
11404     }
11405
11406     if (game_em.game_over)                              // game lost
11407       game.all_players_gone = TRUE;
11408   }
11409   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11410   {
11411     if (game_sp.level_solved &&
11412         !game_sp.game_over)                             // game won
11413     {
11414       LevelSolved();
11415
11416       game_sp.game_over = TRUE;
11417
11418       game.all_players_gone = TRUE;
11419     }
11420
11421     if (game_sp.game_over)                              // game lost
11422       game.all_players_gone = TRUE;
11423   }
11424   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11425   {
11426     if (game_mm.level_solved &&
11427         !game_mm.game_over)                             // game won
11428     {
11429       LevelSolved();
11430
11431       game_mm.game_over = TRUE;
11432
11433       game.all_players_gone = TRUE;
11434     }
11435
11436     if (game_mm.game_over)                              // game lost
11437       game.all_players_gone = TRUE;
11438   }
11439 }
11440
11441 static void CheckLevelTime(void)
11442 {
11443   int i;
11444
11445   if (TimeFrames >= FRAMES_PER_SECOND)
11446   {
11447     TimeFrames = 0;
11448     TapeTime++;
11449
11450     for (i = 0; i < MAX_PLAYERS; i++)
11451     {
11452       struct PlayerInfo *player = &stored_player[i];
11453
11454       if (SHIELD_ON(player))
11455       {
11456         player->shield_normal_time_left--;
11457
11458         if (player->shield_deadly_time_left > 0)
11459           player->shield_deadly_time_left--;
11460       }
11461     }
11462
11463     if (!game.LevelSolved && !level.use_step_counter)
11464     {
11465       TimePlayed++;
11466
11467       if (TimeLeft > 0)
11468       {
11469         TimeLeft--;
11470
11471         if (TimeLeft <= 10 && setup.time_limit)
11472           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11473
11474         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11475            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11476
11477         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11478
11479         if (!TimeLeft && setup.time_limit)
11480         {
11481           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11482             game_em.lev->killed_out_of_time = TRUE;
11483           else
11484             for (i = 0; i < MAX_PLAYERS; i++)
11485               KillPlayer(&stored_player[i]);
11486         }
11487       }
11488       else if (game.no_time_limit && !game.all_players_gone)
11489       {
11490         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11491       }
11492
11493       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11494     }
11495
11496     if (tape.recording || tape.playing)
11497       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11498   }
11499
11500   if (tape.recording || tape.playing)
11501     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11502
11503   UpdateAndDisplayGameControlValues();
11504 }
11505
11506 void AdvanceFrameAndPlayerCounters(int player_nr)
11507 {
11508   int i;
11509
11510   // advance frame counters (global frame counter and time frame counter)
11511   FrameCounter++;
11512   TimeFrames++;
11513
11514   // advance player counters (counters for move delay, move animation etc.)
11515   for (i = 0; i < MAX_PLAYERS; i++)
11516   {
11517     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11518     int move_delay_value = stored_player[i].move_delay_value;
11519     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11520
11521     if (!advance_player_counters)       // not all players may be affected
11522       continue;
11523
11524     if (move_frames == 0)       // less than one move per game frame
11525     {
11526       int stepsize = TILEX / move_delay_value;
11527       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11528       int count = (stored_player[i].is_moving ?
11529                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11530
11531       if (count % delay == 0)
11532         move_frames = 1;
11533     }
11534
11535     stored_player[i].Frame += move_frames;
11536
11537     if (stored_player[i].MovPos != 0)
11538       stored_player[i].StepFrame += move_frames;
11539
11540     if (stored_player[i].move_delay > 0)
11541       stored_player[i].move_delay--;
11542
11543     // due to bugs in previous versions, counter must count up, not down
11544     if (stored_player[i].push_delay != -1)
11545       stored_player[i].push_delay++;
11546
11547     if (stored_player[i].drop_delay > 0)
11548       stored_player[i].drop_delay--;
11549
11550     if (stored_player[i].is_dropping_pressed)
11551       stored_player[i].drop_pressed_delay++;
11552   }
11553 }
11554
11555 void StartGameActions(boolean init_network_game, boolean record_tape,
11556                       int random_seed)
11557 {
11558   unsigned int new_random_seed = InitRND(random_seed);
11559
11560   if (record_tape)
11561     TapeStartRecording(new_random_seed);
11562
11563   if (init_network_game)
11564   {
11565     SendToServer_LevelFile();
11566     SendToServer_StartPlaying();
11567
11568     return;
11569   }
11570
11571   InitGame();
11572 }
11573
11574 static void GameActionsExt(void)
11575 {
11576 #if 0
11577   static unsigned int game_frame_delay = 0;
11578 #endif
11579   unsigned int game_frame_delay_value;
11580   byte *recorded_player_action;
11581   byte summarized_player_action = 0;
11582   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11583   int i;
11584
11585   // detect endless loops, caused by custom element programming
11586   if (recursion_loop_detected && recursion_loop_depth == 0)
11587   {
11588     char *message = getStringCat3("Internal Error! Element ",
11589                                   EL_NAME(recursion_loop_element),
11590                                   " caused endless loop! Quit the game?");
11591
11592     Warn("element '%s' caused endless loop in game engine",
11593          EL_NAME(recursion_loop_element));
11594
11595     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11596
11597     recursion_loop_detected = FALSE;    // if game should be continued
11598
11599     free(message);
11600
11601     return;
11602   }
11603
11604   if (game.restart_level)
11605     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11606
11607   CheckLevelSolved();
11608
11609   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11610     GameWon();
11611
11612   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11613     TapeStop();
11614
11615   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11616     return;
11617
11618   game_frame_delay_value =
11619     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11620
11621   if (tape.playing && tape.warp_forward && !tape.pausing)
11622     game_frame_delay_value = 0;
11623
11624   SetVideoFrameDelay(game_frame_delay_value);
11625
11626   // (de)activate virtual buttons depending on current game status
11627   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11628   {
11629     if (game.all_players_gone)  // if no players there to be controlled anymore
11630       SetOverlayActive(FALSE);
11631     else if (!tape.playing)     // if game continues after tape stopped playing
11632       SetOverlayActive(TRUE);
11633   }
11634
11635 #if 0
11636 #if 0
11637   // ---------- main game synchronization point ----------
11638
11639   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11640
11641   Debug("game:playing:skip", "skip == %d", skip);
11642
11643 #else
11644   // ---------- main game synchronization point ----------
11645
11646   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11647 #endif
11648 #endif
11649
11650   if (network_playing && !network_player_action_received)
11651   {
11652     // try to get network player actions in time
11653
11654     // last chance to get network player actions without main loop delay
11655     HandleNetworking();
11656
11657     // game was quit by network peer
11658     if (game_status != GAME_MODE_PLAYING)
11659       return;
11660
11661     // check if network player actions still missing and game still running
11662     if (!network_player_action_received && !checkGameEnded())
11663       return;           // failed to get network player actions in time
11664
11665     // do not yet reset "network_player_action_received" (for tape.pausing)
11666   }
11667
11668   if (tape.pausing)
11669     return;
11670
11671   // at this point we know that we really continue executing the game
11672
11673   network_player_action_received = FALSE;
11674
11675   // when playing tape, read previously recorded player input from tape data
11676   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11677
11678   local_player->effective_mouse_action = local_player->mouse_action;
11679
11680   if (recorded_player_action != NULL)
11681     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11682                                  recorded_player_action);
11683
11684   // TapePlayAction() may return NULL when toggling to "pause before death"
11685   if (tape.pausing)
11686     return;
11687
11688   if (tape.set_centered_player)
11689   {
11690     game.centered_player_nr_next = tape.centered_player_nr_next;
11691     game.set_centered_player = TRUE;
11692   }
11693
11694   for (i = 0; i < MAX_PLAYERS; i++)
11695   {
11696     summarized_player_action |= stored_player[i].action;
11697
11698     if (!network_playing && (game.team_mode || tape.playing))
11699       stored_player[i].effective_action = stored_player[i].action;
11700   }
11701
11702   if (network_playing && !checkGameEnded())
11703     SendToServer_MovePlayer(summarized_player_action);
11704
11705   // summarize all actions at local players mapped input device position
11706   // (this allows using different input devices in single player mode)
11707   if (!network.enabled && !game.team_mode)
11708     stored_player[map_player_action[local_player->index_nr]].effective_action =
11709       summarized_player_action;
11710
11711   // summarize all actions at centered player in local team mode
11712   if (tape.recording &&
11713       setup.team_mode && !network.enabled &&
11714       setup.input_on_focus &&
11715       game.centered_player_nr != -1)
11716   {
11717     for (i = 0; i < MAX_PLAYERS; i++)
11718       stored_player[map_player_action[i]].effective_action =
11719         (i == game.centered_player_nr ? summarized_player_action : 0);
11720   }
11721
11722   if (recorded_player_action != NULL)
11723     for (i = 0; i < MAX_PLAYERS; i++)
11724       stored_player[i].effective_action = recorded_player_action[i];
11725
11726   for (i = 0; i < MAX_PLAYERS; i++)
11727   {
11728     tape_action[i] = stored_player[i].effective_action;
11729
11730     /* (this may happen in the RND game engine if a player was not present on
11731        the playfield on level start, but appeared later from a custom element */
11732     if (setup.team_mode &&
11733         tape.recording &&
11734         tape_action[i] &&
11735         !tape.player_participates[i])
11736       tape.player_participates[i] = TRUE;
11737   }
11738
11739   SetTapeActionFromMouseAction(tape_action,
11740                                &local_player->effective_mouse_action);
11741
11742   // only record actions from input devices, but not programmed actions
11743   if (tape.recording)
11744     TapeRecordAction(tape_action);
11745
11746   // remember if game was played (especially after tape stopped playing)
11747   if (!tape.playing && summarized_player_action)
11748     game.GamePlayed = TRUE;
11749
11750 #if USE_NEW_PLAYER_ASSIGNMENTS
11751   // !!! also map player actions in single player mode !!!
11752   // if (game.team_mode)
11753   if (1)
11754   {
11755     byte mapped_action[MAX_PLAYERS];
11756
11757 #if DEBUG_PLAYER_ACTIONS
11758     for (i = 0; i < MAX_PLAYERS; i++)
11759       DebugContinued("", "%d, ", stored_player[i].effective_action);
11760 #endif
11761
11762     for (i = 0; i < MAX_PLAYERS; i++)
11763       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11764
11765     for (i = 0; i < MAX_PLAYERS; i++)
11766       stored_player[i].effective_action = mapped_action[i];
11767
11768 #if DEBUG_PLAYER_ACTIONS
11769     DebugContinued("", "=> ");
11770     for (i = 0; i < MAX_PLAYERS; i++)
11771       DebugContinued("", "%d, ", stored_player[i].effective_action);
11772     DebugContinued("game:playing:player", "\n");
11773 #endif
11774   }
11775 #if DEBUG_PLAYER_ACTIONS
11776   else
11777   {
11778     for (i = 0; i < MAX_PLAYERS; i++)
11779       DebugContinued("", "%d, ", stored_player[i].effective_action);
11780     DebugContinued("game:playing:player", "\n");
11781   }
11782 #endif
11783 #endif
11784
11785   for (i = 0; i < MAX_PLAYERS; i++)
11786   {
11787     // allow engine snapshot in case of changed movement attempt
11788     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11789         (stored_player[i].effective_action & KEY_MOTION))
11790       game.snapshot.changed_action = TRUE;
11791
11792     // allow engine snapshot in case of snapping/dropping attempt
11793     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11794         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11795       game.snapshot.changed_action = TRUE;
11796
11797     game.snapshot.last_action[i] = stored_player[i].effective_action;
11798   }
11799
11800   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11801   {
11802     GameActions_EM_Main();
11803   }
11804   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11805   {
11806     GameActions_SP_Main();
11807   }
11808   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11809   {
11810     GameActions_MM_Main();
11811   }
11812   else
11813   {
11814     GameActions_RND_Main();
11815   }
11816
11817   BlitScreenToBitmap(backbuffer);
11818
11819   CheckLevelSolved();
11820   CheckLevelTime();
11821
11822   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11823
11824   if (global.show_frames_per_second)
11825   {
11826     static unsigned int fps_counter = 0;
11827     static int fps_frames = 0;
11828     unsigned int fps_delay_ms = Counter() - fps_counter;
11829
11830     fps_frames++;
11831
11832     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11833     {
11834       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11835
11836       fps_frames = 0;
11837       fps_counter = Counter();
11838
11839       // always draw FPS to screen after FPS value was updated
11840       redraw_mask |= REDRAW_FPS;
11841     }
11842
11843     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11844     if (GetDrawDeactivationMask() == REDRAW_NONE)
11845       redraw_mask |= REDRAW_FPS;
11846   }
11847 }
11848
11849 static void GameActions_CheckSaveEngineSnapshot(void)
11850 {
11851   if (!game.snapshot.save_snapshot)
11852     return;
11853
11854   // clear flag for saving snapshot _before_ saving snapshot
11855   game.snapshot.save_snapshot = FALSE;
11856
11857   SaveEngineSnapshotToList();
11858 }
11859
11860 void GameActions(void)
11861 {
11862   GameActionsExt();
11863
11864   GameActions_CheckSaveEngineSnapshot();
11865 }
11866
11867 void GameActions_EM_Main(void)
11868 {
11869   byte effective_action[MAX_PLAYERS];
11870   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11871   int i;
11872
11873   for (i = 0; i < MAX_PLAYERS; i++)
11874     effective_action[i] = stored_player[i].effective_action;
11875
11876   GameActions_EM(effective_action, warp_mode);
11877 }
11878
11879 void GameActions_SP_Main(void)
11880 {
11881   byte effective_action[MAX_PLAYERS];
11882   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11883   int i;
11884
11885   for (i = 0; i < MAX_PLAYERS; i++)
11886     effective_action[i] = stored_player[i].effective_action;
11887
11888   GameActions_SP(effective_action, warp_mode);
11889
11890   for (i = 0; i < MAX_PLAYERS; i++)
11891   {
11892     if (stored_player[i].force_dropping)
11893       stored_player[i].action |= KEY_BUTTON_DROP;
11894
11895     stored_player[i].force_dropping = FALSE;
11896   }
11897 }
11898
11899 void GameActions_MM_Main(void)
11900 {
11901   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11902
11903   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11904 }
11905
11906 void GameActions_RND_Main(void)
11907 {
11908   GameActions_RND();
11909 }
11910
11911 void GameActions_RND(void)
11912 {
11913   static struct MouseActionInfo mouse_action_last = { 0 };
11914   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11915   int magic_wall_x = 0, magic_wall_y = 0;
11916   int i, x, y, element, graphic, last_gfx_frame;
11917
11918   InitPlayfieldScanModeVars();
11919
11920   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11921   {
11922     SCAN_PLAYFIELD(x, y)
11923     {
11924       ChangeCount[x][y] = 0;
11925       ChangeEvent[x][y] = -1;
11926     }
11927   }
11928
11929   if (game.set_centered_player)
11930   {
11931     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11932
11933     // switching to "all players" only possible if all players fit to screen
11934     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11935     {
11936       game.centered_player_nr_next = game.centered_player_nr;
11937       game.set_centered_player = FALSE;
11938     }
11939
11940     // do not switch focus to non-existing (or non-active) player
11941     if (game.centered_player_nr_next >= 0 &&
11942         !stored_player[game.centered_player_nr_next].active)
11943     {
11944       game.centered_player_nr_next = game.centered_player_nr;
11945       game.set_centered_player = FALSE;
11946     }
11947   }
11948
11949   if (game.set_centered_player &&
11950       ScreenMovPos == 0)        // screen currently aligned at tile position
11951   {
11952     int sx, sy;
11953
11954     if (game.centered_player_nr_next == -1)
11955     {
11956       setScreenCenteredToAllPlayers(&sx, &sy);
11957     }
11958     else
11959     {
11960       sx = stored_player[game.centered_player_nr_next].jx;
11961       sy = stored_player[game.centered_player_nr_next].jy;
11962     }
11963
11964     game.centered_player_nr = game.centered_player_nr_next;
11965     game.set_centered_player = FALSE;
11966
11967     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11968     DrawGameDoorValues();
11969   }
11970
11971   // check single step mode (set flag and clear again if any player is active)
11972   game.enter_single_step_mode =
11973     (tape.single_step && tape.recording && !tape.pausing);
11974
11975   for (i = 0; i < MAX_PLAYERS; i++)
11976   {
11977     int actual_player_action = stored_player[i].effective_action;
11978
11979 #if 1
11980     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11981        - rnd_equinox_tetrachloride 048
11982        - rnd_equinox_tetrachloride_ii 096
11983        - rnd_emanuel_schmieg 002
11984        - doctor_sloan_ww 001, 020
11985     */
11986     if (stored_player[i].MovPos == 0)
11987       CheckGravityMovement(&stored_player[i]);
11988 #endif
11989
11990     // overwrite programmed action with tape action
11991     if (stored_player[i].programmed_action)
11992       actual_player_action = stored_player[i].programmed_action;
11993
11994     PlayerActions(&stored_player[i], actual_player_action);
11995
11996     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11997   }
11998
11999   // single step pause mode may already have been toggled by "ScrollPlayer()"
12000   if (game.enter_single_step_mode && !tape.pausing)
12001     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12002
12003   ScrollScreen(NULL, SCROLL_GO_ON);
12004
12005   /* for backwards compatibility, the following code emulates a fixed bug that
12006      occured when pushing elements (causing elements that just made their last
12007      pushing step to already (if possible) make their first falling step in the
12008      same game frame, which is bad); this code is also needed to use the famous
12009      "spring push bug" which is used in older levels and might be wanted to be
12010      used also in newer levels, but in this case the buggy pushing code is only
12011      affecting the "spring" element and no other elements */
12012
12013   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12014   {
12015     for (i = 0; i < MAX_PLAYERS; i++)
12016     {
12017       struct PlayerInfo *player = &stored_player[i];
12018       int x = player->jx;
12019       int y = player->jy;
12020
12021       if (player->active && player->is_pushing && player->is_moving &&
12022           IS_MOVING(x, y) &&
12023           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12024            Tile[x][y] == EL_SPRING))
12025       {
12026         ContinueMoving(x, y);
12027
12028         // continue moving after pushing (this is actually a bug)
12029         if (!IS_MOVING(x, y))
12030           Stop[x][y] = FALSE;
12031       }
12032     }
12033   }
12034
12035   SCAN_PLAYFIELD(x, y)
12036   {
12037     Last[x][y] = Tile[x][y];
12038
12039     ChangeCount[x][y] = 0;
12040     ChangeEvent[x][y] = -1;
12041
12042     // this must be handled before main playfield loop
12043     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12044     {
12045       MovDelay[x][y]--;
12046       if (MovDelay[x][y] <= 0)
12047         RemoveField(x, y);
12048     }
12049
12050     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12051     {
12052       MovDelay[x][y]--;
12053       if (MovDelay[x][y] <= 0)
12054       {
12055         int element = Store[x][y];
12056         int move_direction = MovDir[x][y];
12057         int player_index_bit = Store2[x][y];
12058
12059         Store[x][y] = 0;
12060         Store2[x][y] = 0;
12061
12062         RemoveField(x, y);
12063         TEST_DrawLevelField(x, y);
12064
12065         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12066       }
12067     }
12068
12069 #if DEBUG
12070     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12071     {
12072       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12073             x, y);
12074       Debug("game:playing:GameActions_RND", "This should never happen!");
12075
12076       ChangePage[x][y] = -1;
12077     }
12078 #endif
12079
12080     Stop[x][y] = FALSE;
12081     if (WasJustMoving[x][y] > 0)
12082       WasJustMoving[x][y]--;
12083     if (WasJustFalling[x][y] > 0)
12084       WasJustFalling[x][y]--;
12085     if (CheckCollision[x][y] > 0)
12086       CheckCollision[x][y]--;
12087     if (CheckImpact[x][y] > 0)
12088       CheckImpact[x][y]--;
12089
12090     GfxFrame[x][y]++;
12091
12092     /* reset finished pushing action (not done in ContinueMoving() to allow
12093        continuous pushing animation for elements with zero push delay) */
12094     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12095     {
12096       ResetGfxAnimation(x, y);
12097       TEST_DrawLevelField(x, y);
12098     }
12099
12100 #if DEBUG
12101     if (IS_BLOCKED(x, y))
12102     {
12103       int oldx, oldy;
12104
12105       Blocked2Moving(x, y, &oldx, &oldy);
12106       if (!IS_MOVING(oldx, oldy))
12107       {
12108         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12109         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12110         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12111         Debug("game:playing:GameActions_RND", "This should never happen!");
12112       }
12113     }
12114 #endif
12115   }
12116
12117   if (mouse_action.button)
12118   {
12119     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12120
12121     x = mouse_action.lx;
12122     y = mouse_action.ly;
12123     element = Tile[x][y];
12124
12125     if (new_button)
12126     {
12127       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12128       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12129     }
12130
12131     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12132     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12133   }
12134
12135   SCAN_PLAYFIELD(x, y)
12136   {
12137     element = Tile[x][y];
12138     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12139     last_gfx_frame = GfxFrame[x][y];
12140
12141     ResetGfxFrame(x, y);
12142
12143     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12144       DrawLevelGraphicAnimation(x, y, graphic);
12145
12146     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12147         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12148       ResetRandomAnimationValue(x, y);
12149
12150     SetRandomAnimationValue(x, y);
12151
12152     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12153
12154     if (IS_INACTIVE(element))
12155     {
12156       if (IS_ANIMATED(graphic))
12157         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12158
12159       continue;
12160     }
12161
12162     // this may take place after moving, so 'element' may have changed
12163     if (IS_CHANGING(x, y) &&
12164         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12165     {
12166       int page = element_info[element].event_page_nr[CE_DELAY];
12167
12168       HandleElementChange(x, y, page);
12169
12170       element = Tile[x][y];
12171       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12172     }
12173
12174     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12175     {
12176       StartMoving(x, y);
12177
12178       element = Tile[x][y];
12179       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12180
12181       if (IS_ANIMATED(graphic) &&
12182           !IS_MOVING(x, y) &&
12183           !Stop[x][y])
12184         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12185
12186       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12187         TEST_DrawTwinkleOnField(x, y);
12188     }
12189     else if (element == EL_ACID)
12190     {
12191       if (!Stop[x][y])
12192         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12193     }
12194     else if ((element == EL_EXIT_OPEN ||
12195               element == EL_EM_EXIT_OPEN ||
12196               element == EL_SP_EXIT_OPEN ||
12197               element == EL_STEEL_EXIT_OPEN ||
12198               element == EL_EM_STEEL_EXIT_OPEN ||
12199               element == EL_SP_TERMINAL ||
12200               element == EL_SP_TERMINAL_ACTIVE ||
12201               element == EL_EXTRA_TIME ||
12202               element == EL_SHIELD_NORMAL ||
12203               element == EL_SHIELD_DEADLY) &&
12204              IS_ANIMATED(graphic))
12205       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12206     else if (IS_MOVING(x, y))
12207       ContinueMoving(x, y);
12208     else if (IS_ACTIVE_BOMB(element))
12209       CheckDynamite(x, y);
12210     else if (element == EL_AMOEBA_GROWING)
12211       AmoebaGrowing(x, y);
12212     else if (element == EL_AMOEBA_SHRINKING)
12213       AmoebaShrinking(x, y);
12214
12215 #if !USE_NEW_AMOEBA_CODE
12216     else if (IS_AMOEBALIVE(element))
12217       AmoebaReproduce(x, y);
12218 #endif
12219
12220     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12221       Life(x, y);
12222     else if (element == EL_EXIT_CLOSED)
12223       CheckExit(x, y);
12224     else if (element == EL_EM_EXIT_CLOSED)
12225       CheckExitEM(x, y);
12226     else if (element == EL_STEEL_EXIT_CLOSED)
12227       CheckExitSteel(x, y);
12228     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12229       CheckExitSteelEM(x, y);
12230     else if (element == EL_SP_EXIT_CLOSED)
12231       CheckExitSP(x, y);
12232     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12233              element == EL_EXPANDABLE_STEELWALL_GROWING)
12234       MauerWaechst(x, y);
12235     else if (element == EL_EXPANDABLE_WALL ||
12236              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12237              element == EL_EXPANDABLE_WALL_VERTICAL ||
12238              element == EL_EXPANDABLE_WALL_ANY ||
12239              element == EL_BD_EXPANDABLE_WALL)
12240       MauerAbleger(x, y);
12241     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12242              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12243              element == EL_EXPANDABLE_STEELWALL_ANY)
12244       MauerAblegerStahl(x, y);
12245     else if (element == EL_FLAMES)
12246       CheckForDragon(x, y);
12247     else if (element == EL_EXPLOSION)
12248       ; // drawing of correct explosion animation is handled separately
12249     else if (element == EL_ELEMENT_SNAPPING ||
12250              element == EL_DIAGONAL_SHRINKING ||
12251              element == EL_DIAGONAL_GROWING)
12252     {
12253       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12254
12255       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12256     }
12257     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12258       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12259
12260     if (IS_BELT_ACTIVE(element))
12261       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12262
12263     if (game.magic_wall_active)
12264     {
12265       int jx = local_player->jx, jy = local_player->jy;
12266
12267       // play the element sound at the position nearest to the player
12268       if ((element == EL_MAGIC_WALL_FULL ||
12269            element == EL_MAGIC_WALL_ACTIVE ||
12270            element == EL_MAGIC_WALL_EMPTYING ||
12271            element == EL_BD_MAGIC_WALL_FULL ||
12272            element == EL_BD_MAGIC_WALL_ACTIVE ||
12273            element == EL_BD_MAGIC_WALL_EMPTYING ||
12274            element == EL_DC_MAGIC_WALL_FULL ||
12275            element == EL_DC_MAGIC_WALL_ACTIVE ||
12276            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12277           ABS(x - jx) + ABS(y - jy) <
12278           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12279       {
12280         magic_wall_x = x;
12281         magic_wall_y = y;
12282       }
12283     }
12284   }
12285
12286 #if USE_NEW_AMOEBA_CODE
12287   // new experimental amoeba growth stuff
12288   if (!(FrameCounter % 8))
12289   {
12290     static unsigned int random = 1684108901;
12291
12292     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12293     {
12294       x = RND(lev_fieldx);
12295       y = RND(lev_fieldy);
12296       element = Tile[x][y];
12297
12298       if (!IS_PLAYER(x,y) &&
12299           (element == EL_EMPTY ||
12300            CAN_GROW_INTO(element) ||
12301            element == EL_QUICKSAND_EMPTY ||
12302            element == EL_QUICKSAND_FAST_EMPTY ||
12303            element == EL_ACID_SPLASH_LEFT ||
12304            element == EL_ACID_SPLASH_RIGHT))
12305       {
12306         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12307             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12308             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12309             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12310           Tile[x][y] = EL_AMOEBA_DROP;
12311       }
12312
12313       random = random * 129 + 1;
12314     }
12315   }
12316 #endif
12317
12318   game.explosions_delayed = FALSE;
12319
12320   SCAN_PLAYFIELD(x, y)
12321   {
12322     element = Tile[x][y];
12323
12324     if (ExplodeField[x][y])
12325       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12326     else if (element == EL_EXPLOSION)
12327       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12328
12329     ExplodeField[x][y] = EX_TYPE_NONE;
12330   }
12331
12332   game.explosions_delayed = TRUE;
12333
12334   if (game.magic_wall_active)
12335   {
12336     if (!(game.magic_wall_time_left % 4))
12337     {
12338       int element = Tile[magic_wall_x][magic_wall_y];
12339
12340       if (element == EL_BD_MAGIC_WALL_FULL ||
12341           element == EL_BD_MAGIC_WALL_ACTIVE ||
12342           element == EL_BD_MAGIC_WALL_EMPTYING)
12343         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12344       else if (element == EL_DC_MAGIC_WALL_FULL ||
12345                element == EL_DC_MAGIC_WALL_ACTIVE ||
12346                element == EL_DC_MAGIC_WALL_EMPTYING)
12347         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12348       else
12349         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12350     }
12351
12352     if (game.magic_wall_time_left > 0)
12353     {
12354       game.magic_wall_time_left--;
12355
12356       if (!game.magic_wall_time_left)
12357       {
12358         SCAN_PLAYFIELD(x, y)
12359         {
12360           element = Tile[x][y];
12361
12362           if (element == EL_MAGIC_WALL_ACTIVE ||
12363               element == EL_MAGIC_WALL_FULL)
12364           {
12365             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12366             TEST_DrawLevelField(x, y);
12367           }
12368           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12369                    element == EL_BD_MAGIC_WALL_FULL)
12370           {
12371             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12372             TEST_DrawLevelField(x, y);
12373           }
12374           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12375                    element == EL_DC_MAGIC_WALL_FULL)
12376           {
12377             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12378             TEST_DrawLevelField(x, y);
12379           }
12380         }
12381
12382         game.magic_wall_active = FALSE;
12383       }
12384     }
12385   }
12386
12387   if (game.light_time_left > 0)
12388   {
12389     game.light_time_left--;
12390
12391     if (game.light_time_left == 0)
12392       RedrawAllLightSwitchesAndInvisibleElements();
12393   }
12394
12395   if (game.timegate_time_left > 0)
12396   {
12397     game.timegate_time_left--;
12398
12399     if (game.timegate_time_left == 0)
12400       CloseAllOpenTimegates();
12401   }
12402
12403   if (game.lenses_time_left > 0)
12404   {
12405     game.lenses_time_left--;
12406
12407     if (game.lenses_time_left == 0)
12408       RedrawAllInvisibleElementsForLenses();
12409   }
12410
12411   if (game.magnify_time_left > 0)
12412   {
12413     game.magnify_time_left--;
12414
12415     if (game.magnify_time_left == 0)
12416       RedrawAllInvisibleElementsForMagnifier();
12417   }
12418
12419   for (i = 0; i < MAX_PLAYERS; i++)
12420   {
12421     struct PlayerInfo *player = &stored_player[i];
12422
12423     if (SHIELD_ON(player))
12424     {
12425       if (player->shield_deadly_time_left)
12426         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12427       else if (player->shield_normal_time_left)
12428         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12429     }
12430   }
12431
12432 #if USE_DELAYED_GFX_REDRAW
12433   SCAN_PLAYFIELD(x, y)
12434   {
12435     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12436     {
12437       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12438          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12439
12440       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12441         DrawLevelField(x, y);
12442
12443       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12444         DrawLevelFieldCrumbled(x, y);
12445
12446       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12447         DrawLevelFieldCrumbledNeighbours(x, y);
12448
12449       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12450         DrawTwinkleOnField(x, y);
12451     }
12452
12453     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12454   }
12455 #endif
12456
12457   DrawAllPlayers();
12458   PlayAllPlayersSound();
12459
12460   for (i = 0; i < MAX_PLAYERS; i++)
12461   {
12462     struct PlayerInfo *player = &stored_player[i];
12463
12464     if (player->show_envelope != 0 && (!player->active ||
12465                                        player->MovPos == 0))
12466     {
12467       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12468
12469       player->show_envelope = 0;
12470     }
12471   }
12472
12473   // use random number generator in every frame to make it less predictable
12474   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12475     RND(1);
12476
12477   mouse_action_last = mouse_action;
12478 }
12479
12480 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12481 {
12482   int min_x = x, min_y = y, max_x = x, max_y = y;
12483   int scr_fieldx = getScreenFieldSizeX();
12484   int scr_fieldy = getScreenFieldSizeY();
12485   int i;
12486
12487   for (i = 0; i < MAX_PLAYERS; i++)
12488   {
12489     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12490
12491     if (!stored_player[i].active || &stored_player[i] == player)
12492       continue;
12493
12494     min_x = MIN(min_x, jx);
12495     min_y = MIN(min_y, jy);
12496     max_x = MAX(max_x, jx);
12497     max_y = MAX(max_y, jy);
12498   }
12499
12500   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12501 }
12502
12503 static boolean AllPlayersInVisibleScreen(void)
12504 {
12505   int i;
12506
12507   for (i = 0; i < MAX_PLAYERS; i++)
12508   {
12509     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12510
12511     if (!stored_player[i].active)
12512       continue;
12513
12514     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12515       return FALSE;
12516   }
12517
12518   return TRUE;
12519 }
12520
12521 void ScrollLevel(int dx, int dy)
12522 {
12523   int scroll_offset = 2 * TILEX_VAR;
12524   int x, y;
12525
12526   BlitBitmap(drawto_field, drawto_field,
12527              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12528              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12529              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12530              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12531              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12532              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12533
12534   if (dx != 0)
12535   {
12536     x = (dx == 1 ? BX1 : BX2);
12537     for (y = BY1; y <= BY2; y++)
12538       DrawScreenField(x, y);
12539   }
12540
12541   if (dy != 0)
12542   {
12543     y = (dy == 1 ? BY1 : BY2);
12544     for (x = BX1; x <= BX2; x++)
12545       DrawScreenField(x, y);
12546   }
12547
12548   redraw_mask |= REDRAW_FIELD;
12549 }
12550
12551 static boolean canFallDown(struct PlayerInfo *player)
12552 {
12553   int jx = player->jx, jy = player->jy;
12554
12555   return (IN_LEV_FIELD(jx, jy + 1) &&
12556           (IS_FREE(jx, jy + 1) ||
12557            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12558           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12559           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12560 }
12561
12562 static boolean canPassField(int x, int y, int move_dir)
12563 {
12564   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12565   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12566   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12567   int nextx = x + dx;
12568   int nexty = y + dy;
12569   int element = Tile[x][y];
12570
12571   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12572           !CAN_MOVE(element) &&
12573           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12574           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12575           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12576 }
12577
12578 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12579 {
12580   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12581   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12582   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12583   int newx = x + dx;
12584   int newy = y + dy;
12585
12586   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12587           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12588           (IS_DIGGABLE(Tile[newx][newy]) ||
12589            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12590            canPassField(newx, newy, move_dir)));
12591 }
12592
12593 static void CheckGravityMovement(struct PlayerInfo *player)
12594 {
12595   if (player->gravity && !player->programmed_action)
12596   {
12597     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12598     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12599     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12600     int jx = player->jx, jy = player->jy;
12601     boolean player_is_moving_to_valid_field =
12602       (!player_is_snapping &&
12603        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12604         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12605     boolean player_can_fall_down = canFallDown(player);
12606
12607     if (player_can_fall_down &&
12608         !player_is_moving_to_valid_field)
12609       player->programmed_action = MV_DOWN;
12610   }
12611 }
12612
12613 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12614 {
12615   return CheckGravityMovement(player);
12616
12617   if (player->gravity && !player->programmed_action)
12618   {
12619     int jx = player->jx, jy = player->jy;
12620     boolean field_under_player_is_free =
12621       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12622     boolean player_is_standing_on_valid_field =
12623       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12624        (IS_WALKABLE(Tile[jx][jy]) &&
12625         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12626
12627     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12628       player->programmed_action = MV_DOWN;
12629   }
12630 }
12631
12632 /*
12633   MovePlayerOneStep()
12634   -----------------------------------------------------------------------------
12635   dx, dy:               direction (non-diagonal) to try to move the player to
12636   real_dx, real_dy:     direction as read from input device (can be diagonal)
12637 */
12638
12639 boolean MovePlayerOneStep(struct PlayerInfo *player,
12640                           int dx, int dy, int real_dx, int real_dy)
12641 {
12642   int jx = player->jx, jy = player->jy;
12643   int new_jx = jx + dx, new_jy = jy + dy;
12644   int can_move;
12645   boolean player_can_move = !player->cannot_move;
12646
12647   if (!player->active || (!dx && !dy))
12648     return MP_NO_ACTION;
12649
12650   player->MovDir = (dx < 0 ? MV_LEFT :
12651                     dx > 0 ? MV_RIGHT :
12652                     dy < 0 ? MV_UP :
12653                     dy > 0 ? MV_DOWN :  MV_NONE);
12654
12655   if (!IN_LEV_FIELD(new_jx, new_jy))
12656     return MP_NO_ACTION;
12657
12658   if (!player_can_move)
12659   {
12660     if (player->MovPos == 0)
12661     {
12662       player->is_moving = FALSE;
12663       player->is_digging = FALSE;
12664       player->is_collecting = FALSE;
12665       player->is_snapping = FALSE;
12666       player->is_pushing = FALSE;
12667     }
12668   }
12669
12670   if (!network.enabled && game.centered_player_nr == -1 &&
12671       !AllPlayersInSight(player, new_jx, new_jy))
12672     return MP_NO_ACTION;
12673
12674   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12675   if (can_move != MP_MOVING)
12676     return can_move;
12677
12678   // check if DigField() has caused relocation of the player
12679   if (player->jx != jx || player->jy != jy)
12680     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12681
12682   StorePlayer[jx][jy] = 0;
12683   player->last_jx = jx;
12684   player->last_jy = jy;
12685   player->jx = new_jx;
12686   player->jy = new_jy;
12687   StorePlayer[new_jx][new_jy] = player->element_nr;
12688
12689   if (player->move_delay_value_next != -1)
12690   {
12691     player->move_delay_value = player->move_delay_value_next;
12692     player->move_delay_value_next = -1;
12693   }
12694
12695   player->MovPos =
12696     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12697
12698   player->step_counter++;
12699
12700   PlayerVisit[jx][jy] = FrameCounter;
12701
12702   player->is_moving = TRUE;
12703
12704 #if 1
12705   // should better be called in MovePlayer(), but this breaks some tapes
12706   ScrollPlayer(player, SCROLL_INIT);
12707 #endif
12708
12709   return MP_MOVING;
12710 }
12711
12712 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12713 {
12714   int jx = player->jx, jy = player->jy;
12715   int old_jx = jx, old_jy = jy;
12716   int moved = MP_NO_ACTION;
12717
12718   if (!player->active)
12719     return FALSE;
12720
12721   if (!dx && !dy)
12722   {
12723     if (player->MovPos == 0)
12724     {
12725       player->is_moving = FALSE;
12726       player->is_digging = FALSE;
12727       player->is_collecting = FALSE;
12728       player->is_snapping = FALSE;
12729       player->is_pushing = FALSE;
12730     }
12731
12732     return FALSE;
12733   }
12734
12735   if (player->move_delay > 0)
12736     return FALSE;
12737
12738   player->move_delay = -1;              // set to "uninitialized" value
12739
12740   // store if player is automatically moved to next field
12741   player->is_auto_moving = (player->programmed_action != MV_NONE);
12742
12743   // remove the last programmed player action
12744   player->programmed_action = 0;
12745
12746   if (player->MovPos)
12747   {
12748     // should only happen if pre-1.2 tape recordings are played
12749     // this is only for backward compatibility
12750
12751     int original_move_delay_value = player->move_delay_value;
12752
12753 #if DEBUG
12754     Debug("game:playing:MovePlayer",
12755           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12756           tape.counter);
12757 #endif
12758
12759     // scroll remaining steps with finest movement resolution
12760     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12761
12762     while (player->MovPos)
12763     {
12764       ScrollPlayer(player, SCROLL_GO_ON);
12765       ScrollScreen(NULL, SCROLL_GO_ON);
12766
12767       AdvanceFrameAndPlayerCounters(player->index_nr);
12768
12769       DrawAllPlayers();
12770       BackToFront_WithFrameDelay(0);
12771     }
12772
12773     player->move_delay_value = original_move_delay_value;
12774   }
12775
12776   player->is_active = FALSE;
12777
12778   if (player->last_move_dir & MV_HORIZONTAL)
12779   {
12780     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12781       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12782   }
12783   else
12784   {
12785     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12786       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12787   }
12788
12789   if (!moved && !player->is_active)
12790   {
12791     player->is_moving = FALSE;
12792     player->is_digging = FALSE;
12793     player->is_collecting = FALSE;
12794     player->is_snapping = FALSE;
12795     player->is_pushing = FALSE;
12796   }
12797
12798   jx = player->jx;
12799   jy = player->jy;
12800
12801   if (moved & MP_MOVING && !ScreenMovPos &&
12802       (player->index_nr == game.centered_player_nr ||
12803        game.centered_player_nr == -1))
12804   {
12805     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12806
12807     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12808     {
12809       // actual player has left the screen -- scroll in that direction
12810       if (jx != old_jx)         // player has moved horizontally
12811         scroll_x += (jx - old_jx);
12812       else                      // player has moved vertically
12813         scroll_y += (jy - old_jy);
12814     }
12815     else
12816     {
12817       int offset_raw = game.scroll_delay_value;
12818
12819       if (jx != old_jx)         // player has moved horizontally
12820       {
12821         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12822         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12823         int new_scroll_x = jx - MIDPOSX + offset_x;
12824
12825         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12826             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12827           scroll_x = new_scroll_x;
12828
12829         // don't scroll over playfield boundaries
12830         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12831
12832         // don't scroll more than one field at a time
12833         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12834
12835         // don't scroll against the player's moving direction
12836         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12837             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12838           scroll_x = old_scroll_x;
12839       }
12840       else                      // player has moved vertically
12841       {
12842         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12843         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12844         int new_scroll_y = jy - MIDPOSY + offset_y;
12845
12846         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12847             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12848           scroll_y = new_scroll_y;
12849
12850         // don't scroll over playfield boundaries
12851         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12852
12853         // don't scroll more than one field at a time
12854         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12855
12856         // don't scroll against the player's moving direction
12857         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12858             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12859           scroll_y = old_scroll_y;
12860       }
12861     }
12862
12863     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12864     {
12865       if (!network.enabled && game.centered_player_nr == -1 &&
12866           !AllPlayersInVisibleScreen())
12867       {
12868         scroll_x = old_scroll_x;
12869         scroll_y = old_scroll_y;
12870       }
12871       else
12872       {
12873         ScrollScreen(player, SCROLL_INIT);
12874         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12875       }
12876     }
12877   }
12878
12879   player->StepFrame = 0;
12880
12881   if (moved & MP_MOVING)
12882   {
12883     if (old_jx != jx && old_jy == jy)
12884       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12885     else if (old_jx == jx && old_jy != jy)
12886       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12887
12888     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12889
12890     player->last_move_dir = player->MovDir;
12891     player->is_moving = TRUE;
12892     player->is_snapping = FALSE;
12893     player->is_switching = FALSE;
12894     player->is_dropping = FALSE;
12895     player->is_dropping_pressed = FALSE;
12896     player->drop_pressed_delay = 0;
12897
12898 #if 0
12899     // should better be called here than above, but this breaks some tapes
12900     ScrollPlayer(player, SCROLL_INIT);
12901 #endif
12902   }
12903   else
12904   {
12905     CheckGravityMovementWhenNotMoving(player);
12906
12907     player->is_moving = FALSE;
12908
12909     /* at this point, the player is allowed to move, but cannot move right now
12910        (e.g. because of something blocking the way) -- ensure that the player
12911        is also allowed to move in the next frame (in old versions before 3.1.1,
12912        the player was forced to wait again for eight frames before next try) */
12913
12914     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12915       player->move_delay = 0;   // allow direct movement in the next frame
12916   }
12917
12918   if (player->move_delay == -1)         // not yet initialized by DigField()
12919     player->move_delay = player->move_delay_value;
12920
12921   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12922   {
12923     TestIfPlayerTouchesBadThing(jx, jy);
12924     TestIfPlayerTouchesCustomElement(jx, jy);
12925   }
12926
12927   if (!player->active)
12928     RemovePlayer(player);
12929
12930   return moved;
12931 }
12932
12933 void ScrollPlayer(struct PlayerInfo *player, int mode)
12934 {
12935   int jx = player->jx, jy = player->jy;
12936   int last_jx = player->last_jx, last_jy = player->last_jy;
12937   int move_stepsize = TILEX / player->move_delay_value;
12938
12939   if (!player->active)
12940     return;
12941
12942   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12943     return;
12944
12945   if (mode == SCROLL_INIT)
12946   {
12947     player->actual_frame_counter = FrameCounter;
12948     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12949
12950     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12951         Tile[last_jx][last_jy] == EL_EMPTY)
12952     {
12953       int last_field_block_delay = 0;   // start with no blocking at all
12954       int block_delay_adjustment = player->block_delay_adjustment;
12955
12956       // if player blocks last field, add delay for exactly one move
12957       if (player->block_last_field)
12958       {
12959         last_field_block_delay += player->move_delay_value;
12960
12961         // when blocking enabled, prevent moving up despite gravity
12962         if (player->gravity && player->MovDir == MV_UP)
12963           block_delay_adjustment = -1;
12964       }
12965
12966       // add block delay adjustment (also possible when not blocking)
12967       last_field_block_delay += block_delay_adjustment;
12968
12969       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12970       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12971     }
12972
12973     if (player->MovPos != 0)    // player has not yet reached destination
12974       return;
12975   }
12976   else if (!FrameReached(&player->actual_frame_counter, 1))
12977     return;
12978
12979   if (player->MovPos != 0)
12980   {
12981     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12982     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12983
12984     // before DrawPlayer() to draw correct player graphic for this case
12985     if (player->MovPos == 0)
12986       CheckGravityMovement(player);
12987   }
12988
12989   if (player->MovPos == 0)      // player reached destination field
12990   {
12991     if (player->move_delay_reset_counter > 0)
12992     {
12993       player->move_delay_reset_counter--;
12994
12995       if (player->move_delay_reset_counter == 0)
12996       {
12997         // continue with normal speed after quickly moving through gate
12998         HALVE_PLAYER_SPEED(player);
12999
13000         // be able to make the next move without delay
13001         player->move_delay = 0;
13002       }
13003     }
13004
13005     player->last_jx = jx;
13006     player->last_jy = jy;
13007
13008     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13009         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13010         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13011         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13012         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13013         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13014         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13015         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13016     {
13017       ExitPlayer(player);
13018
13019       if (game.players_still_needed == 0 &&
13020           (game.friends_still_needed == 0 ||
13021            IS_SP_ELEMENT(Tile[jx][jy])))
13022         LevelSolved();
13023     }
13024
13025     // this breaks one level: "machine", level 000
13026     {
13027       int move_direction = player->MovDir;
13028       int enter_side = MV_DIR_OPPOSITE(move_direction);
13029       int leave_side = move_direction;
13030       int old_jx = last_jx;
13031       int old_jy = last_jy;
13032       int old_element = Tile[old_jx][old_jy];
13033       int new_element = Tile[jx][jy];
13034
13035       if (IS_CUSTOM_ELEMENT(old_element))
13036         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13037                                    CE_LEFT_BY_PLAYER,
13038                                    player->index_bit, leave_side);
13039
13040       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13041                                           CE_PLAYER_LEAVES_X,
13042                                           player->index_bit, leave_side);
13043
13044       if (IS_CUSTOM_ELEMENT(new_element))
13045         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13046                                    player->index_bit, enter_side);
13047
13048       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13049                                           CE_PLAYER_ENTERS_X,
13050                                           player->index_bit, enter_side);
13051
13052       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13053                                         CE_MOVE_OF_X, move_direction);
13054     }
13055
13056     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13057     {
13058       TestIfPlayerTouchesBadThing(jx, jy);
13059       TestIfPlayerTouchesCustomElement(jx, jy);
13060
13061       /* needed because pushed element has not yet reached its destination,
13062          so it would trigger a change event at its previous field location */
13063       if (!player->is_pushing)
13064         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13065
13066       if (level.finish_dig_collect &&
13067           (player->is_digging || player->is_collecting))
13068       {
13069         int last_element = player->last_removed_element;
13070         int move_direction = player->MovDir;
13071         int enter_side = MV_DIR_OPPOSITE(move_direction);
13072         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13073                             CE_PLAYER_COLLECTS_X);
13074
13075         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13076                                             player->index_bit, enter_side);
13077
13078         player->last_removed_element = EL_UNDEFINED;
13079       }
13080
13081       if (!player->active)
13082         RemovePlayer(player);
13083     }
13084
13085     if (!game.LevelSolved && level.use_step_counter)
13086     {
13087       int i;
13088
13089       TimePlayed++;
13090
13091       if (TimeLeft > 0)
13092       {
13093         TimeLeft--;
13094
13095         if (TimeLeft <= 10 && setup.time_limit)
13096           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13097
13098         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13099
13100         DisplayGameControlValues();
13101
13102         if (!TimeLeft && setup.time_limit)
13103           for (i = 0; i < MAX_PLAYERS; i++)
13104             KillPlayer(&stored_player[i]);
13105       }
13106       else if (game.no_time_limit && !game.all_players_gone)
13107       {
13108         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13109
13110         DisplayGameControlValues();
13111       }
13112     }
13113
13114     if (tape.single_step && tape.recording && !tape.pausing &&
13115         !player->programmed_action)
13116       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13117
13118     if (!player->programmed_action)
13119       CheckSaveEngineSnapshot(player);
13120   }
13121 }
13122
13123 void ScrollScreen(struct PlayerInfo *player, int mode)
13124 {
13125   static unsigned int screen_frame_counter = 0;
13126
13127   if (mode == SCROLL_INIT)
13128   {
13129     // set scrolling step size according to actual player's moving speed
13130     ScrollStepSize = TILEX / player->move_delay_value;
13131
13132     screen_frame_counter = FrameCounter;
13133     ScreenMovDir = player->MovDir;
13134     ScreenMovPos = player->MovPos;
13135     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13136     return;
13137   }
13138   else if (!FrameReached(&screen_frame_counter, 1))
13139     return;
13140
13141   if (ScreenMovPos)
13142   {
13143     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13144     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13145     redraw_mask |= REDRAW_FIELD;
13146   }
13147   else
13148     ScreenMovDir = MV_NONE;
13149 }
13150
13151 void TestIfPlayerTouchesCustomElement(int x, int y)
13152 {
13153   static int xy[4][2] =
13154   {
13155     { 0, -1 },
13156     { -1, 0 },
13157     { +1, 0 },
13158     { 0, +1 }
13159   };
13160   static int trigger_sides[4][2] =
13161   {
13162     // center side       border side
13163     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13164     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13165     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13166     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13167   };
13168   static int touch_dir[4] =
13169   {
13170     MV_LEFT | MV_RIGHT,
13171     MV_UP   | MV_DOWN,
13172     MV_UP   | MV_DOWN,
13173     MV_LEFT | MV_RIGHT
13174   };
13175   int center_element = Tile[x][y];      // should always be non-moving!
13176   int i;
13177
13178   for (i = 0; i < NUM_DIRECTIONS; i++)
13179   {
13180     int xx = x + xy[i][0];
13181     int yy = y + xy[i][1];
13182     int center_side = trigger_sides[i][0];
13183     int border_side = trigger_sides[i][1];
13184     int border_element;
13185
13186     if (!IN_LEV_FIELD(xx, yy))
13187       continue;
13188
13189     if (IS_PLAYER(x, y))                // player found at center element
13190     {
13191       struct PlayerInfo *player = PLAYERINFO(x, y);
13192
13193       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13194         border_element = Tile[xx][yy];          // may be moving!
13195       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13196         border_element = Tile[xx][yy];
13197       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13198         border_element = MovingOrBlocked2Element(xx, yy);
13199       else
13200         continue;               // center and border element do not touch
13201
13202       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13203                                  player->index_bit, border_side);
13204       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13205                                           CE_PLAYER_TOUCHES_X,
13206                                           player->index_bit, border_side);
13207
13208       {
13209         /* use player element that is initially defined in the level playfield,
13210            not the player element that corresponds to the runtime player number
13211            (example: a level that contains EL_PLAYER_3 as the only player would
13212            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13213         int player_element = PLAYERINFO(x, y)->initial_element;
13214
13215         CheckElementChangeBySide(xx, yy, border_element, player_element,
13216                                  CE_TOUCHING_X, border_side);
13217       }
13218     }
13219     else if (IS_PLAYER(xx, yy))         // player found at border element
13220     {
13221       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13222
13223       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13224       {
13225         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13226           continue;             // center and border element do not touch
13227       }
13228
13229       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13230                                  player->index_bit, center_side);
13231       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13232                                           CE_PLAYER_TOUCHES_X,
13233                                           player->index_bit, center_side);
13234
13235       {
13236         /* use player element that is initially defined in the level playfield,
13237            not the player element that corresponds to the runtime player number
13238            (example: a level that contains EL_PLAYER_3 as the only player would
13239            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13240         int player_element = PLAYERINFO(xx, yy)->initial_element;
13241
13242         CheckElementChangeBySide(x, y, center_element, player_element,
13243                                  CE_TOUCHING_X, center_side);
13244       }
13245
13246       break;
13247     }
13248   }
13249 }
13250
13251 void TestIfElementTouchesCustomElement(int x, int y)
13252 {
13253   static int xy[4][2] =
13254   {
13255     { 0, -1 },
13256     { -1, 0 },
13257     { +1, 0 },
13258     { 0, +1 }
13259   };
13260   static int trigger_sides[4][2] =
13261   {
13262     // center side      border side
13263     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13264     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13265     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13266     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13267   };
13268   static int touch_dir[4] =
13269   {
13270     MV_LEFT | MV_RIGHT,
13271     MV_UP   | MV_DOWN,
13272     MV_UP   | MV_DOWN,
13273     MV_LEFT | MV_RIGHT
13274   };
13275   boolean change_center_element = FALSE;
13276   int center_element = Tile[x][y];      // should always be non-moving!
13277   int border_element_old[NUM_DIRECTIONS];
13278   int i;
13279
13280   for (i = 0; i < NUM_DIRECTIONS; i++)
13281   {
13282     int xx = x + xy[i][0];
13283     int yy = y + xy[i][1];
13284     int border_element;
13285
13286     border_element_old[i] = -1;
13287
13288     if (!IN_LEV_FIELD(xx, yy))
13289       continue;
13290
13291     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13292       border_element = Tile[xx][yy];    // may be moving!
13293     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13294       border_element = Tile[xx][yy];
13295     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13296       border_element = MovingOrBlocked2Element(xx, yy);
13297     else
13298       continue;                 // center and border element do not touch
13299
13300     border_element_old[i] = border_element;
13301   }
13302
13303   for (i = 0; i < NUM_DIRECTIONS; i++)
13304   {
13305     int xx = x + xy[i][0];
13306     int yy = y + xy[i][1];
13307     int center_side = trigger_sides[i][0];
13308     int border_element = border_element_old[i];
13309
13310     if (border_element == -1)
13311       continue;
13312
13313     // check for change of border element
13314     CheckElementChangeBySide(xx, yy, border_element, center_element,
13315                              CE_TOUCHING_X, center_side);
13316
13317     // (center element cannot be player, so we dont have to check this here)
13318   }
13319
13320   for (i = 0; i < NUM_DIRECTIONS; i++)
13321   {
13322     int xx = x + xy[i][0];
13323     int yy = y + xy[i][1];
13324     int border_side = trigger_sides[i][1];
13325     int border_element = border_element_old[i];
13326
13327     if (border_element == -1)
13328       continue;
13329
13330     // check for change of center element (but change it only once)
13331     if (!change_center_element)
13332       change_center_element =
13333         CheckElementChangeBySide(x, y, center_element, border_element,
13334                                  CE_TOUCHING_X, border_side);
13335
13336     if (IS_PLAYER(xx, yy))
13337     {
13338       /* use player element that is initially defined in the level playfield,
13339          not the player element that corresponds to the runtime player number
13340          (example: a level that contains EL_PLAYER_3 as the only player would
13341          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13342       int player_element = PLAYERINFO(xx, yy)->initial_element;
13343
13344       CheckElementChangeBySide(x, y, center_element, player_element,
13345                                CE_TOUCHING_X, border_side);
13346     }
13347   }
13348 }
13349
13350 void TestIfElementHitsCustomElement(int x, int y, int direction)
13351 {
13352   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13353   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13354   int hitx = x + dx, hity = y + dy;
13355   int hitting_element = Tile[x][y];
13356   int touched_element;
13357
13358   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13359     return;
13360
13361   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13362                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13363
13364   if (IN_LEV_FIELD(hitx, hity))
13365   {
13366     int opposite_direction = MV_DIR_OPPOSITE(direction);
13367     int hitting_side = direction;
13368     int touched_side = opposite_direction;
13369     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13370                           MovDir[hitx][hity] != direction ||
13371                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13372
13373     object_hit = TRUE;
13374
13375     if (object_hit)
13376     {
13377       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13378                                CE_HITTING_X, touched_side);
13379
13380       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13381                                CE_HIT_BY_X, hitting_side);
13382
13383       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13384                                CE_HIT_BY_SOMETHING, opposite_direction);
13385
13386       if (IS_PLAYER(hitx, hity))
13387       {
13388         /* use player element that is initially defined in the level playfield,
13389            not the player element that corresponds to the runtime player number
13390            (example: a level that contains EL_PLAYER_3 as the only player would
13391            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13392         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13393
13394         CheckElementChangeBySide(x, y, hitting_element, player_element,
13395                                  CE_HITTING_X, touched_side);
13396       }
13397     }
13398   }
13399
13400   // "hitting something" is also true when hitting the playfield border
13401   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13402                            CE_HITTING_SOMETHING, direction);
13403 }
13404
13405 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13406 {
13407   int i, kill_x = -1, kill_y = -1;
13408
13409   int bad_element = -1;
13410   static int test_xy[4][2] =
13411   {
13412     { 0, -1 },
13413     { -1, 0 },
13414     { +1, 0 },
13415     { 0, +1 }
13416   };
13417   static int test_dir[4] =
13418   {
13419     MV_UP,
13420     MV_LEFT,
13421     MV_RIGHT,
13422     MV_DOWN
13423   };
13424
13425   for (i = 0; i < NUM_DIRECTIONS; i++)
13426   {
13427     int test_x, test_y, test_move_dir, test_element;
13428
13429     test_x = good_x + test_xy[i][0];
13430     test_y = good_y + test_xy[i][1];
13431
13432     if (!IN_LEV_FIELD(test_x, test_y))
13433       continue;
13434
13435     test_move_dir =
13436       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13437
13438     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13439
13440     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13441        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13442     */
13443     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13444         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13445     {
13446       kill_x = test_x;
13447       kill_y = test_y;
13448       bad_element = test_element;
13449
13450       break;
13451     }
13452   }
13453
13454   if (kill_x != -1 || kill_y != -1)
13455   {
13456     if (IS_PLAYER(good_x, good_y))
13457     {
13458       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13459
13460       if (player->shield_deadly_time_left > 0 &&
13461           !IS_INDESTRUCTIBLE(bad_element))
13462         Bang(kill_x, kill_y);
13463       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13464         KillPlayer(player);
13465     }
13466     else
13467       Bang(good_x, good_y);
13468   }
13469 }
13470
13471 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13472 {
13473   int i, kill_x = -1, kill_y = -1;
13474   int bad_element = Tile[bad_x][bad_y];
13475   static int test_xy[4][2] =
13476   {
13477     { 0, -1 },
13478     { -1, 0 },
13479     { +1, 0 },
13480     { 0, +1 }
13481   };
13482   static int touch_dir[4] =
13483   {
13484     MV_LEFT | MV_RIGHT,
13485     MV_UP   | MV_DOWN,
13486     MV_UP   | MV_DOWN,
13487     MV_LEFT | MV_RIGHT
13488   };
13489   static int test_dir[4] =
13490   {
13491     MV_UP,
13492     MV_LEFT,
13493     MV_RIGHT,
13494     MV_DOWN
13495   };
13496
13497   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13498     return;
13499
13500   for (i = 0; i < NUM_DIRECTIONS; i++)
13501   {
13502     int test_x, test_y, test_move_dir, test_element;
13503
13504     test_x = bad_x + test_xy[i][0];
13505     test_y = bad_y + test_xy[i][1];
13506
13507     if (!IN_LEV_FIELD(test_x, test_y))
13508       continue;
13509
13510     test_move_dir =
13511       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13512
13513     test_element = Tile[test_x][test_y];
13514
13515     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13516        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13517     */
13518     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13519         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13520     {
13521       // good thing is player or penguin that does not move away
13522       if (IS_PLAYER(test_x, test_y))
13523       {
13524         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13525
13526         if (bad_element == EL_ROBOT && player->is_moving)
13527           continue;     // robot does not kill player if he is moving
13528
13529         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13530         {
13531           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13532             continue;           // center and border element do not touch
13533         }
13534
13535         kill_x = test_x;
13536         kill_y = test_y;
13537
13538         break;
13539       }
13540       else if (test_element == EL_PENGUIN)
13541       {
13542         kill_x = test_x;
13543         kill_y = test_y;
13544
13545         break;
13546       }
13547     }
13548   }
13549
13550   if (kill_x != -1 || kill_y != -1)
13551   {
13552     if (IS_PLAYER(kill_x, kill_y))
13553     {
13554       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13555
13556       if (player->shield_deadly_time_left > 0 &&
13557           !IS_INDESTRUCTIBLE(bad_element))
13558         Bang(bad_x, bad_y);
13559       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13560         KillPlayer(player);
13561     }
13562     else
13563       Bang(kill_x, kill_y);
13564   }
13565 }
13566
13567 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13568 {
13569   int bad_element = Tile[bad_x][bad_y];
13570   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13571   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13572   int test_x = bad_x + dx, test_y = bad_y + dy;
13573   int test_move_dir, test_element;
13574   int kill_x = -1, kill_y = -1;
13575
13576   if (!IN_LEV_FIELD(test_x, test_y))
13577     return;
13578
13579   test_move_dir =
13580     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13581
13582   test_element = Tile[test_x][test_y];
13583
13584   if (test_move_dir != bad_move_dir)
13585   {
13586     // good thing can be player or penguin that does not move away
13587     if (IS_PLAYER(test_x, test_y))
13588     {
13589       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13590
13591       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13592          player as being hit when he is moving towards the bad thing, because
13593          the "get hit by" condition would be lost after the player stops) */
13594       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13595         return;         // player moves away from bad thing
13596
13597       kill_x = test_x;
13598       kill_y = test_y;
13599     }
13600     else if (test_element == EL_PENGUIN)
13601     {
13602       kill_x = test_x;
13603       kill_y = test_y;
13604     }
13605   }
13606
13607   if (kill_x != -1 || kill_y != -1)
13608   {
13609     if (IS_PLAYER(kill_x, kill_y))
13610     {
13611       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13612
13613       if (player->shield_deadly_time_left > 0 &&
13614           !IS_INDESTRUCTIBLE(bad_element))
13615         Bang(bad_x, bad_y);
13616       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13617         KillPlayer(player);
13618     }
13619     else
13620       Bang(kill_x, kill_y);
13621   }
13622 }
13623
13624 void TestIfPlayerTouchesBadThing(int x, int y)
13625 {
13626   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13627 }
13628
13629 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13630 {
13631   TestIfGoodThingHitsBadThing(x, y, move_dir);
13632 }
13633
13634 void TestIfBadThingTouchesPlayer(int x, int y)
13635 {
13636   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13637 }
13638
13639 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13640 {
13641   TestIfBadThingHitsGoodThing(x, y, move_dir);
13642 }
13643
13644 void TestIfFriendTouchesBadThing(int x, int y)
13645 {
13646   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13647 }
13648
13649 void TestIfBadThingTouchesFriend(int x, int y)
13650 {
13651   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13652 }
13653
13654 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13655 {
13656   int i, kill_x = bad_x, kill_y = bad_y;
13657   static int xy[4][2] =
13658   {
13659     { 0, -1 },
13660     { -1, 0 },
13661     { +1, 0 },
13662     { 0, +1 }
13663   };
13664
13665   for (i = 0; i < NUM_DIRECTIONS; i++)
13666   {
13667     int x, y, element;
13668
13669     x = bad_x + xy[i][0];
13670     y = bad_y + xy[i][1];
13671     if (!IN_LEV_FIELD(x, y))
13672       continue;
13673
13674     element = Tile[x][y];
13675     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13676         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13677     {
13678       kill_x = x;
13679       kill_y = y;
13680       break;
13681     }
13682   }
13683
13684   if (kill_x != bad_x || kill_y != bad_y)
13685     Bang(bad_x, bad_y);
13686 }
13687
13688 void KillPlayer(struct PlayerInfo *player)
13689 {
13690   int jx = player->jx, jy = player->jy;
13691
13692   if (!player->active)
13693     return;
13694
13695 #if 0
13696   Debug("game:playing:KillPlayer",
13697         "0: killed == %d, active == %d, reanimated == %d",
13698         player->killed, player->active, player->reanimated);
13699 #endif
13700
13701   /* the following code was introduced to prevent an infinite loop when calling
13702      -> Bang()
13703      -> CheckTriggeredElementChangeExt()
13704      -> ExecuteCustomElementAction()
13705      -> KillPlayer()
13706      -> (infinitely repeating the above sequence of function calls)
13707      which occurs when killing the player while having a CE with the setting
13708      "kill player X when explosion of <player X>"; the solution using a new
13709      field "player->killed" was chosen for backwards compatibility, although
13710      clever use of the fields "player->active" etc. would probably also work */
13711 #if 1
13712   if (player->killed)
13713     return;
13714 #endif
13715
13716   player->killed = TRUE;
13717
13718   // remove accessible field at the player's position
13719   Tile[jx][jy] = EL_EMPTY;
13720
13721   // deactivate shield (else Bang()/Explode() would not work right)
13722   player->shield_normal_time_left = 0;
13723   player->shield_deadly_time_left = 0;
13724
13725 #if 0
13726   Debug("game:playing:KillPlayer",
13727         "1: killed == %d, active == %d, reanimated == %d",
13728         player->killed, player->active, player->reanimated);
13729 #endif
13730
13731   Bang(jx, jy);
13732
13733 #if 0
13734   Debug("game:playing:KillPlayer",
13735         "2: killed == %d, active == %d, reanimated == %d",
13736         player->killed, player->active, player->reanimated);
13737 #endif
13738
13739   if (player->reanimated)       // killed player may have been reanimated
13740     player->killed = player->reanimated = FALSE;
13741   else
13742     BuryPlayer(player);
13743 }
13744
13745 static void KillPlayerUnlessEnemyProtected(int x, int y)
13746 {
13747   if (!PLAYER_ENEMY_PROTECTED(x, y))
13748     KillPlayer(PLAYERINFO(x, y));
13749 }
13750
13751 static void KillPlayerUnlessExplosionProtected(int x, int y)
13752 {
13753   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13754     KillPlayer(PLAYERINFO(x, y));
13755 }
13756
13757 void BuryPlayer(struct PlayerInfo *player)
13758 {
13759   int jx = player->jx, jy = player->jy;
13760
13761   if (!player->active)
13762     return;
13763
13764   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13765   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13766
13767   RemovePlayer(player);
13768
13769   player->buried = TRUE;
13770
13771   if (game.all_players_gone)
13772     game.GameOver = TRUE;
13773 }
13774
13775 void RemovePlayer(struct PlayerInfo *player)
13776 {
13777   int jx = player->jx, jy = player->jy;
13778   int i, found = FALSE;
13779
13780   player->present = FALSE;
13781   player->active = FALSE;
13782
13783   // required for some CE actions (even if the player is not active anymore)
13784   player->MovPos = 0;
13785
13786   if (!ExplodeField[jx][jy])
13787     StorePlayer[jx][jy] = 0;
13788
13789   if (player->is_moving)
13790     TEST_DrawLevelField(player->last_jx, player->last_jy);
13791
13792   for (i = 0; i < MAX_PLAYERS; i++)
13793     if (stored_player[i].active)
13794       found = TRUE;
13795
13796   if (!found)
13797   {
13798     game.all_players_gone = TRUE;
13799     game.GameOver = TRUE;
13800   }
13801
13802   game.exit_x = game.robot_wheel_x = jx;
13803   game.exit_y = game.robot_wheel_y = jy;
13804 }
13805
13806 void ExitPlayer(struct PlayerInfo *player)
13807 {
13808   DrawPlayer(player);   // needed here only to cleanup last field
13809   RemovePlayer(player);
13810
13811   if (game.players_still_needed > 0)
13812     game.players_still_needed--;
13813 }
13814
13815 static void SetFieldForSnapping(int x, int y, int element, int direction,
13816                                 int player_index_bit)
13817 {
13818   struct ElementInfo *ei = &element_info[element];
13819   int direction_bit = MV_DIR_TO_BIT(direction);
13820   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13821   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13822                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13823
13824   Tile[x][y] = EL_ELEMENT_SNAPPING;
13825   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13826   MovDir[x][y] = direction;
13827   Store[x][y] = element;
13828   Store2[x][y] = player_index_bit;
13829
13830   ResetGfxAnimation(x, y);
13831
13832   GfxElement[x][y] = element;
13833   GfxAction[x][y] = action;
13834   GfxDir[x][y] = direction;
13835   GfxFrame[x][y] = -1;
13836 }
13837
13838 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13839                                    int player_index_bit)
13840 {
13841   TestIfElementTouchesCustomElement(x, y);      // for empty space
13842
13843   if (level.finish_dig_collect)
13844   {
13845     int dig_side = MV_DIR_OPPOSITE(direction);
13846
13847     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13848                                         player_index_bit, dig_side);
13849   }
13850 }
13851
13852 /*
13853   =============================================================================
13854   checkDiagonalPushing()
13855   -----------------------------------------------------------------------------
13856   check if diagonal input device direction results in pushing of object
13857   (by checking if the alternative direction is walkable, diggable, ...)
13858   =============================================================================
13859 */
13860
13861 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13862                                     int x, int y, int real_dx, int real_dy)
13863 {
13864   int jx, jy, dx, dy, xx, yy;
13865
13866   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13867     return TRUE;
13868
13869   // diagonal direction: check alternative direction
13870   jx = player->jx;
13871   jy = player->jy;
13872   dx = x - jx;
13873   dy = y - jy;
13874   xx = jx + (dx == 0 ? real_dx : 0);
13875   yy = jy + (dy == 0 ? real_dy : 0);
13876
13877   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13878 }
13879
13880 /*
13881   =============================================================================
13882   DigField()
13883   -----------------------------------------------------------------------------
13884   x, y:                 field next to player (non-diagonal) to try to dig to
13885   real_dx, real_dy:     direction as read from input device (can be diagonal)
13886   =============================================================================
13887 */
13888
13889 static int DigField(struct PlayerInfo *player,
13890                     int oldx, int oldy, int x, int y,
13891                     int real_dx, int real_dy, int mode)
13892 {
13893   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13894   boolean player_was_pushing = player->is_pushing;
13895   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13896   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13897   int jx = oldx, jy = oldy;
13898   int dx = x - jx, dy = y - jy;
13899   int nextx = x + dx, nexty = y + dy;
13900   int move_direction = (dx == -1 ? MV_LEFT  :
13901                         dx == +1 ? MV_RIGHT :
13902                         dy == -1 ? MV_UP    :
13903                         dy == +1 ? MV_DOWN  : MV_NONE);
13904   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13905   int dig_side = MV_DIR_OPPOSITE(move_direction);
13906   int old_element = Tile[jx][jy];
13907   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13908   int collect_count;
13909
13910   if (is_player)                // function can also be called by EL_PENGUIN
13911   {
13912     if (player->MovPos == 0)
13913     {
13914       player->is_digging = FALSE;
13915       player->is_collecting = FALSE;
13916     }
13917
13918     if (player->MovPos == 0)    // last pushing move finished
13919       player->is_pushing = FALSE;
13920
13921     if (mode == DF_NO_PUSH)     // player just stopped pushing
13922     {
13923       player->is_switching = FALSE;
13924       player->push_delay = -1;
13925
13926       return MP_NO_ACTION;
13927     }
13928   }
13929
13930   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13931     old_element = Back[jx][jy];
13932
13933   // in case of element dropped at player position, check background
13934   else if (Back[jx][jy] != EL_EMPTY &&
13935            game.engine_version >= VERSION_IDENT(2,2,0,0))
13936     old_element = Back[jx][jy];
13937
13938   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13939     return MP_NO_ACTION;        // field has no opening in this direction
13940
13941   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13942     return MP_NO_ACTION;        // field has no opening in this direction
13943
13944   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13945   {
13946     SplashAcid(x, y);
13947
13948     Tile[jx][jy] = player->artwork_element;
13949     InitMovingField(jx, jy, MV_DOWN);
13950     Store[jx][jy] = EL_ACID;
13951     ContinueMoving(jx, jy);
13952     BuryPlayer(player);
13953
13954     return MP_DONT_RUN_INTO;
13955   }
13956
13957   if (player_can_move && DONT_RUN_INTO(element))
13958   {
13959     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13960
13961     return MP_DONT_RUN_INTO;
13962   }
13963
13964   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13965     return MP_NO_ACTION;
13966
13967   collect_count = element_info[element].collect_count_initial;
13968
13969   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13970     return MP_NO_ACTION;
13971
13972   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13973     player_can_move = player_can_move_or_snap;
13974
13975   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13976       game.engine_version >= VERSION_IDENT(2,2,0,0))
13977   {
13978     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13979                                player->index_bit, dig_side);
13980     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13981                                         player->index_bit, dig_side);
13982
13983     if (element == EL_DC_LANDMINE)
13984       Bang(x, y);
13985
13986     if (Tile[x][y] != element)          // field changed by snapping
13987       return MP_ACTION;
13988
13989     return MP_NO_ACTION;
13990   }
13991
13992   if (player->gravity && is_player && !player->is_auto_moving &&
13993       canFallDown(player) && move_direction != MV_DOWN &&
13994       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13995     return MP_NO_ACTION;        // player cannot walk here due to gravity
13996
13997   if (player_can_move &&
13998       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13999   {
14000     int sound_element = SND_ELEMENT(element);
14001     int sound_action = ACTION_WALKING;
14002
14003     if (IS_RND_GATE(element))
14004     {
14005       if (!player->key[RND_GATE_NR(element)])
14006         return MP_NO_ACTION;
14007     }
14008     else if (IS_RND_GATE_GRAY(element))
14009     {
14010       if (!player->key[RND_GATE_GRAY_NR(element)])
14011         return MP_NO_ACTION;
14012     }
14013     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14014     {
14015       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14016         return MP_NO_ACTION;
14017     }
14018     else if (element == EL_EXIT_OPEN ||
14019              element == EL_EM_EXIT_OPEN ||
14020              element == EL_EM_EXIT_OPENING ||
14021              element == EL_STEEL_EXIT_OPEN ||
14022              element == EL_EM_STEEL_EXIT_OPEN ||
14023              element == EL_EM_STEEL_EXIT_OPENING ||
14024              element == EL_SP_EXIT_OPEN ||
14025              element == EL_SP_EXIT_OPENING)
14026     {
14027       sound_action = ACTION_PASSING;    // player is passing exit
14028     }
14029     else if (element == EL_EMPTY)
14030     {
14031       sound_action = ACTION_MOVING;             // nothing to walk on
14032     }
14033
14034     // play sound from background or player, whatever is available
14035     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14036       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14037     else
14038       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14039   }
14040   else if (player_can_move &&
14041            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14042   {
14043     if (!ACCESS_FROM(element, opposite_direction))
14044       return MP_NO_ACTION;      // field not accessible from this direction
14045
14046     if (CAN_MOVE(element))      // only fixed elements can be passed!
14047       return MP_NO_ACTION;
14048
14049     if (IS_EM_GATE(element))
14050     {
14051       if (!player->key[EM_GATE_NR(element)])
14052         return MP_NO_ACTION;
14053     }
14054     else if (IS_EM_GATE_GRAY(element))
14055     {
14056       if (!player->key[EM_GATE_GRAY_NR(element)])
14057         return MP_NO_ACTION;
14058     }
14059     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14060     {
14061       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14062         return MP_NO_ACTION;
14063     }
14064     else if (IS_EMC_GATE(element))
14065     {
14066       if (!player->key[EMC_GATE_NR(element)])
14067         return MP_NO_ACTION;
14068     }
14069     else if (IS_EMC_GATE_GRAY(element))
14070     {
14071       if (!player->key[EMC_GATE_GRAY_NR(element)])
14072         return MP_NO_ACTION;
14073     }
14074     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14075     {
14076       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14077         return MP_NO_ACTION;
14078     }
14079     else if (element == EL_DC_GATE_WHITE ||
14080              element == EL_DC_GATE_WHITE_GRAY ||
14081              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14082     {
14083       if (player->num_white_keys == 0)
14084         return MP_NO_ACTION;
14085
14086       player->num_white_keys--;
14087     }
14088     else if (IS_SP_PORT(element))
14089     {
14090       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14091           element == EL_SP_GRAVITY_PORT_RIGHT ||
14092           element == EL_SP_GRAVITY_PORT_UP ||
14093           element == EL_SP_GRAVITY_PORT_DOWN)
14094         player->gravity = !player->gravity;
14095       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14096                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14097                element == EL_SP_GRAVITY_ON_PORT_UP ||
14098                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14099         player->gravity = TRUE;
14100       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14101                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14102                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14103                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14104         player->gravity = FALSE;
14105     }
14106
14107     // automatically move to the next field with double speed
14108     player->programmed_action = move_direction;
14109
14110     if (player->move_delay_reset_counter == 0)
14111     {
14112       player->move_delay_reset_counter = 2;     // two double speed steps
14113
14114       DOUBLE_PLAYER_SPEED(player);
14115     }
14116
14117     PlayLevelSoundAction(x, y, ACTION_PASSING);
14118   }
14119   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14120   {
14121     RemoveField(x, y);
14122
14123     if (mode != DF_SNAP)
14124     {
14125       GfxElement[x][y] = GFX_ELEMENT(element);
14126       player->is_digging = TRUE;
14127     }
14128
14129     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14130
14131     // use old behaviour for old levels (digging)
14132     if (!level.finish_dig_collect)
14133     {
14134       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14135                                           player->index_bit, dig_side);
14136
14137       // if digging triggered player relocation, finish digging tile
14138       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14139         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14140     }
14141
14142     if (mode == DF_SNAP)
14143     {
14144       if (level.block_snap_field)
14145         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14146       else
14147         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14148
14149       // use old behaviour for old levels (snapping)
14150       if (!level.finish_dig_collect)
14151         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14152                                             player->index_bit, dig_side);
14153     }
14154   }
14155   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14156   {
14157     RemoveField(x, y);
14158
14159     if (is_player && mode != DF_SNAP)
14160     {
14161       GfxElement[x][y] = element;
14162       player->is_collecting = TRUE;
14163     }
14164
14165     if (element == EL_SPEED_PILL)
14166     {
14167       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14168     }
14169     else if (element == EL_EXTRA_TIME && level.time > 0)
14170     {
14171       TimeLeft += level.extra_time;
14172
14173       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14174
14175       DisplayGameControlValues();
14176     }
14177     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14178     {
14179       player->shield_normal_time_left += level.shield_normal_time;
14180       if (element == EL_SHIELD_DEADLY)
14181         player->shield_deadly_time_left += level.shield_deadly_time;
14182     }
14183     else if (element == EL_DYNAMITE ||
14184              element == EL_EM_DYNAMITE ||
14185              element == EL_SP_DISK_RED)
14186     {
14187       if (player->inventory_size < MAX_INVENTORY_SIZE)
14188         player->inventory_element[player->inventory_size++] = element;
14189
14190       DrawGameDoorValues();
14191     }
14192     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14193     {
14194       player->dynabomb_count++;
14195       player->dynabombs_left++;
14196     }
14197     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14198     {
14199       player->dynabomb_size++;
14200     }
14201     else if (element == EL_DYNABOMB_INCREASE_POWER)
14202     {
14203       player->dynabomb_xl = TRUE;
14204     }
14205     else if (IS_KEY(element))
14206     {
14207       player->key[KEY_NR(element)] = TRUE;
14208
14209       DrawGameDoorValues();
14210     }
14211     else if (element == EL_DC_KEY_WHITE)
14212     {
14213       player->num_white_keys++;
14214
14215       // display white keys?
14216       // DrawGameDoorValues();
14217     }
14218     else if (IS_ENVELOPE(element))
14219     {
14220       player->show_envelope = element;
14221     }
14222     else if (element == EL_EMC_LENSES)
14223     {
14224       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14225
14226       RedrawAllInvisibleElementsForLenses();
14227     }
14228     else if (element == EL_EMC_MAGNIFIER)
14229     {
14230       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14231
14232       RedrawAllInvisibleElementsForMagnifier();
14233     }
14234     else if (IS_DROPPABLE(element) ||
14235              IS_THROWABLE(element))     // can be collected and dropped
14236     {
14237       int i;
14238
14239       if (collect_count == 0)
14240         player->inventory_infinite_element = element;
14241       else
14242         for (i = 0; i < collect_count; i++)
14243           if (player->inventory_size < MAX_INVENTORY_SIZE)
14244             player->inventory_element[player->inventory_size++] = element;
14245
14246       DrawGameDoorValues();
14247     }
14248     else if (collect_count > 0)
14249     {
14250       game.gems_still_needed -= collect_count;
14251       if (game.gems_still_needed < 0)
14252         game.gems_still_needed = 0;
14253
14254       game.snapshot.collected_item = TRUE;
14255
14256       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14257
14258       DisplayGameControlValues();
14259     }
14260
14261     RaiseScoreElement(element);
14262     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14263
14264     // use old behaviour for old levels (collecting)
14265     if (!level.finish_dig_collect && is_player)
14266     {
14267       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14268                                           player->index_bit, dig_side);
14269
14270       // if collecting triggered player relocation, finish collecting tile
14271       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14272         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14273     }
14274
14275     if (mode == DF_SNAP)
14276     {
14277       if (level.block_snap_field)
14278         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14279       else
14280         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14281
14282       // use old behaviour for old levels (snapping)
14283       if (!level.finish_dig_collect)
14284         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14285                                             player->index_bit, dig_side);
14286     }
14287   }
14288   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14289   {
14290     if (mode == DF_SNAP && element != EL_BD_ROCK)
14291       return MP_NO_ACTION;
14292
14293     if (CAN_FALL(element) && dy)
14294       return MP_NO_ACTION;
14295
14296     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14297         !(element == EL_SPRING && level.use_spring_bug))
14298       return MP_NO_ACTION;
14299
14300     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14301         ((move_direction & MV_VERTICAL &&
14302           ((element_info[element].move_pattern & MV_LEFT &&
14303             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14304            (element_info[element].move_pattern & MV_RIGHT &&
14305             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14306          (move_direction & MV_HORIZONTAL &&
14307           ((element_info[element].move_pattern & MV_UP &&
14308             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14309            (element_info[element].move_pattern & MV_DOWN &&
14310             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14311       return MP_NO_ACTION;
14312
14313     // do not push elements already moving away faster than player
14314     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14315         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14316       return MP_NO_ACTION;
14317
14318     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14319     {
14320       if (player->push_delay_value == -1 || !player_was_pushing)
14321         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14322     }
14323     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14324     {
14325       if (player->push_delay_value == -1)
14326         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14327     }
14328     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14329     {
14330       if (!player->is_pushing)
14331         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14332     }
14333
14334     player->is_pushing = TRUE;
14335     player->is_active = TRUE;
14336
14337     if (!(IN_LEV_FIELD(nextx, nexty) &&
14338           (IS_FREE(nextx, nexty) ||
14339            (IS_SB_ELEMENT(element) &&
14340             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14341            (IS_CUSTOM_ELEMENT(element) &&
14342             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14343       return MP_NO_ACTION;
14344
14345     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14346       return MP_NO_ACTION;
14347
14348     if (player->push_delay == -1)       // new pushing; restart delay
14349       player->push_delay = 0;
14350
14351     if (player->push_delay < player->push_delay_value &&
14352         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14353         element != EL_SPRING && element != EL_BALLOON)
14354     {
14355       // make sure that there is no move delay before next try to push
14356       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14357         player->move_delay = 0;
14358
14359       return MP_NO_ACTION;
14360     }
14361
14362     if (IS_CUSTOM_ELEMENT(element) &&
14363         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14364     {
14365       if (!DigFieldByCE(nextx, nexty, element))
14366         return MP_NO_ACTION;
14367     }
14368
14369     if (IS_SB_ELEMENT(element))
14370     {
14371       boolean sokoban_task_solved = FALSE;
14372
14373       if (element == EL_SOKOBAN_FIELD_FULL)
14374       {
14375         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14376
14377         IncrementSokobanFieldsNeeded();
14378         IncrementSokobanObjectsNeeded();
14379       }
14380
14381       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14382       {
14383         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14384
14385         DecrementSokobanFieldsNeeded();
14386         DecrementSokobanObjectsNeeded();
14387
14388         // sokoban object was pushed from empty field to sokoban field
14389         if (Back[x][y] == EL_EMPTY)
14390           sokoban_task_solved = TRUE;
14391       }
14392
14393       Tile[x][y] = EL_SOKOBAN_OBJECT;
14394
14395       if (Back[x][y] == Back[nextx][nexty])
14396         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14397       else if (Back[x][y] != 0)
14398         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14399                                     ACTION_EMPTYING);
14400       else
14401         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14402                                     ACTION_FILLING);
14403
14404       if (sokoban_task_solved &&
14405           game.sokoban_fields_still_needed == 0 &&
14406           game.sokoban_objects_still_needed == 0 &&
14407           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14408       {
14409         game.players_still_needed = 0;
14410
14411         LevelSolved();
14412
14413         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14414       }
14415     }
14416     else
14417       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14418
14419     InitMovingField(x, y, move_direction);
14420     GfxAction[x][y] = ACTION_PUSHING;
14421
14422     if (mode == DF_SNAP)
14423       ContinueMoving(x, y);
14424     else
14425       MovPos[x][y] = (dx != 0 ? dx : dy);
14426
14427     Pushed[x][y] = TRUE;
14428     Pushed[nextx][nexty] = TRUE;
14429
14430     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14431       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14432     else
14433       player->push_delay_value = -1;    // get new value later
14434
14435     // check for element change _after_ element has been pushed
14436     if (game.use_change_when_pushing_bug)
14437     {
14438       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14439                                  player->index_bit, dig_side);
14440       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14441                                           player->index_bit, dig_side);
14442     }
14443   }
14444   else if (IS_SWITCHABLE(element))
14445   {
14446     if (PLAYER_SWITCHING(player, x, y))
14447     {
14448       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14449                                           player->index_bit, dig_side);
14450
14451       return MP_ACTION;
14452     }
14453
14454     player->is_switching = TRUE;
14455     player->switch_x = x;
14456     player->switch_y = y;
14457
14458     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14459
14460     if (element == EL_ROBOT_WHEEL)
14461     {
14462       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14463
14464       game.robot_wheel_x = x;
14465       game.robot_wheel_y = y;
14466       game.robot_wheel_active = TRUE;
14467
14468       TEST_DrawLevelField(x, y);
14469     }
14470     else if (element == EL_SP_TERMINAL)
14471     {
14472       int xx, yy;
14473
14474       SCAN_PLAYFIELD(xx, yy)
14475       {
14476         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14477         {
14478           Bang(xx, yy);
14479         }
14480         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14481         {
14482           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14483
14484           ResetGfxAnimation(xx, yy);
14485           TEST_DrawLevelField(xx, yy);
14486         }
14487       }
14488     }
14489     else if (IS_BELT_SWITCH(element))
14490     {
14491       ToggleBeltSwitch(x, y);
14492     }
14493     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14494              element == EL_SWITCHGATE_SWITCH_DOWN ||
14495              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14496              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14497     {
14498       ToggleSwitchgateSwitch(x, y);
14499     }
14500     else if (element == EL_LIGHT_SWITCH ||
14501              element == EL_LIGHT_SWITCH_ACTIVE)
14502     {
14503       ToggleLightSwitch(x, y);
14504     }
14505     else if (element == EL_TIMEGATE_SWITCH ||
14506              element == EL_DC_TIMEGATE_SWITCH)
14507     {
14508       ActivateTimegateSwitch(x, y);
14509     }
14510     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14511              element == EL_BALLOON_SWITCH_RIGHT ||
14512              element == EL_BALLOON_SWITCH_UP    ||
14513              element == EL_BALLOON_SWITCH_DOWN  ||
14514              element == EL_BALLOON_SWITCH_NONE  ||
14515              element == EL_BALLOON_SWITCH_ANY)
14516     {
14517       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14518                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14519                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14520                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14521                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14522                              move_direction);
14523     }
14524     else if (element == EL_LAMP)
14525     {
14526       Tile[x][y] = EL_LAMP_ACTIVE;
14527       game.lights_still_needed--;
14528
14529       ResetGfxAnimation(x, y);
14530       TEST_DrawLevelField(x, y);
14531     }
14532     else if (element == EL_TIME_ORB_FULL)
14533     {
14534       Tile[x][y] = EL_TIME_ORB_EMPTY;
14535
14536       if (level.time > 0 || level.use_time_orb_bug)
14537       {
14538         TimeLeft += level.time_orb_time;
14539         game.no_time_limit = FALSE;
14540
14541         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14542
14543         DisplayGameControlValues();
14544       }
14545
14546       ResetGfxAnimation(x, y);
14547       TEST_DrawLevelField(x, y);
14548     }
14549     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14550              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14551     {
14552       int xx, yy;
14553
14554       game.ball_active = !game.ball_active;
14555
14556       SCAN_PLAYFIELD(xx, yy)
14557       {
14558         int e = Tile[xx][yy];
14559
14560         if (game.ball_active)
14561         {
14562           if (e == EL_EMC_MAGIC_BALL)
14563             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14564           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14565             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14566         }
14567         else
14568         {
14569           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14570             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14571           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14572             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14573         }
14574       }
14575     }
14576
14577     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14578                                         player->index_bit, dig_side);
14579
14580     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14581                                         player->index_bit, dig_side);
14582
14583     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14584                                         player->index_bit, dig_side);
14585
14586     return MP_ACTION;
14587   }
14588   else
14589   {
14590     if (!PLAYER_SWITCHING(player, x, y))
14591     {
14592       player->is_switching = TRUE;
14593       player->switch_x = x;
14594       player->switch_y = y;
14595
14596       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14597                                  player->index_bit, dig_side);
14598       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14599                                           player->index_bit, dig_side);
14600
14601       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14602                                  player->index_bit, dig_side);
14603       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14604                                           player->index_bit, dig_side);
14605     }
14606
14607     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14608                                player->index_bit, dig_side);
14609     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14610                                         player->index_bit, dig_side);
14611
14612     return MP_NO_ACTION;
14613   }
14614
14615   player->push_delay = -1;
14616
14617   if (is_player)                // function can also be called by EL_PENGUIN
14618   {
14619     if (Tile[x][y] != element)          // really digged/collected something
14620     {
14621       player->is_collecting = !player->is_digging;
14622       player->is_active = TRUE;
14623
14624       player->last_removed_element = element;
14625     }
14626   }
14627
14628   return MP_MOVING;
14629 }
14630
14631 static boolean DigFieldByCE(int x, int y, int digging_element)
14632 {
14633   int element = Tile[x][y];
14634
14635   if (!IS_FREE(x, y))
14636   {
14637     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14638                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14639                   ACTION_BREAKING);
14640
14641     // no element can dig solid indestructible elements
14642     if (IS_INDESTRUCTIBLE(element) &&
14643         !IS_DIGGABLE(element) &&
14644         !IS_COLLECTIBLE(element))
14645       return FALSE;
14646
14647     if (AmoebaNr[x][y] &&
14648         (element == EL_AMOEBA_FULL ||
14649          element == EL_BD_AMOEBA ||
14650          element == EL_AMOEBA_GROWING))
14651     {
14652       AmoebaCnt[AmoebaNr[x][y]]--;
14653       AmoebaCnt2[AmoebaNr[x][y]]--;
14654     }
14655
14656     if (IS_MOVING(x, y))
14657       RemoveMovingField(x, y);
14658     else
14659     {
14660       RemoveField(x, y);
14661       TEST_DrawLevelField(x, y);
14662     }
14663
14664     // if digged element was about to explode, prevent the explosion
14665     ExplodeField[x][y] = EX_TYPE_NONE;
14666
14667     PlayLevelSoundAction(x, y, action);
14668   }
14669
14670   Store[x][y] = EL_EMPTY;
14671
14672   // this makes it possible to leave the removed element again
14673   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14674     Store[x][y] = element;
14675
14676   return TRUE;
14677 }
14678
14679 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14680 {
14681   int jx = player->jx, jy = player->jy;
14682   int x = jx + dx, y = jy + dy;
14683   int snap_direction = (dx == -1 ? MV_LEFT  :
14684                         dx == +1 ? MV_RIGHT :
14685                         dy == -1 ? MV_UP    :
14686                         dy == +1 ? MV_DOWN  : MV_NONE);
14687   boolean can_continue_snapping = (level.continuous_snapping &&
14688                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14689
14690   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14691     return FALSE;
14692
14693   if (!player->active || !IN_LEV_FIELD(x, y))
14694     return FALSE;
14695
14696   if (dx && dy)
14697     return FALSE;
14698
14699   if (!dx && !dy)
14700   {
14701     if (player->MovPos == 0)
14702       player->is_pushing = FALSE;
14703
14704     player->is_snapping = FALSE;
14705
14706     if (player->MovPos == 0)
14707     {
14708       player->is_moving = FALSE;
14709       player->is_digging = FALSE;
14710       player->is_collecting = FALSE;
14711     }
14712
14713     return FALSE;
14714   }
14715
14716   // prevent snapping with already pressed snap key when not allowed
14717   if (player->is_snapping && !can_continue_snapping)
14718     return FALSE;
14719
14720   player->MovDir = snap_direction;
14721
14722   if (player->MovPos == 0)
14723   {
14724     player->is_moving = FALSE;
14725     player->is_digging = FALSE;
14726     player->is_collecting = FALSE;
14727   }
14728
14729   player->is_dropping = FALSE;
14730   player->is_dropping_pressed = FALSE;
14731   player->drop_pressed_delay = 0;
14732
14733   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14734     return FALSE;
14735
14736   player->is_snapping = TRUE;
14737   player->is_active = TRUE;
14738
14739   if (player->MovPos == 0)
14740   {
14741     player->is_moving = FALSE;
14742     player->is_digging = FALSE;
14743     player->is_collecting = FALSE;
14744   }
14745
14746   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14747     TEST_DrawLevelField(player->last_jx, player->last_jy);
14748
14749   TEST_DrawLevelField(x, y);
14750
14751   return TRUE;
14752 }
14753
14754 static boolean DropElement(struct PlayerInfo *player)
14755 {
14756   int old_element, new_element;
14757   int dropx = player->jx, dropy = player->jy;
14758   int drop_direction = player->MovDir;
14759   int drop_side = drop_direction;
14760   int drop_element = get_next_dropped_element(player);
14761
14762   /* do not drop an element on top of another element; when holding drop key
14763      pressed without moving, dropped element must move away before the next
14764      element can be dropped (this is especially important if the next element
14765      is dynamite, which can be placed on background for historical reasons) */
14766   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14767     return MP_ACTION;
14768
14769   if (IS_THROWABLE(drop_element))
14770   {
14771     dropx += GET_DX_FROM_DIR(drop_direction);
14772     dropy += GET_DY_FROM_DIR(drop_direction);
14773
14774     if (!IN_LEV_FIELD(dropx, dropy))
14775       return FALSE;
14776   }
14777
14778   old_element = Tile[dropx][dropy];     // old element at dropping position
14779   new_element = drop_element;           // default: no change when dropping
14780
14781   // check if player is active, not moving and ready to drop
14782   if (!player->active || player->MovPos || player->drop_delay > 0)
14783     return FALSE;
14784
14785   // check if player has anything that can be dropped
14786   if (new_element == EL_UNDEFINED)
14787     return FALSE;
14788
14789   // only set if player has anything that can be dropped
14790   player->is_dropping_pressed = TRUE;
14791
14792   // check if drop key was pressed long enough for EM style dynamite
14793   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14794     return FALSE;
14795
14796   // check if anything can be dropped at the current position
14797   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14798     return FALSE;
14799
14800   // collected custom elements can only be dropped on empty fields
14801   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14802     return FALSE;
14803
14804   if (old_element != EL_EMPTY)
14805     Back[dropx][dropy] = old_element;   // store old element on this field
14806
14807   ResetGfxAnimation(dropx, dropy);
14808   ResetRandomAnimationValue(dropx, dropy);
14809
14810   if (player->inventory_size > 0 ||
14811       player->inventory_infinite_element != EL_UNDEFINED)
14812   {
14813     if (player->inventory_size > 0)
14814     {
14815       player->inventory_size--;
14816
14817       DrawGameDoorValues();
14818
14819       if (new_element == EL_DYNAMITE)
14820         new_element = EL_DYNAMITE_ACTIVE;
14821       else if (new_element == EL_EM_DYNAMITE)
14822         new_element = EL_EM_DYNAMITE_ACTIVE;
14823       else if (new_element == EL_SP_DISK_RED)
14824         new_element = EL_SP_DISK_RED_ACTIVE;
14825     }
14826
14827     Tile[dropx][dropy] = new_element;
14828
14829     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14830       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14831                           el2img(Tile[dropx][dropy]), 0);
14832
14833     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14834
14835     // needed if previous element just changed to "empty" in the last frame
14836     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14837
14838     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14839                                player->index_bit, drop_side);
14840     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14841                                         CE_PLAYER_DROPS_X,
14842                                         player->index_bit, drop_side);
14843
14844     TestIfElementTouchesCustomElement(dropx, dropy);
14845   }
14846   else          // player is dropping a dyna bomb
14847   {
14848     player->dynabombs_left--;
14849
14850     Tile[dropx][dropy] = new_element;
14851
14852     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14853       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14854                           el2img(Tile[dropx][dropy]), 0);
14855
14856     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14857   }
14858
14859   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14860     InitField_WithBug1(dropx, dropy, FALSE);
14861
14862   new_element = Tile[dropx][dropy];     // element might have changed
14863
14864   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14865       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14866   {
14867     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14868       MovDir[dropx][dropy] = drop_direction;
14869
14870     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14871
14872     // do not cause impact style collision by dropping elements that can fall
14873     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14874   }
14875
14876   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14877   player->is_dropping = TRUE;
14878
14879   player->drop_pressed_delay = 0;
14880   player->is_dropping_pressed = FALSE;
14881
14882   player->drop_x = dropx;
14883   player->drop_y = dropy;
14884
14885   return TRUE;
14886 }
14887
14888 // ----------------------------------------------------------------------------
14889 // game sound playing functions
14890 // ----------------------------------------------------------------------------
14891
14892 static int *loop_sound_frame = NULL;
14893 static int *loop_sound_volume = NULL;
14894
14895 void InitPlayLevelSound(void)
14896 {
14897   int num_sounds = getSoundListSize();
14898
14899   checked_free(loop_sound_frame);
14900   checked_free(loop_sound_volume);
14901
14902   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14903   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14904 }
14905
14906 static void PlayLevelSound(int x, int y, int nr)
14907 {
14908   int sx = SCREENX(x), sy = SCREENY(y);
14909   int volume, stereo_position;
14910   int max_distance = 8;
14911   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14912
14913   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14914       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14915     return;
14916
14917   if (!IN_LEV_FIELD(x, y) ||
14918       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14919       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14920     return;
14921
14922   volume = SOUND_MAX_VOLUME;
14923
14924   if (!IN_SCR_FIELD(sx, sy))
14925   {
14926     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14927     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14928
14929     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14930   }
14931
14932   stereo_position = (SOUND_MAX_LEFT +
14933                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14934                      (SCR_FIELDX + 2 * max_distance));
14935
14936   if (IS_LOOP_SOUND(nr))
14937   {
14938     /* This assures that quieter loop sounds do not overwrite louder ones,
14939        while restarting sound volume comparison with each new game frame. */
14940
14941     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14942       return;
14943
14944     loop_sound_volume[nr] = volume;
14945     loop_sound_frame[nr] = FrameCounter;
14946   }
14947
14948   PlaySoundExt(nr, volume, stereo_position, type);
14949 }
14950
14951 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14952 {
14953   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14954                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14955                  y < LEVELY(BY1) ? LEVELY(BY1) :
14956                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14957                  sound_action);
14958 }
14959
14960 static void PlayLevelSoundAction(int x, int y, int action)
14961 {
14962   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14963 }
14964
14965 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14966 {
14967   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14968
14969   if (sound_effect != SND_UNDEFINED)
14970     PlayLevelSound(x, y, sound_effect);
14971 }
14972
14973 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14974                                               int action)
14975 {
14976   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14977
14978   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14979     PlayLevelSound(x, y, sound_effect);
14980 }
14981
14982 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14983 {
14984   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14985
14986   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14987     PlayLevelSound(x, y, sound_effect);
14988 }
14989
14990 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14991 {
14992   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14993
14994   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14995     StopSound(sound_effect);
14996 }
14997
14998 static int getLevelMusicNr(void)
14999 {
15000   if (levelset.music[level_nr] != MUS_UNDEFINED)
15001     return levelset.music[level_nr];            // from config file
15002   else
15003     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15004 }
15005
15006 static void FadeLevelSounds(void)
15007 {
15008   FadeSounds();
15009 }
15010
15011 static void FadeLevelMusic(void)
15012 {
15013   int music_nr = getLevelMusicNr();
15014   char *curr_music = getCurrentlyPlayingMusicFilename();
15015   char *next_music = getMusicInfoEntryFilename(music_nr);
15016
15017   if (!strEqual(curr_music, next_music))
15018     FadeMusic();
15019 }
15020
15021 void FadeLevelSoundsAndMusic(void)
15022 {
15023   FadeLevelSounds();
15024   FadeLevelMusic();
15025 }
15026
15027 static void PlayLevelMusic(void)
15028 {
15029   int music_nr = getLevelMusicNr();
15030   char *curr_music = getCurrentlyPlayingMusicFilename();
15031   char *next_music = getMusicInfoEntryFilename(music_nr);
15032
15033   if (!strEqual(curr_music, next_music))
15034     PlayMusicLoop(music_nr);
15035 }
15036
15037 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15038 {
15039   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15040   int offset = 0;
15041   int x = xx - offset;
15042   int y = yy - offset;
15043
15044   switch (sample)
15045   {
15046     case SOUND_blank:
15047       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15048       break;
15049
15050     case SOUND_roll:
15051       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15052       break;
15053
15054     case SOUND_stone:
15055       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15056       break;
15057
15058     case SOUND_nut:
15059       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15060       break;
15061
15062     case SOUND_crack:
15063       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15064       break;
15065
15066     case SOUND_bug:
15067       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15068       break;
15069
15070     case SOUND_tank:
15071       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15072       break;
15073
15074     case SOUND_android_clone:
15075       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15076       break;
15077
15078     case SOUND_android_move:
15079       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15080       break;
15081
15082     case SOUND_spring:
15083       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15084       break;
15085
15086     case SOUND_slurp:
15087       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15088       break;
15089
15090     case SOUND_eater:
15091       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15092       break;
15093
15094     case SOUND_eater_eat:
15095       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15096       break;
15097
15098     case SOUND_alien:
15099       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15100       break;
15101
15102     case SOUND_collect:
15103       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15104       break;
15105
15106     case SOUND_diamond:
15107       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15108       break;
15109
15110     case SOUND_squash:
15111       // !!! CHECK THIS !!!
15112 #if 1
15113       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15114 #else
15115       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15116 #endif
15117       break;
15118
15119     case SOUND_wonderfall:
15120       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15121       break;
15122
15123     case SOUND_drip:
15124       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15125       break;
15126
15127     case SOUND_push:
15128       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15129       break;
15130
15131     case SOUND_dirt:
15132       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15133       break;
15134
15135     case SOUND_acid:
15136       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15137       break;
15138
15139     case SOUND_ball:
15140       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15141       break;
15142
15143     case SOUND_slide:
15144       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15145       break;
15146
15147     case SOUND_wonder:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15149       break;
15150
15151     case SOUND_door:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15153       break;
15154
15155     case SOUND_exit_open:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15157       break;
15158
15159     case SOUND_exit_leave:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15161       break;
15162
15163     case SOUND_dynamite:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15165       break;
15166
15167     case SOUND_tick:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15169       break;
15170
15171     case SOUND_press:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15173       break;
15174
15175     case SOUND_wheel:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15177       break;
15178
15179     case SOUND_boom:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15181       break;
15182
15183     case SOUND_die:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15185       break;
15186
15187     case SOUND_time:
15188       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15189       break;
15190
15191     default:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15193       break;
15194   }
15195 }
15196
15197 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15198 {
15199   int element = map_element_SP_to_RND(element_sp);
15200   int action = map_action_SP_to_RND(action_sp);
15201   int offset = (setup.sp_show_border_elements ? 0 : 1);
15202   int x = xx - offset;
15203   int y = yy - offset;
15204
15205   PlayLevelSoundElementAction(x, y, element, action);
15206 }
15207
15208 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15209 {
15210   int element = map_element_MM_to_RND(element_mm);
15211   int action = map_action_MM_to_RND(action_mm);
15212   int offset = 0;
15213   int x = xx - offset;
15214   int y = yy - offset;
15215
15216   if (!IS_MM_ELEMENT(element))
15217     element = EL_MM_DEFAULT;
15218
15219   PlayLevelSoundElementAction(x, y, element, action);
15220 }
15221
15222 void PlaySound_MM(int sound_mm)
15223 {
15224   int sound = map_sound_MM_to_RND(sound_mm);
15225
15226   if (sound == SND_UNDEFINED)
15227     return;
15228
15229   PlaySound(sound);
15230 }
15231
15232 void PlaySoundLoop_MM(int sound_mm)
15233 {
15234   int sound = map_sound_MM_to_RND(sound_mm);
15235
15236   if (sound == SND_UNDEFINED)
15237     return;
15238
15239   PlaySoundLoop(sound);
15240 }
15241
15242 void StopSound_MM(int sound_mm)
15243 {
15244   int sound = map_sound_MM_to_RND(sound_mm);
15245
15246   if (sound == SND_UNDEFINED)
15247     return;
15248
15249   StopSound(sound);
15250 }
15251
15252 void RaiseScore(int value)
15253 {
15254   game.score += value;
15255
15256   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15257
15258   DisplayGameControlValues();
15259 }
15260
15261 void RaiseScoreElement(int element)
15262 {
15263   switch (element)
15264   {
15265     case EL_EMERALD:
15266     case EL_BD_DIAMOND:
15267     case EL_EMERALD_YELLOW:
15268     case EL_EMERALD_RED:
15269     case EL_EMERALD_PURPLE:
15270     case EL_SP_INFOTRON:
15271       RaiseScore(level.score[SC_EMERALD]);
15272       break;
15273     case EL_DIAMOND:
15274       RaiseScore(level.score[SC_DIAMOND]);
15275       break;
15276     case EL_CRYSTAL:
15277       RaiseScore(level.score[SC_CRYSTAL]);
15278       break;
15279     case EL_PEARL:
15280       RaiseScore(level.score[SC_PEARL]);
15281       break;
15282     case EL_BUG:
15283     case EL_BD_BUTTERFLY:
15284     case EL_SP_ELECTRON:
15285       RaiseScore(level.score[SC_BUG]);
15286       break;
15287     case EL_SPACESHIP:
15288     case EL_BD_FIREFLY:
15289     case EL_SP_SNIKSNAK:
15290       RaiseScore(level.score[SC_SPACESHIP]);
15291       break;
15292     case EL_YAMYAM:
15293     case EL_DARK_YAMYAM:
15294       RaiseScore(level.score[SC_YAMYAM]);
15295       break;
15296     case EL_ROBOT:
15297       RaiseScore(level.score[SC_ROBOT]);
15298       break;
15299     case EL_PACMAN:
15300       RaiseScore(level.score[SC_PACMAN]);
15301       break;
15302     case EL_NUT:
15303       RaiseScore(level.score[SC_NUT]);
15304       break;
15305     case EL_DYNAMITE:
15306     case EL_EM_DYNAMITE:
15307     case EL_SP_DISK_RED:
15308     case EL_DYNABOMB_INCREASE_NUMBER:
15309     case EL_DYNABOMB_INCREASE_SIZE:
15310     case EL_DYNABOMB_INCREASE_POWER:
15311       RaiseScore(level.score[SC_DYNAMITE]);
15312       break;
15313     case EL_SHIELD_NORMAL:
15314     case EL_SHIELD_DEADLY:
15315       RaiseScore(level.score[SC_SHIELD]);
15316       break;
15317     case EL_EXTRA_TIME:
15318       RaiseScore(level.extra_time_score);
15319       break;
15320     case EL_KEY_1:
15321     case EL_KEY_2:
15322     case EL_KEY_3:
15323     case EL_KEY_4:
15324     case EL_EM_KEY_1:
15325     case EL_EM_KEY_2:
15326     case EL_EM_KEY_3:
15327     case EL_EM_KEY_4:
15328     case EL_EMC_KEY_5:
15329     case EL_EMC_KEY_6:
15330     case EL_EMC_KEY_7:
15331     case EL_EMC_KEY_8:
15332     case EL_DC_KEY_WHITE:
15333       RaiseScore(level.score[SC_KEY]);
15334       break;
15335     default:
15336       RaiseScore(element_info[element].collect_score);
15337       break;
15338   }
15339 }
15340
15341 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15342 {
15343   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15344   {
15345     // closing door required in case of envelope style request dialogs
15346     if (!skip_request)
15347     {
15348       // prevent short reactivation of overlay buttons while closing door
15349       SetOverlayActive(FALSE);
15350
15351       CloseDoor(DOOR_CLOSE_1);
15352     }
15353
15354     if (network.enabled)
15355       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15356     else
15357     {
15358       if (quick_quit)
15359         FadeSkipNextFadeIn();
15360
15361       SetGameStatus(GAME_MODE_MAIN);
15362
15363       DrawMainMenu();
15364     }
15365   }
15366   else          // continue playing the game
15367   {
15368     if (tape.playing && tape.deactivate_display)
15369       TapeDeactivateDisplayOff(TRUE);
15370
15371     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15372
15373     if (tape.playing && tape.deactivate_display)
15374       TapeDeactivateDisplayOn();
15375   }
15376 }
15377
15378 void RequestQuitGame(boolean ask_if_really_quit)
15379 {
15380   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15381   boolean skip_request = game.all_players_gone || quick_quit;
15382
15383   RequestQuitGameExt(skip_request, quick_quit,
15384                      "Do you really want to quit the game?");
15385 }
15386
15387 void RequestRestartGame(char *message)
15388 {
15389   game.restart_game_message = NULL;
15390
15391   boolean has_started_game = hasStartedNetworkGame();
15392   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15393
15394   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15395   {
15396     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15397   }
15398   else
15399   {
15400     // needed in case of envelope request to close game panel
15401     CloseDoor(DOOR_CLOSE_1);
15402
15403     SetGameStatus(GAME_MODE_MAIN);
15404
15405     DrawMainMenu();
15406   }
15407 }
15408
15409 void CheckGameOver(void)
15410 {
15411   static boolean last_game_over = FALSE;
15412   static int game_over_delay = 0;
15413   int game_over_delay_value = 50;
15414   boolean game_over = checkGameFailed();
15415
15416   // do not handle game over if request dialog is already active
15417   if (game.request_active)
15418     return;
15419
15420   // do not ask to play again if game was never actually played
15421   if (!game.GamePlayed)
15422     return;
15423
15424   if (!game_over)
15425   {
15426     last_game_over = FALSE;
15427     game_over_delay = game_over_delay_value;
15428
15429     return;
15430   }
15431
15432   if (game_over_delay > 0)
15433   {
15434     game_over_delay--;
15435
15436     return;
15437   }
15438
15439   if (last_game_over != game_over)
15440     game.restart_game_message = (hasStartedNetworkGame() ?
15441                                  "Game over! Play it again?" :
15442                                  "Game over!");
15443
15444   last_game_over = game_over;
15445 }
15446
15447 boolean checkGameSolved(void)
15448 {
15449   // set for all game engines if level was solved
15450   return game.LevelSolved_GameEnd;
15451 }
15452
15453 boolean checkGameFailed(void)
15454 {
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15456     return (game_em.game_over && !game_em.level_solved);
15457   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15458     return (game_sp.game_over && !game_sp.level_solved);
15459   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15460     return (game_mm.game_over && !game_mm.level_solved);
15461   else                          // GAME_ENGINE_TYPE_RND
15462     return (game.GameOver && !game.LevelSolved);
15463 }
15464
15465 boolean checkGameEnded(void)
15466 {
15467   return (checkGameSolved() || checkGameFailed());
15468 }
15469
15470
15471 // ----------------------------------------------------------------------------
15472 // random generator functions
15473 // ----------------------------------------------------------------------------
15474
15475 unsigned int InitEngineRandom_RND(int seed)
15476 {
15477   game.num_random_calls = 0;
15478
15479   return InitEngineRandom(seed);
15480 }
15481
15482 unsigned int RND(int max)
15483 {
15484   if (max > 0)
15485   {
15486     game.num_random_calls++;
15487
15488     return GetEngineRandom(max);
15489   }
15490
15491   return 0;
15492 }
15493
15494
15495 // ----------------------------------------------------------------------------
15496 // game engine snapshot handling functions
15497 // ----------------------------------------------------------------------------
15498
15499 struct EngineSnapshotInfo
15500 {
15501   // runtime values for custom element collect score
15502   int collect_score[NUM_CUSTOM_ELEMENTS];
15503
15504   // runtime values for group element choice position
15505   int choice_pos[NUM_GROUP_ELEMENTS];
15506
15507   // runtime values for belt position animations
15508   int belt_graphic[4][NUM_BELT_PARTS];
15509   int belt_anim_mode[4][NUM_BELT_PARTS];
15510 };
15511
15512 static struct EngineSnapshotInfo engine_snapshot_rnd;
15513 static char *snapshot_level_identifier = NULL;
15514 static int snapshot_level_nr = -1;
15515
15516 static void SaveEngineSnapshotValues_RND(void)
15517 {
15518   static int belt_base_active_element[4] =
15519   {
15520     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15521     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15522     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15523     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15524   };
15525   int i, j;
15526
15527   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15528   {
15529     int element = EL_CUSTOM_START + i;
15530
15531     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15532   }
15533
15534   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15535   {
15536     int element = EL_GROUP_START + i;
15537
15538     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15539   }
15540
15541   for (i = 0; i < 4; i++)
15542   {
15543     for (j = 0; j < NUM_BELT_PARTS; j++)
15544     {
15545       int element = belt_base_active_element[i] + j;
15546       int graphic = el2img(element);
15547       int anim_mode = graphic_info[graphic].anim_mode;
15548
15549       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15550       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15551     }
15552   }
15553 }
15554
15555 static void LoadEngineSnapshotValues_RND(void)
15556 {
15557   unsigned int num_random_calls = game.num_random_calls;
15558   int i, j;
15559
15560   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15561   {
15562     int element = EL_CUSTOM_START + i;
15563
15564     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15565   }
15566
15567   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15568   {
15569     int element = EL_GROUP_START + i;
15570
15571     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15572   }
15573
15574   for (i = 0; i < 4; i++)
15575   {
15576     for (j = 0; j < NUM_BELT_PARTS; j++)
15577     {
15578       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15579       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15580
15581       graphic_info[graphic].anim_mode = anim_mode;
15582     }
15583   }
15584
15585   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15586   {
15587     InitRND(tape.random_seed);
15588     for (i = 0; i < num_random_calls; i++)
15589       RND(1);
15590   }
15591
15592   if (game.num_random_calls != num_random_calls)
15593   {
15594     Error("number of random calls out of sync");
15595     Error("number of random calls should be %d", num_random_calls);
15596     Error("number of random calls is %d", game.num_random_calls);
15597
15598     Fail("this should not happen -- please debug");
15599   }
15600 }
15601
15602 void FreeEngineSnapshotSingle(void)
15603 {
15604   FreeSnapshotSingle();
15605
15606   setString(&snapshot_level_identifier, NULL);
15607   snapshot_level_nr = -1;
15608 }
15609
15610 void FreeEngineSnapshotList(void)
15611 {
15612   FreeSnapshotList();
15613 }
15614
15615 static ListNode *SaveEngineSnapshotBuffers(void)
15616 {
15617   ListNode *buffers = NULL;
15618
15619   // copy some special values to a structure better suited for the snapshot
15620
15621   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15622     SaveEngineSnapshotValues_RND();
15623   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15624     SaveEngineSnapshotValues_EM();
15625   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15626     SaveEngineSnapshotValues_SP(&buffers);
15627   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15628     SaveEngineSnapshotValues_MM(&buffers);
15629
15630   // save values stored in special snapshot structure
15631
15632   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15633     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15634   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15635     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15636   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15637     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15638   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15639     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15640
15641   // save further RND engine values
15642
15643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15646
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15652
15653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15656
15657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15658
15659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15661
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15680
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15683
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15687
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15690
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15696
15697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15699
15700 #if 0
15701   ListNode *node = engine_snapshot_list_rnd;
15702   int num_bytes = 0;
15703
15704   while (node != NULL)
15705   {
15706     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15707
15708     node = node->next;
15709   }
15710
15711   Debug("game:playing:SaveEngineSnapshotBuffers",
15712         "size of engine snapshot: %d bytes", num_bytes);
15713 #endif
15714
15715   return buffers;
15716 }
15717
15718 void SaveEngineSnapshotSingle(void)
15719 {
15720   ListNode *buffers = SaveEngineSnapshotBuffers();
15721
15722   // finally save all snapshot buffers to single snapshot
15723   SaveSnapshotSingle(buffers);
15724
15725   // save level identification information
15726   setString(&snapshot_level_identifier, leveldir_current->identifier);
15727   snapshot_level_nr = level_nr;
15728 }
15729
15730 boolean CheckSaveEngineSnapshotToList(void)
15731 {
15732   boolean save_snapshot =
15733     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15734      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15735       game.snapshot.changed_action) ||
15736      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15737       game.snapshot.collected_item));
15738
15739   game.snapshot.changed_action = FALSE;
15740   game.snapshot.collected_item = FALSE;
15741   game.snapshot.save_snapshot = save_snapshot;
15742
15743   return save_snapshot;
15744 }
15745
15746 void SaveEngineSnapshotToList(void)
15747 {
15748   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15749       tape.quick_resume)
15750     return;
15751
15752   ListNode *buffers = SaveEngineSnapshotBuffers();
15753
15754   // finally save all snapshot buffers to snapshot list
15755   SaveSnapshotToList(buffers);
15756 }
15757
15758 void SaveEngineSnapshotToListInitial(void)
15759 {
15760   FreeEngineSnapshotList();
15761
15762   SaveEngineSnapshotToList();
15763 }
15764
15765 static void LoadEngineSnapshotValues(void)
15766 {
15767   // restore special values from snapshot structure
15768
15769   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15770     LoadEngineSnapshotValues_RND();
15771   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15772     LoadEngineSnapshotValues_EM();
15773   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15774     LoadEngineSnapshotValues_SP();
15775   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15776     LoadEngineSnapshotValues_MM();
15777 }
15778
15779 void LoadEngineSnapshotSingle(void)
15780 {
15781   LoadSnapshotSingle();
15782
15783   LoadEngineSnapshotValues();
15784 }
15785
15786 static void LoadEngineSnapshot_Undo(int steps)
15787 {
15788   LoadSnapshotFromList_Older(steps);
15789
15790   LoadEngineSnapshotValues();
15791 }
15792
15793 static void LoadEngineSnapshot_Redo(int steps)
15794 {
15795   LoadSnapshotFromList_Newer(steps);
15796
15797   LoadEngineSnapshotValues();
15798 }
15799
15800 boolean CheckEngineSnapshotSingle(void)
15801 {
15802   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15803           snapshot_level_nr == level_nr);
15804 }
15805
15806 boolean CheckEngineSnapshotList(void)
15807 {
15808   return CheckSnapshotList();
15809 }
15810
15811
15812 // ---------- new game button stuff -------------------------------------------
15813
15814 static struct
15815 {
15816   int graphic;
15817   struct XY *pos;
15818   int gadget_id;
15819   boolean *setup_value;
15820   boolean allowed_on_tape;
15821   boolean is_touch_button;
15822   char *infotext;
15823 } gamebutton_info[NUM_GAME_BUTTONS] =
15824 {
15825   {
15826     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15827     GAME_CTRL_ID_STOP,                          NULL,
15828     TRUE, FALSE,                                "stop game"
15829   },
15830   {
15831     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15832     GAME_CTRL_ID_PAUSE,                         NULL,
15833     TRUE, FALSE,                                "pause game"
15834   },
15835   {
15836     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15837     GAME_CTRL_ID_PLAY,                          NULL,
15838     TRUE, FALSE,                                "play game"
15839   },
15840   {
15841     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15842     GAME_CTRL_ID_UNDO,                          NULL,
15843     TRUE, FALSE,                                "undo step"
15844   },
15845   {
15846     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15847     GAME_CTRL_ID_REDO,                          NULL,
15848     TRUE, FALSE,                                "redo step"
15849   },
15850   {
15851     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15852     GAME_CTRL_ID_SAVE,                          NULL,
15853     TRUE, FALSE,                                "save game"
15854   },
15855   {
15856     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15857     GAME_CTRL_ID_PAUSE2,                        NULL,
15858     TRUE, FALSE,                                "pause game"
15859   },
15860   {
15861     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15862     GAME_CTRL_ID_LOAD,                          NULL,
15863     TRUE, FALSE,                                "load game"
15864   },
15865   {
15866     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15867     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15868     FALSE, FALSE,                               "stop game"
15869   },
15870   {
15871     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15872     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15873     FALSE, FALSE,                               "pause game"
15874   },
15875   {
15876     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15877     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15878     FALSE, FALSE,                               "play game"
15879   },
15880   {
15881     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15882     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15883     FALSE, TRUE,                                "stop game"
15884   },
15885   {
15886     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15887     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15888     FALSE, TRUE,                                "pause game"
15889   },
15890   {
15891     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15892     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15893     TRUE, FALSE,                                "background music on/off"
15894   },
15895   {
15896     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15897     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15898     TRUE, FALSE,                                "sound loops on/off"
15899   },
15900   {
15901     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15902     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15903     TRUE, FALSE,                                "normal sounds on/off"
15904   },
15905   {
15906     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15907     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15908     FALSE, FALSE,                               "background music on/off"
15909   },
15910   {
15911     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15912     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15913     FALSE, FALSE,                               "sound loops on/off"
15914   },
15915   {
15916     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15917     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15918     FALSE, FALSE,                               "normal sounds on/off"
15919   }
15920 };
15921
15922 void CreateGameButtons(void)
15923 {
15924   int i;
15925
15926   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15927   {
15928     int graphic = gamebutton_info[i].graphic;
15929     struct GraphicInfo *gfx = &graphic_info[graphic];
15930     struct XY *pos = gamebutton_info[i].pos;
15931     struct GadgetInfo *gi;
15932     int button_type;
15933     boolean checked;
15934     unsigned int event_mask;
15935     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15936     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15937     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15938     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15939     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15940     int gd_x   = gfx->src_x;
15941     int gd_y   = gfx->src_y;
15942     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15943     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15944     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15945     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15946     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15947     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15948     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15949     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15950     int id = i;
15951
15952     if (gfx->bitmap == NULL)
15953     {
15954       game_gadget[id] = NULL;
15955
15956       continue;
15957     }
15958
15959     if (id == GAME_CTRL_ID_STOP ||
15960         id == GAME_CTRL_ID_PANEL_STOP ||
15961         id == GAME_CTRL_ID_TOUCH_STOP ||
15962         id == GAME_CTRL_ID_PLAY ||
15963         id == GAME_CTRL_ID_PANEL_PLAY ||
15964         id == GAME_CTRL_ID_SAVE ||
15965         id == GAME_CTRL_ID_LOAD)
15966     {
15967       button_type = GD_TYPE_NORMAL_BUTTON;
15968       checked = FALSE;
15969       event_mask = GD_EVENT_RELEASED;
15970     }
15971     else if (id == GAME_CTRL_ID_UNDO ||
15972              id == GAME_CTRL_ID_REDO)
15973     {
15974       button_type = GD_TYPE_NORMAL_BUTTON;
15975       checked = FALSE;
15976       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15977     }
15978     else
15979     {
15980       button_type = GD_TYPE_CHECK_BUTTON;
15981       checked = (gamebutton_info[i].setup_value != NULL ?
15982                  *gamebutton_info[i].setup_value : FALSE);
15983       event_mask = GD_EVENT_PRESSED;
15984     }
15985
15986     gi = CreateGadget(GDI_CUSTOM_ID, id,
15987                       GDI_IMAGE_ID, graphic,
15988                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15989                       GDI_X, base_x + x,
15990                       GDI_Y, base_y + y,
15991                       GDI_WIDTH, gfx->width,
15992                       GDI_HEIGHT, gfx->height,
15993                       GDI_TYPE, button_type,
15994                       GDI_STATE, GD_BUTTON_UNPRESSED,
15995                       GDI_CHECKED, checked,
15996                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15997                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15998                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15999                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16000                       GDI_DIRECT_DRAW, FALSE,
16001                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16002                       GDI_EVENT_MASK, event_mask,
16003                       GDI_CALLBACK_ACTION, HandleGameButtons,
16004                       GDI_END);
16005
16006     if (gi == NULL)
16007       Fail("cannot create gadget");
16008
16009     game_gadget[id] = gi;
16010   }
16011 }
16012
16013 void FreeGameButtons(void)
16014 {
16015   int i;
16016
16017   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16018     FreeGadget(game_gadget[i]);
16019 }
16020
16021 static void UnmapGameButtonsAtSamePosition(int id)
16022 {
16023   int i;
16024
16025   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16026     if (i != id &&
16027         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16028         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16029       UnmapGadget(game_gadget[i]);
16030 }
16031
16032 static void UnmapGameButtonsAtSamePosition_All(void)
16033 {
16034   if (setup.show_snapshot_buttons)
16035   {
16036     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16037     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16038     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16039   }
16040   else
16041   {
16042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16043     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16045
16046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16047     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16048     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16049   }
16050 }
16051
16052 static void MapGameButtonsAtSamePosition(int id)
16053 {
16054   int i;
16055
16056   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16057     if (i != id &&
16058         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16059         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16060       MapGadget(game_gadget[i]);
16061
16062   UnmapGameButtonsAtSamePosition_All();
16063 }
16064
16065 void MapUndoRedoButtons(void)
16066 {
16067   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16068   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16069
16070   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16071   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16072 }
16073
16074 void UnmapUndoRedoButtons(void)
16075 {
16076   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16077   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16078
16079   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16080   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16081 }
16082
16083 void ModifyPauseButtons(void)
16084 {
16085   static int ids[] =
16086   {
16087     GAME_CTRL_ID_PAUSE,
16088     GAME_CTRL_ID_PAUSE2,
16089     GAME_CTRL_ID_PANEL_PAUSE,
16090     GAME_CTRL_ID_TOUCH_PAUSE,
16091     -1
16092   };
16093   int i;
16094
16095   for (i = 0; ids[i] > -1; i++)
16096     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16097 }
16098
16099 static void MapGameButtonsExt(boolean on_tape)
16100 {
16101   int i;
16102
16103   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16104     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16105         i != GAME_CTRL_ID_UNDO &&
16106         i != GAME_CTRL_ID_REDO)
16107       MapGadget(game_gadget[i]);
16108
16109   UnmapGameButtonsAtSamePosition_All();
16110
16111   RedrawGameButtons();
16112 }
16113
16114 static void UnmapGameButtonsExt(boolean on_tape)
16115 {
16116   int i;
16117
16118   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16119     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16120       UnmapGadget(game_gadget[i]);
16121 }
16122
16123 static void RedrawGameButtonsExt(boolean on_tape)
16124 {
16125   int i;
16126
16127   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16128     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16129       RedrawGadget(game_gadget[i]);
16130 }
16131
16132 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16133 {
16134   if (gi == NULL)
16135     return;
16136
16137   gi->checked = state;
16138 }
16139
16140 static void RedrawSoundButtonGadget(int id)
16141 {
16142   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16143              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16144              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16145              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16146              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16147              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16148              id);
16149
16150   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16151   RedrawGadget(game_gadget[id2]);
16152 }
16153
16154 void MapGameButtons(void)
16155 {
16156   MapGameButtonsExt(FALSE);
16157 }
16158
16159 void UnmapGameButtons(void)
16160 {
16161   UnmapGameButtonsExt(FALSE);
16162 }
16163
16164 void RedrawGameButtons(void)
16165 {
16166   RedrawGameButtonsExt(FALSE);
16167 }
16168
16169 void MapGameButtonsOnTape(void)
16170 {
16171   MapGameButtonsExt(TRUE);
16172 }
16173
16174 void UnmapGameButtonsOnTape(void)
16175 {
16176   UnmapGameButtonsExt(TRUE);
16177 }
16178
16179 void RedrawGameButtonsOnTape(void)
16180 {
16181   RedrawGameButtonsExt(TRUE);
16182 }
16183
16184 static void GameUndoRedoExt(void)
16185 {
16186   ClearPlayerAction();
16187
16188   tape.pausing = TRUE;
16189
16190   RedrawPlayfield();
16191   UpdateAndDisplayGameControlValues();
16192
16193   DrawCompleteVideoDisplay();
16194   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16195   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16196   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16197
16198   BackToFront();
16199 }
16200
16201 static void GameUndo(int steps)
16202 {
16203   if (!CheckEngineSnapshotList())
16204     return;
16205
16206   LoadEngineSnapshot_Undo(steps);
16207
16208   GameUndoRedoExt();
16209 }
16210
16211 static void GameRedo(int steps)
16212 {
16213   if (!CheckEngineSnapshotList())
16214     return;
16215
16216   LoadEngineSnapshot_Redo(steps);
16217
16218   GameUndoRedoExt();
16219 }
16220
16221 static void HandleGameButtonsExt(int id, int button)
16222 {
16223   static boolean game_undo_executed = FALSE;
16224   int steps = BUTTON_STEPSIZE(button);
16225   boolean handle_game_buttons =
16226     (game_status == GAME_MODE_PLAYING ||
16227      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16228
16229   if (!handle_game_buttons)
16230     return;
16231
16232   switch (id)
16233   {
16234     case GAME_CTRL_ID_STOP:
16235     case GAME_CTRL_ID_PANEL_STOP:
16236     case GAME_CTRL_ID_TOUCH_STOP:
16237       if (game_status == GAME_MODE_MAIN)
16238         break;
16239
16240       if (tape.playing)
16241         TapeStop();
16242       else
16243         RequestQuitGame(TRUE);
16244
16245       break;
16246
16247     case GAME_CTRL_ID_PAUSE:
16248     case GAME_CTRL_ID_PAUSE2:
16249     case GAME_CTRL_ID_PANEL_PAUSE:
16250     case GAME_CTRL_ID_TOUCH_PAUSE:
16251       if (network.enabled && game_status == GAME_MODE_PLAYING)
16252       {
16253         if (tape.pausing)
16254           SendToServer_ContinuePlaying();
16255         else
16256           SendToServer_PausePlaying();
16257       }
16258       else
16259         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16260
16261       game_undo_executed = FALSE;
16262
16263       break;
16264
16265     case GAME_CTRL_ID_PLAY:
16266     case GAME_CTRL_ID_PANEL_PLAY:
16267       if (game_status == GAME_MODE_MAIN)
16268       {
16269         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16270       }
16271       else if (tape.pausing)
16272       {
16273         if (network.enabled)
16274           SendToServer_ContinuePlaying();
16275         else
16276           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16277       }
16278       break;
16279
16280     case GAME_CTRL_ID_UNDO:
16281       // Important: When using "save snapshot when collecting an item" mode,
16282       // load last (current) snapshot for first "undo" after pressing "pause"
16283       // (else the last-but-one snapshot would be loaded, because the snapshot
16284       // pointer already points to the last snapshot when pressing "pause",
16285       // which is fine for "every step/move" mode, but not for "every collect")
16286       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16287           !game_undo_executed)
16288         steps--;
16289
16290       game_undo_executed = TRUE;
16291
16292       GameUndo(steps);
16293       break;
16294
16295     case GAME_CTRL_ID_REDO:
16296       GameRedo(steps);
16297       break;
16298
16299     case GAME_CTRL_ID_SAVE:
16300       TapeQuickSave();
16301       break;
16302
16303     case GAME_CTRL_ID_LOAD:
16304       TapeQuickLoad();
16305       break;
16306
16307     case SOUND_CTRL_ID_MUSIC:
16308     case SOUND_CTRL_ID_PANEL_MUSIC:
16309       if (setup.sound_music)
16310       { 
16311         setup.sound_music = FALSE;
16312
16313         FadeMusic();
16314       }
16315       else if (audio.music_available)
16316       { 
16317         setup.sound = setup.sound_music = TRUE;
16318
16319         SetAudioMode(setup.sound);
16320
16321         if (game_status == GAME_MODE_PLAYING)
16322           PlayLevelMusic();
16323       }
16324
16325       RedrawSoundButtonGadget(id);
16326
16327       break;
16328
16329     case SOUND_CTRL_ID_LOOPS:
16330     case SOUND_CTRL_ID_PANEL_LOOPS:
16331       if (setup.sound_loops)
16332         setup.sound_loops = FALSE;
16333       else if (audio.loops_available)
16334       {
16335         setup.sound = setup.sound_loops = TRUE;
16336
16337         SetAudioMode(setup.sound);
16338       }
16339
16340       RedrawSoundButtonGadget(id);
16341
16342       break;
16343
16344     case SOUND_CTRL_ID_SIMPLE:
16345     case SOUND_CTRL_ID_PANEL_SIMPLE:
16346       if (setup.sound_simple)
16347         setup.sound_simple = FALSE;
16348       else if (audio.sound_available)
16349       {
16350         setup.sound = setup.sound_simple = TRUE;
16351
16352         SetAudioMode(setup.sound);
16353       }
16354
16355       RedrawSoundButtonGadget(id);
16356
16357       break;
16358
16359     default:
16360       break;
16361   }
16362 }
16363
16364 static void HandleGameButtons(struct GadgetInfo *gi)
16365 {
16366   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16367 }
16368
16369 void HandleSoundButtonKeys(Key key)
16370 {
16371   if (key == setup.shortcut.sound_simple)
16372     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16373   else if (key == setup.shortcut.sound_loops)
16374     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16375   else if (key == setup.shortcut.sound_music)
16376     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16377 }