prevented mapping undo/redo snapshot buttons on main menu screen
[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_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_INVENTORY_COUNT ||
2685           nr == GAME_PANEL_SCORE ||
2686           nr == GAME_PANEL_HIGHSCORE ||
2687           nr == GAME_PANEL_TIME)
2688       {
2689         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2690
2691         if (use_dynamic_size)           // use dynamic number of digits
2692         {
2693           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2694                               nr == GAME_PANEL_INVENTORY_COUNT ||
2695                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2696           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2697                           nr == GAME_PANEL_INVENTORY_COUNT ||
2698                           nr == GAME_PANEL_TIME ? 1 : 2);
2699           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2700                        nr == GAME_PANEL_INVENTORY_COUNT ||
2701                        nr == GAME_PANEL_TIME ? 3 : 5);
2702           int size2 = size1 + size_add;
2703           int font1 = pos->font;
2704           int font2 = pos->font_alt;
2705
2706           size = (value < value_change ? size1 : size2);
2707           font = (value < value_change ? font1 : font2);
2708         }
2709       }
2710
2711       // correct text size if "digits" is zero or less
2712       if (size <= 0)
2713         size = strlen(int2str(value, size));
2714
2715       // dynamically correct text alignment
2716       pos->width = size * getFontWidth(font);
2717
2718       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2719                   int2str(value, size), font, mask_mode);
2720     }
2721     else if (type == TYPE_ELEMENT)
2722     {
2723       int element, graphic;
2724       Bitmap *src_bitmap;
2725       int src_x, src_y;
2726       int width, height;
2727       int dst_x = PANEL_XPOS(pos);
2728       int dst_y = PANEL_YPOS(pos);
2729
2730       if (value != EL_UNDEFINED && value != EL_EMPTY)
2731       {
2732         element = value;
2733         graphic = el2panelimg(value);
2734
2735 #if 0
2736         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2737               element, EL_NAME(element), size);
2738 #endif
2739
2740         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2741           size = TILESIZE;
2742
2743         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2744                               &src_x, &src_y);
2745
2746         width  = graphic_info[graphic].width  * size / TILESIZE;
2747         height = graphic_info[graphic].height * size / TILESIZE;
2748
2749         if (draw_masked)
2750           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2751                            dst_x, dst_y);
2752         else
2753           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2754                      dst_x, dst_y);
2755       }
2756     }
2757     else if (type == TYPE_GRAPHIC)
2758     {
2759       int graphic        = gpc->graphic;
2760       int graphic_active = gpc->graphic_active;
2761       Bitmap *src_bitmap;
2762       int src_x, src_y;
2763       int width, height;
2764       int dst_x = PANEL_XPOS(pos);
2765       int dst_y = PANEL_YPOS(pos);
2766       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2767                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2768
2769       if (graphic != IMG_UNDEFINED && !skip)
2770       {
2771         if (pos->style == STYLE_REVERSE)
2772           value = 100 - value;
2773
2774         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2775
2776         if (pos->direction & MV_HORIZONTAL)
2777         {
2778           width  = graphic_info[graphic_active].width * value / 100;
2779           height = graphic_info[graphic_active].height;
2780
2781           if (pos->direction == MV_LEFT)
2782           {
2783             src_x += graphic_info[graphic_active].width - width;
2784             dst_x += graphic_info[graphic_active].width - width;
2785           }
2786         }
2787         else
2788         {
2789           width  = graphic_info[graphic_active].width;
2790           height = graphic_info[graphic_active].height * value / 100;
2791
2792           if (pos->direction == MV_UP)
2793           {
2794             src_y += graphic_info[graphic_active].height - height;
2795             dst_y += graphic_info[graphic_active].height - height;
2796           }
2797         }
2798
2799         if (draw_masked)
2800           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2801                            dst_x, dst_y);
2802         else
2803           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2804                      dst_x, dst_y);
2805
2806         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2807
2808         if (pos->direction & MV_HORIZONTAL)
2809         {
2810           if (pos->direction == MV_RIGHT)
2811           {
2812             src_x += width;
2813             dst_x += width;
2814           }
2815           else
2816           {
2817             dst_x = PANEL_XPOS(pos);
2818           }
2819
2820           width = graphic_info[graphic].width - width;
2821         }
2822         else
2823         {
2824           if (pos->direction == MV_DOWN)
2825           {
2826             src_y += height;
2827             dst_y += height;
2828           }
2829           else
2830           {
2831             dst_y = PANEL_YPOS(pos);
2832           }
2833
2834           height = graphic_info[graphic].height - height;
2835         }
2836
2837         if (draw_masked)
2838           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2839                            dst_x, dst_y);
2840         else
2841           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2842                      dst_x, dst_y);
2843       }
2844     }
2845     else if (type == TYPE_STRING)
2846     {
2847       boolean active = (value != 0);
2848       char *state_normal = "off";
2849       char *state_active = "on";
2850       char *state = (active ? state_active : state_normal);
2851       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2852                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2853                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2854                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2855
2856       if (nr == GAME_PANEL_GRAVITY_STATE)
2857       {
2858         int font1 = pos->font;          // (used for normal state)
2859         int font2 = pos->font_alt;      // (used for active state)
2860
2861         font = (active ? font2 : font1);
2862       }
2863
2864       if (s != NULL)
2865       {
2866         char *s_cut;
2867
2868         if (size <= 0)
2869         {
2870           // don't truncate output if "chars" is zero or less
2871           size = strlen(s);
2872
2873           // dynamically correct text alignment
2874           pos->width = size * getFontWidth(font);
2875         }
2876
2877         s_cut = getStringCopyN(s, size);
2878
2879         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2880                     s_cut, font, mask_mode);
2881
2882         free(s_cut);
2883       }
2884     }
2885
2886     redraw_mask |= REDRAW_DOOR_1;
2887   }
2888
2889   SetGameStatus(GAME_MODE_PLAYING);
2890 }
2891
2892 void UpdateAndDisplayGameControlValues(void)
2893 {
2894   if (tape.deactivate_display)
2895     return;
2896
2897   UpdateGameControlValues();
2898   DisplayGameControlValues();
2899 }
2900
2901 void UpdateGameDoorValues(void)
2902 {
2903   UpdateGameControlValues();
2904 }
2905
2906 void DrawGameDoorValues(void)
2907 {
2908   DisplayGameControlValues();
2909 }
2910
2911
2912 // ============================================================================
2913 // InitGameEngine()
2914 // ----------------------------------------------------------------------------
2915 // initialize game engine due to level / tape version number
2916 // ============================================================================
2917
2918 static void InitGameEngine(void)
2919 {
2920   int i, j, k, l, x, y;
2921
2922   // set game engine from tape file when re-playing, else from level file
2923   game.engine_version = (tape.playing ? tape.engine_version :
2924                          level.game_version);
2925
2926   // set single or multi-player game mode (needed for re-playing tapes)
2927   game.team_mode = setup.team_mode;
2928
2929   if (tape.playing)
2930   {
2931     int num_players = 0;
2932
2933     for (i = 0; i < MAX_PLAYERS; i++)
2934       if (tape.player_participates[i])
2935         num_players++;
2936
2937     // multi-player tapes contain input data for more than one player
2938     game.team_mode = (num_players > 1);
2939   }
2940
2941 #if 0
2942   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2943         level.game_version);
2944   Debug("game:init:level", "          tape.file_version   == %06d",
2945         tape.file_version);
2946   Debug("game:init:level", "          tape.game_version   == %06d",
2947         tape.game_version);
2948   Debug("game:init:level", "          tape.engine_version == %06d",
2949         tape.engine_version);
2950   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2951         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2952 #endif
2953
2954   // --------------------------------------------------------------------------
2955   // set flags for bugs and changes according to active game engine version
2956   // --------------------------------------------------------------------------
2957
2958   /*
2959     Summary of bugfix:
2960     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2961
2962     Bug was introduced in version:
2963     2.0.1
2964
2965     Bug was fixed in version:
2966     4.2.0.0
2967
2968     Description:
2969     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2970     but the property "can fall" was missing, which caused some levels to be
2971     unsolvable. This was fixed in version 4.2.0.0.
2972
2973     Affected levels/tapes:
2974     An example for a tape that was fixed by this bugfix is tape 029 from the
2975     level set "rnd_sam_bateman".
2976     The wrong behaviour will still be used for all levels or tapes that were
2977     created/recorded with it. An example for this is tape 023 from the level
2978     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2979   */
2980
2981   boolean use_amoeba_dropping_cannot_fall_bug =
2982     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2983       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2984      (tape.playing &&
2985       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2986       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2987
2988   /*
2989     Summary of bugfix/change:
2990     Fixed move speed of elements entering or leaving magic wall.
2991
2992     Fixed/changed in version:
2993     2.0.1
2994
2995     Description:
2996     Before 2.0.1, move speed of elements entering or leaving magic wall was
2997     twice as fast as it is now.
2998     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2999
3000     Affected levels/tapes:
3001     The first condition is generally needed for all levels/tapes before version
3002     2.0.1, which might use the old behaviour before it was changed; known tapes
3003     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3004     The second condition is an exception from the above case and is needed for
3005     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3006     above, but before it was known that this change would break tapes like the
3007     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3008     although the engine version while recording maybe was before 2.0.1. There
3009     are a lot of tapes that are affected by this exception, like tape 006 from
3010     the level set "rnd_conor_mancone".
3011   */
3012
3013   boolean use_old_move_stepsize_for_magic_wall =
3014     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3015      !(tape.playing &&
3016        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3017        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3018
3019   /*
3020     Summary of bugfix/change:
3021     Fixed handling for custom elements that change when pushed by the player.
3022
3023     Fixed/changed in version:
3024     3.1.0
3025
3026     Description:
3027     Before 3.1.0, custom elements that "change when pushing" changed directly
3028     after the player started pushing them (until then handled in "DigField()").
3029     Since 3.1.0, these custom elements are not changed until the "pushing"
3030     move of the element is finished (now handled in "ContinueMoving()").
3031
3032     Affected levels/tapes:
3033     The first condition is generally needed for all levels/tapes before version
3034     3.1.0, which might use the old behaviour before it was changed; known tapes
3035     that are affected are some tapes from the level set "Walpurgis Gardens" by
3036     Jamie Cullen.
3037     The second condition is an exception from the above case and is needed for
3038     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3039     above (including some development versions of 3.1.0), but before it was
3040     known that this change would break tapes like the above and was fixed in
3041     3.1.1, so that the changed behaviour was active although the engine version
3042     while recording maybe was before 3.1.0. There is at least one tape that is
3043     affected by this exception, which is the tape for the one-level set "Bug
3044     Machine" by Juergen Bonhagen.
3045   */
3046
3047   game.use_change_when_pushing_bug =
3048     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3049      !(tape.playing &&
3050        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3051        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for blocking the field the player leaves when moving.
3056
3057     Fixed/changed in version:
3058     3.1.1
3059
3060     Description:
3061     Before 3.1.1, when "block last field when moving" was enabled, the field
3062     the player is leaving when moving was blocked for the time of the move,
3063     and was directly unblocked afterwards. This resulted in the last field
3064     being blocked for exactly one less than the number of frames of one player
3065     move. Additionally, even when blocking was disabled, the last field was
3066     blocked for exactly one frame.
3067     Since 3.1.1, due to changes in player movement handling, the last field
3068     is not blocked at all when blocking is disabled. When blocking is enabled,
3069     the last field is blocked for exactly the number of frames of one player
3070     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3071     last field is blocked for exactly one more than the number of frames of
3072     one player move.
3073
3074     Affected levels/tapes:
3075     (!!! yet to be determined -- probably many !!!)
3076   */
3077
3078   game.use_block_last_field_bug =
3079     (game.engine_version < VERSION_IDENT(3,1,1,0));
3080
3081   /* various special flags and settings for native Emerald Mine game engine */
3082
3083   game_em.use_single_button =
3084     (game.engine_version > VERSION_IDENT(4,0,0,2));
3085
3086   game_em.use_snap_key_bug =
3087     (game.engine_version < VERSION_IDENT(4,0,1,0));
3088
3089   game_em.use_random_bug =
3090     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3091
3092   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3093
3094   game_em.use_old_explosions            = use_old_em_engine;
3095   game_em.use_old_android               = use_old_em_engine;
3096   game_em.use_old_push_elements         = use_old_em_engine;
3097   game_em.use_old_push_into_acid        = use_old_em_engine;
3098
3099   game_em.use_wrap_around               = !use_old_em_engine;
3100
3101   // --------------------------------------------------------------------------
3102
3103   // set maximal allowed number of custom element changes per game frame
3104   game.max_num_changes_per_frame = 1;
3105
3106   // default scan direction: scan playfield from top/left to bottom/right
3107   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3108
3109   // dynamically adjust element properties according to game engine version
3110   InitElementPropertiesEngine(game.engine_version);
3111
3112   // ---------- initialize special element properties -------------------------
3113
3114   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3115   if (use_amoeba_dropping_cannot_fall_bug)
3116     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3117
3118   // ---------- initialize player's initial move delay ------------------------
3119
3120   // dynamically adjust player properties according to level information
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     game.initial_move_delay_value[i] =
3123       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3124
3125   // dynamically adjust player properties according to game engine version
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     game.initial_move_delay[i] =
3128       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3129        game.initial_move_delay_value[i] : 0);
3130
3131   // ---------- initialize player's initial push delay ------------------------
3132
3133   // dynamically adjust player properties according to game engine version
3134   game.initial_push_delay_value =
3135     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3136
3137   // ---------- initialize changing elements ----------------------------------
3138
3139   // initialize changing elements information
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141   {
3142     struct ElementInfo *ei = &element_info[i];
3143
3144     // this pointer might have been changed in the level editor
3145     ei->change = &ei->change_page[0];
3146
3147     if (!IS_CUSTOM_ELEMENT(i))
3148     {
3149       ei->change->target_element = EL_EMPTY_SPACE;
3150       ei->change->delay_fixed = 0;
3151       ei->change->delay_random = 0;
3152       ei->change->delay_frames = 1;
3153     }
3154
3155     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3156     {
3157       ei->has_change_event[j] = FALSE;
3158
3159       ei->event_page_nr[j] = 0;
3160       ei->event_page[j] = &ei->change_page[0];
3161     }
3162   }
3163
3164   // add changing elements from pre-defined list
3165   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3166   {
3167     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3168     struct ElementInfo *ei = &element_info[ch_delay->element];
3169
3170     ei->change->target_element       = ch_delay->target_element;
3171     ei->change->delay_fixed          = ch_delay->change_delay;
3172
3173     ei->change->pre_change_function  = ch_delay->pre_change_function;
3174     ei->change->change_function      = ch_delay->change_function;
3175     ei->change->post_change_function = ch_delay->post_change_function;
3176
3177     ei->change->can_change = TRUE;
3178     ei->change->can_change_or_has_action = TRUE;
3179
3180     ei->has_change_event[CE_DELAY] = TRUE;
3181
3182     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3183     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3184   }
3185
3186   // ---------- initialize internal run-time variables ------------------------
3187
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       ei->change_page[j].can_change_or_has_action =
3195         (ei->change_page[j].can_change |
3196          ei->change_page[j].has_action);
3197     }
3198   }
3199
3200   // add change events from custom element configuration
3201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3202   {
3203     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3204
3205     for (j = 0; j < ei->num_change_pages; j++)
3206     {
3207       if (!ei->change_page[j].can_change_or_has_action)
3208         continue;
3209
3210       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3211       {
3212         // only add event page for the first page found with this event
3213         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3214         {
3215           ei->has_change_event[k] = TRUE;
3216
3217           ei->event_page_nr[k] = j;
3218           ei->event_page[k] = &ei->change_page[j];
3219         }
3220       }
3221     }
3222   }
3223
3224   // ---------- initialize reference elements in change conditions ------------
3225
3226   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3227   {
3228     int element = EL_CUSTOM_START + i;
3229     struct ElementInfo *ei = &element_info[element];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       int trigger_element = ei->change_page[j].initial_trigger_element;
3234
3235       if (trigger_element >= EL_PREV_CE_8 &&
3236           trigger_element <= EL_NEXT_CE_8)
3237         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3238
3239       ei->change_page[j].trigger_element = trigger_element;
3240     }
3241   }
3242
3243   // ---------- initialize run-time trigger player and element ----------------
3244
3245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246   {
3247     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248
3249     for (j = 0; j < ei->num_change_pages; j++)
3250     {
3251       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3252       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3253       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3254       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3255       ei->change_page[j].actual_trigger_ce_value = 0;
3256       ei->change_page[j].actual_trigger_ce_score = 0;
3257     }
3258   }
3259
3260   // ---------- initialize trigger events -------------------------------------
3261
3262   // initialize trigger events information
3263   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3264     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3265       trigger_events[i][j] = FALSE;
3266
3267   // add trigger events from element change event properties
3268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       if (!ei->change_page[j].can_change_or_has_action)
3275         continue;
3276
3277       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3278       {
3279         int trigger_element = ei->change_page[j].trigger_element;
3280
3281         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3282         {
3283           if (ei->change_page[j].has_event[k])
3284           {
3285             if (IS_GROUP_ELEMENT(trigger_element))
3286             {
3287               struct ElementGroupInfo *group =
3288                 element_info[trigger_element].group;
3289
3290               for (l = 0; l < group->num_elements_resolved; l++)
3291                 trigger_events[group->element_resolved[l]][k] = TRUE;
3292             }
3293             else if (trigger_element == EL_ANY_ELEMENT)
3294               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3295                 trigger_events[l][k] = TRUE;
3296             else
3297               trigger_events[trigger_element][k] = TRUE;
3298           }
3299         }
3300       }
3301     }
3302   }
3303
3304   // ---------- initialize push delay -----------------------------------------
3305
3306   // initialize push delay values to default
3307   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3308   {
3309     if (!IS_CUSTOM_ELEMENT(i))
3310     {
3311       // set default push delay values (corrected since version 3.0.7-1)
3312       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3313       {
3314         element_info[i].push_delay_fixed = 2;
3315         element_info[i].push_delay_random = 8;
3316       }
3317       else
3318       {
3319         element_info[i].push_delay_fixed = 8;
3320         element_info[i].push_delay_random = 8;
3321       }
3322     }
3323   }
3324
3325   // set push delay value for certain elements from pre-defined list
3326   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3327   {
3328     int e = push_delay_list[i].element;
3329
3330     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3331     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3332   }
3333
3334   // set push delay value for Supaplex elements for newer engine versions
3335   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3336   {
3337     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338     {
3339       if (IS_SP_ELEMENT(i))
3340       {
3341         // set SP push delay to just enough to push under a falling zonk
3342         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3343
3344         element_info[i].push_delay_fixed  = delay;
3345         element_info[i].push_delay_random = 0;
3346       }
3347     }
3348   }
3349
3350   // ---------- initialize move stepsize --------------------------------------
3351
3352   // initialize move stepsize values to default
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     if (!IS_CUSTOM_ELEMENT(i))
3355       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3356
3357   // set move stepsize value for certain elements from pre-defined list
3358   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = move_stepsize_list[i].element;
3361
3362     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3363
3364     // set move stepsize value for certain elements for older engine versions
3365     if (use_old_move_stepsize_for_magic_wall)
3366     {
3367       if (e == EL_MAGIC_WALL_FILLING ||
3368           e == EL_MAGIC_WALL_EMPTYING ||
3369           e == EL_BD_MAGIC_WALL_FILLING ||
3370           e == EL_BD_MAGIC_WALL_EMPTYING)
3371         element_info[e].move_stepsize *= 2;
3372     }
3373   }
3374
3375   // ---------- initialize collect score --------------------------------------
3376
3377   // initialize collect score values for custom elements from initial value
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379     if (IS_CUSTOM_ELEMENT(i))
3380       element_info[i].collect_score = element_info[i].collect_score_initial;
3381
3382   // ---------- initialize collect count --------------------------------------
3383
3384   // initialize collect count values for non-custom elements
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].collect_count_initial = 0;
3388
3389   // add collect count values for all elements from pre-defined list
3390   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3391     element_info[collect_count_list[i].element].collect_count_initial =
3392       collect_count_list[i].count;
3393
3394   // ---------- initialize access direction -----------------------------------
3395
3396   // initialize access direction values to default (access from every side)
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3400
3401   // set access direction value for certain elements from pre-defined list
3402   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3403     element_info[access_direction_list[i].element].access_direction =
3404       access_direction_list[i].direction;
3405
3406   // ---------- initialize explosion content ----------------------------------
3407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3408   {
3409     if (IS_CUSTOM_ELEMENT(i))
3410       continue;
3411
3412     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3413     {
3414       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3415
3416       element_info[i].content.e[x][y] =
3417         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3418          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3419          i == EL_PLAYER_3 ? EL_EMERALD :
3420          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3421          i == EL_MOLE ? EL_EMERALD_RED :
3422          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3423          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3424          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3425          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3426          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3427          i == EL_WALL_EMERALD ? EL_EMERALD :
3428          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3429          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3430          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3431          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3432          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3433          i == EL_WALL_PEARL ? EL_PEARL :
3434          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3435          EL_EMPTY);
3436     }
3437   }
3438
3439   // ---------- initialize recursion detection --------------------------------
3440   recursion_loop_depth = 0;
3441   recursion_loop_detected = FALSE;
3442   recursion_loop_element = EL_UNDEFINED;
3443
3444   // ---------- initialize graphics engine ------------------------------------
3445   game.scroll_delay_value =
3446     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3447      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3448      !setup.forced_scroll_delay           ? 0 :
3449      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3450   game.scroll_delay_value =
3451     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3452
3453   // ---------- initialize game engine snapshots ------------------------------
3454   for (i = 0; i < MAX_PLAYERS; i++)
3455     game.snapshot.last_action[i] = 0;
3456   game.snapshot.changed_action = FALSE;
3457   game.snapshot.collected_item = FALSE;
3458   game.snapshot.mode =
3459     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3460      SNAPSHOT_MODE_EVERY_STEP :
3461      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3462      SNAPSHOT_MODE_EVERY_MOVE :
3463      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3464      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3465   game.snapshot.save_snapshot = FALSE;
3466
3467   // ---------- initialize level time for Supaplex engine ---------------------
3468   // Supaplex levels with time limit currently unsupported -- should be added
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3470     level.time = 0;
3471
3472   // ---------- initialize flags for handling game actions --------------------
3473
3474   // set flags for game actions to default values
3475   game.use_key_actions = TRUE;
3476   game.use_mouse_actions = FALSE;
3477
3478   // when using Mirror Magic game engine, handle mouse events only
3479   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3480   {
3481     game.use_key_actions = FALSE;
3482     game.use_mouse_actions = TRUE;
3483   }
3484
3485   // check for custom elements with mouse click events
3486   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3487   {
3488     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3489     {
3490       int element = EL_CUSTOM_START + i;
3491
3492       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3493           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3494           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3495           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3496         game.use_mouse_actions = TRUE;
3497     }
3498   }
3499 }
3500
3501 static int get_num_special_action(int element, int action_first,
3502                                   int action_last)
3503 {
3504   int num_special_action = 0;
3505   int i, j;
3506
3507   for (i = action_first; i <= action_last; i++)
3508   {
3509     boolean found = FALSE;
3510
3511     for (j = 0; j < NUM_DIRECTIONS; j++)
3512       if (el_act_dir2img(element, i, j) !=
3513           el_act_dir2img(element, ACTION_DEFAULT, j))
3514         found = TRUE;
3515
3516     if (found)
3517       num_special_action++;
3518     else
3519       break;
3520   }
3521
3522   return num_special_action;
3523 }
3524
3525
3526 // ============================================================================
3527 // InitGame()
3528 // ----------------------------------------------------------------------------
3529 // initialize and start new game
3530 // ============================================================================
3531
3532 #if DEBUG_INIT_PLAYER
3533 static void DebugPrintPlayerStatus(char *message)
3534 {
3535   int i;
3536
3537   if (!options.debug)
3538     return;
3539
3540   Debug("game:init:player", "%s:", message);
3541
3542   for (i = 0; i < MAX_PLAYERS; i++)
3543   {
3544     struct PlayerInfo *player = &stored_player[i];
3545
3546     Debug("game:init:player",
3547           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3548           i + 1,
3549           player->present,
3550           player->connected,
3551           player->connected_locally,
3552           player->connected_network,
3553           player->active,
3554           (local_player == player ? " (local player)" : ""));
3555   }
3556 }
3557 #endif
3558
3559 void InitGame(void)
3560 {
3561   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3562   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3563   int fade_mask = REDRAW_FIELD;
3564
3565   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3566   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3567   int initial_move_dir = MV_DOWN;
3568   int i, j, x, y;
3569
3570   // required here to update video display before fading (FIX THIS)
3571   DrawMaskedBorder(REDRAW_DOOR_2);
3572
3573   if (!game.restart_level)
3574     CloseDoor(DOOR_CLOSE_1);
3575
3576   SetGameStatus(GAME_MODE_PLAYING);
3577
3578   if (level_editor_test_game)
3579     FadeSkipNextFadeOut();
3580   else
3581     FadeSetEnterScreen();
3582
3583   if (CheckFadeAll())
3584     fade_mask = REDRAW_ALL;
3585
3586   FadeLevelSoundsAndMusic();
3587
3588   ExpireSoundLoops(TRUE);
3589
3590   FadeOut(fade_mask);
3591
3592   if (level_editor_test_game)
3593     FadeSkipNextFadeIn();
3594
3595   // needed if different viewport properties defined for playing
3596   ChangeViewportPropertiesIfNeeded();
3597
3598   ClearField();
3599
3600   DrawCompleteVideoDisplay();
3601
3602   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3603
3604   InitGameEngine();
3605   InitGameControlValues();
3606
3607   if (tape.recording)
3608   {
3609     // initialize tape actions from game when recording tape
3610     tape.use_key_actions   = game.use_key_actions;
3611     tape.use_mouse_actions = game.use_mouse_actions;
3612
3613     // initialize visible playfield size when recording tape (for team mode)
3614     tape.scr_fieldx = SCR_FIELDX;
3615     tape.scr_fieldy = SCR_FIELDY;
3616   }
3617
3618   // don't play tapes over network
3619   network_playing = (network.enabled && !tape.playing);
3620
3621   for (i = 0; i < MAX_PLAYERS; i++)
3622   {
3623     struct PlayerInfo *player = &stored_player[i];
3624
3625     player->index_nr = i;
3626     player->index_bit = (1 << i);
3627     player->element_nr = EL_PLAYER_1 + i;
3628
3629     player->present = FALSE;
3630     player->active = FALSE;
3631     player->mapped = FALSE;
3632
3633     player->killed = FALSE;
3634     player->reanimated = FALSE;
3635     player->buried = FALSE;
3636
3637     player->action = 0;
3638     player->effective_action = 0;
3639     player->programmed_action = 0;
3640     player->snap_action = 0;
3641
3642     player->mouse_action.lx = 0;
3643     player->mouse_action.ly = 0;
3644     player->mouse_action.button = 0;
3645     player->mouse_action.button_hint = 0;
3646
3647     player->effective_mouse_action.lx = 0;
3648     player->effective_mouse_action.ly = 0;
3649     player->effective_mouse_action.button = 0;
3650     player->effective_mouse_action.button_hint = 0;
3651
3652     for (j = 0; j < MAX_NUM_KEYS; j++)
3653       player->key[j] = FALSE;
3654
3655     player->num_white_keys = 0;
3656
3657     player->dynabomb_count = 0;
3658     player->dynabomb_size = 1;
3659     player->dynabombs_left = 0;
3660     player->dynabomb_xl = FALSE;
3661
3662     player->MovDir = initial_move_dir;
3663     player->MovPos = 0;
3664     player->GfxPos = 0;
3665     player->GfxDir = initial_move_dir;
3666     player->GfxAction = ACTION_DEFAULT;
3667     player->Frame = 0;
3668     player->StepFrame = 0;
3669
3670     player->initial_element = player->element_nr;
3671     player->artwork_element =
3672       (level.use_artwork_element[i] ? level.artwork_element[i] :
3673        player->element_nr);
3674     player->use_murphy = FALSE;
3675
3676     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3677     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3678
3679     player->gravity = level.initial_player_gravity[i];
3680
3681     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3682
3683     player->actual_frame_counter = 0;
3684
3685     player->step_counter = 0;
3686
3687     player->last_move_dir = initial_move_dir;
3688
3689     player->is_active = FALSE;
3690
3691     player->is_waiting = FALSE;
3692     player->is_moving = FALSE;
3693     player->is_auto_moving = FALSE;
3694     player->is_digging = FALSE;
3695     player->is_snapping = FALSE;
3696     player->is_collecting = FALSE;
3697     player->is_pushing = FALSE;
3698     player->is_switching = FALSE;
3699     player->is_dropping = FALSE;
3700     player->is_dropping_pressed = FALSE;
3701
3702     player->is_bored = FALSE;
3703     player->is_sleeping = FALSE;
3704
3705     player->was_waiting = TRUE;
3706     player->was_moving = FALSE;
3707     player->was_snapping = FALSE;
3708     player->was_dropping = FALSE;
3709
3710     player->force_dropping = FALSE;
3711
3712     player->frame_counter_bored = -1;
3713     player->frame_counter_sleeping = -1;
3714
3715     player->anim_delay_counter = 0;
3716     player->post_delay_counter = 0;
3717
3718     player->dir_waiting = initial_move_dir;
3719     player->action_waiting = ACTION_DEFAULT;
3720     player->last_action_waiting = ACTION_DEFAULT;
3721     player->special_action_bored = ACTION_DEFAULT;
3722     player->special_action_sleeping = ACTION_DEFAULT;
3723
3724     player->switch_x = -1;
3725     player->switch_y = -1;
3726
3727     player->drop_x = -1;
3728     player->drop_y = -1;
3729
3730     player->show_envelope = 0;
3731
3732     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3733
3734     player->push_delay       = -1;      // initialized when pushing starts
3735     player->push_delay_value = game.initial_push_delay_value;
3736
3737     player->drop_delay = 0;
3738     player->drop_pressed_delay = 0;
3739
3740     player->last_jx = -1;
3741     player->last_jy = -1;
3742     player->jx = -1;
3743     player->jy = -1;
3744
3745     player->shield_normal_time_left = 0;
3746     player->shield_deadly_time_left = 0;
3747
3748     player->last_removed_element = EL_UNDEFINED;
3749
3750     player->inventory_infinite_element = EL_UNDEFINED;
3751     player->inventory_size = 0;
3752
3753     if (level.use_initial_inventory[i])
3754     {
3755       for (j = 0; j < level.initial_inventory_size[i]; j++)
3756       {
3757         int element = level.initial_inventory_content[i][j];
3758         int collect_count = element_info[element].collect_count_initial;
3759         int k;
3760
3761         if (!IS_CUSTOM_ELEMENT(element))
3762           collect_count = 1;
3763
3764         if (collect_count == 0)
3765           player->inventory_infinite_element = element;
3766         else
3767           for (k = 0; k < collect_count; k++)
3768             if (player->inventory_size < MAX_INVENTORY_SIZE)
3769               player->inventory_element[player->inventory_size++] = element;
3770       }
3771     }
3772
3773     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3774     SnapField(player, 0, 0);
3775
3776     map_player_action[i] = i;
3777   }
3778
3779   network_player_action_received = FALSE;
3780
3781   // initial null action
3782   if (network_playing)
3783     SendToServer_MovePlayer(MV_NONE);
3784
3785   FrameCounter = 0;
3786   TimeFrames = 0;
3787   TimePlayed = 0;
3788   TimeLeft = level.time;
3789   TapeTime = 0;
3790
3791   ScreenMovDir = MV_NONE;
3792   ScreenMovPos = 0;
3793   ScreenGfxPos = 0;
3794
3795   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3796
3797   game.robot_wheel_x = -1;
3798   game.robot_wheel_y = -1;
3799
3800   game.exit_x = -1;
3801   game.exit_y = -1;
3802
3803   game.all_players_gone = FALSE;
3804
3805   game.LevelSolved = FALSE;
3806   game.GameOver = FALSE;
3807
3808   game.GamePlayed = !tape.playing;
3809
3810   game.LevelSolved_GameWon = FALSE;
3811   game.LevelSolved_GameEnd = FALSE;
3812   game.LevelSolved_SaveTape = FALSE;
3813   game.LevelSolved_SaveScore = FALSE;
3814
3815   game.LevelSolved_CountingTime = 0;
3816   game.LevelSolved_CountingScore = 0;
3817   game.LevelSolved_CountingHealth = 0;
3818
3819   game.panel.active = TRUE;
3820
3821   game.no_time_limit = (level.time == 0);
3822
3823   game.yamyam_content_nr = 0;
3824   game.robot_wheel_active = FALSE;
3825   game.magic_wall_active = FALSE;
3826   game.magic_wall_time_left = 0;
3827   game.light_time_left = 0;
3828   game.timegate_time_left = 0;
3829   game.switchgate_pos = 0;
3830   game.wind_direction = level.wind_direction_initial;
3831
3832   game.time_final = 0;
3833   game.score_time_final = 0;
3834
3835   game.score = 0;
3836   game.score_final = 0;
3837
3838   game.health = MAX_HEALTH;
3839   game.health_final = MAX_HEALTH;
3840
3841   game.gems_still_needed = level.gems_needed;
3842   game.sokoban_fields_still_needed = 0;
3843   game.sokoban_objects_still_needed = 0;
3844   game.lights_still_needed = 0;
3845   game.players_still_needed = 0;
3846   game.friends_still_needed = 0;
3847
3848   game.lenses_time_left = 0;
3849   game.magnify_time_left = 0;
3850
3851   game.ball_active = level.ball_active_initial;
3852   game.ball_content_nr = 0;
3853
3854   game.explosions_delayed = TRUE;
3855
3856   game.envelope_active = FALSE;
3857
3858   // special case: set custom artwork setting to initial value
3859   game.use_masked_elements = game.use_masked_elements_initial;
3860
3861   for (i = 0; i < NUM_BELTS; i++)
3862   {
3863     game.belt_dir[i] = MV_NONE;
3864     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3865   }
3866
3867   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3868     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3869
3870 #if DEBUG_INIT_PLAYER
3871   DebugPrintPlayerStatus("Player status at level initialization");
3872 #endif
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     Tile[x][y] = Last[x][y] = level.field[x][y];
3877     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3878     ChangeDelay[x][y] = 0;
3879     ChangePage[x][y] = -1;
3880     CustomValue[x][y] = 0;              // initialized in InitField()
3881     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3882     AmoebaNr[x][y] = 0;
3883     WasJustMoving[x][y] = 0;
3884     WasJustFalling[x][y] = 0;
3885     CheckCollision[x][y] = 0;
3886     CheckImpact[x][y] = 0;
3887     Stop[x][y] = FALSE;
3888     Pushed[x][y] = FALSE;
3889
3890     ChangeCount[x][y] = 0;
3891     ChangeEvent[x][y] = -1;
3892
3893     ExplodePhase[x][y] = 0;
3894     ExplodeDelay[x][y] = 0;
3895     ExplodeField[x][y] = EX_TYPE_NONE;
3896
3897     RunnerVisit[x][y] = 0;
3898     PlayerVisit[x][y] = 0;
3899
3900     GfxFrame[x][y] = 0;
3901     GfxRandom[x][y] = INIT_GFX_RANDOM();
3902     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3903     GfxElement[x][y] = EL_UNDEFINED;
3904     GfxElementEmpty[x][y] = EL_EMPTY;
3905     GfxAction[x][y] = ACTION_DEFAULT;
3906     GfxDir[x][y] = MV_NONE;
3907     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3908   }
3909
3910   SCAN_PLAYFIELD(x, y)
3911   {
3912     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3913       emulate_bd = FALSE;
3914     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3915       emulate_sp = FALSE;
3916
3917     InitField(x, y, TRUE);
3918
3919     ResetGfxAnimation(x, y);
3920   }
3921
3922   InitBeltMovement();
3923
3924   for (i = 0; i < MAX_PLAYERS; i++)
3925   {
3926     struct PlayerInfo *player = &stored_player[i];
3927
3928     // set number of special actions for bored and sleeping animation
3929     player->num_special_action_bored =
3930       get_num_special_action(player->artwork_element,
3931                              ACTION_BORING_1, ACTION_BORING_LAST);
3932     player->num_special_action_sleeping =
3933       get_num_special_action(player->artwork_element,
3934                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3935   }
3936
3937   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3938                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3939
3940   // initialize type of slippery elements
3941   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3942   {
3943     if (!IS_CUSTOM_ELEMENT(i))
3944     {
3945       // default: elements slip down either to the left or right randomly
3946       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3947
3948       // SP style elements prefer to slip down on the left side
3949       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3950         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3951
3952       // BD style elements prefer to slip down on the left side
3953       if (game.emulation == EMU_BOULDERDASH)
3954         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3955     }
3956   }
3957
3958   // initialize explosion and ignition delay
3959   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3960   {
3961     if (!IS_CUSTOM_ELEMENT(i))
3962     {
3963       int num_phase = 8;
3964       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3965                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3966                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3967       int last_phase = (num_phase + 1) * delay;
3968       int half_phase = (num_phase / 2) * delay;
3969
3970       element_info[i].explosion_delay = last_phase - 1;
3971       element_info[i].ignition_delay = half_phase;
3972
3973       if (i == EL_BLACK_ORB)
3974         element_info[i].ignition_delay = 1;
3975     }
3976   }
3977
3978   // correct non-moving belts to start moving left
3979   for (i = 0; i < NUM_BELTS; i++)
3980     if (game.belt_dir[i] == MV_NONE)
3981       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3982
3983 #if USE_NEW_PLAYER_ASSIGNMENTS
3984   // use preferred player also in local single-player mode
3985   if (!network.enabled && !game.team_mode)
3986   {
3987     int new_index_nr = setup.network_player_nr;
3988
3989     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3990     {
3991       for (i = 0; i < MAX_PLAYERS; i++)
3992         stored_player[i].connected_locally = FALSE;
3993
3994       stored_player[new_index_nr].connected_locally = TRUE;
3995     }
3996   }
3997
3998   for (i = 0; i < MAX_PLAYERS; i++)
3999   {
4000     stored_player[i].connected = FALSE;
4001
4002     // in network game mode, the local player might not be the first player
4003     if (stored_player[i].connected_locally)
4004       local_player = &stored_player[i];
4005   }
4006
4007   if (!network.enabled)
4008     local_player->connected = TRUE;
4009
4010   if (tape.playing)
4011   {
4012     for (i = 0; i < MAX_PLAYERS; i++)
4013       stored_player[i].connected = tape.player_participates[i];
4014   }
4015   else if (network.enabled)
4016   {
4017     // add team mode players connected over the network (needed for correct
4018     // assignment of player figures from level to locally playing players)
4019
4020     for (i = 0; i < MAX_PLAYERS; i++)
4021       if (stored_player[i].connected_network)
4022         stored_player[i].connected = TRUE;
4023   }
4024   else if (game.team_mode)
4025   {
4026     // try to guess locally connected team mode players (needed for correct
4027     // assignment of player figures from level to locally playing players)
4028
4029     for (i = 0; i < MAX_PLAYERS; i++)
4030       if (setup.input[i].use_joystick ||
4031           setup.input[i].key.left != KSYM_UNDEFINED)
4032         stored_player[i].connected = TRUE;
4033   }
4034
4035 #if DEBUG_INIT_PLAYER
4036   DebugPrintPlayerStatus("Player status after level initialization");
4037 #endif
4038
4039 #if DEBUG_INIT_PLAYER
4040   Debug("game:init:player", "Reassigning players ...");
4041 #endif
4042
4043   // check if any connected player was not found in playfield
4044   for (i = 0; i < MAX_PLAYERS; i++)
4045   {
4046     struct PlayerInfo *player = &stored_player[i];
4047
4048     if (player->connected && !player->present)
4049     {
4050       struct PlayerInfo *field_player = NULL;
4051
4052 #if DEBUG_INIT_PLAYER
4053       Debug("game:init:player",
4054             "- looking for field player for player %d ...", i + 1);
4055 #endif
4056
4057       // assign first free player found that is present in the playfield
4058
4059       // first try: look for unmapped playfield player that is not connected
4060       for (j = 0; j < MAX_PLAYERS; j++)
4061         if (field_player == NULL &&
4062             stored_player[j].present &&
4063             !stored_player[j].mapped &&
4064             !stored_player[j].connected)
4065           field_player = &stored_player[j];
4066
4067       // second try: look for *any* unmapped playfield player
4068       for (j = 0; j < MAX_PLAYERS; j++)
4069         if (field_player == NULL &&
4070             stored_player[j].present &&
4071             !stored_player[j].mapped)
4072           field_player = &stored_player[j];
4073
4074       if (field_player != NULL)
4075       {
4076         int jx = field_player->jx, jy = field_player->jy;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- found player %d",
4080               field_player->index_nr + 1);
4081 #endif
4082
4083         player->present = FALSE;
4084         player->active = FALSE;
4085
4086         field_player->present = TRUE;
4087         field_player->active = TRUE;
4088
4089         /*
4090         player->initial_element = field_player->initial_element;
4091         player->artwork_element = field_player->artwork_element;
4092
4093         player->block_last_field       = field_player->block_last_field;
4094         player->block_delay_adjustment = field_player->block_delay_adjustment;
4095         */
4096
4097         StorePlayer[jx][jy] = field_player->element_nr;
4098
4099         field_player->jx = field_player->last_jx = jx;
4100         field_player->jy = field_player->last_jy = jy;
4101
4102         if (local_player == player)
4103           local_player = field_player;
4104
4105         map_player_action[field_player->index_nr] = i;
4106
4107         field_player->mapped = TRUE;
4108
4109 #if DEBUG_INIT_PLAYER
4110         Debug("game:init:player", "- map_player_action[%d] == %d",
4111               field_player->index_nr + 1, i + 1);
4112 #endif
4113       }
4114     }
4115
4116     if (player->connected && player->present)
4117       player->mapped = TRUE;
4118   }
4119
4120 #if DEBUG_INIT_PLAYER
4121   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4122 #endif
4123
4124 #else
4125
4126   // check if any connected player was not found in playfield
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128   {
4129     struct PlayerInfo *player = &stored_player[i];
4130
4131     if (player->connected && !player->present)
4132     {
4133       for (j = 0; j < MAX_PLAYERS; j++)
4134       {
4135         struct PlayerInfo *field_player = &stored_player[j];
4136         int jx = field_player->jx, jy = field_player->jy;
4137
4138         // assign first free player found that is present in the playfield
4139         if (field_player->present && !field_player->connected)
4140         {
4141           player->present = TRUE;
4142           player->active = TRUE;
4143
4144           field_player->present = FALSE;
4145           field_player->active = FALSE;
4146
4147           player->initial_element = field_player->initial_element;
4148           player->artwork_element = field_player->artwork_element;
4149
4150           player->block_last_field       = field_player->block_last_field;
4151           player->block_delay_adjustment = field_player->block_delay_adjustment;
4152
4153           StorePlayer[jx][jy] = player->element_nr;
4154
4155           player->jx = player->last_jx = jx;
4156           player->jy = player->last_jy = jy;
4157
4158           break;
4159         }
4160       }
4161     }
4162   }
4163 #endif
4164
4165 #if 0
4166   Debug("game:init:player", "local_player->present == %d",
4167         local_player->present);
4168 #endif
4169
4170   // set focus to local player for network games, else to all players
4171   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4172   game.centered_player_nr_next = game.centered_player_nr;
4173   game.set_centered_player = FALSE;
4174   game.set_centered_player_wrap = FALSE;
4175
4176   if (network_playing && tape.recording)
4177   {
4178     // store client dependent player focus when recording network games
4179     tape.centered_player_nr_next = game.centered_player_nr_next;
4180     tape.set_centered_player = TRUE;
4181   }
4182
4183   if (tape.playing)
4184   {
4185     // when playing a tape, eliminate all players who do not participate
4186
4187 #if USE_NEW_PLAYER_ASSIGNMENTS
4188
4189     if (!game.team_mode)
4190     {
4191       for (i = 0; i < MAX_PLAYERS; i++)
4192       {
4193         if (stored_player[i].active &&
4194             !tape.player_participates[map_player_action[i]])
4195         {
4196           struct PlayerInfo *player = &stored_player[i];
4197           int jx = player->jx, jy = player->jy;
4198
4199 #if DEBUG_INIT_PLAYER
4200           Debug("game:init:player", "Removing player %d at (%d, %d)",
4201                 i + 1, jx, jy);
4202 #endif
4203
4204           player->active = FALSE;
4205           StorePlayer[jx][jy] = 0;
4206           Tile[jx][jy] = EL_EMPTY;
4207         }
4208       }
4209     }
4210
4211 #else
4212
4213     for (i = 0; i < MAX_PLAYERS; i++)
4214     {
4215       if (stored_player[i].active &&
4216           !tape.player_participates[i])
4217       {
4218         struct PlayerInfo *player = &stored_player[i];
4219         int jx = player->jx, jy = player->jy;
4220
4221         player->active = FALSE;
4222         StorePlayer[jx][jy] = 0;
4223         Tile[jx][jy] = EL_EMPTY;
4224       }
4225     }
4226 #endif
4227   }
4228   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4229   {
4230     // when in single player mode, eliminate all but the local player
4231
4232     for (i = 0; i < MAX_PLAYERS; i++)
4233     {
4234       struct PlayerInfo *player = &stored_player[i];
4235
4236       if (player->active && player != local_player)
4237       {
4238         int jx = player->jx, jy = player->jy;
4239
4240         player->active = FALSE;
4241         player->present = FALSE;
4242
4243         StorePlayer[jx][jy] = 0;
4244         Tile[jx][jy] = EL_EMPTY;
4245       }
4246     }
4247   }
4248
4249   for (i = 0; i < MAX_PLAYERS; i++)
4250     if (stored_player[i].active)
4251       game.players_still_needed++;
4252
4253   if (level.solved_by_one_player)
4254     game.players_still_needed = 1;
4255
4256   // when recording the game, store which players take part in the game
4257   if (tape.recording)
4258   {
4259 #if USE_NEW_PLAYER_ASSIGNMENTS
4260     for (i = 0; i < MAX_PLAYERS; i++)
4261       if (stored_player[i].connected)
4262         tape.player_participates[i] = TRUE;
4263 #else
4264     for (i = 0; i < MAX_PLAYERS; i++)
4265       if (stored_player[i].active)
4266         tape.player_participates[i] = TRUE;
4267 #endif
4268   }
4269
4270 #if DEBUG_INIT_PLAYER
4271   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4272 #endif
4273
4274   if (BorderElement == EL_EMPTY)
4275   {
4276     SBX_Left = 0;
4277     SBX_Right = lev_fieldx - SCR_FIELDX;
4278     SBY_Upper = 0;
4279     SBY_Lower = lev_fieldy - SCR_FIELDY;
4280   }
4281   else
4282   {
4283     SBX_Left = -1;
4284     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4285     SBY_Upper = -1;
4286     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4287   }
4288
4289   if (full_lev_fieldx <= SCR_FIELDX)
4290     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4291   if (full_lev_fieldy <= SCR_FIELDY)
4292     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4293
4294   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4295     SBX_Left--;
4296   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4297     SBY_Upper--;
4298
4299   // if local player not found, look for custom element that might create
4300   // the player (make some assumptions about the right custom element)
4301   if (!local_player->present)
4302   {
4303     int start_x = 0, start_y = 0;
4304     int found_rating = 0;
4305     int found_element = EL_UNDEFINED;
4306     int player_nr = local_player->index_nr;
4307
4308     SCAN_PLAYFIELD(x, y)
4309     {
4310       int element = Tile[x][y];
4311       int content;
4312       int xx, yy;
4313       boolean is_player;
4314
4315       if (level.use_start_element[player_nr] &&
4316           level.start_element[player_nr] == element &&
4317           found_rating < 4)
4318       {
4319         start_x = x;
4320         start_y = y;
4321
4322         found_rating = 4;
4323         found_element = element;
4324       }
4325
4326       if (!IS_CUSTOM_ELEMENT(element))
4327         continue;
4328
4329       if (CAN_CHANGE(element))
4330       {
4331         for (i = 0; i < element_info[element].num_change_pages; i++)
4332         {
4333           // check for player created from custom element as single target
4334           content = element_info[element].change_page[i].target_element;
4335           is_player = IS_PLAYER_ELEMENT(content);
4336
4337           if (is_player && (found_rating < 3 ||
4338                             (found_rating == 3 && element < found_element)))
4339           {
4340             start_x = x;
4341             start_y = y;
4342
4343             found_rating = 3;
4344             found_element = element;
4345           }
4346         }
4347       }
4348
4349       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4350       {
4351         // check for player created from custom element as explosion content
4352         content = element_info[element].content.e[xx][yy];
4353         is_player = IS_PLAYER_ELEMENT(content);
4354
4355         if (is_player && (found_rating < 2 ||
4356                           (found_rating == 2 && element < found_element)))
4357         {
4358           start_x = x + xx - 1;
4359           start_y = y + yy - 1;
4360
4361           found_rating = 2;
4362           found_element = element;
4363         }
4364
4365         if (!CAN_CHANGE(element))
4366           continue;
4367
4368         for (i = 0; i < element_info[element].num_change_pages; i++)
4369         {
4370           // check for player created from custom element as extended target
4371           content =
4372             element_info[element].change_page[i].target_content.e[xx][yy];
4373
4374           is_player = IS_PLAYER_ELEMENT(content);
4375
4376           if (is_player && (found_rating < 1 ||
4377                             (found_rating == 1 && element < found_element)))
4378           {
4379             start_x = x + xx - 1;
4380             start_y = y + yy - 1;
4381
4382             found_rating = 1;
4383             found_element = element;
4384           }
4385         }
4386       }
4387     }
4388
4389     scroll_x = SCROLL_POSITION_X(start_x);
4390     scroll_y = SCROLL_POSITION_Y(start_y);
4391   }
4392   else
4393   {
4394     scroll_x = SCROLL_POSITION_X(local_player->jx);
4395     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4396   }
4397
4398   // !!! FIX THIS (START) !!!
4399   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4400   {
4401     InitGameEngine_EM();
4402   }
4403   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4404   {
4405     InitGameEngine_SP();
4406   }
4407   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4408   {
4409     InitGameEngine_MM();
4410   }
4411   else
4412   {
4413     DrawLevel(REDRAW_FIELD);
4414     DrawAllPlayers();
4415
4416     // after drawing the level, correct some elements
4417     if (game.timegate_time_left == 0)
4418       CloseAllOpenTimegates();
4419   }
4420
4421   // blit playfield from scroll buffer to normal back buffer for fading in
4422   BlitScreenToBitmap(backbuffer);
4423   // !!! FIX THIS (END) !!!
4424
4425   DrawMaskedBorder(fade_mask);
4426
4427   FadeIn(fade_mask);
4428
4429 #if 1
4430   // full screen redraw is required at this point in the following cases:
4431   // - special editor door undrawn when game was started from level editor
4432   // - drawing area (playfield) was changed and has to be removed completely
4433   redraw_mask = REDRAW_ALL;
4434   BackToFront();
4435 #endif
4436
4437   if (!game.restart_level)
4438   {
4439     // copy default game door content to main double buffer
4440
4441     // !!! CHECK AGAIN !!!
4442     SetPanelBackground();
4443     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4444     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4445   }
4446
4447   SetPanelBackground();
4448   SetDrawBackgroundMask(REDRAW_DOOR_1);
4449
4450   UpdateAndDisplayGameControlValues();
4451
4452   if (!game.restart_level)
4453   {
4454     UnmapGameButtons();
4455     UnmapTapeButtons();
4456
4457     FreeGameButtons();
4458     CreateGameButtons();
4459
4460     MapGameButtons();
4461     MapTapeButtons();
4462
4463     // copy actual game door content to door double buffer for OpenDoor()
4464     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4465
4466     OpenDoor(DOOR_OPEN_ALL);
4467
4468     KeyboardAutoRepeatOffUnlessAutoplay();
4469
4470 #if DEBUG_INIT_PLAYER
4471     DebugPrintPlayerStatus("Player status (final)");
4472 #endif
4473   }
4474
4475   UnmapAllGadgets();
4476
4477   MapGameButtons();
4478   MapTapeButtons();
4479
4480   if (!game.restart_level && !tape.playing)
4481   {
4482     LevelStats_incPlayed(level_nr);
4483
4484     SaveLevelSetup_SeriesInfo();
4485   }
4486
4487   game.restart_level = FALSE;
4488   game.restart_game_message = NULL;
4489
4490   game.request_active = FALSE;
4491   game.request_active_or_moving = FALSE;
4492
4493   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4494     InitGameActions_MM();
4495
4496   SaveEngineSnapshotToListInitial();
4497
4498   if (!game.restart_level)
4499   {
4500     PlaySound(SND_GAME_STARTING);
4501
4502     if (setup.sound_music)
4503       PlayLevelMusic();
4504   }
4505
4506   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4507 }
4508
4509 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4510                         int actual_player_x, int actual_player_y)
4511 {
4512   // this is used for non-R'n'D game engines to update certain engine values
4513
4514   // needed to determine if sounds are played within the visible screen area
4515   scroll_x = actual_scroll_x;
4516   scroll_y = actual_scroll_y;
4517
4518   // needed to get player position for "follow finger" playing input method
4519   local_player->jx = actual_player_x;
4520   local_player->jy = actual_player_y;
4521 }
4522
4523 void InitMovDir(int x, int y)
4524 {
4525   int i, element = Tile[x][y];
4526   static int xy[4][2] =
4527   {
4528     {  0, +1 },
4529     { +1,  0 },
4530     {  0, -1 },
4531     { -1,  0 }
4532   };
4533   static int direction[3][4] =
4534   {
4535     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4536     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4537     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4538   };
4539
4540   switch (element)
4541   {
4542     case EL_BUG_RIGHT:
4543     case EL_BUG_UP:
4544     case EL_BUG_LEFT:
4545     case EL_BUG_DOWN:
4546       Tile[x][y] = EL_BUG;
4547       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4548       break;
4549
4550     case EL_SPACESHIP_RIGHT:
4551     case EL_SPACESHIP_UP:
4552     case EL_SPACESHIP_LEFT:
4553     case EL_SPACESHIP_DOWN:
4554       Tile[x][y] = EL_SPACESHIP;
4555       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4556       break;
4557
4558     case EL_BD_BUTTERFLY_RIGHT:
4559     case EL_BD_BUTTERFLY_UP:
4560     case EL_BD_BUTTERFLY_LEFT:
4561     case EL_BD_BUTTERFLY_DOWN:
4562       Tile[x][y] = EL_BD_BUTTERFLY;
4563       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4564       break;
4565
4566     case EL_BD_FIREFLY_RIGHT:
4567     case EL_BD_FIREFLY_UP:
4568     case EL_BD_FIREFLY_LEFT:
4569     case EL_BD_FIREFLY_DOWN:
4570       Tile[x][y] = EL_BD_FIREFLY;
4571       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4572       break;
4573
4574     case EL_PACMAN_RIGHT:
4575     case EL_PACMAN_UP:
4576     case EL_PACMAN_LEFT:
4577     case EL_PACMAN_DOWN:
4578       Tile[x][y] = EL_PACMAN;
4579       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4580       break;
4581
4582     case EL_YAMYAM_LEFT:
4583     case EL_YAMYAM_RIGHT:
4584     case EL_YAMYAM_UP:
4585     case EL_YAMYAM_DOWN:
4586       Tile[x][y] = EL_YAMYAM;
4587       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4588       break;
4589
4590     case EL_SP_SNIKSNAK:
4591       MovDir[x][y] = MV_UP;
4592       break;
4593
4594     case EL_SP_ELECTRON:
4595       MovDir[x][y] = MV_LEFT;
4596       break;
4597
4598     case EL_MOLE_LEFT:
4599     case EL_MOLE_RIGHT:
4600     case EL_MOLE_UP:
4601     case EL_MOLE_DOWN:
4602       Tile[x][y] = EL_MOLE;
4603       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4604       break;
4605
4606     case EL_SPRING_LEFT:
4607     case EL_SPRING_RIGHT:
4608       Tile[x][y] = EL_SPRING;
4609       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4610       break;
4611
4612     default:
4613       if (IS_CUSTOM_ELEMENT(element))
4614       {
4615         struct ElementInfo *ei = &element_info[element];
4616         int move_direction_initial = ei->move_direction_initial;
4617         int move_pattern = ei->move_pattern;
4618
4619         if (move_direction_initial == MV_START_PREVIOUS)
4620         {
4621           if (MovDir[x][y] != MV_NONE)
4622             return;
4623
4624           move_direction_initial = MV_START_AUTOMATIC;
4625         }
4626
4627         if (move_direction_initial == MV_START_RANDOM)
4628           MovDir[x][y] = 1 << RND(4);
4629         else if (move_direction_initial & MV_ANY_DIRECTION)
4630           MovDir[x][y] = move_direction_initial;
4631         else if (move_pattern == MV_ALL_DIRECTIONS ||
4632                  move_pattern == MV_TURNING_LEFT ||
4633                  move_pattern == MV_TURNING_RIGHT ||
4634                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4635                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4636                  move_pattern == MV_TURNING_RANDOM)
4637           MovDir[x][y] = 1 << RND(4);
4638         else if (move_pattern == MV_HORIZONTAL)
4639           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4640         else if (move_pattern == MV_VERTICAL)
4641           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4642         else if (move_pattern & MV_ANY_DIRECTION)
4643           MovDir[x][y] = element_info[element].move_pattern;
4644         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4645                  move_pattern == MV_ALONG_RIGHT_SIDE)
4646         {
4647           // use random direction as default start direction
4648           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4649             MovDir[x][y] = 1 << RND(4);
4650
4651           for (i = 0; i < NUM_DIRECTIONS; i++)
4652           {
4653             int x1 = x + xy[i][0];
4654             int y1 = y + xy[i][1];
4655
4656             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4657             {
4658               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4659                 MovDir[x][y] = direction[0][i];
4660               else
4661                 MovDir[x][y] = direction[1][i];
4662
4663               break;
4664             }
4665           }
4666         }                
4667       }
4668       else
4669       {
4670         MovDir[x][y] = 1 << RND(4);
4671
4672         if (element != EL_BUG &&
4673             element != EL_SPACESHIP &&
4674             element != EL_BD_BUTTERFLY &&
4675             element != EL_BD_FIREFLY)
4676           break;
4677
4678         for (i = 0; i < NUM_DIRECTIONS; i++)
4679         {
4680           int x1 = x + xy[i][0];
4681           int y1 = y + xy[i][1];
4682
4683           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4684           {
4685             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4686             {
4687               MovDir[x][y] = direction[0][i];
4688               break;
4689             }
4690             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4691                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4692             {
4693               MovDir[x][y] = direction[1][i];
4694               break;
4695             }
4696           }
4697         }
4698       }
4699       break;
4700   }
4701
4702   GfxDir[x][y] = MovDir[x][y];
4703 }
4704
4705 void InitAmoebaNr(int x, int y)
4706 {
4707   int i;
4708   int group_nr = AmoebaNeighbourNr(x, y);
4709
4710   if (group_nr == 0)
4711   {
4712     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4713     {
4714       if (AmoebaCnt[i] == 0)
4715       {
4716         group_nr = i;
4717         break;
4718       }
4719     }
4720   }
4721
4722   AmoebaNr[x][y] = group_nr;
4723   AmoebaCnt[group_nr]++;
4724   AmoebaCnt2[group_nr]++;
4725 }
4726
4727 static void LevelSolved_SetFinalGameValues(void)
4728 {
4729   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4730   game.score_time_final = (level.use_step_counter ? TimePlayed :
4731                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4732
4733   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4734                       game_em.lev->score :
4735                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4736                       game_mm.score :
4737                       game.score);
4738
4739   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                        MM_HEALTH(game_mm.laser_overload_value) :
4741                        game.health);
4742
4743   game.LevelSolved_CountingTime = game.time_final;
4744   game.LevelSolved_CountingScore = game.score_final;
4745   game.LevelSolved_CountingHealth = game.health_final;
4746 }
4747
4748 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4749 {
4750   game.LevelSolved_CountingTime = time;
4751   game.LevelSolved_CountingScore = score;
4752   game.LevelSolved_CountingHealth = health;
4753
4754   game_panel_controls[GAME_PANEL_TIME].value = time;
4755   game_panel_controls[GAME_PANEL_SCORE].value = score;
4756   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4757
4758   DisplayGameControlValues();
4759 }
4760
4761 static void LevelSolved(void)
4762 {
4763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4764       game.players_still_needed > 0)
4765     return;
4766
4767   game.LevelSolved = TRUE;
4768   game.GameOver = TRUE;
4769
4770   // needed here to display correct panel values while player walks into exit
4771   LevelSolved_SetFinalGameValues();
4772 }
4773
4774 void GameWon(void)
4775 {
4776   static int time_count_steps;
4777   static int time, time_final;
4778   static float score, score_final; // needed for time score < 10 for 10 seconds
4779   static int health, health_final;
4780   static int game_over_delay_1 = 0;
4781   static int game_over_delay_2 = 0;
4782   static int game_over_delay_3 = 0;
4783   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4784   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4785
4786   if (!game.LevelSolved_GameWon)
4787   {
4788     int i;
4789
4790     // do not start end game actions before the player stops moving (to exit)
4791     if (local_player->active && local_player->MovPos)
4792       return;
4793
4794     // calculate final game values after player finished walking into exit
4795     LevelSolved_SetFinalGameValues();
4796
4797     game.LevelSolved_GameWon = TRUE;
4798     game.LevelSolved_SaveTape = tape.recording;
4799     game.LevelSolved_SaveScore = !tape.playing;
4800
4801     if (!tape.playing)
4802     {
4803       LevelStats_incSolved(level_nr);
4804
4805       SaveLevelSetup_SeriesInfo();
4806     }
4807
4808     if (tape.auto_play)         // tape might already be stopped here
4809       tape.auto_play_level_solved = TRUE;
4810
4811     TapeStop();
4812
4813     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4814     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4815     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4816
4817     time = time_final = game.time_final;
4818     score = score_final = game.score_final;
4819     health = health_final = game.health_final;
4820
4821     // update game panel values before (delayed) counting of score (if any)
4822     LevelSolved_DisplayFinalGameValues(time, score, health);
4823
4824     // if level has time score defined, calculate new final game values
4825     if (time_score > 0)
4826     {
4827       int time_final_max = 999;
4828       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4829       int time_frames = 0;
4830       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4831       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4832
4833       if (TimeLeft > 0)
4834       {
4835         time_final = 0;
4836         time_frames = time_frames_left;
4837       }
4838       else if (game.no_time_limit && TimePlayed < time_final_max)
4839       {
4840         time_final = time_final_max;
4841         time_frames = time_frames_final_max - time_frames_played;
4842       }
4843
4844       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4845
4846       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4847
4848       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4849       {
4850         health_final = 0;
4851         score_final += health * time_score;
4852       }
4853
4854       game.score_final = score_final;
4855       game.health_final = health_final;
4856     }
4857
4858     // if not counting score after game, immediately update game panel values
4859     if (level_editor_test_game || !setup.count_score_after_game)
4860     {
4861       time = time_final;
4862       score = score_final;
4863
4864       LevelSolved_DisplayFinalGameValues(time, score, health);
4865     }
4866
4867     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4868     {
4869       // check if last player has left the level
4870       if (game.exit_x >= 0 &&
4871           game.exit_y >= 0)
4872       {
4873         int x = game.exit_x;
4874         int y = game.exit_y;
4875         int element = Tile[x][y];
4876
4877         // close exit door after last player
4878         if ((game.all_players_gone &&
4879              (element == EL_EXIT_OPEN ||
4880               element == EL_SP_EXIT_OPEN ||
4881               element == EL_STEEL_EXIT_OPEN)) ||
4882             element == EL_EM_EXIT_OPEN ||
4883             element == EL_EM_STEEL_EXIT_OPEN)
4884         {
4885
4886           Tile[x][y] =
4887             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4888              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4889              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4890              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4891              EL_EM_STEEL_EXIT_CLOSING);
4892
4893           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4894         }
4895
4896         // player disappears
4897         DrawLevelField(x, y);
4898       }
4899
4900       for (i = 0; i < MAX_PLAYERS; i++)
4901       {
4902         struct PlayerInfo *player = &stored_player[i];
4903
4904         if (player->present)
4905         {
4906           RemovePlayer(player);
4907
4908           // player disappears
4909           DrawLevelField(player->jx, player->jy);
4910         }
4911       }
4912     }
4913
4914     PlaySound(SND_GAME_WINNING);
4915   }
4916
4917   if (setup.count_score_after_game)
4918   {
4919     if (time != time_final)
4920     {
4921       if (game_over_delay_1 > 0)
4922       {
4923         game_over_delay_1--;
4924
4925         return;
4926       }
4927
4928       int time_to_go = ABS(time_final - time);
4929       int time_count_dir = (time < time_final ? +1 : -1);
4930
4931       if (time_to_go < time_count_steps)
4932         time_count_steps = 1;
4933
4934       time  += time_count_steps * time_count_dir;
4935       score += time_count_steps * time_score;
4936
4937       // set final score to correct rounding differences after counting score
4938       if (time == time_final)
4939         score = score_final;
4940
4941       LevelSolved_DisplayFinalGameValues(time, score, health);
4942
4943       if (time == time_final)
4944         StopSound(SND_GAME_LEVELTIME_BONUS);
4945       else if (setup.sound_loops)
4946         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4947       else
4948         PlaySound(SND_GAME_LEVELTIME_BONUS);
4949
4950       return;
4951     }
4952
4953     if (health != health_final)
4954     {
4955       if (game_over_delay_2 > 0)
4956       {
4957         game_over_delay_2--;
4958
4959         return;
4960       }
4961
4962       int health_count_dir = (health < health_final ? +1 : -1);
4963
4964       health += health_count_dir;
4965       score  += time_score;
4966
4967       LevelSolved_DisplayFinalGameValues(time, score, health);
4968
4969       if (health == health_final)
4970         StopSound(SND_GAME_LEVELTIME_BONUS);
4971       else if (setup.sound_loops)
4972         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4973       else
4974         PlaySound(SND_GAME_LEVELTIME_BONUS);
4975
4976       return;
4977     }
4978   }
4979
4980   game.panel.active = FALSE;
4981
4982   if (game_over_delay_3 > 0)
4983   {
4984     game_over_delay_3--;
4985
4986     return;
4987   }
4988
4989   GameEnd();
4990 }
4991
4992 void GameEnd(void)
4993 {
4994   // used instead of "level_nr" (needed for network games)
4995   int last_level_nr = levelset.level_nr;
4996   boolean tape_saved = FALSE;
4997
4998   game.LevelSolved_GameEnd = TRUE;
4999
5000   if (game.LevelSolved_SaveTape)
5001   {
5002     // make sure that request dialog to save tape does not open door again
5003     if (!global.use_envelope_request)
5004       CloseDoor(DOOR_CLOSE_1);
5005
5006     // ask to save tape
5007     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5008
5009     // set unique basename for score tape (also saved in high score table)
5010     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5011   }
5012
5013   // if no tape is to be saved, close both doors simultaneously
5014   CloseDoor(DOOR_CLOSE_ALL);
5015
5016   if (level_editor_test_game)
5017   {
5018     SetGameStatus(GAME_MODE_MAIN);
5019
5020     DrawMainMenu();
5021
5022     return;
5023   }
5024
5025   if (!game.LevelSolved_SaveScore)
5026   {
5027     SetGameStatus(GAME_MODE_MAIN);
5028
5029     DrawMainMenu();
5030
5031     return;
5032   }
5033
5034   if (level_nr == leveldir_current->handicap_level)
5035   {
5036     leveldir_current->handicap_level++;
5037
5038     SaveLevelSetup_SeriesInfo();
5039   }
5040
5041   // save score and score tape before potentially erasing tape below
5042   NewHighScore(last_level_nr, tape_saved);
5043
5044   if (setup.increment_levels &&
5045       level_nr < leveldir_current->last_level &&
5046       !network_playing)
5047   {
5048     level_nr++;         // advance to next level
5049     TapeErase();        // start with empty tape
5050
5051     if (setup.auto_play_next_level)
5052     {
5053       LoadLevel(level_nr);
5054
5055       SaveLevelSetup_SeriesInfo();
5056     }
5057   }
5058
5059   if (scores.last_added >= 0 && setup.show_scores_after_game)
5060   {
5061     SetGameStatus(GAME_MODE_SCORES);
5062
5063     DrawHallOfFame(last_level_nr);
5064   }
5065   else if (setup.auto_play_next_level && setup.increment_levels &&
5066            last_level_nr < leveldir_current->last_level &&
5067            !network_playing)
5068   {
5069     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5070   }
5071   else
5072   {
5073     SetGameStatus(GAME_MODE_MAIN);
5074
5075     DrawMainMenu();
5076   }
5077 }
5078
5079 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5080                          boolean one_score_entry_per_name)
5081 {
5082   int i;
5083
5084   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5085     return -1;
5086
5087   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5088   {
5089     struct ScoreEntry *entry = &list->entry[i];
5090     boolean score_is_better = (new_entry->score >  entry->score);
5091     boolean score_is_equal  = (new_entry->score == entry->score);
5092     boolean time_is_better  = (new_entry->time  <  entry->time);
5093     boolean time_is_equal   = (new_entry->time  == entry->time);
5094     boolean better_by_score = (score_is_better ||
5095                                (score_is_equal && time_is_better));
5096     boolean better_by_time  = (time_is_better ||
5097                                (time_is_equal && score_is_better));
5098     boolean is_better = (level.rate_time_over_score ? better_by_time :
5099                          better_by_score);
5100     boolean entry_is_empty = (entry->score == 0 &&
5101                               entry->time == 0);
5102
5103     // prevent adding server score entries if also existing in local score file
5104     // (special case: historic score entries have an empty tape basename entry)
5105     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5106         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5107     {
5108       // special case: use server score instead of local score value if higher
5109       // (historic scores might have been truncated to 16-bit values locally)
5110       if (score_is_better)
5111         entry->score = new_entry->score;
5112
5113       return -1;
5114     }
5115
5116     if (is_better || entry_is_empty)
5117     {
5118       // player has made it to the hall of fame
5119
5120       if (i < MAX_SCORE_ENTRIES - 1)
5121       {
5122         int m = MAX_SCORE_ENTRIES - 1;
5123         int l;
5124
5125         if (one_score_entry_per_name)
5126         {
5127           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5128             if (strEqual(list->entry[l].name, new_entry->name))
5129               m = l;
5130
5131           if (m == i)   // player's new highscore overwrites his old one
5132             goto put_into_list;
5133         }
5134
5135         for (l = m; l > i; l--)
5136           list->entry[l] = list->entry[l - 1];
5137       }
5138
5139       put_into_list:
5140
5141       *entry = *new_entry;
5142
5143       return i;
5144     }
5145     else if (one_score_entry_per_name &&
5146              strEqual(entry->name, new_entry->name))
5147     {
5148       // player already in high score list with better score or time
5149
5150       return -1;
5151     }
5152   }
5153
5154   return -1;
5155 }
5156
5157 void NewHighScore(int level_nr, boolean tape_saved)
5158 {
5159   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5160   boolean one_per_name = FALSE;
5161
5162   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5163   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5164
5165   new_entry.score = game.score_final;
5166   new_entry.time = game.score_time_final;
5167
5168   LoadScore(level_nr);
5169
5170   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5171
5172   if (scores.last_added < 0)
5173     return;
5174
5175   SaveScore(level_nr);
5176
5177   // store last added local score entry (before merging server scores)
5178   scores.last_added_local = scores.last_added;
5179
5180   if (!game.LevelSolved_SaveTape)
5181     return;
5182
5183   SaveScoreTape(level_nr);
5184
5185   if (setup.ask_for_using_api_server)
5186   {
5187     setup.use_api_server =
5188       Request("Upload your score and tape to the high score server?", REQ_ASK);
5189
5190     if (!setup.use_api_server)
5191       Request("Not using high score server! Use setup menu to enable again!",
5192               REQ_CONFIRM);
5193
5194     runtime.use_api_server = setup.use_api_server;
5195
5196     // after asking for using API server once, do not ask again
5197     setup.ask_for_using_api_server = FALSE;
5198
5199     SaveSetup_ServerSetup();
5200   }
5201
5202   SaveServerScore(level_nr, tape_saved);
5203 }
5204
5205 void MergeServerScore(void)
5206 {
5207   struct ScoreEntry last_added_entry;
5208   boolean one_per_name = FALSE;
5209   int i;
5210
5211   if (scores.last_added >= 0)
5212     last_added_entry = scores.entry[scores.last_added];
5213
5214   for (i = 0; i < server_scores.num_entries; i++)
5215   {
5216     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5217
5218     if (pos >= 0 && pos <= scores.last_added)
5219       scores.last_added++;
5220   }
5221
5222   if (scores.last_added >= MAX_SCORE_ENTRIES)
5223   {
5224     scores.last_added = MAX_SCORE_ENTRIES - 1;
5225     scores.force_last_added = TRUE;
5226
5227     scores.entry[scores.last_added] = last_added_entry;
5228   }
5229 }
5230
5231 static int getElementMoveStepsizeExt(int x, int y, int direction)
5232 {
5233   int element = Tile[x][y];
5234   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5235   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5236   int horiz_move = (dx != 0);
5237   int sign = (horiz_move ? dx : dy);
5238   int step = sign * element_info[element].move_stepsize;
5239
5240   // special values for move stepsize for spring and things on conveyor belt
5241   if (horiz_move)
5242   {
5243     if (CAN_FALL(element) &&
5244         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5245       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5246     else if (element == EL_SPRING)
5247       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5248   }
5249
5250   return step;
5251 }
5252
5253 static int getElementMoveStepsize(int x, int y)
5254 {
5255   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5256 }
5257
5258 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5259 {
5260   if (player->GfxAction != action || player->GfxDir != dir)
5261   {
5262     player->GfxAction = action;
5263     player->GfxDir = dir;
5264     player->Frame = 0;
5265     player->StepFrame = 0;
5266   }
5267 }
5268
5269 static void ResetGfxFrame(int x, int y)
5270 {
5271   // profiling showed that "autotest" spends 10~20% of its time in this function
5272   if (DrawingDeactivatedField())
5273     return;
5274
5275   int element = Tile[x][y];
5276   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5277
5278   if (graphic_info[graphic].anim_global_sync)
5279     GfxFrame[x][y] = FrameCounter;
5280   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5281     GfxFrame[x][y] = CustomValue[x][y];
5282   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5283     GfxFrame[x][y] = element_info[element].collect_score;
5284   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5285     GfxFrame[x][y] = ChangeDelay[x][y];
5286 }
5287
5288 static void ResetGfxAnimation(int x, int y)
5289 {
5290   GfxAction[x][y] = ACTION_DEFAULT;
5291   GfxDir[x][y] = MovDir[x][y];
5292   GfxFrame[x][y] = 0;
5293
5294   ResetGfxFrame(x, y);
5295 }
5296
5297 static void ResetRandomAnimationValue(int x, int y)
5298 {
5299   GfxRandom[x][y] = INIT_GFX_RANDOM();
5300 }
5301
5302 static void InitMovingField(int x, int y, int direction)
5303 {
5304   int element = Tile[x][y];
5305   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5306   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5307   int newx = x + dx;
5308   int newy = y + dy;
5309   boolean is_moving_before, is_moving_after;
5310
5311   // check if element was/is moving or being moved before/after mode change
5312   is_moving_before = (WasJustMoving[x][y] != 0);
5313   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5314
5315   // reset animation only for moving elements which change direction of moving
5316   // or which just started or stopped moving
5317   // (else CEs with property "can move" / "not moving" are reset each frame)
5318   if (is_moving_before != is_moving_after ||
5319       direction != MovDir[x][y])
5320     ResetGfxAnimation(x, y);
5321
5322   MovDir[x][y] = direction;
5323   GfxDir[x][y] = direction;
5324
5325   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5326                      direction == MV_DOWN && CAN_FALL(element) ?
5327                      ACTION_FALLING : ACTION_MOVING);
5328
5329   // this is needed for CEs with property "can move" / "not moving"
5330
5331   if (is_moving_after)
5332   {
5333     if (Tile[newx][newy] == EL_EMPTY)
5334       Tile[newx][newy] = EL_BLOCKED;
5335
5336     MovDir[newx][newy] = MovDir[x][y];
5337
5338     CustomValue[newx][newy] = CustomValue[x][y];
5339
5340     GfxFrame[newx][newy] = GfxFrame[x][y];
5341     GfxRandom[newx][newy] = GfxRandom[x][y];
5342     GfxAction[newx][newy] = GfxAction[x][y];
5343     GfxDir[newx][newy] = GfxDir[x][y];
5344   }
5345 }
5346
5347 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5348 {
5349   int direction = MovDir[x][y];
5350   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5351   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5352
5353   *goes_to_x = newx;
5354   *goes_to_y = newy;
5355 }
5356
5357 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5358 {
5359   int oldx = x, oldy = y;
5360   int direction = MovDir[x][y];
5361
5362   if (direction == MV_LEFT)
5363     oldx++;
5364   else if (direction == MV_RIGHT)
5365     oldx--;
5366   else if (direction == MV_UP)
5367     oldy++;
5368   else if (direction == MV_DOWN)
5369     oldy--;
5370
5371   *comes_from_x = oldx;
5372   *comes_from_y = oldy;
5373 }
5374
5375 static int MovingOrBlocked2Element(int x, int y)
5376 {
5377   int element = Tile[x][y];
5378
5379   if (element == EL_BLOCKED)
5380   {
5381     int oldx, oldy;
5382
5383     Blocked2Moving(x, y, &oldx, &oldy);
5384     return Tile[oldx][oldy];
5385   }
5386   else
5387     return element;
5388 }
5389
5390 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5391 {
5392   // like MovingOrBlocked2Element(), but if element is moving
5393   // and (x,y) is the field the moving element is just leaving,
5394   // return EL_BLOCKED instead of the element value
5395   int element = Tile[x][y];
5396
5397   if (IS_MOVING(x, y))
5398   {
5399     if (element == EL_BLOCKED)
5400     {
5401       int oldx, oldy;
5402
5403       Blocked2Moving(x, y, &oldx, &oldy);
5404       return Tile[oldx][oldy];
5405     }
5406     else
5407       return EL_BLOCKED;
5408   }
5409   else
5410     return element;
5411 }
5412
5413 static void RemoveField(int x, int y)
5414 {
5415   Tile[x][y] = EL_EMPTY;
5416
5417   MovPos[x][y] = 0;
5418   MovDir[x][y] = 0;
5419   MovDelay[x][y] = 0;
5420
5421   CustomValue[x][y] = 0;
5422
5423   AmoebaNr[x][y] = 0;
5424   ChangeDelay[x][y] = 0;
5425   ChangePage[x][y] = -1;
5426   Pushed[x][y] = FALSE;
5427
5428   GfxElement[x][y] = EL_UNDEFINED;
5429   GfxAction[x][y] = ACTION_DEFAULT;
5430   GfxDir[x][y] = MV_NONE;
5431 }
5432
5433 static void RemoveMovingField(int x, int y)
5434 {
5435   int oldx = x, oldy = y, newx = x, newy = y;
5436   int element = Tile[x][y];
5437   int next_element = EL_UNDEFINED;
5438
5439   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5440     return;
5441
5442   if (IS_MOVING(x, y))
5443   {
5444     Moving2Blocked(x, y, &newx, &newy);
5445
5446     if (Tile[newx][newy] != EL_BLOCKED)
5447     {
5448       // element is moving, but target field is not free (blocked), but
5449       // already occupied by something different (example: acid pool);
5450       // in this case, only remove the moving field, but not the target
5451
5452       RemoveField(oldx, oldy);
5453
5454       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5455
5456       TEST_DrawLevelField(oldx, oldy);
5457
5458       return;
5459     }
5460   }
5461   else if (element == EL_BLOCKED)
5462   {
5463     Blocked2Moving(x, y, &oldx, &oldy);
5464     if (!IS_MOVING(oldx, oldy))
5465       return;
5466   }
5467
5468   if (element == EL_BLOCKED &&
5469       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5470        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5471        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5472        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5473        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5474        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5475     next_element = get_next_element(Tile[oldx][oldy]);
5476
5477   RemoveField(oldx, oldy);
5478   RemoveField(newx, newy);
5479
5480   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5481
5482   if (next_element != EL_UNDEFINED)
5483     Tile[oldx][oldy] = next_element;
5484
5485   TEST_DrawLevelField(oldx, oldy);
5486   TEST_DrawLevelField(newx, newy);
5487 }
5488
5489 void DrawDynamite(int x, int y)
5490 {
5491   int sx = SCREENX(x), sy = SCREENY(y);
5492   int graphic = el2img(Tile[x][y]);
5493   int frame;
5494
5495   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5496     return;
5497
5498   if (IS_WALKABLE_INSIDE(Back[x][y]))
5499     return;
5500
5501   if (Back[x][y])
5502     DrawLevelElement(x, y, Back[x][y]);
5503   else if (Store[x][y])
5504     DrawLevelElement(x, y, Store[x][y]);
5505   else if (game.use_masked_elements)
5506     DrawLevelElement(x, y, EL_EMPTY);
5507
5508   frame = getGraphicAnimationFrameXY(graphic, x, y);
5509
5510   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5511     DrawGraphicThruMask(sx, sy, graphic, frame);
5512   else
5513     DrawGraphic(sx, sy, graphic, frame);
5514 }
5515
5516 static void CheckDynamite(int x, int y)
5517 {
5518   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5519   {
5520     MovDelay[x][y]--;
5521
5522     if (MovDelay[x][y] != 0)
5523     {
5524       DrawDynamite(x, y);
5525       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5526
5527       return;
5528     }
5529   }
5530
5531   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5532
5533   Bang(x, y);
5534 }
5535
5536 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5537 {
5538   boolean num_checked_players = 0;
5539   int i;
5540
5541   for (i = 0; i < MAX_PLAYERS; i++)
5542   {
5543     if (stored_player[i].active)
5544     {
5545       int sx = stored_player[i].jx;
5546       int sy = stored_player[i].jy;
5547
5548       if (num_checked_players == 0)
5549       {
5550         *sx1 = *sx2 = sx;
5551         *sy1 = *sy2 = sy;
5552       }
5553       else
5554       {
5555         *sx1 = MIN(*sx1, sx);
5556         *sy1 = MIN(*sy1, sy);
5557         *sx2 = MAX(*sx2, sx);
5558         *sy2 = MAX(*sy2, sy);
5559       }
5560
5561       num_checked_players++;
5562     }
5563   }
5564 }
5565
5566 static boolean checkIfAllPlayersFitToScreen_RND(void)
5567 {
5568   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5569
5570   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5571
5572   return (sx2 - sx1 < SCR_FIELDX &&
5573           sy2 - sy1 < SCR_FIELDY);
5574 }
5575
5576 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5577 {
5578   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5579
5580   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5581
5582   *sx = (sx1 + sx2) / 2;
5583   *sy = (sy1 + sy2) / 2;
5584 }
5585
5586 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5587                                boolean center_screen, boolean quick_relocation)
5588 {
5589   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5590   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5591   boolean no_delay = (tape.warp_forward);
5592   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5593   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5594   int new_scroll_x, new_scroll_y;
5595
5596   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5597   {
5598     // case 1: quick relocation inside visible screen (without scrolling)
5599
5600     RedrawPlayfield();
5601
5602     return;
5603   }
5604
5605   if (!level.shifted_relocation || center_screen)
5606   {
5607     // relocation _with_ centering of screen
5608
5609     new_scroll_x = SCROLL_POSITION_X(x);
5610     new_scroll_y = SCROLL_POSITION_Y(y);
5611   }
5612   else
5613   {
5614     // relocation _without_ centering of screen
5615
5616     int center_scroll_x = SCROLL_POSITION_X(old_x);
5617     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5618     int offset_x = x + (scroll_x - center_scroll_x);
5619     int offset_y = y + (scroll_y - center_scroll_y);
5620
5621     // for new screen position, apply previous offset to center position
5622     new_scroll_x = SCROLL_POSITION_X(offset_x);
5623     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5624   }
5625
5626   if (quick_relocation)
5627   {
5628     // case 2: quick relocation (redraw without visible scrolling)
5629
5630     scroll_x = new_scroll_x;
5631     scroll_y = new_scroll_y;
5632
5633     RedrawPlayfield();
5634
5635     return;
5636   }
5637
5638   // case 3: visible relocation (with scrolling to new position)
5639
5640   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5641
5642   SetVideoFrameDelay(wait_delay_value);
5643
5644   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5645   {
5646     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5647     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5648
5649     if (dx == 0 && dy == 0)             // no scrolling needed at all
5650       break;
5651
5652     scroll_x -= dx;
5653     scroll_y -= dy;
5654
5655     // set values for horizontal/vertical screen scrolling (half tile size)
5656     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5657     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5658     int pos_x = dx * TILEX / 2;
5659     int pos_y = dy * TILEY / 2;
5660     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5661     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5662
5663     ScrollLevel(dx, dy);
5664     DrawAllPlayers();
5665
5666     // scroll in two steps of half tile size to make things smoother
5667     BlitScreenToBitmapExt_RND(window, fx, fy);
5668
5669     // scroll second step to align at full tile size
5670     BlitScreenToBitmap(window);
5671   }
5672
5673   DrawAllPlayers();
5674   BackToFront();
5675
5676   SetVideoFrameDelay(frame_delay_value_old);
5677 }
5678
5679 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5680 {
5681   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5682   int player_nr = GET_PLAYER_NR(el_player);
5683   struct PlayerInfo *player = &stored_player[player_nr];
5684   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5685   boolean no_delay = (tape.warp_forward);
5686   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5687   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5688   int old_jx = player->jx;
5689   int old_jy = player->jy;
5690   int old_element = Tile[old_jx][old_jy];
5691   int element = Tile[jx][jy];
5692   boolean player_relocated = (old_jx != jx || old_jy != jy);
5693
5694   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5695   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5696   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5697   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5698   int leave_side_horiz = move_dir_horiz;
5699   int leave_side_vert  = move_dir_vert;
5700   int enter_side = enter_side_horiz | enter_side_vert;
5701   int leave_side = leave_side_horiz | leave_side_vert;
5702
5703   if (player->buried)           // do not reanimate dead player
5704     return;
5705
5706   if (!player_relocated)        // no need to relocate the player
5707     return;
5708
5709   if (IS_PLAYER(jx, jy))        // player already placed at new position
5710   {
5711     RemoveField(jx, jy);        // temporarily remove newly placed player
5712     DrawLevelField(jx, jy);
5713   }
5714
5715   if (player->present)
5716   {
5717     while (player->MovPos)
5718     {
5719       ScrollPlayer(player, SCROLL_GO_ON);
5720       ScrollScreen(NULL, SCROLL_GO_ON);
5721
5722       AdvanceFrameAndPlayerCounters(player->index_nr);
5723
5724       DrawPlayer(player);
5725
5726       BackToFront_WithFrameDelay(wait_delay_value);
5727     }
5728
5729     DrawPlayer(player);         // needed here only to cleanup last field
5730     DrawLevelField(player->jx, player->jy);     // remove player graphic
5731
5732     player->is_moving = FALSE;
5733   }
5734
5735   if (IS_CUSTOM_ELEMENT(old_element))
5736     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5737                                CE_LEFT_BY_PLAYER,
5738                                player->index_bit, leave_side);
5739
5740   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5741                                       CE_PLAYER_LEAVES_X,
5742                                       player->index_bit, leave_side);
5743
5744   Tile[jx][jy] = el_player;
5745   InitPlayerField(jx, jy, el_player, TRUE);
5746
5747   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5748      possible that the relocation target field did not contain a player element,
5749      but a walkable element, to which the new player was relocated -- in this
5750      case, restore that (already initialized!) element on the player field */
5751   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5752   {
5753     Tile[jx][jy] = element;     // restore previously existing element
5754   }
5755
5756   // only visually relocate centered player
5757   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5758                      FALSE, level.instant_relocation);
5759
5760   TestIfPlayerTouchesBadThing(jx, jy);
5761   TestIfPlayerTouchesCustomElement(jx, jy);
5762
5763   if (IS_CUSTOM_ELEMENT(element))
5764     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5765                                player->index_bit, enter_side);
5766
5767   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5768                                       player->index_bit, enter_side);
5769
5770   if (player->is_switching)
5771   {
5772     /* ensure that relocation while still switching an element does not cause
5773        a new element to be treated as also switched directly after relocation
5774        (this is important for teleporter switches that teleport the player to
5775        a place where another teleporter switch is in the same direction, which
5776        would then incorrectly be treated as immediately switched before the
5777        direction key that caused the switch was released) */
5778
5779     player->switch_x += jx - old_jx;
5780     player->switch_y += jy - old_jy;
5781   }
5782 }
5783
5784 static void Explode(int ex, int ey, int phase, int mode)
5785 {
5786   int x, y;
5787   int last_phase;
5788   int border_element;
5789
5790   // !!! eliminate this variable !!!
5791   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5792
5793   if (game.explosions_delayed)
5794   {
5795     ExplodeField[ex][ey] = mode;
5796     return;
5797   }
5798
5799   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5800   {
5801     int center_element = Tile[ex][ey];
5802     int artwork_element, explosion_element;     // set these values later
5803
5804     // remove things displayed in background while burning dynamite
5805     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5806       Back[ex][ey] = 0;
5807
5808     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5809     {
5810       // put moving element to center field (and let it explode there)
5811       center_element = MovingOrBlocked2Element(ex, ey);
5812       RemoveMovingField(ex, ey);
5813       Tile[ex][ey] = center_element;
5814     }
5815
5816     // now "center_element" is finally determined -- set related values now
5817     artwork_element = center_element;           // for custom player artwork
5818     explosion_element = center_element;         // for custom player artwork
5819
5820     if (IS_PLAYER(ex, ey))
5821     {
5822       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5823
5824       artwork_element = stored_player[player_nr].artwork_element;
5825
5826       if (level.use_explosion_element[player_nr])
5827       {
5828         explosion_element = level.explosion_element[player_nr];
5829         artwork_element = explosion_element;
5830       }
5831     }
5832
5833     if (mode == EX_TYPE_NORMAL ||
5834         mode == EX_TYPE_CENTER ||
5835         mode == EX_TYPE_CROSS)
5836       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5837
5838     last_phase = element_info[explosion_element].explosion_delay + 1;
5839
5840     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5841     {
5842       int xx = x - ex + 1;
5843       int yy = y - ey + 1;
5844       int element;
5845
5846       if (!IN_LEV_FIELD(x, y) ||
5847           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5848           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5849         continue;
5850
5851       element = Tile[x][y];
5852
5853       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5854       {
5855         element = MovingOrBlocked2Element(x, y);
5856
5857         if (!IS_EXPLOSION_PROOF(element))
5858           RemoveMovingField(x, y);
5859       }
5860
5861       // indestructible elements can only explode in center (but not flames)
5862       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5863                                            mode == EX_TYPE_BORDER)) ||
5864           element == EL_FLAMES)
5865         continue;
5866
5867       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5868          behaviour, for example when touching a yamyam that explodes to rocks
5869          with active deadly shield, a rock is created under the player !!! */
5870       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5871 #if 0
5872       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5873           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5874            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5875 #else
5876       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5877 #endif
5878       {
5879         if (IS_ACTIVE_BOMB(element))
5880         {
5881           // re-activate things under the bomb like gate or penguin
5882           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5883           Back[x][y] = 0;
5884         }
5885
5886         continue;
5887       }
5888
5889       // save walkable background elements while explosion on same tile
5890       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5891           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5892         Back[x][y] = element;
5893
5894       // ignite explodable elements reached by other explosion
5895       if (element == EL_EXPLOSION)
5896         element = Store2[x][y];
5897
5898       if (AmoebaNr[x][y] &&
5899           (element == EL_AMOEBA_FULL ||
5900            element == EL_BD_AMOEBA ||
5901            element == EL_AMOEBA_GROWING))
5902       {
5903         AmoebaCnt[AmoebaNr[x][y]]--;
5904         AmoebaCnt2[AmoebaNr[x][y]]--;
5905       }
5906
5907       RemoveField(x, y);
5908
5909       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5910       {
5911         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5912
5913         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5914
5915         if (PLAYERINFO(ex, ey)->use_murphy)
5916           Store[x][y] = EL_EMPTY;
5917       }
5918
5919       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5920       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5921       else if (IS_PLAYER_ELEMENT(center_element))
5922         Store[x][y] = EL_EMPTY;
5923       else if (center_element == EL_YAMYAM)
5924         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5925       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5926         Store[x][y] = element_info[center_element].content.e[xx][yy];
5927 #if 1
5928       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5929       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5930       // otherwise) -- FIX THIS !!!
5931       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5932         Store[x][y] = element_info[element].content.e[1][1];
5933 #else
5934       else if (!CAN_EXPLODE(element))
5935         Store[x][y] = element_info[element].content.e[1][1];
5936 #endif
5937       else
5938         Store[x][y] = EL_EMPTY;
5939
5940       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5941           center_element == EL_AMOEBA_TO_DIAMOND)
5942         Store2[x][y] = element;
5943
5944       Tile[x][y] = EL_EXPLOSION;
5945       GfxElement[x][y] = artwork_element;
5946
5947       ExplodePhase[x][y] = 1;
5948       ExplodeDelay[x][y] = last_phase;
5949
5950       Stop[x][y] = TRUE;
5951     }
5952
5953     if (center_element == EL_YAMYAM)
5954       game.yamyam_content_nr =
5955         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5956
5957     return;
5958   }
5959
5960   if (Stop[ex][ey])
5961     return;
5962
5963   x = ex;
5964   y = ey;
5965
5966   if (phase == 1)
5967     GfxFrame[x][y] = 0;         // restart explosion animation
5968
5969   last_phase = ExplodeDelay[x][y];
5970
5971   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5972
5973   // this can happen if the player leaves an explosion just in time
5974   if (GfxElement[x][y] == EL_UNDEFINED)
5975     GfxElement[x][y] = EL_EMPTY;
5976
5977   border_element = Store2[x][y];
5978   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5979     border_element = StorePlayer[x][y];
5980
5981   if (phase == element_info[border_element].ignition_delay ||
5982       phase == last_phase)
5983   {
5984     boolean border_explosion = FALSE;
5985
5986     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5987         !PLAYER_EXPLOSION_PROTECTED(x, y))
5988     {
5989       KillPlayerUnlessExplosionProtected(x, y);
5990       border_explosion = TRUE;
5991     }
5992     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5993     {
5994       Tile[x][y] = Store2[x][y];
5995       Store2[x][y] = 0;
5996       Bang(x, y);
5997       border_explosion = TRUE;
5998     }
5999     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6000     {
6001       AmoebaToDiamond(x, y);
6002       Store2[x][y] = 0;
6003       border_explosion = TRUE;
6004     }
6005
6006     // if an element just explodes due to another explosion (chain-reaction),
6007     // do not immediately end the new explosion when it was the last frame of
6008     // the explosion (as it would be done in the following "if"-statement!)
6009     if (border_explosion && phase == last_phase)
6010       return;
6011   }
6012
6013   // this can happen if the player was just killed by an explosion
6014   if (GfxElement[x][y] == EL_UNDEFINED)
6015     GfxElement[x][y] = EL_EMPTY;
6016
6017   if (phase == last_phase)
6018   {
6019     int element;
6020
6021     element = Tile[x][y] = Store[x][y];
6022     Store[x][y] = Store2[x][y] = 0;
6023     GfxElement[x][y] = EL_UNDEFINED;
6024
6025     // player can escape from explosions and might therefore be still alive
6026     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6027         element <= EL_PLAYER_IS_EXPLODING_4)
6028     {
6029       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6030       int explosion_element = EL_PLAYER_1 + player_nr;
6031       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6032       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6033
6034       if (level.use_explosion_element[player_nr])
6035         explosion_element = level.explosion_element[player_nr];
6036
6037       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6038                     element_info[explosion_element].content.e[xx][yy]);
6039     }
6040
6041     // restore probably existing indestructible background element
6042     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6043       element = Tile[x][y] = Back[x][y];
6044     Back[x][y] = 0;
6045
6046     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6047     GfxDir[x][y] = MV_NONE;
6048     ChangeDelay[x][y] = 0;
6049     ChangePage[x][y] = -1;
6050
6051     CustomValue[x][y] = 0;
6052
6053     InitField_WithBug2(x, y, FALSE);
6054
6055     TEST_DrawLevelField(x, y);
6056
6057     TestIfElementTouchesCustomElement(x, y);
6058
6059     if (GFX_CRUMBLED(element))
6060       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6061
6062     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6063       StorePlayer[x][y] = 0;
6064
6065     if (IS_PLAYER_ELEMENT(element))
6066       RelocatePlayer(x, y, element);
6067   }
6068   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6069   {
6070     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6071     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6072
6073     if (phase == delay)
6074       TEST_DrawLevelFieldCrumbled(x, y);
6075
6076     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6077     {
6078       DrawLevelElement(x, y, Back[x][y]);
6079       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6080     }
6081     else if (IS_WALKABLE_UNDER(Back[x][y]))
6082     {
6083       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6084       DrawLevelElementThruMask(x, y, Back[x][y]);
6085     }
6086     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6087       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6088   }
6089 }
6090
6091 static void DynaExplode(int ex, int ey)
6092 {
6093   int i, j;
6094   int dynabomb_element = Tile[ex][ey];
6095   int dynabomb_size = 1;
6096   boolean dynabomb_xl = FALSE;
6097   struct PlayerInfo *player;
6098   static int xy[4][2] =
6099   {
6100     { 0, -1 },
6101     { -1, 0 },
6102     { +1, 0 },
6103     { 0, +1 }
6104   };
6105
6106   if (IS_ACTIVE_BOMB(dynabomb_element))
6107   {
6108     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6109     dynabomb_size = player->dynabomb_size;
6110     dynabomb_xl = player->dynabomb_xl;
6111     player->dynabombs_left++;
6112   }
6113
6114   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6115
6116   for (i = 0; i < NUM_DIRECTIONS; i++)
6117   {
6118     for (j = 1; j <= dynabomb_size; j++)
6119     {
6120       int x = ex + j * xy[i][0];
6121       int y = ey + j * xy[i][1];
6122       int element;
6123
6124       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6125         break;
6126
6127       element = Tile[x][y];
6128
6129       // do not restart explosions of fields with active bombs
6130       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6131         continue;
6132
6133       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6134
6135       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6136           !IS_DIGGABLE(element) && !dynabomb_xl)
6137         break;
6138     }
6139   }
6140 }
6141
6142 void Bang(int x, int y)
6143 {
6144   int element = MovingOrBlocked2Element(x, y);
6145   int explosion_type = EX_TYPE_NORMAL;
6146
6147   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6148   {
6149     struct PlayerInfo *player = PLAYERINFO(x, y);
6150
6151     element = Tile[x][y] = player->initial_element;
6152
6153     if (level.use_explosion_element[player->index_nr])
6154     {
6155       int explosion_element = level.explosion_element[player->index_nr];
6156
6157       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6158         explosion_type = EX_TYPE_CROSS;
6159       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6160         explosion_type = EX_TYPE_CENTER;
6161     }
6162   }
6163
6164   switch (element)
6165   {
6166     case EL_BUG:
6167     case EL_SPACESHIP:
6168     case EL_BD_BUTTERFLY:
6169     case EL_BD_FIREFLY:
6170     case EL_YAMYAM:
6171     case EL_DARK_YAMYAM:
6172     case EL_ROBOT:
6173     case EL_PACMAN:
6174     case EL_MOLE:
6175       RaiseScoreElement(element);
6176       break;
6177
6178     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6179     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6180     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6181     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6182     case EL_DYNABOMB_INCREASE_NUMBER:
6183     case EL_DYNABOMB_INCREASE_SIZE:
6184     case EL_DYNABOMB_INCREASE_POWER:
6185       explosion_type = EX_TYPE_DYNA;
6186       break;
6187
6188     case EL_DC_LANDMINE:
6189       explosion_type = EX_TYPE_CENTER;
6190       break;
6191
6192     case EL_PENGUIN:
6193     case EL_LAMP:
6194     case EL_LAMP_ACTIVE:
6195     case EL_AMOEBA_TO_DIAMOND:
6196       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6197         explosion_type = EX_TYPE_CENTER;
6198       break;
6199
6200     default:
6201       if (element_info[element].explosion_type == EXPLODES_CROSS)
6202         explosion_type = EX_TYPE_CROSS;
6203       else if (element_info[element].explosion_type == EXPLODES_1X1)
6204         explosion_type = EX_TYPE_CENTER;
6205       break;
6206   }
6207
6208   if (explosion_type == EX_TYPE_DYNA)
6209     DynaExplode(x, y);
6210   else
6211     Explode(x, y, EX_PHASE_START, explosion_type);
6212
6213   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6214 }
6215
6216 static void SplashAcid(int x, int y)
6217 {
6218   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6219       (!IN_LEV_FIELD(x - 1, y - 2) ||
6220        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6221     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6222
6223   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6224       (!IN_LEV_FIELD(x + 1, y - 2) ||
6225        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6226     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6227
6228   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6229 }
6230
6231 static void InitBeltMovement(void)
6232 {
6233   static int belt_base_element[4] =
6234   {
6235     EL_CONVEYOR_BELT_1_LEFT,
6236     EL_CONVEYOR_BELT_2_LEFT,
6237     EL_CONVEYOR_BELT_3_LEFT,
6238     EL_CONVEYOR_BELT_4_LEFT
6239   };
6240   static int belt_base_active_element[4] =
6241   {
6242     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6243     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6244     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6245     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6246   };
6247
6248   int x, y, i, j;
6249
6250   // set frame order for belt animation graphic according to belt direction
6251   for (i = 0; i < NUM_BELTS; i++)
6252   {
6253     int belt_nr = i;
6254
6255     for (j = 0; j < NUM_BELT_PARTS; j++)
6256     {
6257       int element = belt_base_active_element[belt_nr] + j;
6258       int graphic_1 = el2img(element);
6259       int graphic_2 = el2panelimg(element);
6260
6261       if (game.belt_dir[i] == MV_LEFT)
6262       {
6263         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6264         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6265       }
6266       else
6267       {
6268         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6269         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6270       }
6271     }
6272   }
6273
6274   SCAN_PLAYFIELD(x, y)
6275   {
6276     int element = Tile[x][y];
6277
6278     for (i = 0; i < NUM_BELTS; i++)
6279     {
6280       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6281       {
6282         int e_belt_nr = getBeltNrFromBeltElement(element);
6283         int belt_nr = i;
6284
6285         if (e_belt_nr == belt_nr)
6286         {
6287           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6288
6289           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6290         }
6291       }
6292     }
6293   }
6294 }
6295
6296 static void ToggleBeltSwitch(int x, int y)
6297 {
6298   static int belt_base_element[4] =
6299   {
6300     EL_CONVEYOR_BELT_1_LEFT,
6301     EL_CONVEYOR_BELT_2_LEFT,
6302     EL_CONVEYOR_BELT_3_LEFT,
6303     EL_CONVEYOR_BELT_4_LEFT
6304   };
6305   static int belt_base_active_element[4] =
6306   {
6307     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6308     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6309     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6310     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6311   };
6312   static int belt_base_switch_element[4] =
6313   {
6314     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6315     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6316     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6317     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6318   };
6319   static int belt_move_dir[4] =
6320   {
6321     MV_LEFT,
6322     MV_NONE,
6323     MV_RIGHT,
6324     MV_NONE,
6325   };
6326
6327   int element = Tile[x][y];
6328   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6329   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6330   int belt_dir = belt_move_dir[belt_dir_nr];
6331   int xx, yy, i;
6332
6333   if (!IS_BELT_SWITCH(element))
6334     return;
6335
6336   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6337   game.belt_dir[belt_nr] = belt_dir;
6338
6339   if (belt_dir_nr == 3)
6340     belt_dir_nr = 1;
6341
6342   // set frame order for belt animation graphic according to belt direction
6343   for (i = 0; i < NUM_BELT_PARTS; i++)
6344   {
6345     int element = belt_base_active_element[belt_nr] + i;
6346     int graphic_1 = el2img(element);
6347     int graphic_2 = el2panelimg(element);
6348
6349     if (belt_dir == MV_LEFT)
6350     {
6351       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6352       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6353     }
6354     else
6355     {
6356       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6357       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6358     }
6359   }
6360
6361   SCAN_PLAYFIELD(xx, yy)
6362   {
6363     int element = Tile[xx][yy];
6364
6365     if (IS_BELT_SWITCH(element))
6366     {
6367       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6368
6369       if (e_belt_nr == belt_nr)
6370       {
6371         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6372         TEST_DrawLevelField(xx, yy);
6373       }
6374     }
6375     else if (IS_BELT(element) && belt_dir != MV_NONE)
6376     {
6377       int e_belt_nr = getBeltNrFromBeltElement(element);
6378
6379       if (e_belt_nr == belt_nr)
6380       {
6381         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6382
6383         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6384         TEST_DrawLevelField(xx, yy);
6385       }
6386     }
6387     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6388     {
6389       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6390
6391       if (e_belt_nr == belt_nr)
6392       {
6393         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6394
6395         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6396         TEST_DrawLevelField(xx, yy);
6397       }
6398     }
6399   }
6400 }
6401
6402 static void ToggleSwitchgateSwitch(int x, int y)
6403 {
6404   int xx, yy;
6405
6406   game.switchgate_pos = !game.switchgate_pos;
6407
6408   SCAN_PLAYFIELD(xx, yy)
6409   {
6410     int element = Tile[xx][yy];
6411
6412     if (element == EL_SWITCHGATE_SWITCH_UP)
6413     {
6414       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6415       TEST_DrawLevelField(xx, yy);
6416     }
6417     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6418     {
6419       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6420       TEST_DrawLevelField(xx, yy);
6421     }
6422     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6423     {
6424       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6425       TEST_DrawLevelField(xx, yy);
6426     }
6427     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6428     {
6429       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6430       TEST_DrawLevelField(xx, yy);
6431     }
6432     else if (element == EL_SWITCHGATE_OPEN ||
6433              element == EL_SWITCHGATE_OPENING)
6434     {
6435       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6436
6437       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6438     }
6439     else if (element == EL_SWITCHGATE_CLOSED ||
6440              element == EL_SWITCHGATE_CLOSING)
6441     {
6442       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6443
6444       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6445     }
6446   }
6447 }
6448
6449 static int getInvisibleActiveFromInvisibleElement(int element)
6450 {
6451   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6452           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6453           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6454           element);
6455 }
6456
6457 static int getInvisibleFromInvisibleActiveElement(int element)
6458 {
6459   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6460           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6461           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6462           element);
6463 }
6464
6465 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6466 {
6467   int x, y;
6468
6469   SCAN_PLAYFIELD(x, y)
6470   {
6471     int element = Tile[x][y];
6472
6473     if (element == EL_LIGHT_SWITCH &&
6474         game.light_time_left > 0)
6475     {
6476       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6477       TEST_DrawLevelField(x, y);
6478     }
6479     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6480              game.light_time_left == 0)
6481     {
6482       Tile[x][y] = EL_LIGHT_SWITCH;
6483       TEST_DrawLevelField(x, y);
6484     }
6485     else if (element == EL_EMC_DRIPPER &&
6486              game.light_time_left > 0)
6487     {
6488       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6489       TEST_DrawLevelField(x, y);
6490     }
6491     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6492              game.light_time_left == 0)
6493     {
6494       Tile[x][y] = EL_EMC_DRIPPER;
6495       TEST_DrawLevelField(x, y);
6496     }
6497     else if (element == EL_INVISIBLE_STEELWALL ||
6498              element == EL_INVISIBLE_WALL ||
6499              element == EL_INVISIBLE_SAND)
6500     {
6501       if (game.light_time_left > 0)
6502         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6503
6504       TEST_DrawLevelField(x, y);
6505
6506       // uncrumble neighbour fields, if needed
6507       if (element == EL_INVISIBLE_SAND)
6508         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6509     }
6510     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6511              element == EL_INVISIBLE_WALL_ACTIVE ||
6512              element == EL_INVISIBLE_SAND_ACTIVE)
6513     {
6514       if (game.light_time_left == 0)
6515         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6516
6517       TEST_DrawLevelField(x, y);
6518
6519       // re-crumble neighbour fields, if needed
6520       if (element == EL_INVISIBLE_SAND)
6521         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6522     }
6523   }
6524 }
6525
6526 static void RedrawAllInvisibleElementsForLenses(void)
6527 {
6528   int x, y;
6529
6530   SCAN_PLAYFIELD(x, y)
6531   {
6532     int element = Tile[x][y];
6533
6534     if (element == EL_EMC_DRIPPER &&
6535         game.lenses_time_left > 0)
6536     {
6537       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6538       TEST_DrawLevelField(x, y);
6539     }
6540     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6541              game.lenses_time_left == 0)
6542     {
6543       Tile[x][y] = EL_EMC_DRIPPER;
6544       TEST_DrawLevelField(x, y);
6545     }
6546     else if (element == EL_INVISIBLE_STEELWALL ||
6547              element == EL_INVISIBLE_WALL ||
6548              element == EL_INVISIBLE_SAND)
6549     {
6550       if (game.lenses_time_left > 0)
6551         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6552
6553       TEST_DrawLevelField(x, y);
6554
6555       // uncrumble neighbour fields, if needed
6556       if (element == EL_INVISIBLE_SAND)
6557         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6558     }
6559     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6560              element == EL_INVISIBLE_WALL_ACTIVE ||
6561              element == EL_INVISIBLE_SAND_ACTIVE)
6562     {
6563       if (game.lenses_time_left == 0)
6564         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6565
6566       TEST_DrawLevelField(x, y);
6567
6568       // re-crumble neighbour fields, if needed
6569       if (element == EL_INVISIBLE_SAND)
6570         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6571     }
6572   }
6573 }
6574
6575 static void RedrawAllInvisibleElementsForMagnifier(void)
6576 {
6577   int x, y;
6578
6579   SCAN_PLAYFIELD(x, y)
6580   {
6581     int element = Tile[x][y];
6582
6583     if (element == EL_EMC_FAKE_GRASS &&
6584         game.magnify_time_left > 0)
6585     {
6586       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6587       TEST_DrawLevelField(x, y);
6588     }
6589     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6590              game.magnify_time_left == 0)
6591     {
6592       Tile[x][y] = EL_EMC_FAKE_GRASS;
6593       TEST_DrawLevelField(x, y);
6594     }
6595     else if (IS_GATE_GRAY(element) &&
6596              game.magnify_time_left > 0)
6597     {
6598       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6599                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6600                     IS_EM_GATE_GRAY(element) ?
6601                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6602                     IS_EMC_GATE_GRAY(element) ?
6603                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6604                     IS_DC_GATE_GRAY(element) ?
6605                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6606                     element);
6607       TEST_DrawLevelField(x, y);
6608     }
6609     else if (IS_GATE_GRAY_ACTIVE(element) &&
6610              game.magnify_time_left == 0)
6611     {
6612       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6613                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6614                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6615                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6616                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6617                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6618                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6619                     EL_DC_GATE_WHITE_GRAY :
6620                     element);
6621       TEST_DrawLevelField(x, y);
6622     }
6623   }
6624 }
6625
6626 static void ToggleLightSwitch(int x, int y)
6627 {
6628   int element = Tile[x][y];
6629
6630   game.light_time_left =
6631     (element == EL_LIGHT_SWITCH ?
6632      level.time_light * FRAMES_PER_SECOND : 0);
6633
6634   RedrawAllLightSwitchesAndInvisibleElements();
6635 }
6636
6637 static void ActivateTimegateSwitch(int x, int y)
6638 {
6639   int xx, yy;
6640
6641   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6642
6643   SCAN_PLAYFIELD(xx, yy)
6644   {
6645     int element = Tile[xx][yy];
6646
6647     if (element == EL_TIMEGATE_CLOSED ||
6648         element == EL_TIMEGATE_CLOSING)
6649     {
6650       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6651       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6652     }
6653
6654     /*
6655     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6656     {
6657       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6658       TEST_DrawLevelField(xx, yy);
6659     }
6660     */
6661
6662   }
6663
6664   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6665                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6666 }
6667
6668 static void Impact(int x, int y)
6669 {
6670   boolean last_line = (y == lev_fieldy - 1);
6671   boolean object_hit = FALSE;
6672   boolean impact = (last_line || object_hit);
6673   int element = Tile[x][y];
6674   int smashed = EL_STEELWALL;
6675
6676   if (!last_line)       // check if element below was hit
6677   {
6678     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6679       return;
6680
6681     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6682                                          MovDir[x][y + 1] != MV_DOWN ||
6683                                          MovPos[x][y + 1] <= TILEY / 2));
6684
6685     // do not smash moving elements that left the smashed field in time
6686     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6687         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6688       object_hit = FALSE;
6689
6690 #if USE_QUICKSAND_IMPACT_BUGFIX
6691     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6692     {
6693       RemoveMovingField(x, y + 1);
6694       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6695       Tile[x][y + 2] = EL_ROCK;
6696       TEST_DrawLevelField(x, y + 2);
6697
6698       object_hit = TRUE;
6699     }
6700
6701     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6702     {
6703       RemoveMovingField(x, y + 1);
6704       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6705       Tile[x][y + 2] = EL_ROCK;
6706       TEST_DrawLevelField(x, y + 2);
6707
6708       object_hit = TRUE;
6709     }
6710 #endif
6711
6712     if (object_hit)
6713       smashed = MovingOrBlocked2Element(x, y + 1);
6714
6715     impact = (last_line || object_hit);
6716   }
6717
6718   if (!last_line && smashed == EL_ACID) // element falls into acid
6719   {
6720     SplashAcid(x, y + 1);
6721     return;
6722   }
6723
6724   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6725   // only reset graphic animation if graphic really changes after impact
6726   if (impact &&
6727       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6728   {
6729     ResetGfxAnimation(x, y);
6730     TEST_DrawLevelField(x, y);
6731   }
6732
6733   if (impact && CAN_EXPLODE_IMPACT(element))
6734   {
6735     Bang(x, y);
6736     return;
6737   }
6738   else if (impact && element == EL_PEARL &&
6739            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6740   {
6741     ResetGfxAnimation(x, y);
6742
6743     Tile[x][y] = EL_PEARL_BREAKING;
6744     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6745     return;
6746   }
6747   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6748   {
6749     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6750
6751     return;
6752   }
6753
6754   if (impact && element == EL_AMOEBA_DROP)
6755   {
6756     if (object_hit && IS_PLAYER(x, y + 1))
6757       KillPlayerUnlessEnemyProtected(x, y + 1);
6758     else if (object_hit && smashed == EL_PENGUIN)
6759       Bang(x, y + 1);
6760     else
6761     {
6762       Tile[x][y] = EL_AMOEBA_GROWING;
6763       Store[x][y] = EL_AMOEBA_WET;
6764
6765       ResetRandomAnimationValue(x, y);
6766     }
6767     return;
6768   }
6769
6770   if (object_hit)               // check which object was hit
6771   {
6772     if ((CAN_PASS_MAGIC_WALL(element) && 
6773          (smashed == EL_MAGIC_WALL ||
6774           smashed == EL_BD_MAGIC_WALL)) ||
6775         (CAN_PASS_DC_MAGIC_WALL(element) &&
6776          smashed == EL_DC_MAGIC_WALL))
6777     {
6778       int xx, yy;
6779       int activated_magic_wall =
6780         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6781          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6782          EL_DC_MAGIC_WALL_ACTIVE);
6783
6784       // activate magic wall / mill
6785       SCAN_PLAYFIELD(xx, yy)
6786       {
6787         if (Tile[xx][yy] == smashed)
6788           Tile[xx][yy] = activated_magic_wall;
6789       }
6790
6791       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6792       game.magic_wall_active = TRUE;
6793
6794       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6795                             SND_MAGIC_WALL_ACTIVATING :
6796                             smashed == EL_BD_MAGIC_WALL ?
6797                             SND_BD_MAGIC_WALL_ACTIVATING :
6798                             SND_DC_MAGIC_WALL_ACTIVATING));
6799     }
6800
6801     if (IS_PLAYER(x, y + 1))
6802     {
6803       if (CAN_SMASH_PLAYER(element))
6804       {
6805         KillPlayerUnlessEnemyProtected(x, y + 1);
6806         return;
6807       }
6808     }
6809     else if (smashed == EL_PENGUIN)
6810     {
6811       if (CAN_SMASH_PLAYER(element))
6812       {
6813         Bang(x, y + 1);
6814         return;
6815       }
6816     }
6817     else if (element == EL_BD_DIAMOND)
6818     {
6819       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6820       {
6821         Bang(x, y + 1);
6822         return;
6823       }
6824     }
6825     else if (((element == EL_SP_INFOTRON ||
6826                element == EL_SP_ZONK) &&
6827               (smashed == EL_SP_SNIKSNAK ||
6828                smashed == EL_SP_ELECTRON ||
6829                smashed == EL_SP_DISK_ORANGE)) ||
6830              (element == EL_SP_INFOTRON &&
6831               smashed == EL_SP_DISK_YELLOW))
6832     {
6833       Bang(x, y + 1);
6834       return;
6835     }
6836     else if (CAN_SMASH_EVERYTHING(element))
6837     {
6838       if (IS_CLASSIC_ENEMY(smashed) ||
6839           CAN_EXPLODE_SMASHED(smashed))
6840       {
6841         Bang(x, y + 1);
6842         return;
6843       }
6844       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6845       {
6846         if (smashed == EL_LAMP ||
6847             smashed == EL_LAMP_ACTIVE)
6848         {
6849           Bang(x, y + 1);
6850           return;
6851         }
6852         else if (smashed == EL_NUT)
6853         {
6854           Tile[x][y + 1] = EL_NUT_BREAKING;
6855           PlayLevelSound(x, y, SND_NUT_BREAKING);
6856           RaiseScoreElement(EL_NUT);
6857           return;
6858         }
6859         else if (smashed == EL_PEARL)
6860         {
6861           ResetGfxAnimation(x, y);
6862
6863           Tile[x][y + 1] = EL_PEARL_BREAKING;
6864           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6865           return;
6866         }
6867         else if (smashed == EL_DIAMOND)
6868         {
6869           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6870           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6871           return;
6872         }
6873         else if (IS_BELT_SWITCH(smashed))
6874         {
6875           ToggleBeltSwitch(x, y + 1);
6876         }
6877         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6878                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6879                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6880                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6881         {
6882           ToggleSwitchgateSwitch(x, y + 1);
6883         }
6884         else if (smashed == EL_LIGHT_SWITCH ||
6885                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6886         {
6887           ToggleLightSwitch(x, y + 1);
6888         }
6889         else
6890         {
6891           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6892
6893           CheckElementChangeBySide(x, y + 1, smashed, element,
6894                                    CE_SWITCHED, CH_SIDE_TOP);
6895           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6896                                             CH_SIDE_TOP);
6897         }
6898       }
6899       else
6900       {
6901         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6902       }
6903     }
6904   }
6905
6906   // play sound of magic wall / mill
6907   if (!last_line &&
6908       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6911   {
6912     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6918
6919     return;
6920   }
6921
6922   // play sound of object that hits the ground
6923   if (last_line || object_hit)
6924     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6925 }
6926
6927 static void TurnRoundExt(int x, int y)
6928 {
6929   static struct
6930   {
6931     int dx, dy;
6932   } move_xy[] =
6933   {
6934     {  0,  0 },
6935     { -1,  0 },
6936     { +1,  0 },
6937     {  0,  0 },
6938     {  0, -1 },
6939     {  0,  0 }, { 0, 0 }, { 0, 0 },
6940     {  0, +1 }
6941   };
6942   static struct
6943   {
6944     int left, right, back;
6945   } turn[] =
6946   {
6947     { 0,        0,              0        },
6948     { MV_DOWN,  MV_UP,          MV_RIGHT },
6949     { MV_UP,    MV_DOWN,        MV_LEFT  },
6950     { 0,        0,              0        },
6951     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6952     { 0,        0,              0        },
6953     { 0,        0,              0        },
6954     { 0,        0,              0        },
6955     { MV_RIGHT, MV_LEFT,        MV_UP    }
6956   };
6957
6958   int element = Tile[x][y];
6959   int move_pattern = element_info[element].move_pattern;
6960
6961   int old_move_dir = MovDir[x][y];
6962   int left_dir  = turn[old_move_dir].left;
6963   int right_dir = turn[old_move_dir].right;
6964   int back_dir  = turn[old_move_dir].back;
6965
6966   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6967   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6968   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6969   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6970
6971   int left_x  = x + left_dx,  left_y  = y + left_dy;
6972   int right_x = x + right_dx, right_y = y + right_dy;
6973   int move_x  = x + move_dx,  move_y  = y + move_dy;
6974
6975   int xx, yy;
6976
6977   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6978   {
6979     TestIfBadThingTouchesOtherBadThing(x, y);
6980
6981     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982       MovDir[x][y] = right_dir;
6983     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984       MovDir[x][y] = left_dir;
6985
6986     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6987       MovDelay[x][y] = 9;
6988     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6989       MovDelay[x][y] = 1;
6990   }
6991   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6992   {
6993     TestIfBadThingTouchesOtherBadThing(x, y);
6994
6995     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996       MovDir[x][y] = left_dir;
6997     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998       MovDir[x][y] = right_dir;
6999
7000     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7001       MovDelay[x][y] = 9;
7002     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7003       MovDelay[x][y] = 1;
7004   }
7005   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010       MovDir[x][y] = left_dir;
7011     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012       MovDir[x][y] = right_dir;
7013
7014     if (MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016   }
7017   else if (element == EL_YAMYAM)
7018   {
7019     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7021
7022     if (can_turn_left && can_turn_right)
7023       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024     else if (can_turn_left)
7025       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026     else if (can_turn_right)
7027       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7028     else
7029       MovDir[x][y] = back_dir;
7030
7031     MovDelay[x][y] = 16 + 16 * RND(3);
7032   }
7033   else if (element == EL_DARK_YAMYAM)
7034   {
7035     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7036                                                          left_x, left_y);
7037     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7038                                                          right_x, right_y);
7039
7040     if (can_turn_left && can_turn_right)
7041       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042     else if (can_turn_left)
7043       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044     else if (can_turn_right)
7045       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7046     else
7047       MovDir[x][y] = back_dir;
7048
7049     MovDelay[x][y] = 16 + 16 * RND(3);
7050   }
7051   else if (element == EL_PACMAN)
7052   {
7053     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7055
7056     if (can_turn_left && can_turn_right)
7057       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058     else if (can_turn_left)
7059       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060     else if (can_turn_right)
7061       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7062     else
7063       MovDir[x][y] = back_dir;
7064
7065     MovDelay[x][y] = 6 + RND(40);
7066   }
7067   else if (element == EL_PIG)
7068   {
7069     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072     boolean should_turn_left, should_turn_right, should_move_on;
7073     int rnd_value = 24;
7074     int rnd = RND(rnd_value);
7075
7076     should_turn_left = (can_turn_left &&
7077                         (!can_move_on ||
7078                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079                                                    y + back_dy + left_dy)));
7080     should_turn_right = (can_turn_right &&
7081                          (!can_move_on ||
7082                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083                                                     y + back_dy + right_dy)));
7084     should_move_on = (can_move_on &&
7085                       (!can_turn_left ||
7086                        !can_turn_right ||
7087                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088                                                  y + move_dy + left_dy) ||
7089                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090                                                  y + move_dy + right_dy)));
7091
7092     if (should_turn_left || should_turn_right || should_move_on)
7093     {
7094       if (should_turn_left && should_turn_right && should_move_on)
7095         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7096                         rnd < 2 * rnd_value / 3 ? right_dir :
7097                         old_move_dir);
7098       else if (should_turn_left && should_turn_right)
7099         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100       else if (should_turn_left && should_move_on)
7101         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102       else if (should_turn_right && should_move_on)
7103         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104       else if (should_turn_left)
7105         MovDir[x][y] = left_dir;
7106       else if (should_turn_right)
7107         MovDir[x][y] = right_dir;
7108       else if (should_move_on)
7109         MovDir[x][y] = old_move_dir;
7110     }
7111     else if (can_move_on && rnd > rnd_value / 8)
7112       MovDir[x][y] = old_move_dir;
7113     else if (can_turn_left && can_turn_right)
7114       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115     else if (can_turn_left && rnd > rnd_value / 8)
7116       MovDir[x][y] = left_dir;
7117     else if (can_turn_right && rnd > rnd_value/8)
7118       MovDir[x][y] = right_dir;
7119     else
7120       MovDir[x][y] = back_dir;
7121
7122     xx = x + move_xy[MovDir[x][y]].dx;
7123     yy = y + move_xy[MovDir[x][y]].dy;
7124
7125     if (!IN_LEV_FIELD(xx, yy) ||
7126         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7127       MovDir[x][y] = old_move_dir;
7128
7129     MovDelay[x][y] = 0;
7130   }
7131   else if (element == EL_DRAGON)
7132   {
7133     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7136     int rnd_value = 24;
7137     int rnd = RND(rnd_value);
7138
7139     if (can_move_on && rnd > rnd_value / 8)
7140       MovDir[x][y] = old_move_dir;
7141     else if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143     else if (can_turn_left && rnd > rnd_value / 8)
7144       MovDir[x][y] = left_dir;
7145     else if (can_turn_right && rnd > rnd_value / 8)
7146       MovDir[x][y] = right_dir;
7147     else
7148       MovDir[x][y] = back_dir;
7149
7150     xx = x + move_xy[MovDir[x][y]].dx;
7151     yy = y + move_xy[MovDir[x][y]].dy;
7152
7153     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154       MovDir[x][y] = old_move_dir;
7155
7156     MovDelay[x][y] = 0;
7157   }
7158   else if (element == EL_MOLE)
7159   {
7160     boolean can_move_on =
7161       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7163                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7164     if (!can_move_on)
7165     {
7166       boolean can_turn_left =
7167         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168                               IS_AMOEBOID(Tile[left_x][left_y])));
7169
7170       boolean can_turn_right =
7171         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172                               IS_AMOEBOID(Tile[right_x][right_y])));
7173
7174       if (can_turn_left && can_turn_right)
7175         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176       else if (can_turn_left)
7177         MovDir[x][y] = left_dir;
7178       else
7179         MovDir[x][y] = right_dir;
7180     }
7181
7182     if (MovDir[x][y] != old_move_dir)
7183       MovDelay[x][y] = 9;
7184   }
7185   else if (element == EL_BALLOON)
7186   {
7187     MovDir[x][y] = game.wind_direction;
7188     MovDelay[x][y] = 0;
7189   }
7190   else if (element == EL_SPRING)
7191   {
7192     if (MovDir[x][y] & MV_HORIZONTAL)
7193     {
7194       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7195           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7196       {
7197         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7198         ResetGfxAnimation(move_x, move_y);
7199         TEST_DrawLevelField(move_x, move_y);
7200
7201         MovDir[x][y] = back_dir;
7202       }
7203       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7204                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7205         MovDir[x][y] = MV_NONE;
7206     }
7207
7208     MovDelay[x][y] = 0;
7209   }
7210   else if (element == EL_ROBOT ||
7211            element == EL_SATELLITE ||
7212            element == EL_PENGUIN ||
7213            element == EL_EMC_ANDROID)
7214   {
7215     int attr_x = -1, attr_y = -1;
7216
7217     if (game.all_players_gone)
7218     {
7219       attr_x = game.exit_x;
7220       attr_y = game.exit_y;
7221     }
7222     else
7223     {
7224       int i;
7225
7226       for (i = 0; i < MAX_PLAYERS; i++)
7227       {
7228         struct PlayerInfo *player = &stored_player[i];
7229         int jx = player->jx, jy = player->jy;
7230
7231         if (!player->active)
7232           continue;
7233
7234         if (attr_x == -1 ||
7235             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7236         {
7237           attr_x = jx;
7238           attr_y = jy;
7239         }
7240       }
7241     }
7242
7243     if (element == EL_ROBOT &&
7244         game.robot_wheel_x >= 0 &&
7245         game.robot_wheel_y >= 0 &&
7246         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7247          game.engine_version < VERSION_IDENT(3,1,0,0)))
7248     {
7249       attr_x = game.robot_wheel_x;
7250       attr_y = game.robot_wheel_y;
7251     }
7252
7253     if (element == EL_PENGUIN)
7254     {
7255       int i;
7256       static int xy[4][2] =
7257       {
7258         { 0, -1 },
7259         { -1, 0 },
7260         { +1, 0 },
7261         { 0, +1 }
7262       };
7263
7264       for (i = 0; i < NUM_DIRECTIONS; i++)
7265       {
7266         int ex = x + xy[i][0];
7267         int ey = y + xy[i][1];
7268
7269         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7270                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7271                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7272                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7273         {
7274           attr_x = ex;
7275           attr_y = ey;
7276           break;
7277         }
7278       }
7279     }
7280
7281     MovDir[x][y] = MV_NONE;
7282     if (attr_x < x)
7283       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7284     else if (attr_x > x)
7285       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7286     if (attr_y < y)
7287       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7288     else if (attr_y > y)
7289       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7290
7291     if (element == EL_ROBOT)
7292     {
7293       int newx, newy;
7294
7295       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7296         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7297       Moving2Blocked(x, y, &newx, &newy);
7298
7299       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7300         MovDelay[x][y] = 8 + 8 * !RND(3);
7301       else
7302         MovDelay[x][y] = 16;
7303     }
7304     else if (element == EL_PENGUIN)
7305     {
7306       int newx, newy;
7307
7308       MovDelay[x][y] = 1;
7309
7310       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7311       {
7312         boolean first_horiz = RND(2);
7313         int new_move_dir = MovDir[x][y];
7314
7315         MovDir[x][y] =
7316           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7317         Moving2Blocked(x, y, &newx, &newy);
7318
7319         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7320           return;
7321
7322         MovDir[x][y] =
7323           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7324         Moving2Blocked(x, y, &newx, &newy);
7325
7326         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7327           return;
7328
7329         MovDir[x][y] = old_move_dir;
7330         return;
7331       }
7332     }
7333     else if (element == EL_SATELLITE)
7334     {
7335       int newx, newy;
7336
7337       MovDelay[x][y] = 1;
7338
7339       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7340       {
7341         boolean first_horiz = RND(2);
7342         int new_move_dir = MovDir[x][y];
7343
7344         MovDir[x][y] =
7345           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7346         Moving2Blocked(x, y, &newx, &newy);
7347
7348         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7349           return;
7350
7351         MovDir[x][y] =
7352           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7353         Moving2Blocked(x, y, &newx, &newy);
7354
7355         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7356           return;
7357
7358         MovDir[x][y] = old_move_dir;
7359         return;
7360       }
7361     }
7362     else if (element == EL_EMC_ANDROID)
7363     {
7364       static int check_pos[16] =
7365       {
7366         -1,             //  0 => (invalid)
7367         7,              //  1 => MV_LEFT
7368         3,              //  2 => MV_RIGHT
7369         -1,             //  3 => (invalid)
7370         1,              //  4 =>            MV_UP
7371         0,              //  5 => MV_LEFT  | MV_UP
7372         2,              //  6 => MV_RIGHT | MV_UP
7373         -1,             //  7 => (invalid)
7374         5,              //  8 =>            MV_DOWN
7375         6,              //  9 => MV_LEFT  | MV_DOWN
7376         4,              // 10 => MV_RIGHT | MV_DOWN
7377         -1,             // 11 => (invalid)
7378         -1,             // 12 => (invalid)
7379         -1,             // 13 => (invalid)
7380         -1,             // 14 => (invalid)
7381         -1,             // 15 => (invalid)
7382       };
7383       static struct
7384       {
7385         int dx, dy;
7386         int dir;
7387       } check_xy[8] =
7388       {
7389         { -1, -1,       MV_LEFT  | MV_UP   },
7390         {  0, -1,                  MV_UP   },
7391         { +1, -1,       MV_RIGHT | MV_UP   },
7392         { +1,  0,       MV_RIGHT           },
7393         { +1, +1,       MV_RIGHT | MV_DOWN },
7394         {  0, +1,                  MV_DOWN },
7395         { -1, +1,       MV_LEFT  | MV_DOWN },
7396         { -1,  0,       MV_LEFT            },
7397       };
7398       int start_pos, check_order;
7399       boolean can_clone = FALSE;
7400       int i;
7401
7402       // check if there is any free field around current position
7403       for (i = 0; i < 8; i++)
7404       {
7405         int newx = x + check_xy[i].dx;
7406         int newy = y + check_xy[i].dy;
7407
7408         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7409         {
7410           can_clone = TRUE;
7411
7412           break;
7413         }
7414       }
7415
7416       if (can_clone)            // randomly find an element to clone
7417       {
7418         can_clone = FALSE;
7419
7420         start_pos = check_pos[RND(8)];
7421         check_order = (RND(2) ? -1 : +1);
7422
7423         for (i = 0; i < 8; i++)
7424         {
7425           int pos_raw = start_pos + i * check_order;
7426           int pos = (pos_raw + 8) % 8;
7427           int newx = x + check_xy[pos].dx;
7428           int newy = y + check_xy[pos].dy;
7429
7430           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7431           {
7432             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7433             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7434
7435             Store[x][y] = Tile[newx][newy];
7436
7437             can_clone = TRUE;
7438
7439             break;
7440           }
7441         }
7442       }
7443
7444       if (can_clone)            // randomly find a direction to move
7445       {
7446         can_clone = FALSE;
7447
7448         start_pos = check_pos[RND(8)];
7449         check_order = (RND(2) ? -1 : +1);
7450
7451         for (i = 0; i < 8; i++)
7452         {
7453           int pos_raw = start_pos + i * check_order;
7454           int pos = (pos_raw + 8) % 8;
7455           int newx = x + check_xy[pos].dx;
7456           int newy = y + check_xy[pos].dy;
7457           int new_move_dir = check_xy[pos].dir;
7458
7459           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7460           {
7461             MovDir[x][y] = new_move_dir;
7462             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7463
7464             can_clone = TRUE;
7465
7466             break;
7467           }
7468         }
7469       }
7470
7471       if (can_clone)            // cloning and moving successful
7472         return;
7473
7474       // cannot clone -- try to move towards player
7475
7476       start_pos = check_pos[MovDir[x][y] & 0x0f];
7477       check_order = (RND(2) ? -1 : +1);
7478
7479       for (i = 0; i < 3; i++)
7480       {
7481         // first check start_pos, then previous/next or (next/previous) pos
7482         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7483         int pos = (pos_raw + 8) % 8;
7484         int newx = x + check_xy[pos].dx;
7485         int newy = y + check_xy[pos].dy;
7486         int new_move_dir = check_xy[pos].dir;
7487
7488         if (IS_PLAYER(newx, newy))
7489           break;
7490
7491         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7492         {
7493           MovDir[x][y] = new_move_dir;
7494           MovDelay[x][y] = level.android_move_time * 8 + 1;
7495
7496           break;
7497         }
7498       }
7499     }
7500   }
7501   else if (move_pattern == MV_TURNING_LEFT ||
7502            move_pattern == MV_TURNING_RIGHT ||
7503            move_pattern == MV_TURNING_LEFT_RIGHT ||
7504            move_pattern == MV_TURNING_RIGHT_LEFT ||
7505            move_pattern == MV_TURNING_RANDOM ||
7506            move_pattern == MV_ALL_DIRECTIONS)
7507   {
7508     boolean can_turn_left =
7509       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7510     boolean can_turn_right =
7511       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7512
7513     if (element_info[element].move_stepsize == 0)       // "not moving"
7514       return;
7515
7516     if (move_pattern == MV_TURNING_LEFT)
7517       MovDir[x][y] = left_dir;
7518     else if (move_pattern == MV_TURNING_RIGHT)
7519       MovDir[x][y] = right_dir;
7520     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7521       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7522     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7523       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7524     else if (move_pattern == MV_TURNING_RANDOM)
7525       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7526                       can_turn_right && !can_turn_left ? right_dir :
7527                       RND(2) ? left_dir : right_dir);
7528     else if (can_turn_left && can_turn_right)
7529       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7530     else if (can_turn_left)
7531       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7532     else if (can_turn_right)
7533       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7534     else
7535       MovDir[x][y] = back_dir;
7536
7537     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7538   }
7539   else if (move_pattern == MV_HORIZONTAL ||
7540            move_pattern == MV_VERTICAL)
7541   {
7542     if (move_pattern & old_move_dir)
7543       MovDir[x][y] = back_dir;
7544     else if (move_pattern == MV_HORIZONTAL)
7545       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7546     else if (move_pattern == MV_VERTICAL)
7547       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7548
7549     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7550   }
7551   else if (move_pattern & MV_ANY_DIRECTION)
7552   {
7553     MovDir[x][y] = move_pattern;
7554     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7555   }
7556   else if (move_pattern & MV_WIND_DIRECTION)
7557   {
7558     MovDir[x][y] = game.wind_direction;
7559     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7562   {
7563     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7564       MovDir[x][y] = left_dir;
7565     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7566       MovDir[x][y] = right_dir;
7567
7568     if (MovDir[x][y] != old_move_dir)
7569       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7570   }
7571   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7572   {
7573     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7574       MovDir[x][y] = right_dir;
7575     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7576       MovDir[x][y] = left_dir;
7577
7578     if (MovDir[x][y] != old_move_dir)
7579       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7580   }
7581   else if (move_pattern == MV_TOWARDS_PLAYER ||
7582            move_pattern == MV_AWAY_FROM_PLAYER)
7583   {
7584     int attr_x = -1, attr_y = -1;
7585     int newx, newy;
7586     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7587
7588     if (game.all_players_gone)
7589     {
7590       attr_x = game.exit_x;
7591       attr_y = game.exit_y;
7592     }
7593     else
7594     {
7595       int i;
7596
7597       for (i = 0; i < MAX_PLAYERS; i++)
7598       {
7599         struct PlayerInfo *player = &stored_player[i];
7600         int jx = player->jx, jy = player->jy;
7601
7602         if (!player->active)
7603           continue;
7604
7605         if (attr_x == -1 ||
7606             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7607         {
7608           attr_x = jx;
7609           attr_y = jy;
7610         }
7611       }
7612     }
7613
7614     MovDir[x][y] = MV_NONE;
7615     if (attr_x < x)
7616       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7617     else if (attr_x > x)
7618       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7619     if (attr_y < y)
7620       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7621     else if (attr_y > y)
7622       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7623
7624     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7625
7626     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7627     {
7628       boolean first_horiz = RND(2);
7629       int new_move_dir = MovDir[x][y];
7630
7631       if (element_info[element].move_stepsize == 0)     // "not moving"
7632       {
7633         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7634         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7635
7636         return;
7637       }
7638
7639       MovDir[x][y] =
7640         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7641       Moving2Blocked(x, y, &newx, &newy);
7642
7643       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7644         return;
7645
7646       MovDir[x][y] =
7647         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7648       Moving2Blocked(x, y, &newx, &newy);
7649
7650       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7651         return;
7652
7653       MovDir[x][y] = old_move_dir;
7654     }
7655   }
7656   else if (move_pattern == MV_WHEN_PUSHED ||
7657            move_pattern == MV_WHEN_DROPPED)
7658   {
7659     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7660       MovDir[x][y] = MV_NONE;
7661
7662     MovDelay[x][y] = 0;
7663   }
7664   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7665   {
7666     static int test_xy[7][2] =
7667     {
7668       { 0, -1 },
7669       { -1, 0 },
7670       { +1, 0 },
7671       { 0, +1 },
7672       { 0, -1 },
7673       { -1, 0 },
7674       { +1, 0 },
7675     };
7676     static int test_dir[7] =
7677     {
7678       MV_UP,
7679       MV_LEFT,
7680       MV_RIGHT,
7681       MV_DOWN,
7682       MV_UP,
7683       MV_LEFT,
7684       MV_RIGHT,
7685     };
7686     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7687     int move_preference = -1000000;     // start with very low preference
7688     int new_move_dir = MV_NONE;
7689     int start_test = RND(4);
7690     int i;
7691
7692     for (i = 0; i < NUM_DIRECTIONS; i++)
7693     {
7694       int move_dir = test_dir[start_test + i];
7695       int move_dir_preference;
7696
7697       xx = x + test_xy[start_test + i][0];
7698       yy = y + test_xy[start_test + i][1];
7699
7700       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7701           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7702       {
7703         new_move_dir = move_dir;
7704
7705         break;
7706       }
7707
7708       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7709         continue;
7710
7711       move_dir_preference = -1 * RunnerVisit[xx][yy];
7712       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7713         move_dir_preference = PlayerVisit[xx][yy];
7714
7715       if (move_dir_preference > move_preference)
7716       {
7717         // prefer field that has not been visited for the longest time
7718         move_preference = move_dir_preference;
7719         new_move_dir = move_dir;
7720       }
7721       else if (move_dir_preference == move_preference &&
7722                move_dir == old_move_dir)
7723       {
7724         // prefer last direction when all directions are preferred equally
7725         move_preference = move_dir_preference;
7726         new_move_dir = move_dir;
7727       }
7728     }
7729
7730     MovDir[x][y] = new_move_dir;
7731     if (old_move_dir != new_move_dir)
7732       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7733   }
7734 }
7735
7736 static void TurnRound(int x, int y)
7737 {
7738   int direction = MovDir[x][y];
7739
7740   TurnRoundExt(x, y);
7741
7742   GfxDir[x][y] = MovDir[x][y];
7743
7744   if (direction != MovDir[x][y])
7745     GfxFrame[x][y] = 0;
7746
7747   if (MovDelay[x][y])
7748     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7749
7750   ResetGfxFrame(x, y);
7751 }
7752
7753 static boolean JustBeingPushed(int x, int y)
7754 {
7755   int i;
7756
7757   for (i = 0; i < MAX_PLAYERS; i++)
7758   {
7759     struct PlayerInfo *player = &stored_player[i];
7760
7761     if (player->active && player->is_pushing && player->MovPos)
7762     {
7763       int next_jx = player->jx + (player->jx - player->last_jx);
7764       int next_jy = player->jy + (player->jy - player->last_jy);
7765
7766       if (x == next_jx && y == next_jy)
7767         return TRUE;
7768     }
7769   }
7770
7771   return FALSE;
7772 }
7773
7774 static void StartMoving(int x, int y)
7775 {
7776   boolean started_moving = FALSE;       // some elements can fall _and_ move
7777   int element = Tile[x][y];
7778
7779   if (Stop[x][y])
7780     return;
7781
7782   if (MovDelay[x][y] == 0)
7783     GfxAction[x][y] = ACTION_DEFAULT;
7784
7785   if (CAN_FALL(element) && y < lev_fieldy - 1)
7786   {
7787     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7788         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7789       if (JustBeingPushed(x, y))
7790         return;
7791
7792     if (element == EL_QUICKSAND_FULL)
7793     {
7794       if (IS_FREE(x, y + 1))
7795       {
7796         InitMovingField(x, y, MV_DOWN);
7797         started_moving = TRUE;
7798
7799         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7800 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7801         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7802           Store[x][y] = EL_ROCK;
7803 #else
7804         Store[x][y] = EL_ROCK;
7805 #endif
7806
7807         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7808       }
7809       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7810       {
7811         if (!MovDelay[x][y])
7812         {
7813           MovDelay[x][y] = TILEY + 1;
7814
7815           ResetGfxAnimation(x, y);
7816           ResetGfxAnimation(x, y + 1);
7817         }
7818
7819         if (MovDelay[x][y])
7820         {
7821           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7822           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7823
7824           MovDelay[x][y]--;
7825           if (MovDelay[x][y])
7826             return;
7827         }
7828
7829         Tile[x][y] = EL_QUICKSAND_EMPTY;
7830         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7831         Store[x][y + 1] = Store[x][y];
7832         Store[x][y] = 0;
7833
7834         PlayLevelSoundAction(x, y, ACTION_FILLING);
7835       }
7836       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7837       {
7838         if (!MovDelay[x][y])
7839         {
7840           MovDelay[x][y] = TILEY + 1;
7841
7842           ResetGfxAnimation(x, y);
7843           ResetGfxAnimation(x, y + 1);
7844         }
7845
7846         if (MovDelay[x][y])
7847         {
7848           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7849           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7850
7851           MovDelay[x][y]--;
7852           if (MovDelay[x][y])
7853             return;
7854         }
7855
7856         Tile[x][y] = EL_QUICKSAND_EMPTY;
7857         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7858         Store[x][y + 1] = Store[x][y];
7859         Store[x][y] = 0;
7860
7861         PlayLevelSoundAction(x, y, ACTION_FILLING);
7862       }
7863     }
7864     else if (element == EL_QUICKSAND_FAST_FULL)
7865     {
7866       if (IS_FREE(x, y + 1))
7867       {
7868         InitMovingField(x, y, MV_DOWN);
7869         started_moving = TRUE;
7870
7871         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7872 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7873         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7874           Store[x][y] = EL_ROCK;
7875 #else
7876         Store[x][y] = EL_ROCK;
7877 #endif
7878
7879         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7880       }
7881       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7882       {
7883         if (!MovDelay[x][y])
7884         {
7885           MovDelay[x][y] = TILEY + 1;
7886
7887           ResetGfxAnimation(x, y);
7888           ResetGfxAnimation(x, y + 1);
7889         }
7890
7891         if (MovDelay[x][y])
7892         {
7893           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7894           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7895
7896           MovDelay[x][y]--;
7897           if (MovDelay[x][y])
7898             return;
7899         }
7900
7901         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7902         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7903         Store[x][y + 1] = Store[x][y];
7904         Store[x][y] = 0;
7905
7906         PlayLevelSoundAction(x, y, ACTION_FILLING);
7907       }
7908       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7909       {
7910         if (!MovDelay[x][y])
7911         {
7912           MovDelay[x][y] = TILEY + 1;
7913
7914           ResetGfxAnimation(x, y);
7915           ResetGfxAnimation(x, y + 1);
7916         }
7917
7918         if (MovDelay[x][y])
7919         {
7920           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7921           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7922
7923           MovDelay[x][y]--;
7924           if (MovDelay[x][y])
7925             return;
7926         }
7927
7928         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7929         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7930         Store[x][y + 1] = Store[x][y];
7931         Store[x][y] = 0;
7932
7933         PlayLevelSoundAction(x, y, ACTION_FILLING);
7934       }
7935     }
7936     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7937              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7938     {
7939       InitMovingField(x, y, MV_DOWN);
7940       started_moving = TRUE;
7941
7942       Tile[x][y] = EL_QUICKSAND_FILLING;
7943       Store[x][y] = element;
7944
7945       PlayLevelSoundAction(x, y, ACTION_FILLING);
7946     }
7947     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7948              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7949     {
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952
7953       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7954       Store[x][y] = element;
7955
7956       PlayLevelSoundAction(x, y, ACTION_FILLING);
7957     }
7958     else if (element == EL_MAGIC_WALL_FULL)
7959     {
7960       if (IS_FREE(x, y + 1))
7961       {
7962         InitMovingField(x, y, MV_DOWN);
7963         started_moving = TRUE;
7964
7965         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7966         Store[x][y] = EL_CHANGED(Store[x][y]);
7967       }
7968       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7969       {
7970         if (!MovDelay[x][y])
7971           MovDelay[x][y] = TILEY / 4 + 1;
7972
7973         if (MovDelay[x][y])
7974         {
7975           MovDelay[x][y]--;
7976           if (MovDelay[x][y])
7977             return;
7978         }
7979
7980         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7981         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7982         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7983         Store[x][y] = 0;
7984       }
7985     }
7986     else if (element == EL_BD_MAGIC_WALL_FULL)
7987     {
7988       if (IS_FREE(x, y + 1))
7989       {
7990         InitMovingField(x, y, MV_DOWN);
7991         started_moving = TRUE;
7992
7993         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7994         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7995       }
7996       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7997       {
7998         if (!MovDelay[x][y])
7999           MovDelay[x][y] = TILEY / 4 + 1;
8000
8001         if (MovDelay[x][y])
8002         {
8003           MovDelay[x][y]--;
8004           if (MovDelay[x][y])
8005             return;
8006         }
8007
8008         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8009         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8010         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8011         Store[x][y] = 0;
8012       }
8013     }
8014     else if (element == EL_DC_MAGIC_WALL_FULL)
8015     {
8016       if (IS_FREE(x, y + 1))
8017       {
8018         InitMovingField(x, y, MV_DOWN);
8019         started_moving = TRUE;
8020
8021         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8022         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8023       }
8024       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8025       {
8026         if (!MovDelay[x][y])
8027           MovDelay[x][y] = TILEY / 4 + 1;
8028
8029         if (MovDelay[x][y])
8030         {
8031           MovDelay[x][y]--;
8032           if (MovDelay[x][y])
8033             return;
8034         }
8035
8036         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8037         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8038         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8039         Store[x][y] = 0;
8040       }
8041     }
8042     else if ((CAN_PASS_MAGIC_WALL(element) &&
8043               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8044                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8045              (CAN_PASS_DC_MAGIC_WALL(element) &&
8046               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8047
8048     {
8049       InitMovingField(x, y, MV_DOWN);
8050       started_moving = TRUE;
8051
8052       Tile[x][y] =
8053         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8054          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8055          EL_DC_MAGIC_WALL_FILLING);
8056       Store[x][y] = element;
8057     }
8058     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8059     {
8060       SplashAcid(x, y + 1);
8061
8062       InitMovingField(x, y, MV_DOWN);
8063       started_moving = TRUE;
8064
8065       Store[x][y] = EL_ACID;
8066     }
8067     else if (
8068              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8069               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8070              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8071               CAN_FALL(element) && WasJustFalling[x][y] &&
8072               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8073
8074              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8075               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8076               (Tile[x][y + 1] == EL_BLOCKED)))
8077     {
8078       /* this is needed for a special case not covered by calling "Impact()"
8079          from "ContinueMoving()": if an element moves to a tile directly below
8080          another element which was just falling on that tile (which was empty
8081          in the previous frame), the falling element above would just stop
8082          instead of smashing the element below (in previous version, the above
8083          element was just checked for "moving" instead of "falling", resulting
8084          in incorrect smashes caused by horizontal movement of the above
8085          element; also, the case of the player being the element to smash was
8086          simply not covered here... :-/ ) */
8087
8088       CheckCollision[x][y] = 0;
8089       CheckImpact[x][y] = 0;
8090
8091       Impact(x, y);
8092     }
8093     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8094     {
8095       if (MovDir[x][y] == MV_NONE)
8096       {
8097         InitMovingField(x, y, MV_DOWN);
8098         started_moving = TRUE;
8099       }
8100     }
8101     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8102     {
8103       if (WasJustFalling[x][y]) // prevent animation from being restarted
8104         MovDir[x][y] = MV_DOWN;
8105
8106       InitMovingField(x, y, MV_DOWN);
8107       started_moving = TRUE;
8108     }
8109     else if (element == EL_AMOEBA_DROP)
8110     {
8111       Tile[x][y] = EL_AMOEBA_GROWING;
8112       Store[x][y] = EL_AMOEBA_WET;
8113     }
8114     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8115               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8116              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8117              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8118     {
8119       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8120                                 (IS_FREE(x - 1, y + 1) ||
8121                                  Tile[x - 1][y + 1] == EL_ACID));
8122       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8123                                 (IS_FREE(x + 1, y + 1) ||
8124                                  Tile[x + 1][y + 1] == EL_ACID));
8125       boolean can_fall_any  = (can_fall_left || can_fall_right);
8126       boolean can_fall_both = (can_fall_left && can_fall_right);
8127       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8128
8129       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8130       {
8131         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8132           can_fall_right = FALSE;
8133         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8134           can_fall_left = FALSE;
8135         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8136           can_fall_right = FALSE;
8137         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8138           can_fall_left = FALSE;
8139
8140         can_fall_any  = (can_fall_left || can_fall_right);
8141         can_fall_both = FALSE;
8142       }
8143
8144       if (can_fall_both)
8145       {
8146         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8147           can_fall_right = FALSE;       // slip down on left side
8148         else
8149           can_fall_left = !(can_fall_right = RND(2));
8150
8151         can_fall_both = FALSE;
8152       }
8153
8154       if (can_fall_any)
8155       {
8156         // if not determined otherwise, prefer left side for slipping down
8157         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8158         started_moving = TRUE;
8159       }
8160     }
8161     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8162     {
8163       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8164       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8165       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8166       int belt_dir = game.belt_dir[belt_nr];
8167
8168       if ((belt_dir == MV_LEFT  && left_is_free) ||
8169           (belt_dir == MV_RIGHT && right_is_free))
8170       {
8171         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8172
8173         InitMovingField(x, y, belt_dir);
8174         started_moving = TRUE;
8175
8176         Pushed[x][y] = TRUE;
8177         Pushed[nextx][y] = TRUE;
8178
8179         GfxAction[x][y] = ACTION_DEFAULT;
8180       }
8181       else
8182       {
8183         MovDir[x][y] = 0;       // if element was moving, stop it
8184       }
8185     }
8186   }
8187
8188   // not "else if" because of elements that can fall and move (EL_SPRING)
8189   if (CAN_MOVE(element) && !started_moving)
8190   {
8191     int move_pattern = element_info[element].move_pattern;
8192     int newx, newy;
8193
8194     Moving2Blocked(x, y, &newx, &newy);
8195
8196     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8197       return;
8198
8199     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8200         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8201     {
8202       WasJustMoving[x][y] = 0;
8203       CheckCollision[x][y] = 0;
8204
8205       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8206
8207       if (Tile[x][y] != element)        // element has changed
8208         return;
8209     }
8210
8211     if (!MovDelay[x][y])        // start new movement phase
8212     {
8213       // all objects that can change their move direction after each step
8214       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8215
8216       if (element != EL_YAMYAM &&
8217           element != EL_DARK_YAMYAM &&
8218           element != EL_PACMAN &&
8219           !(move_pattern & MV_ANY_DIRECTION) &&
8220           move_pattern != MV_TURNING_LEFT &&
8221           move_pattern != MV_TURNING_RIGHT &&
8222           move_pattern != MV_TURNING_LEFT_RIGHT &&
8223           move_pattern != MV_TURNING_RIGHT_LEFT &&
8224           move_pattern != MV_TURNING_RANDOM)
8225       {
8226         TurnRound(x, y);
8227
8228         if (MovDelay[x][y] && (element == EL_BUG ||
8229                                element == EL_SPACESHIP ||
8230                                element == EL_SP_SNIKSNAK ||
8231                                element == EL_SP_ELECTRON ||
8232                                element == EL_MOLE))
8233           TEST_DrawLevelField(x, y);
8234       }
8235     }
8236
8237     if (MovDelay[x][y])         // wait some time before next movement
8238     {
8239       MovDelay[x][y]--;
8240
8241       if (element == EL_ROBOT ||
8242           element == EL_YAMYAM ||
8243           element == EL_DARK_YAMYAM)
8244       {
8245         DrawLevelElementAnimationIfNeeded(x, y, element);
8246         PlayLevelSoundAction(x, y, ACTION_WAITING);
8247       }
8248       else if (element == EL_SP_ELECTRON)
8249         DrawLevelElementAnimationIfNeeded(x, y, element);
8250       else if (element == EL_DRAGON)
8251       {
8252         int i;
8253         int dir = MovDir[x][y];
8254         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8255         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8256         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8257                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8258                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8259                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8260         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8261
8262         GfxAction[x][y] = ACTION_ATTACKING;
8263
8264         if (IS_PLAYER(x, y))
8265           DrawPlayerField(x, y);
8266         else
8267           TEST_DrawLevelField(x, y);
8268
8269         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8270
8271         for (i = 1; i <= 3; i++)
8272         {
8273           int xx = x + i * dx;
8274           int yy = y + i * dy;
8275           int sx = SCREENX(xx);
8276           int sy = SCREENY(yy);
8277           int flame_graphic = graphic + (i - 1);
8278
8279           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8280             break;
8281
8282           if (MovDelay[x][y])
8283           {
8284             int flamed = MovingOrBlocked2Element(xx, yy);
8285
8286             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8287               Bang(xx, yy);
8288             else
8289               RemoveMovingField(xx, yy);
8290
8291             ChangeDelay[xx][yy] = 0;
8292
8293             Tile[xx][yy] = EL_FLAMES;
8294
8295             if (IN_SCR_FIELD(sx, sy))
8296             {
8297               TEST_DrawLevelFieldCrumbled(xx, yy);
8298               DrawGraphic(sx, sy, flame_graphic, frame);
8299             }
8300           }
8301           else
8302           {
8303             if (Tile[xx][yy] == EL_FLAMES)
8304               Tile[xx][yy] = EL_EMPTY;
8305             TEST_DrawLevelField(xx, yy);
8306           }
8307         }
8308       }
8309
8310       if (MovDelay[x][y])       // element still has to wait some time
8311       {
8312         PlayLevelSoundAction(x, y, ACTION_WAITING);
8313
8314         return;
8315       }
8316     }
8317
8318     // now make next step
8319
8320     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8321
8322     if (DONT_COLLIDE_WITH(element) &&
8323         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8324         !PLAYER_ENEMY_PROTECTED(newx, newy))
8325     {
8326       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8327
8328       return;
8329     }
8330
8331     else if (CAN_MOVE_INTO_ACID(element) &&
8332              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8333              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8334              (MovDir[x][y] == MV_DOWN ||
8335               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8336     {
8337       SplashAcid(newx, newy);
8338       Store[x][y] = EL_ACID;
8339     }
8340     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8341     {
8342       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8343           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8344           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8345           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8346       {
8347         RemoveField(x, y);
8348         TEST_DrawLevelField(x, y);
8349
8350         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8351         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8352           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8353
8354         game.friends_still_needed--;
8355         if (!game.friends_still_needed &&
8356             !game.GameOver &&
8357             game.all_players_gone)
8358           LevelSolved();
8359
8360         return;
8361       }
8362       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8363       {
8364         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8365           TEST_DrawLevelField(newx, newy);
8366         else
8367           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8368       }
8369       else if (!IS_FREE(newx, newy))
8370       {
8371         GfxAction[x][y] = ACTION_WAITING;
8372
8373         if (IS_PLAYER(x, y))
8374           DrawPlayerField(x, y);
8375         else
8376           TEST_DrawLevelField(x, y);
8377
8378         return;
8379       }
8380     }
8381     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8382     {
8383       if (IS_FOOD_PIG(Tile[newx][newy]))
8384       {
8385         if (IS_MOVING(newx, newy))
8386           RemoveMovingField(newx, newy);
8387         else
8388         {
8389           Tile[newx][newy] = EL_EMPTY;
8390           TEST_DrawLevelField(newx, newy);
8391         }
8392
8393         PlayLevelSound(x, y, SND_PIG_DIGGING);
8394       }
8395       else if (!IS_FREE(newx, newy))
8396       {
8397         if (IS_PLAYER(x, y))
8398           DrawPlayerField(x, y);
8399         else
8400           TEST_DrawLevelField(x, y);
8401
8402         return;
8403       }
8404     }
8405     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8406     {
8407       if (Store[x][y] != EL_EMPTY)
8408       {
8409         boolean can_clone = FALSE;
8410         int xx, yy;
8411
8412         // check if element to clone is still there
8413         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8414         {
8415           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8416           {
8417             can_clone = TRUE;
8418
8419             break;
8420           }
8421         }
8422
8423         // cannot clone or target field not free anymore -- do not clone
8424         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8425           Store[x][y] = EL_EMPTY;
8426       }
8427
8428       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8429       {
8430         if (IS_MV_DIAGONAL(MovDir[x][y]))
8431         {
8432           int diagonal_move_dir = MovDir[x][y];
8433           int stored = Store[x][y];
8434           int change_delay = 8;
8435           int graphic;
8436
8437           // android is moving diagonally
8438
8439           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8440
8441           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8442           GfxElement[x][y] = EL_EMC_ANDROID;
8443           GfxAction[x][y] = ACTION_SHRINKING;
8444           GfxDir[x][y] = diagonal_move_dir;
8445           ChangeDelay[x][y] = change_delay;
8446
8447           if (Store[x][y] == EL_EMPTY)
8448             Store[x][y] = GfxElementEmpty[x][y];
8449
8450           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8451                                    GfxDir[x][y]);
8452
8453           DrawLevelGraphicAnimation(x, y, graphic);
8454           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8455
8456           if (Tile[newx][newy] == EL_ACID)
8457           {
8458             SplashAcid(newx, newy);
8459
8460             return;
8461           }
8462
8463           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8464
8465           Store[newx][newy] = EL_EMC_ANDROID;
8466           GfxElement[newx][newy] = EL_EMC_ANDROID;
8467           GfxAction[newx][newy] = ACTION_GROWING;
8468           GfxDir[newx][newy] = diagonal_move_dir;
8469           ChangeDelay[newx][newy] = change_delay;
8470
8471           graphic = el_act_dir2img(GfxElement[newx][newy],
8472                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8473
8474           DrawLevelGraphicAnimation(newx, newy, graphic);
8475           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8476
8477           return;
8478         }
8479         else
8480         {
8481           Tile[newx][newy] = EL_EMPTY;
8482           TEST_DrawLevelField(newx, newy);
8483
8484           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8485         }
8486       }
8487       else if (!IS_FREE(newx, newy))
8488       {
8489         return;
8490       }
8491     }
8492     else if (IS_CUSTOM_ELEMENT(element) &&
8493              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8494     {
8495       if (!DigFieldByCE(newx, newy, element))
8496         return;
8497
8498       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8499       {
8500         RunnerVisit[x][y] = FrameCounter;
8501         PlayerVisit[x][y] /= 8;         // expire player visit path
8502       }
8503     }
8504     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8505     {
8506       if (!IS_FREE(newx, newy))
8507       {
8508         if (IS_PLAYER(x, y))
8509           DrawPlayerField(x, y);
8510         else
8511           TEST_DrawLevelField(x, y);
8512
8513         return;
8514       }
8515       else
8516       {
8517         boolean wanna_flame = !RND(10);
8518         int dx = newx - x, dy = newy - y;
8519         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8520         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8521         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8522                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8523         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8524                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8525
8526         if ((wanna_flame ||
8527              IS_CLASSIC_ENEMY(element1) ||
8528              IS_CLASSIC_ENEMY(element2)) &&
8529             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8530             element1 != EL_FLAMES && element2 != EL_FLAMES)
8531         {
8532           ResetGfxAnimation(x, y);
8533           GfxAction[x][y] = ACTION_ATTACKING;
8534
8535           if (IS_PLAYER(x, y))
8536             DrawPlayerField(x, y);
8537           else
8538             TEST_DrawLevelField(x, y);
8539
8540           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8541
8542           MovDelay[x][y] = 50;
8543
8544           Tile[newx][newy] = EL_FLAMES;
8545           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8546             Tile[newx1][newy1] = EL_FLAMES;
8547           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8548             Tile[newx2][newy2] = EL_FLAMES;
8549
8550           return;
8551         }
8552       }
8553     }
8554     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8555              Tile[newx][newy] == EL_DIAMOND)
8556     {
8557       if (IS_MOVING(newx, newy))
8558         RemoveMovingField(newx, newy);
8559       else
8560       {
8561         Tile[newx][newy] = EL_EMPTY;
8562         TEST_DrawLevelField(newx, newy);
8563       }
8564
8565       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8566     }
8567     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8568              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8569     {
8570       if (AmoebaNr[newx][newy])
8571       {
8572         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8573         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8574             Tile[newx][newy] == EL_BD_AMOEBA)
8575           AmoebaCnt[AmoebaNr[newx][newy]]--;
8576       }
8577
8578       if (IS_MOVING(newx, newy))
8579       {
8580         RemoveMovingField(newx, newy);
8581       }
8582       else
8583       {
8584         Tile[newx][newy] = EL_EMPTY;
8585         TEST_DrawLevelField(newx, newy);
8586       }
8587
8588       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8589     }
8590     else if ((element == EL_PACMAN || element == EL_MOLE)
8591              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8592     {
8593       if (AmoebaNr[newx][newy])
8594       {
8595         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8596         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8597             Tile[newx][newy] == EL_BD_AMOEBA)
8598           AmoebaCnt[AmoebaNr[newx][newy]]--;
8599       }
8600
8601       if (element == EL_MOLE)
8602       {
8603         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8604         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8605
8606         ResetGfxAnimation(x, y);
8607         GfxAction[x][y] = ACTION_DIGGING;
8608         TEST_DrawLevelField(x, y);
8609
8610         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8611
8612         return;                         // wait for shrinking amoeba
8613       }
8614       else      // element == EL_PACMAN
8615       {
8616         Tile[newx][newy] = EL_EMPTY;
8617         TEST_DrawLevelField(newx, newy);
8618         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8619       }
8620     }
8621     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8622              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8623               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8624     {
8625       // wait for shrinking amoeba to completely disappear
8626       return;
8627     }
8628     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8629     {
8630       // object was running against a wall
8631
8632       TurnRound(x, y);
8633
8634       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8635         DrawLevelElementAnimation(x, y, element);
8636
8637       if (DONT_TOUCH(element))
8638         TestIfBadThingTouchesPlayer(x, y);
8639
8640       return;
8641     }
8642
8643     InitMovingField(x, y, MovDir[x][y]);
8644
8645     PlayLevelSoundAction(x, y, ACTION_MOVING);
8646   }
8647
8648   if (MovDir[x][y])
8649     ContinueMoving(x, y);
8650 }
8651
8652 void ContinueMoving(int x, int y)
8653 {
8654   int element = Tile[x][y];
8655   struct ElementInfo *ei = &element_info[element];
8656   int direction = MovDir[x][y];
8657   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8658   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8659   int newx = x + dx, newy = y + dy;
8660   int stored = Store[x][y];
8661   int stored_new = Store[newx][newy];
8662   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8663   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8664   boolean last_line = (newy == lev_fieldy - 1);
8665   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8666
8667   if (pushed_by_player)         // special case: moving object pushed by player
8668   {
8669     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8670   }
8671   else if (use_step_delay)      // special case: moving object has step delay
8672   {
8673     if (!MovDelay[x][y])
8674       MovPos[x][y] += getElementMoveStepsize(x, y);
8675
8676     if (MovDelay[x][y])
8677       MovDelay[x][y]--;
8678     else
8679       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8680
8681     if (MovDelay[x][y])
8682     {
8683       TEST_DrawLevelField(x, y);
8684
8685       return;   // element is still waiting
8686     }
8687   }
8688   else                          // normal case: generically moving object
8689   {
8690     MovPos[x][y] += getElementMoveStepsize(x, y);
8691   }
8692
8693   if (ABS(MovPos[x][y]) < TILEX)
8694   {
8695     TEST_DrawLevelField(x, y);
8696
8697     return;     // element is still moving
8698   }
8699
8700   // element reached destination field
8701
8702   Tile[x][y] = EL_EMPTY;
8703   Tile[newx][newy] = element;
8704   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8705
8706   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8707   {
8708     element = Tile[newx][newy] = EL_ACID;
8709   }
8710   else if (element == EL_MOLE)
8711   {
8712     Tile[x][y] = EL_SAND;
8713
8714     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8715   }
8716   else if (element == EL_QUICKSAND_FILLING)
8717   {
8718     element = Tile[newx][newy] = get_next_element(element);
8719     Store[newx][newy] = Store[x][y];
8720   }
8721   else if (element == EL_QUICKSAND_EMPTYING)
8722   {
8723     Tile[x][y] = get_next_element(element);
8724     element = Tile[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_QUICKSAND_FAST_FILLING)
8727   {
8728     element = Tile[newx][newy] = get_next_element(element);
8729     Store[newx][newy] = Store[x][y];
8730   }
8731   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8732   {
8733     Tile[x][y] = get_next_element(element);
8734     element = Tile[newx][newy] = Store[x][y];
8735   }
8736   else if (element == EL_MAGIC_WALL_FILLING)
8737   {
8738     element = Tile[newx][newy] = get_next_element(element);
8739     if (!game.magic_wall_active)
8740       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8741     Store[newx][newy] = Store[x][y];
8742   }
8743   else if (element == EL_MAGIC_WALL_EMPTYING)
8744   {
8745     Tile[x][y] = get_next_element(element);
8746     if (!game.magic_wall_active)
8747       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8748     element = Tile[newx][newy] = Store[x][y];
8749
8750     InitField(newx, newy, FALSE);
8751   }
8752   else if (element == EL_BD_MAGIC_WALL_FILLING)
8753   {
8754     element = Tile[newx][newy] = get_next_element(element);
8755     if (!game.magic_wall_active)
8756       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8757     Store[newx][newy] = Store[x][y];
8758   }
8759   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8760   {
8761     Tile[x][y] = get_next_element(element);
8762     if (!game.magic_wall_active)
8763       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8764     element = Tile[newx][newy] = Store[x][y];
8765
8766     InitField(newx, newy, FALSE);
8767   }
8768   else if (element == EL_DC_MAGIC_WALL_FILLING)
8769   {
8770     element = Tile[newx][newy] = get_next_element(element);
8771     if (!game.magic_wall_active)
8772       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8773     Store[newx][newy] = Store[x][y];
8774   }
8775   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8776   {
8777     Tile[x][y] = get_next_element(element);
8778     if (!game.magic_wall_active)
8779       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8780     element = Tile[newx][newy] = Store[x][y];
8781
8782     InitField(newx, newy, FALSE);
8783   }
8784   else if (element == EL_AMOEBA_DROPPING)
8785   {
8786     Tile[x][y] = get_next_element(element);
8787     element = Tile[newx][newy] = Store[x][y];
8788   }
8789   else if (element == EL_SOKOBAN_OBJECT)
8790   {
8791     if (Back[x][y])
8792       Tile[x][y] = Back[x][y];
8793
8794     if (Back[newx][newy])
8795       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8796
8797     Back[x][y] = Back[newx][newy] = 0;
8798   }
8799
8800   Store[x][y] = EL_EMPTY;
8801   MovPos[x][y] = 0;
8802   MovDir[x][y] = 0;
8803   MovDelay[x][y] = 0;
8804
8805   MovDelay[newx][newy] = 0;
8806
8807   if (CAN_CHANGE_OR_HAS_ACTION(element))
8808   {
8809     // copy element change control values to new field
8810     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8811     ChangePage[newx][newy]  = ChangePage[x][y];
8812     ChangeCount[newx][newy] = ChangeCount[x][y];
8813     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8814   }
8815
8816   CustomValue[newx][newy] = CustomValue[x][y];
8817
8818   ChangeDelay[x][y] = 0;
8819   ChangePage[x][y] = -1;
8820   ChangeCount[x][y] = 0;
8821   ChangeEvent[x][y] = -1;
8822
8823   CustomValue[x][y] = 0;
8824
8825   // copy animation control values to new field
8826   GfxFrame[newx][newy]  = GfxFrame[x][y];
8827   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8828   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8829   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8830
8831   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8832
8833   // some elements can leave other elements behind after moving
8834   if (ei->move_leave_element != EL_EMPTY &&
8835       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8836       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8837   {
8838     int move_leave_element = ei->move_leave_element;
8839
8840     // this makes it possible to leave the removed element again
8841     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8842       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8843
8844     Tile[x][y] = move_leave_element;
8845
8846     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8847       MovDir[x][y] = direction;
8848
8849     InitField(x, y, FALSE);
8850
8851     if (GFX_CRUMBLED(Tile[x][y]))
8852       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8853
8854     if (IS_PLAYER_ELEMENT(move_leave_element))
8855       RelocatePlayer(x, y, move_leave_element);
8856   }
8857
8858   // do this after checking for left-behind element
8859   ResetGfxAnimation(x, y);      // reset animation values for old field
8860
8861   if (!CAN_MOVE(element) ||
8862       (CAN_FALL(element) && direction == MV_DOWN &&
8863        (element == EL_SPRING ||
8864         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8865         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8866     GfxDir[x][y] = MovDir[newx][newy] = 0;
8867
8868   TEST_DrawLevelField(x, y);
8869   TEST_DrawLevelField(newx, newy);
8870
8871   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8872
8873   // prevent pushed element from moving on in pushed direction
8874   if (pushed_by_player && CAN_MOVE(element) &&
8875       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8876       !(element_info[element].move_pattern & direction))
8877     TurnRound(newx, newy);
8878
8879   // prevent elements on conveyor belt from moving on in last direction
8880   if (pushed_by_conveyor && CAN_FALL(element) &&
8881       direction & MV_HORIZONTAL)
8882     MovDir[newx][newy] = 0;
8883
8884   if (!pushed_by_player)
8885   {
8886     int nextx = newx + dx, nexty = newy + dy;
8887     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8888
8889     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8890
8891     if (CAN_FALL(element) && direction == MV_DOWN)
8892       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8893
8894     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8895       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8896
8897     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8898       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8899   }
8900
8901   if (DONT_TOUCH(element))      // object may be nasty to player or others
8902   {
8903     TestIfBadThingTouchesPlayer(newx, newy);
8904     TestIfBadThingTouchesFriend(newx, newy);
8905
8906     if (!IS_CUSTOM_ELEMENT(element))
8907       TestIfBadThingTouchesOtherBadThing(newx, newy);
8908   }
8909   else if (element == EL_PENGUIN)
8910     TestIfFriendTouchesBadThing(newx, newy);
8911
8912   if (DONT_GET_HIT_BY(element))
8913   {
8914     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8915   }
8916
8917   // give the player one last chance (one more frame) to move away
8918   if (CAN_FALL(element) && direction == MV_DOWN &&
8919       (last_line || (!IS_FREE(x, newy + 1) &&
8920                      (!IS_PLAYER(x, newy + 1) ||
8921                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8922     Impact(x, newy);
8923
8924   if (pushed_by_player && !game.use_change_when_pushing_bug)
8925   {
8926     int push_side = MV_DIR_OPPOSITE(direction);
8927     struct PlayerInfo *player = PLAYERINFO(x, y);
8928
8929     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8930                                player->index_bit, push_side);
8931     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8932                                         player->index_bit, push_side);
8933   }
8934
8935   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8936     MovDelay[newx][newy] = 1;
8937
8938   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8939
8940   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8941   TestIfElementHitsCustomElement(newx, newy, direction);
8942   TestIfPlayerTouchesCustomElement(newx, newy);
8943   TestIfElementTouchesCustomElement(newx, newy);
8944
8945   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8946       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8947     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8948                              MV_DIR_OPPOSITE(direction));
8949 }
8950
8951 int AmoebaNeighbourNr(int ax, int ay)
8952 {
8953   int i;
8954   int element = Tile[ax][ay];
8955   int group_nr = 0;
8956   static int xy[4][2] =
8957   {
8958     { 0, -1 },
8959     { -1, 0 },
8960     { +1, 0 },
8961     { 0, +1 }
8962   };
8963
8964   for (i = 0; i < NUM_DIRECTIONS; i++)
8965   {
8966     int x = ax + xy[i][0];
8967     int y = ay + xy[i][1];
8968
8969     if (!IN_LEV_FIELD(x, y))
8970       continue;
8971
8972     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8973       group_nr = AmoebaNr[x][y];
8974   }
8975
8976   return group_nr;
8977 }
8978
8979 static void AmoebaMerge(int ax, int ay)
8980 {
8981   int i, x, y, xx, yy;
8982   int new_group_nr = AmoebaNr[ax][ay];
8983   static int xy[4][2] =
8984   {
8985     { 0, -1 },
8986     { -1, 0 },
8987     { +1, 0 },
8988     { 0, +1 }
8989   };
8990
8991   if (new_group_nr == 0)
8992     return;
8993
8994   for (i = 0; i < NUM_DIRECTIONS; i++)
8995   {
8996     x = ax + xy[i][0];
8997     y = ay + xy[i][1];
8998
8999     if (!IN_LEV_FIELD(x, y))
9000       continue;
9001
9002     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9003          Tile[x][y] == EL_BD_AMOEBA ||
9004          Tile[x][y] == EL_AMOEBA_DEAD) &&
9005         AmoebaNr[x][y] != new_group_nr)
9006     {
9007       int old_group_nr = AmoebaNr[x][y];
9008
9009       if (old_group_nr == 0)
9010         return;
9011
9012       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9013       AmoebaCnt[old_group_nr] = 0;
9014       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9015       AmoebaCnt2[old_group_nr] = 0;
9016
9017       SCAN_PLAYFIELD(xx, yy)
9018       {
9019         if (AmoebaNr[xx][yy] == old_group_nr)
9020           AmoebaNr[xx][yy] = new_group_nr;
9021       }
9022     }
9023   }
9024 }
9025
9026 void AmoebaToDiamond(int ax, int ay)
9027 {
9028   int i, x, y;
9029
9030   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9031   {
9032     int group_nr = AmoebaNr[ax][ay];
9033
9034 #ifdef DEBUG
9035     if (group_nr == 0)
9036     {
9037       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9038       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9039
9040       return;
9041     }
9042 #endif
9043
9044     SCAN_PLAYFIELD(x, y)
9045     {
9046       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9047       {
9048         AmoebaNr[x][y] = 0;
9049         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9050       }
9051     }
9052
9053     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9054                             SND_AMOEBA_TURNING_TO_GEM :
9055                             SND_AMOEBA_TURNING_TO_ROCK));
9056     Bang(ax, ay);
9057   }
9058   else
9059   {
9060     static int xy[4][2] =
9061     {
9062       { 0, -1 },
9063       { -1, 0 },
9064       { +1, 0 },
9065       { 0, +1 }
9066     };
9067
9068     for (i = 0; i < NUM_DIRECTIONS; i++)
9069     {
9070       x = ax + xy[i][0];
9071       y = ay + xy[i][1];
9072
9073       if (!IN_LEV_FIELD(x, y))
9074         continue;
9075
9076       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9077       {
9078         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9079                               SND_AMOEBA_TURNING_TO_GEM :
9080                               SND_AMOEBA_TURNING_TO_ROCK));
9081         Bang(x, y);
9082       }
9083     }
9084   }
9085 }
9086
9087 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9088 {
9089   int x, y;
9090   int group_nr = AmoebaNr[ax][ay];
9091   boolean done = FALSE;
9092
9093 #ifdef DEBUG
9094   if (group_nr == 0)
9095   {
9096     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9097     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9098
9099     return;
9100   }
9101 #endif
9102
9103   SCAN_PLAYFIELD(x, y)
9104   {
9105     if (AmoebaNr[x][y] == group_nr &&
9106         (Tile[x][y] == EL_AMOEBA_DEAD ||
9107          Tile[x][y] == EL_BD_AMOEBA ||
9108          Tile[x][y] == EL_AMOEBA_GROWING))
9109     {
9110       AmoebaNr[x][y] = 0;
9111       Tile[x][y] = new_element;
9112       InitField(x, y, FALSE);
9113       TEST_DrawLevelField(x, y);
9114       done = TRUE;
9115     }
9116   }
9117
9118   if (done)
9119     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9120                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9121                             SND_BD_AMOEBA_TURNING_TO_GEM));
9122 }
9123
9124 static void AmoebaGrowing(int x, int y)
9125 {
9126   static unsigned int sound_delay = 0;
9127   static unsigned int sound_delay_value = 0;
9128
9129   if (!MovDelay[x][y])          // start new growing cycle
9130   {
9131     MovDelay[x][y] = 7;
9132
9133     if (DelayReached(&sound_delay, sound_delay_value))
9134     {
9135       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9136       sound_delay_value = 30;
9137     }
9138   }
9139
9140   if (MovDelay[x][y])           // wait some time before growing bigger
9141   {
9142     MovDelay[x][y]--;
9143     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9144     {
9145       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9146                                            6 - MovDelay[x][y]);
9147
9148       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9149     }
9150
9151     if (!MovDelay[x][y])
9152     {
9153       Tile[x][y] = Store[x][y];
9154       Store[x][y] = 0;
9155       TEST_DrawLevelField(x, y);
9156     }
9157   }
9158 }
9159
9160 static void AmoebaShrinking(int x, int y)
9161 {
9162   static unsigned int sound_delay = 0;
9163   static unsigned int sound_delay_value = 0;
9164
9165   if (!MovDelay[x][y])          // start new shrinking cycle
9166   {
9167     MovDelay[x][y] = 7;
9168
9169     if (DelayReached(&sound_delay, sound_delay_value))
9170       sound_delay_value = 30;
9171   }
9172
9173   if (MovDelay[x][y])           // wait some time before shrinking
9174   {
9175     MovDelay[x][y]--;
9176     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9177     {
9178       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9179                                            6 - MovDelay[x][y]);
9180
9181       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9182     }
9183
9184     if (!MovDelay[x][y])
9185     {
9186       Tile[x][y] = EL_EMPTY;
9187       TEST_DrawLevelField(x, y);
9188
9189       // don't let mole enter this field in this cycle;
9190       // (give priority to objects falling to this field from above)
9191       Stop[x][y] = TRUE;
9192     }
9193   }
9194 }
9195
9196 static void AmoebaReproduce(int ax, int ay)
9197 {
9198   int i;
9199   int element = Tile[ax][ay];
9200   int graphic = el2img(element);
9201   int newax = ax, neway = ay;
9202   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9203   static int xy[4][2] =
9204   {
9205     { 0, -1 },
9206     { -1, 0 },
9207     { +1, 0 },
9208     { 0, +1 }
9209   };
9210
9211   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9212   {
9213     Tile[ax][ay] = EL_AMOEBA_DEAD;
9214     TEST_DrawLevelField(ax, ay);
9215     return;
9216   }
9217
9218   if (IS_ANIMATED(graphic))
9219     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9220
9221   if (!MovDelay[ax][ay])        // start making new amoeba field
9222     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9223
9224   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9225   {
9226     MovDelay[ax][ay]--;
9227     if (MovDelay[ax][ay])
9228       return;
9229   }
9230
9231   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9232   {
9233     int start = RND(4);
9234     int x = ax + xy[start][0];
9235     int y = ay + xy[start][1];
9236
9237     if (!IN_LEV_FIELD(x, y))
9238       return;
9239
9240     if (IS_FREE(x, y) ||
9241         CAN_GROW_INTO(Tile[x][y]) ||
9242         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9243         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9244     {
9245       newax = x;
9246       neway = y;
9247     }
9248
9249     if (newax == ax && neway == ay)
9250       return;
9251   }
9252   else                          // normal or "filled" (BD style) amoeba
9253   {
9254     int start = RND(4);
9255     boolean waiting_for_player = FALSE;
9256
9257     for (i = 0; i < NUM_DIRECTIONS; i++)
9258     {
9259       int j = (start + i) % 4;
9260       int x = ax + xy[j][0];
9261       int y = ay + xy[j][1];
9262
9263       if (!IN_LEV_FIELD(x, y))
9264         continue;
9265
9266       if (IS_FREE(x, y) ||
9267           CAN_GROW_INTO(Tile[x][y]) ||
9268           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9269           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9270       {
9271         newax = x;
9272         neway = y;
9273         break;
9274       }
9275       else if (IS_PLAYER(x, y))
9276         waiting_for_player = TRUE;
9277     }
9278
9279     if (newax == ax && neway == ay)             // amoeba cannot grow
9280     {
9281       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9282       {
9283         Tile[ax][ay] = EL_AMOEBA_DEAD;
9284         TEST_DrawLevelField(ax, ay);
9285         AmoebaCnt[AmoebaNr[ax][ay]]--;
9286
9287         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9288         {
9289           if (element == EL_AMOEBA_FULL)
9290             AmoebaToDiamond(ax, ay);
9291           else if (element == EL_BD_AMOEBA)
9292             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9293         }
9294       }
9295       return;
9296     }
9297     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9298     {
9299       // amoeba gets larger by growing in some direction
9300
9301       int new_group_nr = AmoebaNr[ax][ay];
9302
9303 #ifdef DEBUG
9304   if (new_group_nr == 0)
9305   {
9306     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9307           newax, neway);
9308     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9309
9310     return;
9311   }
9312 #endif
9313
9314       AmoebaNr[newax][neway] = new_group_nr;
9315       AmoebaCnt[new_group_nr]++;
9316       AmoebaCnt2[new_group_nr]++;
9317
9318       // if amoeba touches other amoeba(s) after growing, unify them
9319       AmoebaMerge(newax, neway);
9320
9321       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9322       {
9323         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9324         return;
9325       }
9326     }
9327   }
9328
9329   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9330       (neway == lev_fieldy - 1 && newax != ax))
9331   {
9332     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9333     Store[newax][neway] = element;
9334   }
9335   else if (neway == ay || element == EL_EMC_DRIPPER)
9336   {
9337     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9338
9339     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9340   }
9341   else
9342   {
9343     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9344     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9345     Store[ax][ay] = EL_AMOEBA_DROP;
9346     ContinueMoving(ax, ay);
9347     return;
9348   }
9349
9350   TEST_DrawLevelField(newax, neway);
9351 }
9352
9353 static void Life(int ax, int ay)
9354 {
9355   int x1, y1, x2, y2;
9356   int life_time = 40;
9357   int element = Tile[ax][ay];
9358   int graphic = el2img(element);
9359   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9360                          level.biomaze);
9361   boolean changed = FALSE;
9362
9363   if (IS_ANIMATED(graphic))
9364     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9365
9366   if (Stop[ax][ay])
9367     return;
9368
9369   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9370     MovDelay[ax][ay] = life_time;
9371
9372   if (MovDelay[ax][ay])         // wait some time before next cycle
9373   {
9374     MovDelay[ax][ay]--;
9375     if (MovDelay[ax][ay])
9376       return;
9377   }
9378
9379   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9380   {
9381     int xx = ax+x1, yy = ay+y1;
9382     int old_element = Tile[xx][yy];
9383     int num_neighbours = 0;
9384
9385     if (!IN_LEV_FIELD(xx, yy))
9386       continue;
9387
9388     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9389     {
9390       int x = xx+x2, y = yy+y2;
9391
9392       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9393         continue;
9394
9395       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9396       boolean is_neighbour = FALSE;
9397
9398       if (level.use_life_bugs)
9399         is_neighbour =
9400           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9401            (IS_FREE(x, y)                             &&  Stop[x][y]));
9402       else
9403         is_neighbour =
9404           (Last[x][y] == element || is_player_cell);
9405
9406       if (is_neighbour)
9407         num_neighbours++;
9408     }
9409
9410     boolean is_free = FALSE;
9411
9412     if (level.use_life_bugs)
9413       is_free = (IS_FREE(xx, yy));
9414     else
9415       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9416
9417     if (xx == ax && yy == ay)           // field in the middle
9418     {
9419       if (num_neighbours < life_parameter[0] ||
9420           num_neighbours > life_parameter[1])
9421       {
9422         Tile[xx][yy] = EL_EMPTY;
9423         if (Tile[xx][yy] != old_element)
9424           TEST_DrawLevelField(xx, yy);
9425         Stop[xx][yy] = TRUE;
9426         changed = TRUE;
9427       }
9428     }
9429     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9430     {                                   // free border field
9431       if (num_neighbours >= life_parameter[2] &&
9432           num_neighbours <= life_parameter[3])
9433       {
9434         Tile[xx][yy] = element;
9435         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9436         if (Tile[xx][yy] != old_element)
9437           TEST_DrawLevelField(xx, yy);
9438         Stop[xx][yy] = TRUE;
9439         changed = TRUE;
9440       }
9441     }
9442   }
9443
9444   if (changed)
9445     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9446                    SND_GAME_OF_LIFE_GROWING);
9447 }
9448
9449 static void InitRobotWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunRobotWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9457 }
9458
9459 static void StopRobotWheel(int x, int y)
9460 {
9461   if (game.robot_wheel_x == x &&
9462       game.robot_wheel_y == y)
9463   {
9464     game.robot_wheel_x = -1;
9465     game.robot_wheel_y = -1;
9466     game.robot_wheel_active = FALSE;
9467   }
9468 }
9469
9470 static void InitTimegateWheel(int x, int y)
9471 {
9472   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9473 }
9474
9475 static void RunTimegateWheel(int x, int y)
9476 {
9477   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9478 }
9479
9480 static void InitMagicBallDelay(int x, int y)
9481 {
9482   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9483 }
9484
9485 static void ActivateMagicBall(int bx, int by)
9486 {
9487   int x, y;
9488
9489   if (level.ball_random)
9490   {
9491     int pos_border = RND(8);    // select one of the eight border elements
9492     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9493     int xx = pos_content % 3;
9494     int yy = pos_content / 3;
9495
9496     x = bx - 1 + xx;
9497     y = by - 1 + yy;
9498
9499     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9500       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9501   }
9502   else
9503   {
9504     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9505     {
9506       int xx = x - bx + 1;
9507       int yy = y - by + 1;
9508
9509       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9510         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9511     }
9512   }
9513
9514   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9515 }
9516
9517 static void CheckExit(int x, int y)
9518 {
9519   if (game.gems_still_needed > 0 ||
9520       game.sokoban_fields_still_needed > 0 ||
9521       game.sokoban_objects_still_needed > 0 ||
9522       game.lights_still_needed > 0)
9523   {
9524     int element = Tile[x][y];
9525     int graphic = el2img(element);
9526
9527     if (IS_ANIMATED(graphic))
9528       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9529
9530     return;
9531   }
9532
9533   // do not re-open exit door closed after last player
9534   if (game.all_players_gone)
9535     return;
9536
9537   Tile[x][y] = EL_EXIT_OPENING;
9538
9539   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9540 }
9541
9542 static void CheckExitEM(int x, int y)
9543 {
9544   if (game.gems_still_needed > 0 ||
9545       game.sokoban_fields_still_needed > 0 ||
9546       game.sokoban_objects_still_needed > 0 ||
9547       game.lights_still_needed > 0)
9548   {
9549     int element = Tile[x][y];
9550     int graphic = el2img(element);
9551
9552     if (IS_ANIMATED(graphic))
9553       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9554
9555     return;
9556   }
9557
9558   // do not re-open exit door closed after last player
9559   if (game.all_players_gone)
9560     return;
9561
9562   Tile[x][y] = EL_EM_EXIT_OPENING;
9563
9564   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9565 }
9566
9567 static void CheckExitSteel(int x, int y)
9568 {
9569   if (game.gems_still_needed > 0 ||
9570       game.sokoban_fields_still_needed > 0 ||
9571       game.sokoban_objects_still_needed > 0 ||
9572       game.lights_still_needed > 0)
9573   {
9574     int element = Tile[x][y];
9575     int graphic = el2img(element);
9576
9577     if (IS_ANIMATED(graphic))
9578       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9579
9580     return;
9581   }
9582
9583   // do not re-open exit door closed after last player
9584   if (game.all_players_gone)
9585     return;
9586
9587   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9588
9589   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9590 }
9591
9592 static void CheckExitSteelEM(int x, int y)
9593 {
9594   if (game.gems_still_needed > 0 ||
9595       game.sokoban_fields_still_needed > 0 ||
9596       game.sokoban_objects_still_needed > 0 ||
9597       game.lights_still_needed > 0)
9598   {
9599     int element = Tile[x][y];
9600     int graphic = el2img(element);
9601
9602     if (IS_ANIMATED(graphic))
9603       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9604
9605     return;
9606   }
9607
9608   // do not re-open exit door closed after last player
9609   if (game.all_players_gone)
9610     return;
9611
9612   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9613
9614   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9615 }
9616
9617 static void CheckExitSP(int x, int y)
9618 {
9619   if (game.gems_still_needed > 0)
9620   {
9621     int element = Tile[x][y];
9622     int graphic = el2img(element);
9623
9624     if (IS_ANIMATED(graphic))
9625       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9626
9627     return;
9628   }
9629
9630   // do not re-open exit door closed after last player
9631   if (game.all_players_gone)
9632     return;
9633
9634   Tile[x][y] = EL_SP_EXIT_OPENING;
9635
9636   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9637 }
9638
9639 static void CloseAllOpenTimegates(void)
9640 {
9641   int x, y;
9642
9643   SCAN_PLAYFIELD(x, y)
9644   {
9645     int element = Tile[x][y];
9646
9647     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9648     {
9649       Tile[x][y] = EL_TIMEGATE_CLOSING;
9650
9651       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9652     }
9653   }
9654 }
9655
9656 static void DrawTwinkleOnField(int x, int y)
9657 {
9658   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9659     return;
9660
9661   if (Tile[x][y] == EL_BD_DIAMOND)
9662     return;
9663
9664   if (MovDelay[x][y] == 0)      // next animation frame
9665     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9666
9667   if (MovDelay[x][y] != 0)      // wait some time before next frame
9668   {
9669     MovDelay[x][y]--;
9670
9671     DrawLevelElementAnimation(x, y, Tile[x][y]);
9672
9673     if (MovDelay[x][y] != 0)
9674     {
9675       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9676                                            10 - MovDelay[x][y]);
9677
9678       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9679     }
9680   }
9681 }
9682
9683 static void MauerWaechst(int x, int y)
9684 {
9685   int delay = 6;
9686
9687   if (!MovDelay[x][y])          // next animation frame
9688     MovDelay[x][y] = 3 * delay;
9689
9690   if (MovDelay[x][y])           // wait some time before next frame
9691   {
9692     MovDelay[x][y]--;
9693
9694     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9695     {
9696       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9697       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9698
9699       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9700     }
9701
9702     if (!MovDelay[x][y])
9703     {
9704       if (MovDir[x][y] == MV_LEFT)
9705       {
9706         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9707           TEST_DrawLevelField(x - 1, y);
9708       }
9709       else if (MovDir[x][y] == MV_RIGHT)
9710       {
9711         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9712           TEST_DrawLevelField(x + 1, y);
9713       }
9714       else if (MovDir[x][y] == MV_UP)
9715       {
9716         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9717           TEST_DrawLevelField(x, y - 1);
9718       }
9719       else
9720       {
9721         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9722           TEST_DrawLevelField(x, y + 1);
9723       }
9724
9725       Tile[x][y] = Store[x][y];
9726       Store[x][y] = 0;
9727       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9728       TEST_DrawLevelField(x, y);
9729     }
9730   }
9731 }
9732
9733 static void MauerAbleger(int ax, int ay)
9734 {
9735   int element = Tile[ax][ay];
9736   int graphic = el2img(element);
9737   boolean oben_frei = FALSE, unten_frei = FALSE;
9738   boolean links_frei = FALSE, rechts_frei = FALSE;
9739   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9740   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9741   boolean new_wall = FALSE;
9742
9743   if (IS_ANIMATED(graphic))
9744     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9745
9746   if (!MovDelay[ax][ay])        // start building new wall
9747     MovDelay[ax][ay] = 6;
9748
9749   if (MovDelay[ax][ay])         // wait some time before building new wall
9750   {
9751     MovDelay[ax][ay]--;
9752     if (MovDelay[ax][ay])
9753       return;
9754   }
9755
9756   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9757     oben_frei = TRUE;
9758   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9759     unten_frei = TRUE;
9760   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9761     links_frei = TRUE;
9762   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9763     rechts_frei = TRUE;
9764
9765   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9766       element == EL_EXPANDABLE_WALL_ANY)
9767   {
9768     if (oben_frei)
9769     {
9770       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9771       Store[ax][ay-1] = element;
9772       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9773       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9774         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9775                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9776       new_wall = TRUE;
9777     }
9778     if (unten_frei)
9779     {
9780       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9781       Store[ax][ay+1] = element;
9782       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9783       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9784         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9785                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9786       new_wall = TRUE;
9787     }
9788   }
9789
9790   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9791       element == EL_EXPANDABLE_WALL_ANY ||
9792       element == EL_EXPANDABLE_WALL ||
9793       element == EL_BD_EXPANDABLE_WALL)
9794   {
9795     if (links_frei)
9796     {
9797       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9798       Store[ax-1][ay] = element;
9799       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9800       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9801         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9802                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9803       new_wall = TRUE;
9804     }
9805
9806     if (rechts_frei)
9807     {
9808       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9809       Store[ax+1][ay] = element;
9810       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9811       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9812         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9813                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9814       new_wall = TRUE;
9815     }
9816   }
9817
9818   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9819     TEST_DrawLevelField(ax, ay);
9820
9821   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9822     oben_massiv = TRUE;
9823   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9824     unten_massiv = TRUE;
9825   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9826     links_massiv = TRUE;
9827   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9828     rechts_massiv = TRUE;
9829
9830   if (((oben_massiv && unten_massiv) ||
9831        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9832        element == EL_EXPANDABLE_WALL) &&
9833       ((links_massiv && rechts_massiv) ||
9834        element == EL_EXPANDABLE_WALL_VERTICAL))
9835     Tile[ax][ay] = EL_WALL;
9836
9837   if (new_wall)
9838     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9839 }
9840
9841 static void MauerAblegerStahl(int ax, int ay)
9842 {
9843   int element = Tile[ax][ay];
9844   int graphic = el2img(element);
9845   boolean oben_frei = FALSE, unten_frei = FALSE;
9846   boolean links_frei = FALSE, rechts_frei = FALSE;
9847   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9848   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9849   boolean new_wall = FALSE;
9850
9851   if (IS_ANIMATED(graphic))
9852     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9853
9854   if (!MovDelay[ax][ay])        // start building new wall
9855     MovDelay[ax][ay] = 6;
9856
9857   if (MovDelay[ax][ay])         // wait some time before building new wall
9858   {
9859     MovDelay[ax][ay]--;
9860     if (MovDelay[ax][ay])
9861       return;
9862   }
9863
9864   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9865     oben_frei = TRUE;
9866   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9867     unten_frei = TRUE;
9868   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9869     links_frei = TRUE;
9870   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9871     rechts_frei = TRUE;
9872
9873   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9874       element == EL_EXPANDABLE_STEELWALL_ANY)
9875   {
9876     if (oben_frei)
9877     {
9878       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9879       Store[ax][ay-1] = element;
9880       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9881       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9882         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9883                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9884       new_wall = TRUE;
9885     }
9886     if (unten_frei)
9887     {
9888       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9889       Store[ax][ay+1] = element;
9890       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9891       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9892         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9893                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9894       new_wall = TRUE;
9895     }
9896   }
9897
9898   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9899       element == EL_EXPANDABLE_STEELWALL_ANY)
9900   {
9901     if (links_frei)
9902     {
9903       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9904       Store[ax-1][ay] = element;
9905       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9906       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9907         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9908                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9909       new_wall = TRUE;
9910     }
9911
9912     if (rechts_frei)
9913     {
9914       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9915       Store[ax+1][ay] = element;
9916       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9917       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9918         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9919                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9920       new_wall = TRUE;
9921     }
9922   }
9923
9924   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9925     oben_massiv = TRUE;
9926   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9927     unten_massiv = TRUE;
9928   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9929     links_massiv = TRUE;
9930   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9931     rechts_massiv = TRUE;
9932
9933   if (((oben_massiv && unten_massiv) ||
9934        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9935       ((links_massiv && rechts_massiv) ||
9936        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9937     Tile[ax][ay] = EL_STEELWALL;
9938
9939   if (new_wall)
9940     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9941 }
9942
9943 static void CheckForDragon(int x, int y)
9944 {
9945   int i, j;
9946   boolean dragon_found = FALSE;
9947   static int xy[4][2] =
9948   {
9949     { 0, -1 },
9950     { -1, 0 },
9951     { +1, 0 },
9952     { 0, +1 }
9953   };
9954
9955   for (i = 0; i < NUM_DIRECTIONS; i++)
9956   {
9957     for (j = 0; j < 4; j++)
9958     {
9959       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9960
9961       if (IN_LEV_FIELD(xx, yy) &&
9962           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9963       {
9964         if (Tile[xx][yy] == EL_DRAGON)
9965           dragon_found = TRUE;
9966       }
9967       else
9968         break;
9969     }
9970   }
9971
9972   if (!dragon_found)
9973   {
9974     for (i = 0; i < NUM_DIRECTIONS; i++)
9975     {
9976       for (j = 0; j < 3; j++)
9977       {
9978         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9979   
9980         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9981         {
9982           Tile[xx][yy] = EL_EMPTY;
9983           TEST_DrawLevelField(xx, yy);
9984         }
9985         else
9986           break;
9987       }
9988     }
9989   }
9990 }
9991
9992 static void InitBuggyBase(int x, int y)
9993 {
9994   int element = Tile[x][y];
9995   int activating_delay = FRAMES_PER_SECOND / 4;
9996
9997   ChangeDelay[x][y] =
9998     (element == EL_SP_BUGGY_BASE ?
9999      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10000      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10001      activating_delay :
10002      element == EL_SP_BUGGY_BASE_ACTIVE ?
10003      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10004 }
10005
10006 static void WarnBuggyBase(int x, int y)
10007 {
10008   int i;
10009   static int xy[4][2] =
10010   {
10011     { 0, -1 },
10012     { -1, 0 },
10013     { +1, 0 },
10014     { 0, +1 }
10015   };
10016
10017   for (i = 0; i < NUM_DIRECTIONS; i++)
10018   {
10019     int xx = x + xy[i][0];
10020     int yy = y + xy[i][1];
10021
10022     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10023     {
10024       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10025
10026       break;
10027     }
10028   }
10029 }
10030
10031 static void InitTrap(int x, int y)
10032 {
10033   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10034 }
10035
10036 static void ActivateTrap(int x, int y)
10037 {
10038   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10039 }
10040
10041 static void ChangeActiveTrap(int x, int y)
10042 {
10043   int graphic = IMG_TRAP_ACTIVE;
10044
10045   // if new animation frame was drawn, correct crumbled sand border
10046   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10047     TEST_DrawLevelFieldCrumbled(x, y);
10048 }
10049
10050 static int getSpecialActionElement(int element, int number, int base_element)
10051 {
10052   return (element != EL_EMPTY ? element :
10053           number != -1 ? base_element + number - 1 :
10054           EL_EMPTY);
10055 }
10056
10057 static int getModifiedActionNumber(int value_old, int operator, int operand,
10058                                    int value_min, int value_max)
10059 {
10060   int value_new = (operator == CA_MODE_SET      ? operand :
10061                    operator == CA_MODE_ADD      ? value_old + operand :
10062                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10063                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10064                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10065                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10066                    value_old);
10067
10068   return (value_new < value_min ? value_min :
10069           value_new > value_max ? value_max :
10070           value_new);
10071 }
10072
10073 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10074 {
10075   struct ElementInfo *ei = &element_info[element];
10076   struct ElementChangeInfo *change = &ei->change_page[page];
10077   int target_element = change->target_element;
10078   int action_type = change->action_type;
10079   int action_mode = change->action_mode;
10080   int action_arg = change->action_arg;
10081   int action_element = change->action_element;
10082   int i;
10083
10084   if (!change->has_action)
10085     return;
10086
10087   // ---------- determine action paramater values -----------------------------
10088
10089   int level_time_value =
10090     (level.time > 0 ? TimeLeft :
10091      TimePlayed);
10092
10093   int action_arg_element_raw =
10094     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10095      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10096      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10097      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10098      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10099      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10100      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10101      EL_EMPTY);
10102   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10103
10104   int action_arg_direction =
10105     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10106      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10107      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10108      change->actual_trigger_side :
10109      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10110      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10111      MV_NONE);
10112
10113   int action_arg_number_min =
10114     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10115      CA_ARG_MIN);
10116
10117   int action_arg_number_max =
10118     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10119      action_type == CA_SET_LEVEL_GEMS ? 999 :
10120      action_type == CA_SET_LEVEL_TIME ? 9999 :
10121      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10122      action_type == CA_SET_CE_VALUE ? 9999 :
10123      action_type == CA_SET_CE_SCORE ? 9999 :
10124      CA_ARG_MAX);
10125
10126   int action_arg_number_reset =
10127     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10128      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10129      action_type == CA_SET_LEVEL_TIME ? level.time :
10130      action_type == CA_SET_LEVEL_SCORE ? 0 :
10131      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10132      action_type == CA_SET_CE_SCORE ? 0 :
10133      0);
10134
10135   int action_arg_number =
10136     (action_arg <= CA_ARG_MAX ? action_arg :
10137      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10138      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10139      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10140      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10141      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10142      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10143      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10144      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10145      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10146      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10147      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10148      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10149      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10150      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10151      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10152      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10153      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10154      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10155      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10156      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10157      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10158      -1);
10159
10160   int action_arg_number_old =
10161     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10162      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10163      action_type == CA_SET_LEVEL_SCORE ? game.score :
10164      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10165      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10166      0);
10167
10168   int action_arg_number_new =
10169     getModifiedActionNumber(action_arg_number_old,
10170                             action_mode, action_arg_number,
10171                             action_arg_number_min, action_arg_number_max);
10172
10173   int trigger_player_bits =
10174     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10175      change->actual_trigger_player_bits : change->trigger_player);
10176
10177   int action_arg_player_bits =
10178     (action_arg >= CA_ARG_PLAYER_1 &&
10179      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10180      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10181      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10182      PLAYER_BITS_ANY);
10183
10184   // ---------- execute action  -----------------------------------------------
10185
10186   switch (action_type)
10187   {
10188     case CA_NO_ACTION:
10189     {
10190       return;
10191     }
10192
10193     // ---------- level actions  ----------------------------------------------
10194
10195     case CA_RESTART_LEVEL:
10196     {
10197       game.restart_level = TRUE;
10198
10199       break;
10200     }
10201
10202     case CA_SHOW_ENVELOPE:
10203     {
10204       int element = getSpecialActionElement(action_arg_element,
10205                                             action_arg_number, EL_ENVELOPE_1);
10206
10207       if (IS_ENVELOPE(element))
10208         local_player->show_envelope = element;
10209
10210       break;
10211     }
10212
10213     case CA_SET_LEVEL_TIME:
10214     {
10215       if (level.time > 0)       // only modify limited time value
10216       {
10217         TimeLeft = action_arg_number_new;
10218
10219         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10220
10221         DisplayGameControlValues();
10222
10223         if (!TimeLeft && setup.time_limit)
10224           for (i = 0; i < MAX_PLAYERS; i++)
10225             KillPlayer(&stored_player[i]);
10226       }
10227
10228       break;
10229     }
10230
10231     case CA_SET_LEVEL_SCORE:
10232     {
10233       game.score = action_arg_number_new;
10234
10235       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10236
10237       DisplayGameControlValues();
10238
10239       break;
10240     }
10241
10242     case CA_SET_LEVEL_GEMS:
10243     {
10244       game.gems_still_needed = action_arg_number_new;
10245
10246       game.snapshot.collected_item = TRUE;
10247
10248       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10249
10250       DisplayGameControlValues();
10251
10252       break;
10253     }
10254
10255     case CA_SET_LEVEL_WIND:
10256     {
10257       game.wind_direction = action_arg_direction;
10258
10259       break;
10260     }
10261
10262     case CA_SET_LEVEL_RANDOM_SEED:
10263     {
10264       // ensure that setting a new random seed while playing is predictable
10265       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10266
10267       break;
10268     }
10269
10270     // ---------- player actions  ---------------------------------------------
10271
10272     case CA_MOVE_PLAYER:
10273     case CA_MOVE_PLAYER_NEW:
10274     {
10275       // automatically move to the next field in specified direction
10276       for (i = 0; i < MAX_PLAYERS; i++)
10277         if (trigger_player_bits & (1 << i))
10278           if (action_type == CA_MOVE_PLAYER ||
10279               stored_player[i].MovPos == 0)
10280             stored_player[i].programmed_action = action_arg_direction;
10281
10282       break;
10283     }
10284
10285     case CA_EXIT_PLAYER:
10286     {
10287       for (i = 0; i < MAX_PLAYERS; i++)
10288         if (action_arg_player_bits & (1 << i))
10289           ExitPlayer(&stored_player[i]);
10290
10291       if (game.players_still_needed == 0)
10292         LevelSolved();
10293
10294       break;
10295     }
10296
10297     case CA_KILL_PLAYER:
10298     {
10299       for (i = 0; i < MAX_PLAYERS; i++)
10300         if (action_arg_player_bits & (1 << i))
10301           KillPlayer(&stored_player[i]);
10302
10303       break;
10304     }
10305
10306     case CA_SET_PLAYER_KEYS:
10307     {
10308       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10309       int element = getSpecialActionElement(action_arg_element,
10310                                             action_arg_number, EL_KEY_1);
10311
10312       if (IS_KEY(element))
10313       {
10314         for (i = 0; i < MAX_PLAYERS; i++)
10315         {
10316           if (trigger_player_bits & (1 << i))
10317           {
10318             stored_player[i].key[KEY_NR(element)] = key_state;
10319
10320             DrawGameDoorValues();
10321           }
10322         }
10323       }
10324
10325       break;
10326     }
10327
10328     case CA_SET_PLAYER_SPEED:
10329     {
10330       for (i = 0; i < MAX_PLAYERS; i++)
10331       {
10332         if (trigger_player_bits & (1 << i))
10333         {
10334           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10335
10336           if (action_arg == CA_ARG_SPEED_FASTER &&
10337               stored_player[i].cannot_move)
10338           {
10339             action_arg_number = STEPSIZE_VERY_SLOW;
10340           }
10341           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10342                    action_arg == CA_ARG_SPEED_FASTER)
10343           {
10344             action_arg_number = 2;
10345             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10346                            CA_MODE_MULTIPLY);
10347           }
10348           else if (action_arg == CA_ARG_NUMBER_RESET)
10349           {
10350             action_arg_number = level.initial_player_stepsize[i];
10351           }
10352
10353           move_stepsize =
10354             getModifiedActionNumber(move_stepsize,
10355                                     action_mode,
10356                                     action_arg_number,
10357                                     action_arg_number_min,
10358                                     action_arg_number_max);
10359
10360           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10361         }
10362       }
10363
10364       break;
10365     }
10366
10367     case CA_SET_PLAYER_SHIELD:
10368     {
10369       for (i = 0; i < MAX_PLAYERS; i++)
10370       {
10371         if (trigger_player_bits & (1 << i))
10372         {
10373           if (action_arg == CA_ARG_SHIELD_OFF)
10374           {
10375             stored_player[i].shield_normal_time_left = 0;
10376             stored_player[i].shield_deadly_time_left = 0;
10377           }
10378           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10379           {
10380             stored_player[i].shield_normal_time_left = 999999;
10381           }
10382           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10383           {
10384             stored_player[i].shield_normal_time_left = 999999;
10385             stored_player[i].shield_deadly_time_left = 999999;
10386           }
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_PLAYER_GRAVITY:
10394     {
10395       for (i = 0; i < MAX_PLAYERS; i++)
10396       {
10397         if (trigger_player_bits & (1 << i))
10398         {
10399           stored_player[i].gravity =
10400             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10401              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10402              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10403              stored_player[i].gravity);
10404         }
10405       }
10406
10407       break;
10408     }
10409
10410     case CA_SET_PLAYER_ARTWORK:
10411     {
10412       for (i = 0; i < MAX_PLAYERS; i++)
10413       {
10414         if (trigger_player_bits & (1 << i))
10415         {
10416           int artwork_element = action_arg_element;
10417
10418           if (action_arg == CA_ARG_ELEMENT_RESET)
10419             artwork_element =
10420               (level.use_artwork_element[i] ? level.artwork_element[i] :
10421                stored_player[i].element_nr);
10422
10423           if (stored_player[i].artwork_element != artwork_element)
10424             stored_player[i].Frame = 0;
10425
10426           stored_player[i].artwork_element = artwork_element;
10427
10428           SetPlayerWaiting(&stored_player[i], FALSE);
10429
10430           // set number of special actions for bored and sleeping animation
10431           stored_player[i].num_special_action_bored =
10432             get_num_special_action(artwork_element,
10433                                    ACTION_BORING_1, ACTION_BORING_LAST);
10434           stored_player[i].num_special_action_sleeping =
10435             get_num_special_action(artwork_element,
10436                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10437         }
10438       }
10439
10440       break;
10441     }
10442
10443     case CA_SET_PLAYER_INVENTORY:
10444     {
10445       for (i = 0; i < MAX_PLAYERS; i++)
10446       {
10447         struct PlayerInfo *player = &stored_player[i];
10448         int j, k;
10449
10450         if (trigger_player_bits & (1 << i))
10451         {
10452           int inventory_element = action_arg_element;
10453
10454           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10455               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10456               action_arg == CA_ARG_ELEMENT_ACTION)
10457           {
10458             int element = inventory_element;
10459             int collect_count = element_info[element].collect_count_initial;
10460
10461             if (!IS_CUSTOM_ELEMENT(element))
10462               collect_count = 1;
10463
10464             if (collect_count == 0)
10465               player->inventory_infinite_element = element;
10466             else
10467               for (k = 0; k < collect_count; k++)
10468                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10469                   player->inventory_element[player->inventory_size++] =
10470                     element;
10471           }
10472           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10473                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10474                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10475           {
10476             if (player->inventory_infinite_element != EL_UNDEFINED &&
10477                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10478                                      action_arg_element_raw))
10479               player->inventory_infinite_element = EL_UNDEFINED;
10480
10481             for (k = 0, j = 0; j < player->inventory_size; j++)
10482             {
10483               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10484                                         action_arg_element_raw))
10485                 player->inventory_element[k++] = player->inventory_element[j];
10486             }
10487
10488             player->inventory_size = k;
10489           }
10490           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10491           {
10492             if (player->inventory_size > 0)
10493             {
10494               for (j = 0; j < player->inventory_size - 1; j++)
10495                 player->inventory_element[j] = player->inventory_element[j + 1];
10496
10497               player->inventory_size--;
10498             }
10499           }
10500           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10501           {
10502             if (player->inventory_size > 0)
10503               player->inventory_size--;
10504           }
10505           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10506           {
10507             player->inventory_infinite_element = EL_UNDEFINED;
10508             player->inventory_size = 0;
10509           }
10510           else if (action_arg == CA_ARG_INVENTORY_RESET)
10511           {
10512             player->inventory_infinite_element = EL_UNDEFINED;
10513             player->inventory_size = 0;
10514
10515             if (level.use_initial_inventory[i])
10516             {
10517               for (j = 0; j < level.initial_inventory_size[i]; j++)
10518               {
10519                 int element = level.initial_inventory_content[i][j];
10520                 int collect_count = element_info[element].collect_count_initial;
10521
10522                 if (!IS_CUSTOM_ELEMENT(element))
10523                   collect_count = 1;
10524
10525                 if (collect_count == 0)
10526                   player->inventory_infinite_element = element;
10527                 else
10528                   for (k = 0; k < collect_count; k++)
10529                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10530                       player->inventory_element[player->inventory_size++] =
10531                         element;
10532               }
10533             }
10534           }
10535         }
10536       }
10537
10538       break;
10539     }
10540
10541     // ---------- CE actions  -------------------------------------------------
10542
10543     case CA_SET_CE_VALUE:
10544     {
10545       int last_ce_value = CustomValue[x][y];
10546
10547       CustomValue[x][y] = action_arg_number_new;
10548
10549       if (CustomValue[x][y] != last_ce_value)
10550       {
10551         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10552         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10553
10554         if (CustomValue[x][y] == 0)
10555         {
10556           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10557           ChangeCount[x][y] = 0;        // allow at least one more change
10558
10559           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10560           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10561         }
10562       }
10563
10564       break;
10565     }
10566
10567     case CA_SET_CE_SCORE:
10568     {
10569       int last_ce_score = ei->collect_score;
10570
10571       ei->collect_score = action_arg_number_new;
10572
10573       if (ei->collect_score != last_ce_score)
10574       {
10575         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10576         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10577
10578         if (ei->collect_score == 0)
10579         {
10580           int xx, yy;
10581
10582           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10583           ChangeCount[x][y] = 0;        // allow at least one more change
10584
10585           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10586           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10587
10588           /*
10589             This is a very special case that seems to be a mixture between
10590             CheckElementChange() and CheckTriggeredElementChange(): while
10591             the first one only affects single elements that are triggered
10592             directly, the second one affects multiple elements in the playfield
10593             that are triggered indirectly by another element. This is a third
10594             case: Changing the CE score always affects multiple identical CEs,
10595             so every affected CE must be checked, not only the single CE for
10596             which the CE score was changed in the first place (as every instance
10597             of that CE shares the same CE score, and therefore also can change)!
10598           */
10599           SCAN_PLAYFIELD(xx, yy)
10600           {
10601             if (Tile[xx][yy] == element)
10602               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10603                                  CE_SCORE_GETS_ZERO);
10604           }
10605         }
10606       }
10607
10608       break;
10609     }
10610
10611     case CA_SET_CE_ARTWORK:
10612     {
10613       int artwork_element = action_arg_element;
10614       boolean reset_frame = FALSE;
10615       int xx, yy;
10616
10617       if (action_arg == CA_ARG_ELEMENT_RESET)
10618         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10619                            element);
10620
10621       if (ei->gfx_element != artwork_element)
10622         reset_frame = TRUE;
10623
10624       ei->gfx_element = artwork_element;
10625
10626       SCAN_PLAYFIELD(xx, yy)
10627       {
10628         if (Tile[xx][yy] == element)
10629         {
10630           if (reset_frame)
10631           {
10632             ResetGfxAnimation(xx, yy);
10633             ResetRandomAnimationValue(xx, yy);
10634           }
10635
10636           TEST_DrawLevelField(xx, yy);
10637         }
10638       }
10639
10640       break;
10641     }
10642
10643     // ---------- engine actions  ---------------------------------------------
10644
10645     case CA_SET_ENGINE_SCAN_MODE:
10646     {
10647       InitPlayfieldScanMode(action_arg);
10648
10649       break;
10650     }
10651
10652     default:
10653       break;
10654   }
10655 }
10656
10657 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10658 {
10659   int old_element = Tile[x][y];
10660   int new_element = GetElementFromGroupElement(element);
10661   int previous_move_direction = MovDir[x][y];
10662   int last_ce_value = CustomValue[x][y];
10663   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10664   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10665   boolean add_player_onto_element = (new_element_is_player &&
10666                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10667                                      IS_WALKABLE(old_element));
10668
10669   if (!add_player_onto_element)
10670   {
10671     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10672       RemoveMovingField(x, y);
10673     else
10674       RemoveField(x, y);
10675
10676     Tile[x][y] = new_element;
10677
10678     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10679       MovDir[x][y] = previous_move_direction;
10680
10681     if (element_info[new_element].use_last_ce_value)
10682       CustomValue[x][y] = last_ce_value;
10683
10684     InitField_WithBug1(x, y, FALSE);
10685
10686     new_element = Tile[x][y];   // element may have changed
10687
10688     ResetGfxAnimation(x, y);
10689     ResetRandomAnimationValue(x, y);
10690
10691     TEST_DrawLevelField(x, y);
10692
10693     if (GFX_CRUMBLED(new_element))
10694       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10695   }
10696
10697   // check if element under the player changes from accessible to unaccessible
10698   // (needed for special case of dropping element which then changes)
10699   // (must be checked after creating new element for walkable group elements)
10700   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10701       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10702   {
10703     Bang(x, y);
10704
10705     return;
10706   }
10707
10708   // "ChangeCount" not set yet to allow "entered by player" change one time
10709   if (new_element_is_player)
10710     RelocatePlayer(x, y, new_element);
10711
10712   if (is_change)
10713     ChangeCount[x][y]++;        // count number of changes in the same frame
10714
10715   TestIfBadThingTouchesPlayer(x, y);
10716   TestIfPlayerTouchesCustomElement(x, y);
10717   TestIfElementTouchesCustomElement(x, y);
10718 }
10719
10720 static void CreateField(int x, int y, int element)
10721 {
10722   CreateFieldExt(x, y, element, FALSE);
10723 }
10724
10725 static void CreateElementFromChange(int x, int y, int element)
10726 {
10727   element = GET_VALID_RUNTIME_ELEMENT(element);
10728
10729   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10730   {
10731     int old_element = Tile[x][y];
10732
10733     // prevent changed element from moving in same engine frame
10734     // unless both old and new element can either fall or move
10735     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10736         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10737       Stop[x][y] = TRUE;
10738   }
10739
10740   CreateFieldExt(x, y, element, TRUE);
10741 }
10742
10743 static boolean ChangeElement(int x, int y, int element, int page)
10744 {
10745   struct ElementInfo *ei = &element_info[element];
10746   struct ElementChangeInfo *change = &ei->change_page[page];
10747   int ce_value = CustomValue[x][y];
10748   int ce_score = ei->collect_score;
10749   int target_element;
10750   int old_element = Tile[x][y];
10751
10752   // always use default change event to prevent running into a loop
10753   if (ChangeEvent[x][y] == -1)
10754     ChangeEvent[x][y] = CE_DELAY;
10755
10756   if (ChangeEvent[x][y] == CE_DELAY)
10757   {
10758     // reset actual trigger element, trigger player and action element
10759     change->actual_trigger_element = EL_EMPTY;
10760     change->actual_trigger_player = EL_EMPTY;
10761     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10762     change->actual_trigger_side = CH_SIDE_NONE;
10763     change->actual_trigger_ce_value = 0;
10764     change->actual_trigger_ce_score = 0;
10765   }
10766
10767   // do not change elements more than a specified maximum number of changes
10768   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10769     return FALSE;
10770
10771   ChangeCount[x][y]++;          // count number of changes in the same frame
10772
10773   if (change->explode)
10774   {
10775     Bang(x, y);
10776
10777     return TRUE;
10778   }
10779
10780   if (change->use_target_content)
10781   {
10782     boolean complete_replace = TRUE;
10783     boolean can_replace[3][3];
10784     int xx, yy;
10785
10786     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10787     {
10788       boolean is_empty;
10789       boolean is_walkable;
10790       boolean is_diggable;
10791       boolean is_collectible;
10792       boolean is_removable;
10793       boolean is_destructible;
10794       int ex = x + xx - 1;
10795       int ey = y + yy - 1;
10796       int content_element = change->target_content.e[xx][yy];
10797       int e;
10798
10799       can_replace[xx][yy] = TRUE;
10800
10801       if (ex == x && ey == y)   // do not check changing element itself
10802         continue;
10803
10804       if (content_element == EL_EMPTY_SPACE)
10805       {
10806         can_replace[xx][yy] = FALSE;    // do not replace border with space
10807
10808         continue;
10809       }
10810
10811       if (!IN_LEV_FIELD(ex, ey))
10812       {
10813         can_replace[xx][yy] = FALSE;
10814         complete_replace = FALSE;
10815
10816         continue;
10817       }
10818
10819       e = Tile[ex][ey];
10820
10821       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10822         e = MovingOrBlocked2Element(ex, ey);
10823
10824       is_empty = (IS_FREE(ex, ey) ||
10825                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10826
10827       is_walkable     = (is_empty || IS_WALKABLE(e));
10828       is_diggable     = (is_empty || IS_DIGGABLE(e));
10829       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10830       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10831       is_removable    = (is_diggable || is_collectible);
10832
10833       can_replace[xx][yy] =
10834         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10835           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10836           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10837           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10838           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10839           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10840          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10841
10842       if (!can_replace[xx][yy])
10843         complete_replace = FALSE;
10844     }
10845
10846     if (!change->only_if_complete || complete_replace)
10847     {
10848       boolean something_has_changed = FALSE;
10849
10850       if (change->only_if_complete && change->use_random_replace &&
10851           RND(100) < change->random_percentage)
10852         return FALSE;
10853
10854       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10855       {
10856         int ex = x + xx - 1;
10857         int ey = y + yy - 1;
10858         int content_element;
10859
10860         if (can_replace[xx][yy] && (!change->use_random_replace ||
10861                                     RND(100) < change->random_percentage))
10862         {
10863           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10864             RemoveMovingField(ex, ey);
10865
10866           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10867
10868           content_element = change->target_content.e[xx][yy];
10869           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10870                                               ce_value, ce_score);
10871
10872           CreateElementFromChange(ex, ey, target_element);
10873
10874           something_has_changed = TRUE;
10875
10876           // for symmetry reasons, freeze newly created border elements
10877           if (ex != x || ey != y)
10878             Stop[ex][ey] = TRUE;        // no more moving in this frame
10879         }
10880       }
10881
10882       if (something_has_changed)
10883       {
10884         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10885         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10886       }
10887     }
10888   }
10889   else
10890   {
10891     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10892                                         ce_value, ce_score);
10893
10894     if (element == EL_DIAGONAL_GROWING ||
10895         element == EL_DIAGONAL_SHRINKING)
10896     {
10897       target_element = Store[x][y];
10898
10899       Store[x][y] = EL_EMPTY;
10900     }
10901
10902     // special case: element changes to player (and may be kept if walkable)
10903     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10904       CreateElementFromChange(x, y, EL_EMPTY);
10905
10906     CreateElementFromChange(x, y, target_element);
10907
10908     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10909     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10910   }
10911
10912   // this uses direct change before indirect change
10913   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10914
10915   return TRUE;
10916 }
10917
10918 static void HandleElementChange(int x, int y, int page)
10919 {
10920   int element = MovingOrBlocked2Element(x, y);
10921   struct ElementInfo *ei = &element_info[element];
10922   struct ElementChangeInfo *change = &ei->change_page[page];
10923   boolean handle_action_before_change = FALSE;
10924
10925 #ifdef DEBUG
10926   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10927       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10928   {
10929     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10930           x, y, element, element_info[element].token_name);
10931     Debug("game:playing:HandleElementChange", "This should never happen!");
10932   }
10933 #endif
10934
10935   // this can happen with classic bombs on walkable, changing elements
10936   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10937   {
10938     return;
10939   }
10940
10941   if (ChangeDelay[x][y] == 0)           // initialize element change
10942   {
10943     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10944
10945     if (change->can_change)
10946     {
10947       // !!! not clear why graphic animation should be reset at all here !!!
10948       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10949       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10950
10951       /*
10952         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10953
10954         When using an animation frame delay of 1 (this only happens with
10955         "sp_zonk.moving.left/right" in the classic graphics), the default
10956         (non-moving) animation shows wrong animation frames (while the
10957         moving animation, like "sp_zonk.moving.left/right", is correct,
10958         so this graphical bug never shows up with the classic graphics).
10959         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10960         be drawn instead of the correct frames 0,1,2,3. This is caused by
10961         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10962         an element change: First when the change delay ("ChangeDelay[][]")
10963         counter has reached zero after decrementing, then a second time in
10964         the next frame (after "GfxFrame[][]" was already incremented) when
10965         "ChangeDelay[][]" is reset to the initial delay value again.
10966
10967         This causes frame 0 to be drawn twice, while the last frame won't
10968         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10969
10970         As some animations may already be cleverly designed around this bug
10971         (at least the "Snake Bite" snake tail animation does this), it cannot
10972         simply be fixed here without breaking such existing animations.
10973         Unfortunately, it cannot easily be detected if a graphics set was
10974         designed "before" or "after" the bug was fixed. As a workaround,
10975         a new graphics set option "game.graphics_engine_version" was added
10976         to be able to specify the game's major release version for which the
10977         graphics set was designed, which can then be used to decide if the
10978         bugfix should be used (version 4 and above) or not (version 3 or
10979         below, or if no version was specified at all, as with old sets).
10980
10981         (The wrong/fixed animation frames can be tested with the test level set
10982         "test_gfxframe" and level "000", which contains a specially prepared
10983         custom element at level position (x/y) == (11/9) which uses the zonk
10984         animation mentioned above. Using "game.graphics_engine_version: 4"
10985         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10986         This can also be seen from the debug output for this test element.)
10987       */
10988
10989       // when a custom element is about to change (for example by change delay),
10990       // do not reset graphic animation when the custom element is moving
10991       if (game.graphics_engine_version < 4 &&
10992           !IS_MOVING(x, y))
10993       {
10994         ResetGfxAnimation(x, y);
10995         ResetRandomAnimationValue(x, y);
10996       }
10997
10998       if (change->pre_change_function)
10999         change->pre_change_function(x, y);
11000     }
11001   }
11002
11003   ChangeDelay[x][y]--;
11004
11005   if (ChangeDelay[x][y] != 0)           // continue element change
11006   {
11007     if (change->can_change)
11008     {
11009       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11010
11011       if (IS_ANIMATED(graphic))
11012         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11013
11014       if (change->change_function)
11015         change->change_function(x, y);
11016     }
11017   }
11018   else                                  // finish element change
11019   {
11020     if (ChangePage[x][y] != -1)         // remember page from delayed change
11021     {
11022       page = ChangePage[x][y];
11023       ChangePage[x][y] = -1;
11024
11025       change = &ei->change_page[page];
11026     }
11027
11028     if (IS_MOVING(x, y))                // never change a running system ;-)
11029     {
11030       ChangeDelay[x][y] = 1;            // try change after next move step
11031       ChangePage[x][y] = page;          // remember page to use for change
11032
11033       return;
11034     }
11035
11036     // special case: set new level random seed before changing element
11037     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11038       handle_action_before_change = TRUE;
11039
11040     if (change->has_action && handle_action_before_change)
11041       ExecuteCustomElementAction(x, y, element, page);
11042
11043     if (change->can_change)
11044     {
11045       if (ChangeElement(x, y, element, page))
11046       {
11047         if (change->post_change_function)
11048           change->post_change_function(x, y);
11049       }
11050     }
11051
11052     if (change->has_action && !handle_action_before_change)
11053       ExecuteCustomElementAction(x, y, element, page);
11054   }
11055 }
11056
11057 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11058                                               int trigger_element,
11059                                               int trigger_event,
11060                                               int trigger_player,
11061                                               int trigger_side,
11062                                               int trigger_page)
11063 {
11064   boolean change_done_any = FALSE;
11065   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11066   int i;
11067
11068   if (!(trigger_events[trigger_element][trigger_event]))
11069     return FALSE;
11070
11071   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11072
11073   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11074   {
11075     int element = EL_CUSTOM_START + i;
11076     boolean change_done = FALSE;
11077     int p;
11078
11079     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11080         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11081       continue;
11082
11083     for (p = 0; p < element_info[element].num_change_pages; p++)
11084     {
11085       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11086
11087       if (change->can_change_or_has_action &&
11088           change->has_event[trigger_event] &&
11089           change->trigger_side & trigger_side &&
11090           change->trigger_player & trigger_player &&
11091           change->trigger_page & trigger_page_bits &&
11092           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11093       {
11094         change->actual_trigger_element = trigger_element;
11095         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11096         change->actual_trigger_player_bits = trigger_player;
11097         change->actual_trigger_side = trigger_side;
11098         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11099         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11100
11101         if ((change->can_change && !change_done) || change->has_action)
11102         {
11103           int x, y;
11104
11105           SCAN_PLAYFIELD(x, y)
11106           {
11107             if (Tile[x][y] == element)
11108             {
11109               if (change->can_change && !change_done)
11110               {
11111                 // if element already changed in this frame, not only prevent
11112                 // another element change (checked in ChangeElement()), but
11113                 // also prevent additional element actions for this element
11114
11115                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11116                     !level.use_action_after_change_bug)
11117                   continue;
11118
11119                 ChangeDelay[x][y] = 1;
11120                 ChangeEvent[x][y] = trigger_event;
11121
11122                 HandleElementChange(x, y, p);
11123               }
11124               else if (change->has_action)
11125               {
11126                 // if element already changed in this frame, not only prevent
11127                 // another element change (checked in ChangeElement()), but
11128                 // also prevent additional element actions for this element
11129
11130                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11131                     !level.use_action_after_change_bug)
11132                   continue;
11133
11134                 ExecuteCustomElementAction(x, y, element, p);
11135                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11136               }
11137             }
11138           }
11139
11140           if (change->can_change)
11141           {
11142             change_done = TRUE;
11143             change_done_any = TRUE;
11144           }
11145         }
11146       }
11147     }
11148   }
11149
11150   RECURSION_LOOP_DETECTION_END();
11151
11152   return change_done_any;
11153 }
11154
11155 static boolean CheckElementChangeExt(int x, int y,
11156                                      int element,
11157                                      int trigger_element,
11158                                      int trigger_event,
11159                                      int trigger_player,
11160                                      int trigger_side)
11161 {
11162   boolean change_done = FALSE;
11163   int p;
11164
11165   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11166       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11167     return FALSE;
11168
11169   if (Tile[x][y] == EL_BLOCKED)
11170   {
11171     Blocked2Moving(x, y, &x, &y);
11172     element = Tile[x][y];
11173   }
11174
11175   // check if element has already changed or is about to change after moving
11176   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11177        Tile[x][y] != element) ||
11178
11179       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11180        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11181         ChangePage[x][y] != -1)))
11182     return FALSE;
11183
11184   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11185
11186   for (p = 0; p < element_info[element].num_change_pages; p++)
11187   {
11188     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11189
11190     /* check trigger element for all events where the element that is checked
11191        for changing interacts with a directly adjacent element -- this is
11192        different to element changes that affect other elements to change on the
11193        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11194     boolean check_trigger_element =
11195       (trigger_event == CE_NEXT_TO_X ||
11196        trigger_event == CE_TOUCHING_X ||
11197        trigger_event == CE_HITTING_X ||
11198        trigger_event == CE_HIT_BY_X ||
11199        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11200
11201     if (change->can_change_or_has_action &&
11202         change->has_event[trigger_event] &&
11203         change->trigger_side & trigger_side &&
11204         change->trigger_player & trigger_player &&
11205         (!check_trigger_element ||
11206          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11207     {
11208       change->actual_trigger_element = trigger_element;
11209       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11210       change->actual_trigger_player_bits = trigger_player;
11211       change->actual_trigger_side = trigger_side;
11212       change->actual_trigger_ce_value = CustomValue[x][y];
11213       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11214
11215       // special case: trigger element not at (x,y) position for some events
11216       if (check_trigger_element)
11217       {
11218         static struct
11219         {
11220           int dx, dy;
11221         } move_xy[] =
11222           {
11223             {  0,  0 },
11224             { -1,  0 },
11225             { +1,  0 },
11226             {  0,  0 },
11227             {  0, -1 },
11228             {  0,  0 }, { 0, 0 }, { 0, 0 },
11229             {  0, +1 }
11230           };
11231
11232         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11233         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11234
11235         change->actual_trigger_ce_value = CustomValue[xx][yy];
11236         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11237       }
11238
11239       if (change->can_change && !change_done)
11240       {
11241         ChangeDelay[x][y] = 1;
11242         ChangeEvent[x][y] = trigger_event;
11243
11244         HandleElementChange(x, y, p);
11245
11246         change_done = TRUE;
11247       }
11248       else if (change->has_action)
11249       {
11250         ExecuteCustomElementAction(x, y, element, p);
11251         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11252       }
11253     }
11254   }
11255
11256   RECURSION_LOOP_DETECTION_END();
11257
11258   return change_done;
11259 }
11260
11261 static void PlayPlayerSound(struct PlayerInfo *player)
11262 {
11263   int jx = player->jx, jy = player->jy;
11264   int sound_element = player->artwork_element;
11265   int last_action = player->last_action_waiting;
11266   int action = player->action_waiting;
11267
11268   if (player->is_waiting)
11269   {
11270     if (action != last_action)
11271       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11272     else
11273       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11274   }
11275   else
11276   {
11277     if (action != last_action)
11278       StopSound(element_info[sound_element].sound[last_action]);
11279
11280     if (last_action == ACTION_SLEEPING)
11281       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11282   }
11283 }
11284
11285 static void PlayAllPlayersSound(void)
11286 {
11287   int i;
11288
11289   for (i = 0; i < MAX_PLAYERS; i++)
11290     if (stored_player[i].active)
11291       PlayPlayerSound(&stored_player[i]);
11292 }
11293
11294 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11295 {
11296   boolean last_waiting = player->is_waiting;
11297   int move_dir = player->MovDir;
11298
11299   player->dir_waiting = move_dir;
11300   player->last_action_waiting = player->action_waiting;
11301
11302   if (is_waiting)
11303   {
11304     if (!last_waiting)          // not waiting -> waiting
11305     {
11306       player->is_waiting = TRUE;
11307
11308       player->frame_counter_bored =
11309         FrameCounter +
11310         game.player_boring_delay_fixed +
11311         GetSimpleRandom(game.player_boring_delay_random);
11312       player->frame_counter_sleeping =
11313         FrameCounter +
11314         game.player_sleeping_delay_fixed +
11315         GetSimpleRandom(game.player_sleeping_delay_random);
11316
11317       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11318     }
11319
11320     if (game.player_sleeping_delay_fixed +
11321         game.player_sleeping_delay_random > 0 &&
11322         player->anim_delay_counter == 0 &&
11323         player->post_delay_counter == 0 &&
11324         FrameCounter >= player->frame_counter_sleeping)
11325       player->is_sleeping = TRUE;
11326     else if (game.player_boring_delay_fixed +
11327              game.player_boring_delay_random > 0 &&
11328              FrameCounter >= player->frame_counter_bored)
11329       player->is_bored = TRUE;
11330
11331     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11332                               player->is_bored ? ACTION_BORING :
11333                               ACTION_WAITING);
11334
11335     if (player->is_sleeping && player->use_murphy)
11336     {
11337       // special case for sleeping Murphy when leaning against non-free tile
11338
11339       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11340           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11341            !IS_MOVING(player->jx - 1, player->jy)))
11342         move_dir = MV_LEFT;
11343       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11344                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11345                 !IS_MOVING(player->jx + 1, player->jy)))
11346         move_dir = MV_RIGHT;
11347       else
11348         player->is_sleeping = FALSE;
11349
11350       player->dir_waiting = move_dir;
11351     }
11352
11353     if (player->is_sleeping)
11354     {
11355       if (player->num_special_action_sleeping > 0)
11356       {
11357         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11358         {
11359           int last_special_action = player->special_action_sleeping;
11360           int num_special_action = player->num_special_action_sleeping;
11361           int special_action =
11362             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11363              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11364              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11365              last_special_action + 1 : ACTION_SLEEPING);
11366           int special_graphic =
11367             el_act_dir2img(player->artwork_element, special_action, move_dir);
11368
11369           player->anim_delay_counter =
11370             graphic_info[special_graphic].anim_delay_fixed +
11371             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11372           player->post_delay_counter =
11373             graphic_info[special_graphic].post_delay_fixed +
11374             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11375
11376           player->special_action_sleeping = special_action;
11377         }
11378
11379         if (player->anim_delay_counter > 0)
11380         {
11381           player->action_waiting = player->special_action_sleeping;
11382           player->anim_delay_counter--;
11383         }
11384         else if (player->post_delay_counter > 0)
11385         {
11386           player->post_delay_counter--;
11387         }
11388       }
11389     }
11390     else if (player->is_bored)
11391     {
11392       if (player->num_special_action_bored > 0)
11393       {
11394         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11395         {
11396           int special_action =
11397             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11398           int special_graphic =
11399             el_act_dir2img(player->artwork_element, special_action, move_dir);
11400
11401           player->anim_delay_counter =
11402             graphic_info[special_graphic].anim_delay_fixed +
11403             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11404           player->post_delay_counter =
11405             graphic_info[special_graphic].post_delay_fixed +
11406             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11407
11408           player->special_action_bored = special_action;
11409         }
11410
11411         if (player->anim_delay_counter > 0)
11412         {
11413           player->action_waiting = player->special_action_bored;
11414           player->anim_delay_counter--;
11415         }
11416         else if (player->post_delay_counter > 0)
11417         {
11418           player->post_delay_counter--;
11419         }
11420       }
11421     }
11422   }
11423   else if (last_waiting)        // waiting -> not waiting
11424   {
11425     player->is_waiting = FALSE;
11426     player->is_bored = FALSE;
11427     player->is_sleeping = FALSE;
11428
11429     player->frame_counter_bored = -1;
11430     player->frame_counter_sleeping = -1;
11431
11432     player->anim_delay_counter = 0;
11433     player->post_delay_counter = 0;
11434
11435     player->dir_waiting = player->MovDir;
11436     player->action_waiting = ACTION_DEFAULT;
11437
11438     player->special_action_bored = ACTION_DEFAULT;
11439     player->special_action_sleeping = ACTION_DEFAULT;
11440   }
11441 }
11442
11443 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11444 {
11445   if ((!player->is_moving  && player->was_moving) ||
11446       (player->MovPos == 0 && player->was_moving) ||
11447       (player->is_snapping && !player->was_snapping) ||
11448       (player->is_dropping && !player->was_dropping))
11449   {
11450     if (!CheckSaveEngineSnapshotToList())
11451       return;
11452
11453     player->was_moving = FALSE;
11454     player->was_snapping = TRUE;
11455     player->was_dropping = TRUE;
11456   }
11457   else
11458   {
11459     if (player->is_moving)
11460       player->was_moving = TRUE;
11461
11462     if (!player->is_snapping)
11463       player->was_snapping = FALSE;
11464
11465     if (!player->is_dropping)
11466       player->was_dropping = FALSE;
11467   }
11468
11469   static struct MouseActionInfo mouse_action_last = { 0 };
11470   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11471   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11472
11473   if (new_released)
11474     CheckSaveEngineSnapshotToList();
11475
11476   mouse_action_last = mouse_action;
11477 }
11478
11479 static void CheckSingleStepMode(struct PlayerInfo *player)
11480 {
11481   if (tape.single_step && tape.recording && !tape.pausing)
11482   {
11483     // as it is called "single step mode", just return to pause mode when the
11484     // player stopped moving after one tile (or never starts moving at all)
11485     // (reverse logic needed here in case single step mode used in team mode)
11486     if (player->is_moving ||
11487         player->is_pushing ||
11488         player->is_dropping_pressed ||
11489         player->effective_mouse_action.button)
11490       game.enter_single_step_mode = FALSE;
11491   }
11492
11493   CheckSaveEngineSnapshot(player);
11494 }
11495
11496 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11497 {
11498   int left      = player_action & JOY_LEFT;
11499   int right     = player_action & JOY_RIGHT;
11500   int up        = player_action & JOY_UP;
11501   int down      = player_action & JOY_DOWN;
11502   int button1   = player_action & JOY_BUTTON_1;
11503   int button2   = player_action & JOY_BUTTON_2;
11504   int dx        = (left ? -1 : right ? 1 : 0);
11505   int dy        = (up   ? -1 : down  ? 1 : 0);
11506
11507   if (!player->active || tape.pausing)
11508     return 0;
11509
11510   if (player_action)
11511   {
11512     if (button1)
11513       SnapField(player, dx, dy);
11514     else
11515     {
11516       if (button2)
11517         DropElement(player);
11518
11519       MovePlayer(player, dx, dy);
11520     }
11521
11522     CheckSingleStepMode(player);
11523
11524     SetPlayerWaiting(player, FALSE);
11525
11526     return player_action;
11527   }
11528   else
11529   {
11530     // no actions for this player (no input at player's configured device)
11531
11532     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11533     SnapField(player, 0, 0);
11534     CheckGravityMovementWhenNotMoving(player);
11535
11536     if (player->MovPos == 0)
11537       SetPlayerWaiting(player, TRUE);
11538
11539     if (player->MovPos == 0)    // needed for tape.playing
11540       player->is_moving = FALSE;
11541
11542     player->is_dropping = FALSE;
11543     player->is_dropping_pressed = FALSE;
11544     player->drop_pressed_delay = 0;
11545
11546     CheckSingleStepMode(player);
11547
11548     return 0;
11549   }
11550 }
11551
11552 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11553                                          byte *tape_action)
11554 {
11555   if (!tape.use_mouse_actions)
11556     return;
11557
11558   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11559   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11560   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11561 }
11562
11563 static void SetTapeActionFromMouseAction(byte *tape_action,
11564                                          struct MouseActionInfo *mouse_action)
11565 {
11566   if (!tape.use_mouse_actions)
11567     return;
11568
11569   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11570   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11571   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11572 }
11573
11574 static void CheckLevelSolved(void)
11575 {
11576   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11577   {
11578     if (game_em.level_solved &&
11579         !game_em.game_over)                             // game won
11580     {
11581       LevelSolved();
11582
11583       game_em.game_over = TRUE;
11584
11585       game.all_players_gone = TRUE;
11586     }
11587
11588     if (game_em.game_over)                              // game lost
11589       game.all_players_gone = TRUE;
11590   }
11591   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11592   {
11593     if (game_sp.level_solved &&
11594         !game_sp.game_over)                             // game won
11595     {
11596       LevelSolved();
11597
11598       game_sp.game_over = TRUE;
11599
11600       game.all_players_gone = TRUE;
11601     }
11602
11603     if (game_sp.game_over)                              // game lost
11604       game.all_players_gone = TRUE;
11605   }
11606   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11607   {
11608     if (game_mm.level_solved &&
11609         !game_mm.game_over)                             // game won
11610     {
11611       LevelSolved();
11612
11613       game_mm.game_over = TRUE;
11614
11615       game.all_players_gone = TRUE;
11616     }
11617
11618     if (game_mm.game_over)                              // game lost
11619       game.all_players_gone = TRUE;
11620   }
11621 }
11622
11623 static void CheckLevelTime_StepCounter(void)
11624 {
11625   int i;
11626
11627   TimePlayed++;
11628
11629   if (TimeLeft > 0)
11630   {
11631     TimeLeft--;
11632
11633     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11634       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11635
11636     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11637
11638     DisplayGameControlValues();
11639
11640     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11641       for (i = 0; i < MAX_PLAYERS; i++)
11642         KillPlayer(&stored_player[i]);
11643   }
11644   else if (game.no_time_limit && !game.all_players_gone)
11645   {
11646     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11647
11648     DisplayGameControlValues();
11649   }
11650 }
11651
11652 static void CheckLevelTime(void)
11653 {
11654   int i;
11655
11656   if (TimeFrames >= FRAMES_PER_SECOND)
11657   {
11658     TimeFrames = 0;
11659     TapeTime++;
11660
11661     for (i = 0; i < MAX_PLAYERS; i++)
11662     {
11663       struct PlayerInfo *player = &stored_player[i];
11664
11665       if (SHIELD_ON(player))
11666       {
11667         player->shield_normal_time_left--;
11668
11669         if (player->shield_deadly_time_left > 0)
11670           player->shield_deadly_time_left--;
11671       }
11672     }
11673
11674     if (!game.LevelSolved && !level.use_step_counter)
11675     {
11676       TimePlayed++;
11677
11678       if (TimeLeft > 0)
11679       {
11680         TimeLeft--;
11681
11682         if (TimeLeft <= 10 && setup.time_limit)
11683           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11684
11685         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11686            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11687
11688         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11689
11690         if (!TimeLeft && setup.time_limit)
11691         {
11692           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11693             game_em.lev->killed_out_of_time = TRUE;
11694           else
11695             for (i = 0; i < MAX_PLAYERS; i++)
11696               KillPlayer(&stored_player[i]);
11697         }
11698       }
11699       else if (game.no_time_limit && !game.all_players_gone)
11700       {
11701         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11702       }
11703
11704       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11705     }
11706
11707     if (tape.recording || tape.playing)
11708       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11709   }
11710
11711   if (tape.recording || tape.playing)
11712     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11713
11714   UpdateAndDisplayGameControlValues();
11715 }
11716
11717 void AdvanceFrameAndPlayerCounters(int player_nr)
11718 {
11719   int i;
11720
11721   // advance frame counters (global frame counter and time frame counter)
11722   FrameCounter++;
11723   TimeFrames++;
11724
11725   // advance player counters (counters for move delay, move animation etc.)
11726   for (i = 0; i < MAX_PLAYERS; i++)
11727   {
11728     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11729     int move_delay_value = stored_player[i].move_delay_value;
11730     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11731
11732     if (!advance_player_counters)       // not all players may be affected
11733       continue;
11734
11735     if (move_frames == 0)       // less than one move per game frame
11736     {
11737       int stepsize = TILEX / move_delay_value;
11738       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11739       int count = (stored_player[i].is_moving ?
11740                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11741
11742       if (count % delay == 0)
11743         move_frames = 1;
11744     }
11745
11746     stored_player[i].Frame += move_frames;
11747
11748     if (stored_player[i].MovPos != 0)
11749       stored_player[i].StepFrame += move_frames;
11750
11751     if (stored_player[i].move_delay > 0)
11752       stored_player[i].move_delay--;
11753
11754     // due to bugs in previous versions, counter must count up, not down
11755     if (stored_player[i].push_delay != -1)
11756       stored_player[i].push_delay++;
11757
11758     if (stored_player[i].drop_delay > 0)
11759       stored_player[i].drop_delay--;
11760
11761     if (stored_player[i].is_dropping_pressed)
11762       stored_player[i].drop_pressed_delay++;
11763   }
11764 }
11765
11766 void StartGameActions(boolean init_network_game, boolean record_tape,
11767                       int random_seed)
11768 {
11769   unsigned int new_random_seed = InitRND(random_seed);
11770
11771   if (record_tape)
11772     TapeStartRecording(new_random_seed);
11773
11774   if (setup.auto_pause_on_start && !tape.pausing)
11775     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11776
11777   if (init_network_game)
11778   {
11779     SendToServer_LevelFile();
11780     SendToServer_StartPlaying();
11781
11782     return;
11783   }
11784
11785   InitGame();
11786 }
11787
11788 static void GameActionsExt(void)
11789 {
11790 #if 0
11791   static unsigned int game_frame_delay = 0;
11792 #endif
11793   unsigned int game_frame_delay_value;
11794   byte *recorded_player_action;
11795   byte summarized_player_action = 0;
11796   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11797   int i;
11798
11799   // detect endless loops, caused by custom element programming
11800   if (recursion_loop_detected && recursion_loop_depth == 0)
11801   {
11802     char *message = getStringCat3("Internal Error! Element ",
11803                                   EL_NAME(recursion_loop_element),
11804                                   " caused endless loop! Quit the game?");
11805
11806     Warn("element '%s' caused endless loop in game engine",
11807          EL_NAME(recursion_loop_element));
11808
11809     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11810
11811     recursion_loop_detected = FALSE;    // if game should be continued
11812
11813     free(message);
11814
11815     return;
11816   }
11817
11818   if (game.restart_level)
11819     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11820
11821   CheckLevelSolved();
11822
11823   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11824     GameWon();
11825
11826   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11827     TapeStop();
11828
11829   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11830     return;
11831
11832   game_frame_delay_value =
11833     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11834
11835   if (tape.playing && tape.warp_forward && !tape.pausing)
11836     game_frame_delay_value = 0;
11837
11838   SetVideoFrameDelay(game_frame_delay_value);
11839
11840   // (de)activate virtual buttons depending on current game status
11841   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11842   {
11843     if (game.all_players_gone)  // if no players there to be controlled anymore
11844       SetOverlayActive(FALSE);
11845     else if (!tape.playing)     // if game continues after tape stopped playing
11846       SetOverlayActive(TRUE);
11847   }
11848
11849 #if 0
11850 #if 0
11851   // ---------- main game synchronization point ----------
11852
11853   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11854
11855   Debug("game:playing:skip", "skip == %d", skip);
11856
11857 #else
11858   // ---------- main game synchronization point ----------
11859
11860   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11861 #endif
11862 #endif
11863
11864   if (network_playing && !network_player_action_received)
11865   {
11866     // try to get network player actions in time
11867
11868     // last chance to get network player actions without main loop delay
11869     HandleNetworking();
11870
11871     // game was quit by network peer
11872     if (game_status != GAME_MODE_PLAYING)
11873       return;
11874
11875     // check if network player actions still missing and game still running
11876     if (!network_player_action_received && !checkGameEnded())
11877       return;           // failed to get network player actions in time
11878
11879     // do not yet reset "network_player_action_received" (for tape.pausing)
11880   }
11881
11882   if (tape.pausing)
11883     return;
11884
11885   // at this point we know that we really continue executing the game
11886
11887   network_player_action_received = FALSE;
11888
11889   // when playing tape, read previously recorded player input from tape data
11890   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11891
11892   local_player->effective_mouse_action = local_player->mouse_action;
11893
11894   if (recorded_player_action != NULL)
11895     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11896                                  recorded_player_action);
11897
11898   // TapePlayAction() may return NULL when toggling to "pause before death"
11899   if (tape.pausing)
11900     return;
11901
11902   if (tape.set_centered_player)
11903   {
11904     game.centered_player_nr_next = tape.centered_player_nr_next;
11905     game.set_centered_player = TRUE;
11906   }
11907
11908   for (i = 0; i < MAX_PLAYERS; i++)
11909   {
11910     summarized_player_action |= stored_player[i].action;
11911
11912     if (!network_playing && (game.team_mode || tape.playing))
11913       stored_player[i].effective_action = stored_player[i].action;
11914   }
11915
11916   if (network_playing && !checkGameEnded())
11917     SendToServer_MovePlayer(summarized_player_action);
11918
11919   // summarize all actions at local players mapped input device position
11920   // (this allows using different input devices in single player mode)
11921   if (!network.enabled && !game.team_mode)
11922     stored_player[map_player_action[local_player->index_nr]].effective_action =
11923       summarized_player_action;
11924
11925   // summarize all actions at centered player in local team mode
11926   if (tape.recording &&
11927       setup.team_mode && !network.enabled &&
11928       setup.input_on_focus &&
11929       game.centered_player_nr != -1)
11930   {
11931     for (i = 0; i < MAX_PLAYERS; i++)
11932       stored_player[map_player_action[i]].effective_action =
11933         (i == game.centered_player_nr ? summarized_player_action : 0);
11934   }
11935
11936   if (recorded_player_action != NULL)
11937     for (i = 0; i < MAX_PLAYERS; i++)
11938       stored_player[i].effective_action = recorded_player_action[i];
11939
11940   for (i = 0; i < MAX_PLAYERS; i++)
11941   {
11942     tape_action[i] = stored_player[i].effective_action;
11943
11944     /* (this may happen in the RND game engine if a player was not present on
11945        the playfield on level start, but appeared later from a custom element */
11946     if (setup.team_mode &&
11947         tape.recording &&
11948         tape_action[i] &&
11949         !tape.player_participates[i])
11950       tape.player_participates[i] = TRUE;
11951   }
11952
11953   SetTapeActionFromMouseAction(tape_action,
11954                                &local_player->effective_mouse_action);
11955
11956   // only record actions from input devices, but not programmed actions
11957   if (tape.recording)
11958     TapeRecordAction(tape_action);
11959
11960   // remember if game was played (especially after tape stopped playing)
11961   if (!tape.playing && summarized_player_action)
11962     game.GamePlayed = TRUE;
11963
11964 #if USE_NEW_PLAYER_ASSIGNMENTS
11965   // !!! also map player actions in single player mode !!!
11966   // if (game.team_mode)
11967   if (1)
11968   {
11969     byte mapped_action[MAX_PLAYERS];
11970
11971 #if DEBUG_PLAYER_ACTIONS
11972     for (i = 0; i < MAX_PLAYERS; i++)
11973       DebugContinued("", "%d, ", stored_player[i].effective_action);
11974 #endif
11975
11976     for (i = 0; i < MAX_PLAYERS; i++)
11977       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11978
11979     for (i = 0; i < MAX_PLAYERS; i++)
11980       stored_player[i].effective_action = mapped_action[i];
11981
11982 #if DEBUG_PLAYER_ACTIONS
11983     DebugContinued("", "=> ");
11984     for (i = 0; i < MAX_PLAYERS; i++)
11985       DebugContinued("", "%d, ", stored_player[i].effective_action);
11986     DebugContinued("game:playing:player", "\n");
11987 #endif
11988   }
11989 #if DEBUG_PLAYER_ACTIONS
11990   else
11991   {
11992     for (i = 0; i < MAX_PLAYERS; i++)
11993       DebugContinued("", "%d, ", stored_player[i].effective_action);
11994     DebugContinued("game:playing:player", "\n");
11995   }
11996 #endif
11997 #endif
11998
11999   for (i = 0; i < MAX_PLAYERS; i++)
12000   {
12001     // allow engine snapshot in case of changed movement attempt
12002     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12003         (stored_player[i].effective_action & KEY_MOTION))
12004       game.snapshot.changed_action = TRUE;
12005
12006     // allow engine snapshot in case of snapping/dropping attempt
12007     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12008         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12009       game.snapshot.changed_action = TRUE;
12010
12011     game.snapshot.last_action[i] = stored_player[i].effective_action;
12012   }
12013
12014   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12015   {
12016     GameActions_EM_Main();
12017   }
12018   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12019   {
12020     GameActions_SP_Main();
12021   }
12022   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12023   {
12024     GameActions_MM_Main();
12025   }
12026   else
12027   {
12028     GameActions_RND_Main();
12029   }
12030
12031   BlitScreenToBitmap(backbuffer);
12032
12033   CheckLevelSolved();
12034   CheckLevelTime();
12035
12036   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12037
12038   if (global.show_frames_per_second)
12039   {
12040     static unsigned int fps_counter = 0;
12041     static int fps_frames = 0;
12042     unsigned int fps_delay_ms = Counter() - fps_counter;
12043
12044     fps_frames++;
12045
12046     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12047     {
12048       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12049
12050       fps_frames = 0;
12051       fps_counter = Counter();
12052
12053       // always draw FPS to screen after FPS value was updated
12054       redraw_mask |= REDRAW_FPS;
12055     }
12056
12057     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12058     if (GetDrawDeactivationMask() == REDRAW_NONE)
12059       redraw_mask |= REDRAW_FPS;
12060   }
12061 }
12062
12063 static void GameActions_CheckSaveEngineSnapshot(void)
12064 {
12065   if (!game.snapshot.save_snapshot)
12066     return;
12067
12068   // clear flag for saving snapshot _before_ saving snapshot
12069   game.snapshot.save_snapshot = FALSE;
12070
12071   SaveEngineSnapshotToList();
12072 }
12073
12074 void GameActions(void)
12075 {
12076   GameActionsExt();
12077
12078   GameActions_CheckSaveEngineSnapshot();
12079 }
12080
12081 void GameActions_EM_Main(void)
12082 {
12083   byte effective_action[MAX_PLAYERS];
12084   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12085   int i;
12086
12087   for (i = 0; i < MAX_PLAYERS; i++)
12088     effective_action[i] = stored_player[i].effective_action;
12089
12090   GameActions_EM(effective_action, warp_mode);
12091 }
12092
12093 void GameActions_SP_Main(void)
12094 {
12095   byte effective_action[MAX_PLAYERS];
12096   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12097   int i;
12098
12099   for (i = 0; i < MAX_PLAYERS; i++)
12100     effective_action[i] = stored_player[i].effective_action;
12101
12102   GameActions_SP(effective_action, warp_mode);
12103
12104   for (i = 0; i < MAX_PLAYERS; i++)
12105   {
12106     if (stored_player[i].force_dropping)
12107       stored_player[i].action |= KEY_BUTTON_DROP;
12108
12109     stored_player[i].force_dropping = FALSE;
12110   }
12111 }
12112
12113 void GameActions_MM_Main(void)
12114 {
12115   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12116
12117   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12118 }
12119
12120 void GameActions_RND_Main(void)
12121 {
12122   GameActions_RND();
12123 }
12124
12125 void GameActions_RND(void)
12126 {
12127   static struct MouseActionInfo mouse_action_last = { 0 };
12128   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12129   int magic_wall_x = 0, magic_wall_y = 0;
12130   int i, x, y, element, graphic, last_gfx_frame;
12131
12132   InitPlayfieldScanModeVars();
12133
12134   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12135   {
12136     SCAN_PLAYFIELD(x, y)
12137     {
12138       ChangeCount[x][y] = 0;
12139       ChangeEvent[x][y] = -1;
12140     }
12141   }
12142
12143   if (game.set_centered_player)
12144   {
12145     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12146
12147     // switching to "all players" only possible if all players fit to screen
12148     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12149     {
12150       game.centered_player_nr_next = game.centered_player_nr;
12151       game.set_centered_player = FALSE;
12152     }
12153
12154     // do not switch focus to non-existing (or non-active) player
12155     if (game.centered_player_nr_next >= 0 &&
12156         !stored_player[game.centered_player_nr_next].active)
12157     {
12158       game.centered_player_nr_next = game.centered_player_nr;
12159       game.set_centered_player = FALSE;
12160     }
12161   }
12162
12163   if (game.set_centered_player &&
12164       ScreenMovPos == 0)        // screen currently aligned at tile position
12165   {
12166     int sx, sy;
12167
12168     if (game.centered_player_nr_next == -1)
12169     {
12170       setScreenCenteredToAllPlayers(&sx, &sy);
12171     }
12172     else
12173     {
12174       sx = stored_player[game.centered_player_nr_next].jx;
12175       sy = stored_player[game.centered_player_nr_next].jy;
12176     }
12177
12178     game.centered_player_nr = game.centered_player_nr_next;
12179     game.set_centered_player = FALSE;
12180
12181     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12182     DrawGameDoorValues();
12183   }
12184
12185   // check single step mode (set flag and clear again if any player is active)
12186   game.enter_single_step_mode =
12187     (tape.single_step && tape.recording && !tape.pausing);
12188
12189   for (i = 0; i < MAX_PLAYERS; i++)
12190   {
12191     int actual_player_action = stored_player[i].effective_action;
12192
12193 #if 1
12194     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12195        - rnd_equinox_tetrachloride 048
12196        - rnd_equinox_tetrachloride_ii 096
12197        - rnd_emanuel_schmieg 002
12198        - doctor_sloan_ww 001, 020
12199     */
12200     if (stored_player[i].MovPos == 0)
12201       CheckGravityMovement(&stored_player[i]);
12202 #endif
12203
12204     // overwrite programmed action with tape action
12205     if (stored_player[i].programmed_action)
12206       actual_player_action = stored_player[i].programmed_action;
12207
12208     PlayerActions(&stored_player[i], actual_player_action);
12209
12210     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12211   }
12212
12213   // single step pause mode may already have been toggled by "ScrollPlayer()"
12214   if (game.enter_single_step_mode && !tape.pausing)
12215     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12216
12217   ScrollScreen(NULL, SCROLL_GO_ON);
12218
12219   /* for backwards compatibility, the following code emulates a fixed bug that
12220      occured when pushing elements (causing elements that just made their last
12221      pushing step to already (if possible) make their first falling step in the
12222      same game frame, which is bad); this code is also needed to use the famous
12223      "spring push bug" which is used in older levels and might be wanted to be
12224      used also in newer levels, but in this case the buggy pushing code is only
12225      affecting the "spring" element and no other elements */
12226
12227   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12228   {
12229     for (i = 0; i < MAX_PLAYERS; i++)
12230     {
12231       struct PlayerInfo *player = &stored_player[i];
12232       int x = player->jx;
12233       int y = player->jy;
12234
12235       if (player->active && player->is_pushing && player->is_moving &&
12236           IS_MOVING(x, y) &&
12237           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12238            Tile[x][y] == EL_SPRING))
12239       {
12240         ContinueMoving(x, y);
12241
12242         // continue moving after pushing (this is actually a bug)
12243         if (!IS_MOVING(x, y))
12244           Stop[x][y] = FALSE;
12245       }
12246     }
12247   }
12248
12249   SCAN_PLAYFIELD(x, y)
12250   {
12251     Last[x][y] = Tile[x][y];
12252
12253     ChangeCount[x][y] = 0;
12254     ChangeEvent[x][y] = -1;
12255
12256     // this must be handled before main playfield loop
12257     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12258     {
12259       MovDelay[x][y]--;
12260       if (MovDelay[x][y] <= 0)
12261         RemoveField(x, y);
12262     }
12263
12264     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12265     {
12266       MovDelay[x][y]--;
12267       if (MovDelay[x][y] <= 0)
12268       {
12269         int element = Store[x][y];
12270         int move_direction = MovDir[x][y];
12271         int player_index_bit = Store2[x][y];
12272
12273         Store[x][y] = 0;
12274         Store2[x][y] = 0;
12275
12276         RemoveField(x, y);
12277         TEST_DrawLevelField(x, y);
12278
12279         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12280
12281         if (IS_ENVELOPE(element))
12282           local_player->show_envelope = element;
12283       }
12284     }
12285
12286 #if DEBUG
12287     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12288     {
12289       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12290             x, y);
12291       Debug("game:playing:GameActions_RND", "This should never happen!");
12292
12293       ChangePage[x][y] = -1;
12294     }
12295 #endif
12296
12297     Stop[x][y] = FALSE;
12298     if (WasJustMoving[x][y] > 0)
12299       WasJustMoving[x][y]--;
12300     if (WasJustFalling[x][y] > 0)
12301       WasJustFalling[x][y]--;
12302     if (CheckCollision[x][y] > 0)
12303       CheckCollision[x][y]--;
12304     if (CheckImpact[x][y] > 0)
12305       CheckImpact[x][y]--;
12306
12307     GfxFrame[x][y]++;
12308
12309     /* reset finished pushing action (not done in ContinueMoving() to allow
12310        continuous pushing animation for elements with zero push delay) */
12311     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12312     {
12313       ResetGfxAnimation(x, y);
12314       TEST_DrawLevelField(x, y);
12315     }
12316
12317 #if DEBUG
12318     if (IS_BLOCKED(x, y))
12319     {
12320       int oldx, oldy;
12321
12322       Blocked2Moving(x, y, &oldx, &oldy);
12323       if (!IS_MOVING(oldx, oldy))
12324       {
12325         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12326         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12327         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12328         Debug("game:playing:GameActions_RND", "This should never happen!");
12329       }
12330     }
12331 #endif
12332   }
12333
12334   if (mouse_action.button)
12335   {
12336     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12337     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12338
12339     x = mouse_action.lx;
12340     y = mouse_action.ly;
12341     element = Tile[x][y];
12342
12343     if (new_button)
12344     {
12345       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12346       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12347                                          ch_button);
12348     }
12349
12350     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12351     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12352                                        ch_button);
12353
12354     if (level.use_step_counter)
12355     {
12356       boolean counted_click = FALSE;
12357
12358       // element clicked that can change when clicked/pressed
12359       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12360           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12361            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12362         counted_click = TRUE;
12363
12364       // element clicked that can trigger change when clicked/pressed
12365       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12366           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12367         counted_click = TRUE;
12368
12369       if (new_button && counted_click)
12370         CheckLevelTime_StepCounter();
12371     }
12372   }
12373
12374   SCAN_PLAYFIELD(x, y)
12375   {
12376     element = Tile[x][y];
12377     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12378     last_gfx_frame = GfxFrame[x][y];
12379
12380     if (element == EL_EMPTY)
12381       graphic = el2img(GfxElementEmpty[x][y]);
12382
12383     ResetGfxFrame(x, y);
12384
12385     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12386       DrawLevelGraphicAnimation(x, y, graphic);
12387
12388     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12389         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12390       ResetRandomAnimationValue(x, y);
12391
12392     SetRandomAnimationValue(x, y);
12393
12394     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12395
12396     if (IS_INACTIVE(element))
12397     {
12398       if (IS_ANIMATED(graphic))
12399         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12400
12401       continue;
12402     }
12403
12404     // this may take place after moving, so 'element' may have changed
12405     if (IS_CHANGING(x, y) &&
12406         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12407     {
12408       int page = element_info[element].event_page_nr[CE_DELAY];
12409
12410       HandleElementChange(x, y, page);
12411
12412       element = Tile[x][y];
12413       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12414     }
12415
12416     CheckNextToConditions(x, y);
12417
12418     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12419     {
12420       StartMoving(x, y);
12421
12422       element = Tile[x][y];
12423       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12424
12425       if (IS_ANIMATED(graphic) &&
12426           !IS_MOVING(x, y) &&
12427           !Stop[x][y])
12428         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12429
12430       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12431         TEST_DrawTwinkleOnField(x, y);
12432     }
12433     else if (element == EL_ACID)
12434     {
12435       if (!Stop[x][y])
12436         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12437     }
12438     else if ((element == EL_EXIT_OPEN ||
12439               element == EL_EM_EXIT_OPEN ||
12440               element == EL_SP_EXIT_OPEN ||
12441               element == EL_STEEL_EXIT_OPEN ||
12442               element == EL_EM_STEEL_EXIT_OPEN ||
12443               element == EL_SP_TERMINAL ||
12444               element == EL_SP_TERMINAL_ACTIVE ||
12445               element == EL_EXTRA_TIME ||
12446               element == EL_SHIELD_NORMAL ||
12447               element == EL_SHIELD_DEADLY) &&
12448              IS_ANIMATED(graphic))
12449       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12450     else if (IS_MOVING(x, y))
12451       ContinueMoving(x, y);
12452     else if (IS_ACTIVE_BOMB(element))
12453       CheckDynamite(x, y);
12454     else if (element == EL_AMOEBA_GROWING)
12455       AmoebaGrowing(x, y);
12456     else if (element == EL_AMOEBA_SHRINKING)
12457       AmoebaShrinking(x, y);
12458
12459 #if !USE_NEW_AMOEBA_CODE
12460     else if (IS_AMOEBALIVE(element))
12461       AmoebaReproduce(x, y);
12462 #endif
12463
12464     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12465       Life(x, y);
12466     else if (element == EL_EXIT_CLOSED)
12467       CheckExit(x, y);
12468     else if (element == EL_EM_EXIT_CLOSED)
12469       CheckExitEM(x, y);
12470     else if (element == EL_STEEL_EXIT_CLOSED)
12471       CheckExitSteel(x, y);
12472     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12473       CheckExitSteelEM(x, y);
12474     else if (element == EL_SP_EXIT_CLOSED)
12475       CheckExitSP(x, y);
12476     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12477              element == EL_EXPANDABLE_STEELWALL_GROWING)
12478       MauerWaechst(x, y);
12479     else if (element == EL_EXPANDABLE_WALL ||
12480              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12481              element == EL_EXPANDABLE_WALL_VERTICAL ||
12482              element == EL_EXPANDABLE_WALL_ANY ||
12483              element == EL_BD_EXPANDABLE_WALL)
12484       MauerAbleger(x, y);
12485     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12486              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12487              element == EL_EXPANDABLE_STEELWALL_ANY)
12488       MauerAblegerStahl(x, y);
12489     else if (element == EL_FLAMES)
12490       CheckForDragon(x, y);
12491     else if (element == EL_EXPLOSION)
12492       ; // drawing of correct explosion animation is handled separately
12493     else if (element == EL_ELEMENT_SNAPPING ||
12494              element == EL_DIAGONAL_SHRINKING ||
12495              element == EL_DIAGONAL_GROWING)
12496     {
12497       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12498
12499       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12500     }
12501     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12502       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12503
12504     if (IS_BELT_ACTIVE(element))
12505       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12506
12507     if (game.magic_wall_active)
12508     {
12509       int jx = local_player->jx, jy = local_player->jy;
12510
12511       // play the element sound at the position nearest to the player
12512       if ((element == EL_MAGIC_WALL_FULL ||
12513            element == EL_MAGIC_WALL_ACTIVE ||
12514            element == EL_MAGIC_WALL_EMPTYING ||
12515            element == EL_BD_MAGIC_WALL_FULL ||
12516            element == EL_BD_MAGIC_WALL_ACTIVE ||
12517            element == EL_BD_MAGIC_WALL_EMPTYING ||
12518            element == EL_DC_MAGIC_WALL_FULL ||
12519            element == EL_DC_MAGIC_WALL_ACTIVE ||
12520            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12521           ABS(x - jx) + ABS(y - jy) <
12522           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12523       {
12524         magic_wall_x = x;
12525         magic_wall_y = y;
12526       }
12527     }
12528   }
12529
12530 #if USE_NEW_AMOEBA_CODE
12531   // new experimental amoeba growth stuff
12532   if (!(FrameCounter % 8))
12533   {
12534     static unsigned int random = 1684108901;
12535
12536     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12537     {
12538       x = RND(lev_fieldx);
12539       y = RND(lev_fieldy);
12540       element = Tile[x][y];
12541
12542       if (!IS_PLAYER(x,y) &&
12543           (element == EL_EMPTY ||
12544            CAN_GROW_INTO(element) ||
12545            element == EL_QUICKSAND_EMPTY ||
12546            element == EL_QUICKSAND_FAST_EMPTY ||
12547            element == EL_ACID_SPLASH_LEFT ||
12548            element == EL_ACID_SPLASH_RIGHT))
12549       {
12550         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12551             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12552             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12553             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12554           Tile[x][y] = EL_AMOEBA_DROP;
12555       }
12556
12557       random = random * 129 + 1;
12558     }
12559   }
12560 #endif
12561
12562   game.explosions_delayed = FALSE;
12563
12564   SCAN_PLAYFIELD(x, y)
12565   {
12566     element = Tile[x][y];
12567
12568     if (ExplodeField[x][y])
12569       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12570     else if (element == EL_EXPLOSION)
12571       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12572
12573     ExplodeField[x][y] = EX_TYPE_NONE;
12574   }
12575
12576   game.explosions_delayed = TRUE;
12577
12578   if (game.magic_wall_active)
12579   {
12580     if (!(game.magic_wall_time_left % 4))
12581     {
12582       int element = Tile[magic_wall_x][magic_wall_y];
12583
12584       if (element == EL_BD_MAGIC_WALL_FULL ||
12585           element == EL_BD_MAGIC_WALL_ACTIVE ||
12586           element == EL_BD_MAGIC_WALL_EMPTYING)
12587         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12588       else if (element == EL_DC_MAGIC_WALL_FULL ||
12589                element == EL_DC_MAGIC_WALL_ACTIVE ||
12590                element == EL_DC_MAGIC_WALL_EMPTYING)
12591         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12592       else
12593         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12594     }
12595
12596     if (game.magic_wall_time_left > 0)
12597     {
12598       game.magic_wall_time_left--;
12599
12600       if (!game.magic_wall_time_left)
12601       {
12602         SCAN_PLAYFIELD(x, y)
12603         {
12604           element = Tile[x][y];
12605
12606           if (element == EL_MAGIC_WALL_ACTIVE ||
12607               element == EL_MAGIC_WALL_FULL)
12608           {
12609             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12610             TEST_DrawLevelField(x, y);
12611           }
12612           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12613                    element == EL_BD_MAGIC_WALL_FULL)
12614           {
12615             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12616             TEST_DrawLevelField(x, y);
12617           }
12618           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12619                    element == EL_DC_MAGIC_WALL_FULL)
12620           {
12621             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12622             TEST_DrawLevelField(x, y);
12623           }
12624         }
12625
12626         game.magic_wall_active = FALSE;
12627       }
12628     }
12629   }
12630
12631   if (game.light_time_left > 0)
12632   {
12633     game.light_time_left--;
12634
12635     if (game.light_time_left == 0)
12636       RedrawAllLightSwitchesAndInvisibleElements();
12637   }
12638
12639   if (game.timegate_time_left > 0)
12640   {
12641     game.timegate_time_left--;
12642
12643     if (game.timegate_time_left == 0)
12644       CloseAllOpenTimegates();
12645   }
12646
12647   if (game.lenses_time_left > 0)
12648   {
12649     game.lenses_time_left--;
12650
12651     if (game.lenses_time_left == 0)
12652       RedrawAllInvisibleElementsForLenses();
12653   }
12654
12655   if (game.magnify_time_left > 0)
12656   {
12657     game.magnify_time_left--;
12658
12659     if (game.magnify_time_left == 0)
12660       RedrawAllInvisibleElementsForMagnifier();
12661   }
12662
12663   for (i = 0; i < MAX_PLAYERS; i++)
12664   {
12665     struct PlayerInfo *player = &stored_player[i];
12666
12667     if (SHIELD_ON(player))
12668     {
12669       if (player->shield_deadly_time_left)
12670         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12671       else if (player->shield_normal_time_left)
12672         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12673     }
12674   }
12675
12676 #if USE_DELAYED_GFX_REDRAW
12677   SCAN_PLAYFIELD(x, y)
12678   {
12679     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12680     {
12681       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12682          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12683
12684       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12685         DrawLevelField(x, y);
12686
12687       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12688         DrawLevelFieldCrumbled(x, y);
12689
12690       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12691         DrawLevelFieldCrumbledNeighbours(x, y);
12692
12693       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12694         DrawTwinkleOnField(x, y);
12695     }
12696
12697     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12698   }
12699 #endif
12700
12701   DrawAllPlayers();
12702   PlayAllPlayersSound();
12703
12704   for (i = 0; i < MAX_PLAYERS; i++)
12705   {
12706     struct PlayerInfo *player = &stored_player[i];
12707
12708     if (player->show_envelope != 0 && (!player->active ||
12709                                        player->MovPos == 0))
12710     {
12711       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12712
12713       player->show_envelope = 0;
12714     }
12715   }
12716
12717   // use random number generator in every frame to make it less predictable
12718   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12719     RND(1);
12720
12721   mouse_action_last = mouse_action;
12722 }
12723
12724 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12725 {
12726   int min_x = x, min_y = y, max_x = x, max_y = y;
12727   int scr_fieldx = getScreenFieldSizeX();
12728   int scr_fieldy = getScreenFieldSizeY();
12729   int i;
12730
12731   for (i = 0; i < MAX_PLAYERS; i++)
12732   {
12733     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12734
12735     if (!stored_player[i].active || &stored_player[i] == player)
12736       continue;
12737
12738     min_x = MIN(min_x, jx);
12739     min_y = MIN(min_y, jy);
12740     max_x = MAX(max_x, jx);
12741     max_y = MAX(max_y, jy);
12742   }
12743
12744   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12745 }
12746
12747 static boolean AllPlayersInVisibleScreen(void)
12748 {
12749   int i;
12750
12751   for (i = 0; i < MAX_PLAYERS; i++)
12752   {
12753     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12754
12755     if (!stored_player[i].active)
12756       continue;
12757
12758     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12759       return FALSE;
12760   }
12761
12762   return TRUE;
12763 }
12764
12765 void ScrollLevel(int dx, int dy)
12766 {
12767   int scroll_offset = 2 * TILEX_VAR;
12768   int x, y;
12769
12770   BlitBitmap(drawto_field, drawto_field,
12771              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12772              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12773              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12774              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12775              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12776              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12777
12778   if (dx != 0)
12779   {
12780     x = (dx == 1 ? BX1 : BX2);
12781     for (y = BY1; y <= BY2; y++)
12782       DrawScreenField(x, y);
12783   }
12784
12785   if (dy != 0)
12786   {
12787     y = (dy == 1 ? BY1 : BY2);
12788     for (x = BX1; x <= BX2; x++)
12789       DrawScreenField(x, y);
12790   }
12791
12792   redraw_mask |= REDRAW_FIELD;
12793 }
12794
12795 static boolean canFallDown(struct PlayerInfo *player)
12796 {
12797   int jx = player->jx, jy = player->jy;
12798
12799   return (IN_LEV_FIELD(jx, jy + 1) &&
12800           (IS_FREE(jx, jy + 1) ||
12801            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12802           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12803           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12804 }
12805
12806 static boolean canPassField(int x, int y, int move_dir)
12807 {
12808   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12809   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12810   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12811   int nextx = x + dx;
12812   int nexty = y + dy;
12813   int element = Tile[x][y];
12814
12815   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12816           !CAN_MOVE(element) &&
12817           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12818           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12819           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12820 }
12821
12822 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12823 {
12824   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12825   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12826   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12827   int newx = x + dx;
12828   int newy = y + dy;
12829
12830   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12831           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12832           (IS_DIGGABLE(Tile[newx][newy]) ||
12833            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12834            canPassField(newx, newy, move_dir)));
12835 }
12836
12837 static void CheckGravityMovement(struct PlayerInfo *player)
12838 {
12839   if (player->gravity && !player->programmed_action)
12840   {
12841     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12842     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12843     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12844     int jx = player->jx, jy = player->jy;
12845     boolean player_is_moving_to_valid_field =
12846       (!player_is_snapping &&
12847        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12848         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12849     boolean player_can_fall_down = canFallDown(player);
12850
12851     if (player_can_fall_down &&
12852         !player_is_moving_to_valid_field)
12853       player->programmed_action = MV_DOWN;
12854   }
12855 }
12856
12857 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12858 {
12859   return CheckGravityMovement(player);
12860
12861   if (player->gravity && !player->programmed_action)
12862   {
12863     int jx = player->jx, jy = player->jy;
12864     boolean field_under_player_is_free =
12865       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12866     boolean player_is_standing_on_valid_field =
12867       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12868        (IS_WALKABLE(Tile[jx][jy]) &&
12869         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12870
12871     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12872       player->programmed_action = MV_DOWN;
12873   }
12874 }
12875
12876 /*
12877   MovePlayerOneStep()
12878   -----------------------------------------------------------------------------
12879   dx, dy:               direction (non-diagonal) to try to move the player to
12880   real_dx, real_dy:     direction as read from input device (can be diagonal)
12881 */
12882
12883 boolean MovePlayerOneStep(struct PlayerInfo *player,
12884                           int dx, int dy, int real_dx, int real_dy)
12885 {
12886   int jx = player->jx, jy = player->jy;
12887   int new_jx = jx + dx, new_jy = jy + dy;
12888   int can_move;
12889   boolean player_can_move = !player->cannot_move;
12890
12891   if (!player->active || (!dx && !dy))
12892     return MP_NO_ACTION;
12893
12894   player->MovDir = (dx < 0 ? MV_LEFT :
12895                     dx > 0 ? MV_RIGHT :
12896                     dy < 0 ? MV_UP :
12897                     dy > 0 ? MV_DOWN :  MV_NONE);
12898
12899   if (!IN_LEV_FIELD(new_jx, new_jy))
12900     return MP_NO_ACTION;
12901
12902   if (!player_can_move)
12903   {
12904     if (player->MovPos == 0)
12905     {
12906       player->is_moving = FALSE;
12907       player->is_digging = FALSE;
12908       player->is_collecting = FALSE;
12909       player->is_snapping = FALSE;
12910       player->is_pushing = FALSE;
12911     }
12912   }
12913
12914   if (!network.enabled && game.centered_player_nr == -1 &&
12915       !AllPlayersInSight(player, new_jx, new_jy))
12916     return MP_NO_ACTION;
12917
12918   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12919   if (can_move != MP_MOVING)
12920     return can_move;
12921
12922   // check if DigField() has caused relocation of the player
12923   if (player->jx != jx || player->jy != jy)
12924     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12925
12926   StorePlayer[jx][jy] = 0;
12927   player->last_jx = jx;
12928   player->last_jy = jy;
12929   player->jx = new_jx;
12930   player->jy = new_jy;
12931   StorePlayer[new_jx][new_jy] = player->element_nr;
12932
12933   if (player->move_delay_value_next != -1)
12934   {
12935     player->move_delay_value = player->move_delay_value_next;
12936     player->move_delay_value_next = -1;
12937   }
12938
12939   player->MovPos =
12940     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12941
12942   player->step_counter++;
12943
12944   PlayerVisit[jx][jy] = FrameCounter;
12945
12946   player->is_moving = TRUE;
12947
12948 #if 1
12949   // should better be called in MovePlayer(), but this breaks some tapes
12950   ScrollPlayer(player, SCROLL_INIT);
12951 #endif
12952
12953   return MP_MOVING;
12954 }
12955
12956 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12957 {
12958   int jx = player->jx, jy = player->jy;
12959   int old_jx = jx, old_jy = jy;
12960   int moved = MP_NO_ACTION;
12961
12962   if (!player->active)
12963     return FALSE;
12964
12965   if (!dx && !dy)
12966   {
12967     if (player->MovPos == 0)
12968     {
12969       player->is_moving = FALSE;
12970       player->is_digging = FALSE;
12971       player->is_collecting = FALSE;
12972       player->is_snapping = FALSE;
12973       player->is_pushing = FALSE;
12974     }
12975
12976     return FALSE;
12977   }
12978
12979   if (player->move_delay > 0)
12980     return FALSE;
12981
12982   player->move_delay = -1;              // set to "uninitialized" value
12983
12984   // store if player is automatically moved to next field
12985   player->is_auto_moving = (player->programmed_action != MV_NONE);
12986
12987   // remove the last programmed player action
12988   player->programmed_action = 0;
12989
12990   if (player->MovPos)
12991   {
12992     // should only happen if pre-1.2 tape recordings are played
12993     // this is only for backward compatibility
12994
12995     int original_move_delay_value = player->move_delay_value;
12996
12997 #if DEBUG
12998     Debug("game:playing:MovePlayer",
12999           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13000           tape.counter);
13001 #endif
13002
13003     // scroll remaining steps with finest movement resolution
13004     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13005
13006     while (player->MovPos)
13007     {
13008       ScrollPlayer(player, SCROLL_GO_ON);
13009       ScrollScreen(NULL, SCROLL_GO_ON);
13010
13011       AdvanceFrameAndPlayerCounters(player->index_nr);
13012
13013       DrawAllPlayers();
13014       BackToFront_WithFrameDelay(0);
13015     }
13016
13017     player->move_delay_value = original_move_delay_value;
13018   }
13019
13020   player->is_active = FALSE;
13021
13022   if (player->last_move_dir & MV_HORIZONTAL)
13023   {
13024     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13025       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13026   }
13027   else
13028   {
13029     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13030       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13031   }
13032
13033   if (!moved && !player->is_active)
13034   {
13035     player->is_moving = FALSE;
13036     player->is_digging = FALSE;
13037     player->is_collecting = FALSE;
13038     player->is_snapping = FALSE;
13039     player->is_pushing = FALSE;
13040   }
13041
13042   jx = player->jx;
13043   jy = player->jy;
13044
13045   if (moved & MP_MOVING && !ScreenMovPos &&
13046       (player->index_nr == game.centered_player_nr ||
13047        game.centered_player_nr == -1))
13048   {
13049     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13050
13051     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13052     {
13053       // actual player has left the screen -- scroll in that direction
13054       if (jx != old_jx)         // player has moved horizontally
13055         scroll_x += (jx - old_jx);
13056       else                      // player has moved vertically
13057         scroll_y += (jy - old_jy);
13058     }
13059     else
13060     {
13061       int offset_raw = game.scroll_delay_value;
13062
13063       if (jx != old_jx)         // player has moved horizontally
13064       {
13065         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13066         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13067         int new_scroll_x = jx - MIDPOSX + offset_x;
13068
13069         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13070             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13071           scroll_x = new_scroll_x;
13072
13073         // don't scroll over playfield boundaries
13074         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13075
13076         // don't scroll more than one field at a time
13077         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13078
13079         // don't scroll against the player's moving direction
13080         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13081             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13082           scroll_x = old_scroll_x;
13083       }
13084       else                      // player has moved vertically
13085       {
13086         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13087         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13088         int new_scroll_y = jy - MIDPOSY + offset_y;
13089
13090         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13091             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13092           scroll_y = new_scroll_y;
13093
13094         // don't scroll over playfield boundaries
13095         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13096
13097         // don't scroll more than one field at a time
13098         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13099
13100         // don't scroll against the player's moving direction
13101         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13102             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13103           scroll_y = old_scroll_y;
13104       }
13105     }
13106
13107     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13108     {
13109       if (!network.enabled && game.centered_player_nr == -1 &&
13110           !AllPlayersInVisibleScreen())
13111       {
13112         scroll_x = old_scroll_x;
13113         scroll_y = old_scroll_y;
13114       }
13115       else
13116       {
13117         ScrollScreen(player, SCROLL_INIT);
13118         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13119       }
13120     }
13121   }
13122
13123   player->StepFrame = 0;
13124
13125   if (moved & MP_MOVING)
13126   {
13127     if (old_jx != jx && old_jy == jy)
13128       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13129     else if (old_jx == jx && old_jy != jy)
13130       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13131
13132     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13133
13134     player->last_move_dir = player->MovDir;
13135     player->is_moving = TRUE;
13136     player->is_snapping = FALSE;
13137     player->is_switching = FALSE;
13138     player->is_dropping = FALSE;
13139     player->is_dropping_pressed = FALSE;
13140     player->drop_pressed_delay = 0;
13141
13142 #if 0
13143     // should better be called here than above, but this breaks some tapes
13144     ScrollPlayer(player, SCROLL_INIT);
13145 #endif
13146   }
13147   else
13148   {
13149     CheckGravityMovementWhenNotMoving(player);
13150
13151     player->is_moving = FALSE;
13152
13153     /* at this point, the player is allowed to move, but cannot move right now
13154        (e.g. because of something blocking the way) -- ensure that the player
13155        is also allowed to move in the next frame (in old versions before 3.1.1,
13156        the player was forced to wait again for eight frames before next try) */
13157
13158     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13159       player->move_delay = 0;   // allow direct movement in the next frame
13160   }
13161
13162   if (player->move_delay == -1)         // not yet initialized by DigField()
13163     player->move_delay = player->move_delay_value;
13164
13165   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13166   {
13167     TestIfPlayerTouchesBadThing(jx, jy);
13168     TestIfPlayerTouchesCustomElement(jx, jy);
13169   }
13170
13171   if (!player->active)
13172     RemovePlayer(player);
13173
13174   return moved;
13175 }
13176
13177 void ScrollPlayer(struct PlayerInfo *player, int mode)
13178 {
13179   int jx = player->jx, jy = player->jy;
13180   int last_jx = player->last_jx, last_jy = player->last_jy;
13181   int move_stepsize = TILEX / player->move_delay_value;
13182
13183   if (!player->active)
13184     return;
13185
13186   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13187     return;
13188
13189   if (mode == SCROLL_INIT)
13190   {
13191     player->actual_frame_counter = FrameCounter;
13192     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13193
13194     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13195         Tile[last_jx][last_jy] == EL_EMPTY)
13196     {
13197       int last_field_block_delay = 0;   // start with no blocking at all
13198       int block_delay_adjustment = player->block_delay_adjustment;
13199
13200       // if player blocks last field, add delay for exactly one move
13201       if (player->block_last_field)
13202       {
13203         last_field_block_delay += player->move_delay_value;
13204
13205         // when blocking enabled, prevent moving up despite gravity
13206         if (player->gravity && player->MovDir == MV_UP)
13207           block_delay_adjustment = -1;
13208       }
13209
13210       // add block delay adjustment (also possible when not blocking)
13211       last_field_block_delay += block_delay_adjustment;
13212
13213       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13214       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13215     }
13216
13217     if (player->MovPos != 0)    // player has not yet reached destination
13218       return;
13219   }
13220   else if (!FrameReached(&player->actual_frame_counter, 1))
13221     return;
13222
13223   if (player->MovPos != 0)
13224   {
13225     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13226     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13227
13228     // before DrawPlayer() to draw correct player graphic for this case
13229     if (player->MovPos == 0)
13230       CheckGravityMovement(player);
13231   }
13232
13233   if (player->MovPos == 0)      // player reached destination field
13234   {
13235     if (player->move_delay_reset_counter > 0)
13236     {
13237       player->move_delay_reset_counter--;
13238
13239       if (player->move_delay_reset_counter == 0)
13240       {
13241         // continue with normal speed after quickly moving through gate
13242         HALVE_PLAYER_SPEED(player);
13243
13244         // be able to make the next move without delay
13245         player->move_delay = 0;
13246       }
13247     }
13248
13249     player->last_jx = jx;
13250     player->last_jy = jy;
13251
13252     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13253         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13254         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13255         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13256         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13257         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13258         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13259         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13260     {
13261       ExitPlayer(player);
13262
13263       if (game.players_still_needed == 0 &&
13264           (game.friends_still_needed == 0 ||
13265            IS_SP_ELEMENT(Tile[jx][jy])))
13266         LevelSolved();
13267     }
13268
13269     // this breaks one level: "machine", level 000
13270     {
13271       int move_direction = player->MovDir;
13272       int enter_side = MV_DIR_OPPOSITE(move_direction);
13273       int leave_side = move_direction;
13274       int old_jx = last_jx;
13275       int old_jy = last_jy;
13276       int old_element = Tile[old_jx][old_jy];
13277       int new_element = Tile[jx][jy];
13278
13279       if (IS_CUSTOM_ELEMENT(old_element))
13280         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13281                                    CE_LEFT_BY_PLAYER,
13282                                    player->index_bit, leave_side);
13283
13284       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13285                                           CE_PLAYER_LEAVES_X,
13286                                           player->index_bit, leave_side);
13287
13288       if (IS_CUSTOM_ELEMENT(new_element))
13289         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13290                                    player->index_bit, enter_side);
13291
13292       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13293                                           CE_PLAYER_ENTERS_X,
13294                                           player->index_bit, enter_side);
13295
13296       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13297                                         CE_MOVE_OF_X, move_direction);
13298     }
13299
13300     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13301     {
13302       TestIfPlayerTouchesBadThing(jx, jy);
13303       TestIfPlayerTouchesCustomElement(jx, jy);
13304
13305       /* needed because pushed element has not yet reached its destination,
13306          so it would trigger a change event at its previous field location */
13307       if (!player->is_pushing)
13308         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13309
13310       if (level.finish_dig_collect &&
13311           (player->is_digging || player->is_collecting))
13312       {
13313         int last_element = player->last_removed_element;
13314         int move_direction = player->MovDir;
13315         int enter_side = MV_DIR_OPPOSITE(move_direction);
13316         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13317                             CE_PLAYER_COLLECTS_X);
13318
13319         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13320                                             player->index_bit, enter_side);
13321
13322         player->last_removed_element = EL_UNDEFINED;
13323       }
13324
13325       if (!player->active)
13326         RemovePlayer(player);
13327     }
13328
13329     if (level.use_step_counter)
13330       CheckLevelTime_StepCounter();
13331
13332     if (tape.single_step && tape.recording && !tape.pausing &&
13333         !player->programmed_action)
13334       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13335
13336     if (!player->programmed_action)
13337       CheckSaveEngineSnapshot(player);
13338   }
13339 }
13340
13341 void ScrollScreen(struct PlayerInfo *player, int mode)
13342 {
13343   static unsigned int screen_frame_counter = 0;
13344
13345   if (mode == SCROLL_INIT)
13346   {
13347     // set scrolling step size according to actual player's moving speed
13348     ScrollStepSize = TILEX / player->move_delay_value;
13349
13350     screen_frame_counter = FrameCounter;
13351     ScreenMovDir = player->MovDir;
13352     ScreenMovPos = player->MovPos;
13353     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13354     return;
13355   }
13356   else if (!FrameReached(&screen_frame_counter, 1))
13357     return;
13358
13359   if (ScreenMovPos)
13360   {
13361     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13362     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13363     redraw_mask |= REDRAW_FIELD;
13364   }
13365   else
13366     ScreenMovDir = MV_NONE;
13367 }
13368
13369 void CheckNextToConditions(int x, int y)
13370 {
13371   int element = Tile[x][y];
13372
13373   if (IS_PLAYER(x, y))
13374     TestIfPlayerNextToCustomElement(x, y);
13375
13376   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13377       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13378     TestIfElementNextToCustomElement(x, y);
13379 }
13380
13381 void TestIfPlayerNextToCustomElement(int x, int y)
13382 {
13383   static int xy[4][2] =
13384   {
13385     { 0, -1 },
13386     { -1, 0 },
13387     { +1, 0 },
13388     { 0, +1 }
13389   };
13390   static int trigger_sides[4][2] =
13391   {
13392     // center side       border side
13393     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13394     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13395     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13396     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13397   };
13398   int i;
13399
13400   if (!IS_PLAYER(x, y))
13401     return;
13402
13403   struct PlayerInfo *player = PLAYERINFO(x, y);
13404
13405   if (player->is_moving)
13406     return;
13407
13408   for (i = 0; i < NUM_DIRECTIONS; i++)
13409   {
13410     int xx = x + xy[i][0];
13411     int yy = y + xy[i][1];
13412     int border_side = trigger_sides[i][1];
13413     int border_element;
13414
13415     if (!IN_LEV_FIELD(xx, yy))
13416       continue;
13417
13418     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13419       continue;         // center and border element not connected
13420
13421     border_element = Tile[xx][yy];
13422
13423     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13424                                player->index_bit, border_side);
13425     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13426                                         CE_PLAYER_NEXT_TO_X,
13427                                         player->index_bit, border_side);
13428
13429     /* use player element that is initially defined in the level playfield,
13430        not the player element that corresponds to the runtime player number
13431        (example: a level that contains EL_PLAYER_3 as the only player would
13432        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13433
13434     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13435                              CE_NEXT_TO_X, border_side);
13436   }
13437 }
13438
13439 void TestIfPlayerTouchesCustomElement(int x, int y)
13440 {
13441   static int xy[4][2] =
13442   {
13443     { 0, -1 },
13444     { -1, 0 },
13445     { +1, 0 },
13446     { 0, +1 }
13447   };
13448   static int trigger_sides[4][2] =
13449   {
13450     // center side       border side
13451     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13452     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13453     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13454     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13455   };
13456   static int touch_dir[4] =
13457   {
13458     MV_LEFT | MV_RIGHT,
13459     MV_UP   | MV_DOWN,
13460     MV_UP   | MV_DOWN,
13461     MV_LEFT | MV_RIGHT
13462   };
13463   int center_element = Tile[x][y];      // should always be non-moving!
13464   int i;
13465
13466   for (i = 0; i < NUM_DIRECTIONS; i++)
13467   {
13468     int xx = x + xy[i][0];
13469     int yy = y + xy[i][1];
13470     int center_side = trigger_sides[i][0];
13471     int border_side = trigger_sides[i][1];
13472     int border_element;
13473
13474     if (!IN_LEV_FIELD(xx, yy))
13475       continue;
13476
13477     if (IS_PLAYER(x, y))                // player found at center element
13478     {
13479       struct PlayerInfo *player = PLAYERINFO(x, y);
13480
13481       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13482         border_element = Tile[xx][yy];          // may be moving!
13483       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13484         border_element = Tile[xx][yy];
13485       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13486         border_element = MovingOrBlocked2Element(xx, yy);
13487       else
13488         continue;               // center and border element do not touch
13489
13490       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13491                                  player->index_bit, border_side);
13492       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13493                                           CE_PLAYER_TOUCHES_X,
13494                                           player->index_bit, border_side);
13495
13496       {
13497         /* use player element that is initially defined in the level playfield,
13498            not the player element that corresponds to the runtime player number
13499            (example: a level that contains EL_PLAYER_3 as the only player would
13500            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13501         int player_element = PLAYERINFO(x, y)->initial_element;
13502
13503         CheckElementChangeBySide(xx, yy, border_element, player_element,
13504                                  CE_TOUCHING_X, border_side);
13505       }
13506     }
13507     else if (IS_PLAYER(xx, yy))         // player found at border element
13508     {
13509       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13510
13511       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13512       {
13513         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13514           continue;             // center and border element do not touch
13515       }
13516
13517       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13518                                  player->index_bit, center_side);
13519       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13520                                           CE_PLAYER_TOUCHES_X,
13521                                           player->index_bit, center_side);
13522
13523       {
13524         /* use player element that is initially defined in the level playfield,
13525            not the player element that corresponds to the runtime player number
13526            (example: a level that contains EL_PLAYER_3 as the only player would
13527            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13528         int player_element = PLAYERINFO(xx, yy)->initial_element;
13529
13530         CheckElementChangeBySide(x, y, center_element, player_element,
13531                                  CE_TOUCHING_X, center_side);
13532       }
13533
13534       break;
13535     }
13536   }
13537 }
13538
13539 void TestIfElementNextToCustomElement(int x, int y)
13540 {
13541   static int xy[4][2] =
13542   {
13543     { 0, -1 },
13544     { -1, 0 },
13545     { +1, 0 },
13546     { 0, +1 }
13547   };
13548   static int trigger_sides[4][2] =
13549   {
13550     // center side      border side
13551     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13552     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13553     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13554     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13555   };
13556   int center_element = Tile[x][y];      // should always be non-moving!
13557   int i;
13558
13559   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13560     return;
13561
13562   for (i = 0; i < NUM_DIRECTIONS; i++)
13563   {
13564     int xx = x + xy[i][0];
13565     int yy = y + xy[i][1];
13566     int border_side = trigger_sides[i][1];
13567     int border_element;
13568
13569     if (!IN_LEV_FIELD(xx, yy))
13570       continue;
13571
13572     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13573       continue;                 // center and border element not connected
13574
13575     border_element = Tile[xx][yy];
13576
13577     // check for change of center element (but change it only once)
13578     if (CheckElementChangeBySide(x, y, center_element, border_element,
13579                                  CE_NEXT_TO_X, border_side))
13580       break;
13581   }
13582 }
13583
13584 void TestIfElementTouchesCustomElement(int x, int y)
13585 {
13586   static int xy[4][2] =
13587   {
13588     { 0, -1 },
13589     { -1, 0 },
13590     { +1, 0 },
13591     { 0, +1 }
13592   };
13593   static int trigger_sides[4][2] =
13594   {
13595     // center side      border side
13596     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13597     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13598     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13599     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13600   };
13601   static int touch_dir[4] =
13602   {
13603     MV_LEFT | MV_RIGHT,
13604     MV_UP   | MV_DOWN,
13605     MV_UP   | MV_DOWN,
13606     MV_LEFT | MV_RIGHT
13607   };
13608   boolean change_center_element = FALSE;
13609   int center_element = Tile[x][y];      // should always be non-moving!
13610   int border_element_old[NUM_DIRECTIONS];
13611   int i;
13612
13613   for (i = 0; i < NUM_DIRECTIONS; i++)
13614   {
13615     int xx = x + xy[i][0];
13616     int yy = y + xy[i][1];
13617     int border_element;
13618
13619     border_element_old[i] = -1;
13620
13621     if (!IN_LEV_FIELD(xx, yy))
13622       continue;
13623
13624     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13625       border_element = Tile[xx][yy];    // may be moving!
13626     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13627       border_element = Tile[xx][yy];
13628     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13629       border_element = MovingOrBlocked2Element(xx, yy);
13630     else
13631       continue;                 // center and border element do not touch
13632
13633     border_element_old[i] = border_element;
13634   }
13635
13636   for (i = 0; i < NUM_DIRECTIONS; i++)
13637   {
13638     int xx = x + xy[i][0];
13639     int yy = y + xy[i][1];
13640     int center_side = trigger_sides[i][0];
13641     int border_element = border_element_old[i];
13642
13643     if (border_element == -1)
13644       continue;
13645
13646     // check for change of border element
13647     CheckElementChangeBySide(xx, yy, border_element, center_element,
13648                              CE_TOUCHING_X, center_side);
13649
13650     // (center element cannot be player, so we dont have to check this here)
13651   }
13652
13653   for (i = 0; i < NUM_DIRECTIONS; i++)
13654   {
13655     int xx = x + xy[i][0];
13656     int yy = y + xy[i][1];
13657     int border_side = trigger_sides[i][1];
13658     int border_element = border_element_old[i];
13659
13660     if (border_element == -1)
13661       continue;
13662
13663     // check for change of center element (but change it only once)
13664     if (!change_center_element)
13665       change_center_element =
13666         CheckElementChangeBySide(x, y, center_element, border_element,
13667                                  CE_TOUCHING_X, border_side);
13668
13669     if (IS_PLAYER(xx, yy))
13670     {
13671       /* use player element that is initially defined in the level playfield,
13672          not the player element that corresponds to the runtime player number
13673          (example: a level that contains EL_PLAYER_3 as the only player would
13674          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13675       int player_element = PLAYERINFO(xx, yy)->initial_element;
13676
13677       CheckElementChangeBySide(x, y, center_element, player_element,
13678                                CE_TOUCHING_X, border_side);
13679     }
13680   }
13681 }
13682
13683 void TestIfElementHitsCustomElement(int x, int y, int direction)
13684 {
13685   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13686   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13687   int hitx = x + dx, hity = y + dy;
13688   int hitting_element = Tile[x][y];
13689   int touched_element;
13690
13691   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13692     return;
13693
13694   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13695                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13696
13697   if (IN_LEV_FIELD(hitx, hity))
13698   {
13699     int opposite_direction = MV_DIR_OPPOSITE(direction);
13700     int hitting_side = direction;
13701     int touched_side = opposite_direction;
13702     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13703                           MovDir[hitx][hity] != direction ||
13704                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13705
13706     object_hit = TRUE;
13707
13708     if (object_hit)
13709     {
13710       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13711                                CE_HITTING_X, touched_side);
13712
13713       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13714                                CE_HIT_BY_X, hitting_side);
13715
13716       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13717                                CE_HIT_BY_SOMETHING, opposite_direction);
13718
13719       if (IS_PLAYER(hitx, hity))
13720       {
13721         /* use player element that is initially defined in the level playfield,
13722            not the player element that corresponds to the runtime player number
13723            (example: a level that contains EL_PLAYER_3 as the only player would
13724            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13725         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13726
13727         CheckElementChangeBySide(x, y, hitting_element, player_element,
13728                                  CE_HITTING_X, touched_side);
13729       }
13730     }
13731   }
13732
13733   // "hitting something" is also true when hitting the playfield border
13734   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13735                            CE_HITTING_SOMETHING, direction);
13736 }
13737
13738 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13739 {
13740   int i, kill_x = -1, kill_y = -1;
13741
13742   int bad_element = -1;
13743   static int test_xy[4][2] =
13744   {
13745     { 0, -1 },
13746     { -1, 0 },
13747     { +1, 0 },
13748     { 0, +1 }
13749   };
13750   static int test_dir[4] =
13751   {
13752     MV_UP,
13753     MV_LEFT,
13754     MV_RIGHT,
13755     MV_DOWN
13756   };
13757
13758   for (i = 0; i < NUM_DIRECTIONS; i++)
13759   {
13760     int test_x, test_y, test_move_dir, test_element;
13761
13762     test_x = good_x + test_xy[i][0];
13763     test_y = good_y + test_xy[i][1];
13764
13765     if (!IN_LEV_FIELD(test_x, test_y))
13766       continue;
13767
13768     test_move_dir =
13769       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13770
13771     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13772
13773     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13774        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13775     */
13776     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13777         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13778     {
13779       kill_x = test_x;
13780       kill_y = test_y;
13781       bad_element = test_element;
13782
13783       break;
13784     }
13785   }
13786
13787   if (kill_x != -1 || kill_y != -1)
13788   {
13789     if (IS_PLAYER(good_x, good_y))
13790     {
13791       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13792
13793       if (player->shield_deadly_time_left > 0 &&
13794           !IS_INDESTRUCTIBLE(bad_element))
13795         Bang(kill_x, kill_y);
13796       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13797         KillPlayer(player);
13798     }
13799     else
13800       Bang(good_x, good_y);
13801   }
13802 }
13803
13804 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13805 {
13806   int i, kill_x = -1, kill_y = -1;
13807   int bad_element = Tile[bad_x][bad_y];
13808   static int test_xy[4][2] =
13809   {
13810     { 0, -1 },
13811     { -1, 0 },
13812     { +1, 0 },
13813     { 0, +1 }
13814   };
13815   static int touch_dir[4] =
13816   {
13817     MV_LEFT | MV_RIGHT,
13818     MV_UP   | MV_DOWN,
13819     MV_UP   | MV_DOWN,
13820     MV_LEFT | MV_RIGHT
13821   };
13822   static int test_dir[4] =
13823   {
13824     MV_UP,
13825     MV_LEFT,
13826     MV_RIGHT,
13827     MV_DOWN
13828   };
13829
13830   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13831     return;
13832
13833   for (i = 0; i < NUM_DIRECTIONS; i++)
13834   {
13835     int test_x, test_y, test_move_dir, test_element;
13836
13837     test_x = bad_x + test_xy[i][0];
13838     test_y = bad_y + test_xy[i][1];
13839
13840     if (!IN_LEV_FIELD(test_x, test_y))
13841       continue;
13842
13843     test_move_dir =
13844       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13845
13846     test_element = Tile[test_x][test_y];
13847
13848     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13849        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13850     */
13851     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13852         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13853     {
13854       // good thing is player or penguin that does not move away
13855       if (IS_PLAYER(test_x, test_y))
13856       {
13857         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13858
13859         if (bad_element == EL_ROBOT && player->is_moving)
13860           continue;     // robot does not kill player if he is moving
13861
13862         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13863         {
13864           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13865             continue;           // center and border element do not touch
13866         }
13867
13868         kill_x = test_x;
13869         kill_y = test_y;
13870
13871         break;
13872       }
13873       else if (test_element == EL_PENGUIN)
13874       {
13875         kill_x = test_x;
13876         kill_y = test_y;
13877
13878         break;
13879       }
13880     }
13881   }
13882
13883   if (kill_x != -1 || kill_y != -1)
13884   {
13885     if (IS_PLAYER(kill_x, kill_y))
13886     {
13887       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13888
13889       if (player->shield_deadly_time_left > 0 &&
13890           !IS_INDESTRUCTIBLE(bad_element))
13891         Bang(bad_x, bad_y);
13892       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13893         KillPlayer(player);
13894     }
13895     else
13896       Bang(kill_x, kill_y);
13897   }
13898 }
13899
13900 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13901 {
13902   int bad_element = Tile[bad_x][bad_y];
13903   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13904   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13905   int test_x = bad_x + dx, test_y = bad_y + dy;
13906   int test_move_dir, test_element;
13907   int kill_x = -1, kill_y = -1;
13908
13909   if (!IN_LEV_FIELD(test_x, test_y))
13910     return;
13911
13912   test_move_dir =
13913     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13914
13915   test_element = Tile[test_x][test_y];
13916
13917   if (test_move_dir != bad_move_dir)
13918   {
13919     // good thing can be player or penguin that does not move away
13920     if (IS_PLAYER(test_x, test_y))
13921     {
13922       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13923
13924       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13925          player as being hit when he is moving towards the bad thing, because
13926          the "get hit by" condition would be lost after the player stops) */
13927       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13928         return;         // player moves away from bad thing
13929
13930       kill_x = test_x;
13931       kill_y = test_y;
13932     }
13933     else if (test_element == EL_PENGUIN)
13934     {
13935       kill_x = test_x;
13936       kill_y = test_y;
13937     }
13938   }
13939
13940   if (kill_x != -1 || kill_y != -1)
13941   {
13942     if (IS_PLAYER(kill_x, kill_y))
13943     {
13944       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13945
13946       if (player->shield_deadly_time_left > 0 &&
13947           !IS_INDESTRUCTIBLE(bad_element))
13948         Bang(bad_x, bad_y);
13949       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13950         KillPlayer(player);
13951     }
13952     else
13953       Bang(kill_x, kill_y);
13954   }
13955 }
13956
13957 void TestIfPlayerTouchesBadThing(int x, int y)
13958 {
13959   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13960 }
13961
13962 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13963 {
13964   TestIfGoodThingHitsBadThing(x, y, move_dir);
13965 }
13966
13967 void TestIfBadThingTouchesPlayer(int x, int y)
13968 {
13969   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13970 }
13971
13972 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13973 {
13974   TestIfBadThingHitsGoodThing(x, y, move_dir);
13975 }
13976
13977 void TestIfFriendTouchesBadThing(int x, int y)
13978 {
13979   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13980 }
13981
13982 void TestIfBadThingTouchesFriend(int x, int y)
13983 {
13984   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13985 }
13986
13987 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13988 {
13989   int i, kill_x = bad_x, kill_y = bad_y;
13990   static int xy[4][2] =
13991   {
13992     { 0, -1 },
13993     { -1, 0 },
13994     { +1, 0 },
13995     { 0, +1 }
13996   };
13997
13998   for (i = 0; i < NUM_DIRECTIONS; i++)
13999   {
14000     int x, y, element;
14001
14002     x = bad_x + xy[i][0];
14003     y = bad_y + xy[i][1];
14004     if (!IN_LEV_FIELD(x, y))
14005       continue;
14006
14007     element = Tile[x][y];
14008     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14009         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14010     {
14011       kill_x = x;
14012       kill_y = y;
14013       break;
14014     }
14015   }
14016
14017   if (kill_x != bad_x || kill_y != bad_y)
14018     Bang(bad_x, bad_y);
14019 }
14020
14021 void KillPlayer(struct PlayerInfo *player)
14022 {
14023   int jx = player->jx, jy = player->jy;
14024
14025   if (!player->active)
14026     return;
14027
14028 #if 0
14029   Debug("game:playing:KillPlayer",
14030         "0: killed == %d, active == %d, reanimated == %d",
14031         player->killed, player->active, player->reanimated);
14032 #endif
14033
14034   /* the following code was introduced to prevent an infinite loop when calling
14035      -> Bang()
14036      -> CheckTriggeredElementChangeExt()
14037      -> ExecuteCustomElementAction()
14038      -> KillPlayer()
14039      -> (infinitely repeating the above sequence of function calls)
14040      which occurs when killing the player while having a CE with the setting
14041      "kill player X when explosion of <player X>"; the solution using a new
14042      field "player->killed" was chosen for backwards compatibility, although
14043      clever use of the fields "player->active" etc. would probably also work */
14044 #if 1
14045   if (player->killed)
14046     return;
14047 #endif
14048
14049   player->killed = TRUE;
14050
14051   // remove accessible field at the player's position
14052   Tile[jx][jy] = EL_EMPTY;
14053
14054   // deactivate shield (else Bang()/Explode() would not work right)
14055   player->shield_normal_time_left = 0;
14056   player->shield_deadly_time_left = 0;
14057
14058 #if 0
14059   Debug("game:playing:KillPlayer",
14060         "1: killed == %d, active == %d, reanimated == %d",
14061         player->killed, player->active, player->reanimated);
14062 #endif
14063
14064   Bang(jx, jy);
14065
14066 #if 0
14067   Debug("game:playing:KillPlayer",
14068         "2: killed == %d, active == %d, reanimated == %d",
14069         player->killed, player->active, player->reanimated);
14070 #endif
14071
14072   if (player->reanimated)       // killed player may have been reanimated
14073     player->killed = player->reanimated = FALSE;
14074   else
14075     BuryPlayer(player);
14076 }
14077
14078 static void KillPlayerUnlessEnemyProtected(int x, int y)
14079 {
14080   if (!PLAYER_ENEMY_PROTECTED(x, y))
14081     KillPlayer(PLAYERINFO(x, y));
14082 }
14083
14084 static void KillPlayerUnlessExplosionProtected(int x, int y)
14085 {
14086   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14087     KillPlayer(PLAYERINFO(x, y));
14088 }
14089
14090 void BuryPlayer(struct PlayerInfo *player)
14091 {
14092   int jx = player->jx, jy = player->jy;
14093
14094   if (!player->active)
14095     return;
14096
14097   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14098   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14099
14100   RemovePlayer(player);
14101
14102   player->buried = TRUE;
14103
14104   if (game.all_players_gone)
14105     game.GameOver = TRUE;
14106 }
14107
14108 void RemovePlayer(struct PlayerInfo *player)
14109 {
14110   int jx = player->jx, jy = player->jy;
14111   int i, found = FALSE;
14112
14113   player->present = FALSE;
14114   player->active = FALSE;
14115
14116   // required for some CE actions (even if the player is not active anymore)
14117   player->MovPos = 0;
14118
14119   if (!ExplodeField[jx][jy])
14120     StorePlayer[jx][jy] = 0;
14121
14122   if (player->is_moving)
14123     TEST_DrawLevelField(player->last_jx, player->last_jy);
14124
14125   for (i = 0; i < MAX_PLAYERS; i++)
14126     if (stored_player[i].active)
14127       found = TRUE;
14128
14129   if (!found)
14130   {
14131     game.all_players_gone = TRUE;
14132     game.GameOver = TRUE;
14133   }
14134
14135   game.exit_x = game.robot_wheel_x = jx;
14136   game.exit_y = game.robot_wheel_y = jy;
14137 }
14138
14139 void ExitPlayer(struct PlayerInfo *player)
14140 {
14141   DrawPlayer(player);   // needed here only to cleanup last field
14142   RemovePlayer(player);
14143
14144   if (game.players_still_needed > 0)
14145     game.players_still_needed--;
14146 }
14147
14148 static void SetFieldForSnapping(int x, int y, int element, int direction,
14149                                 int player_index_bit)
14150 {
14151   struct ElementInfo *ei = &element_info[element];
14152   int direction_bit = MV_DIR_TO_BIT(direction);
14153   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14154   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14155                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14156
14157   Tile[x][y] = EL_ELEMENT_SNAPPING;
14158   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14159   MovDir[x][y] = direction;
14160   Store[x][y] = element;
14161   Store2[x][y] = player_index_bit;
14162
14163   ResetGfxAnimation(x, y);
14164
14165   GfxElement[x][y] = element;
14166   GfxAction[x][y] = action;
14167   GfxDir[x][y] = direction;
14168   GfxFrame[x][y] = -1;
14169 }
14170
14171 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14172                                    int player_index_bit)
14173 {
14174   TestIfElementTouchesCustomElement(x, y);      // for empty space
14175
14176   if (level.finish_dig_collect)
14177   {
14178     int dig_side = MV_DIR_OPPOSITE(direction);
14179     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14180                         CE_PLAYER_COLLECTS_X);
14181
14182     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14183                                         player_index_bit, dig_side);
14184     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14185                                         player_index_bit, dig_side);
14186   }
14187 }
14188
14189 /*
14190   =============================================================================
14191   checkDiagonalPushing()
14192   -----------------------------------------------------------------------------
14193   check if diagonal input device direction results in pushing of object
14194   (by checking if the alternative direction is walkable, diggable, ...)
14195   =============================================================================
14196 */
14197
14198 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14199                                     int x, int y, int real_dx, int real_dy)
14200 {
14201   int jx, jy, dx, dy, xx, yy;
14202
14203   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14204     return TRUE;
14205
14206   // diagonal direction: check alternative direction
14207   jx = player->jx;
14208   jy = player->jy;
14209   dx = x - jx;
14210   dy = y - jy;
14211   xx = jx + (dx == 0 ? real_dx : 0);
14212   yy = jy + (dy == 0 ? real_dy : 0);
14213
14214   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14215 }
14216
14217 /*
14218   =============================================================================
14219   DigField()
14220   -----------------------------------------------------------------------------
14221   x, y:                 field next to player (non-diagonal) to try to dig to
14222   real_dx, real_dy:     direction as read from input device (can be diagonal)
14223   =============================================================================
14224 */
14225
14226 static int DigField(struct PlayerInfo *player,
14227                     int oldx, int oldy, int x, int y,
14228                     int real_dx, int real_dy, int mode)
14229 {
14230   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14231   boolean player_was_pushing = player->is_pushing;
14232   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14233   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14234   int jx = oldx, jy = oldy;
14235   int dx = x - jx, dy = y - jy;
14236   int nextx = x + dx, nexty = y + dy;
14237   int move_direction = (dx == -1 ? MV_LEFT  :
14238                         dx == +1 ? MV_RIGHT :
14239                         dy == -1 ? MV_UP    :
14240                         dy == +1 ? MV_DOWN  : MV_NONE);
14241   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14242   int dig_side = MV_DIR_OPPOSITE(move_direction);
14243   int old_element = Tile[jx][jy];
14244   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14245   int collect_count;
14246
14247   if (is_player)                // function can also be called by EL_PENGUIN
14248   {
14249     if (player->MovPos == 0)
14250     {
14251       player->is_digging = FALSE;
14252       player->is_collecting = FALSE;
14253     }
14254
14255     if (player->MovPos == 0)    // last pushing move finished
14256       player->is_pushing = FALSE;
14257
14258     if (mode == DF_NO_PUSH)     // player just stopped pushing
14259     {
14260       player->is_switching = FALSE;
14261       player->push_delay = -1;
14262
14263       return MP_NO_ACTION;
14264     }
14265   }
14266   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14267     old_element = Back[jx][jy];
14268
14269   // in case of element dropped at player position, check background
14270   else if (Back[jx][jy] != EL_EMPTY &&
14271            game.engine_version >= VERSION_IDENT(2,2,0,0))
14272     old_element = Back[jx][jy];
14273
14274   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14275     return MP_NO_ACTION;        // field has no opening in this direction
14276
14277   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14278     return MP_NO_ACTION;        // field has no opening in this direction
14279
14280   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14281   {
14282     SplashAcid(x, y);
14283
14284     Tile[jx][jy] = player->artwork_element;
14285     InitMovingField(jx, jy, MV_DOWN);
14286     Store[jx][jy] = EL_ACID;
14287     ContinueMoving(jx, jy);
14288     BuryPlayer(player);
14289
14290     return MP_DONT_RUN_INTO;
14291   }
14292
14293   if (player_can_move && DONT_RUN_INTO(element))
14294   {
14295     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14296
14297     return MP_DONT_RUN_INTO;
14298   }
14299
14300   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14301     return MP_NO_ACTION;
14302
14303   collect_count = element_info[element].collect_count_initial;
14304
14305   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14306     return MP_NO_ACTION;
14307
14308   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14309     player_can_move = player_can_move_or_snap;
14310
14311   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14312       game.engine_version >= VERSION_IDENT(2,2,0,0))
14313   {
14314     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14315                                player->index_bit, dig_side);
14316     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14317                                         player->index_bit, dig_side);
14318
14319     if (element == EL_DC_LANDMINE)
14320       Bang(x, y);
14321
14322     if (Tile[x][y] != element)          // field changed by snapping
14323       return MP_ACTION;
14324
14325     return MP_NO_ACTION;
14326   }
14327
14328   if (player->gravity && is_player && !player->is_auto_moving &&
14329       canFallDown(player) && move_direction != MV_DOWN &&
14330       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14331     return MP_NO_ACTION;        // player cannot walk here due to gravity
14332
14333   if (player_can_move &&
14334       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14335   {
14336     int sound_element = SND_ELEMENT(element);
14337     int sound_action = ACTION_WALKING;
14338
14339     if (IS_RND_GATE(element))
14340     {
14341       if (!player->key[RND_GATE_NR(element)])
14342         return MP_NO_ACTION;
14343     }
14344     else if (IS_RND_GATE_GRAY(element))
14345     {
14346       if (!player->key[RND_GATE_GRAY_NR(element)])
14347         return MP_NO_ACTION;
14348     }
14349     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14350     {
14351       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14352         return MP_NO_ACTION;
14353     }
14354     else if (element == EL_EXIT_OPEN ||
14355              element == EL_EM_EXIT_OPEN ||
14356              element == EL_EM_EXIT_OPENING ||
14357              element == EL_STEEL_EXIT_OPEN ||
14358              element == EL_EM_STEEL_EXIT_OPEN ||
14359              element == EL_EM_STEEL_EXIT_OPENING ||
14360              element == EL_SP_EXIT_OPEN ||
14361              element == EL_SP_EXIT_OPENING)
14362     {
14363       sound_action = ACTION_PASSING;    // player is passing exit
14364     }
14365     else if (element == EL_EMPTY)
14366     {
14367       sound_action = ACTION_MOVING;             // nothing to walk on
14368     }
14369
14370     // play sound from background or player, whatever is available
14371     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14372       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14373     else
14374       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14375   }
14376   else if (player_can_move &&
14377            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14378   {
14379     if (!ACCESS_FROM(element, opposite_direction))
14380       return MP_NO_ACTION;      // field not accessible from this direction
14381
14382     if (CAN_MOVE(element))      // only fixed elements can be passed!
14383       return MP_NO_ACTION;
14384
14385     if (IS_EM_GATE(element))
14386     {
14387       if (!player->key[EM_GATE_NR(element)])
14388         return MP_NO_ACTION;
14389     }
14390     else if (IS_EM_GATE_GRAY(element))
14391     {
14392       if (!player->key[EM_GATE_GRAY_NR(element)])
14393         return MP_NO_ACTION;
14394     }
14395     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14396     {
14397       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14398         return MP_NO_ACTION;
14399     }
14400     else if (IS_EMC_GATE(element))
14401     {
14402       if (!player->key[EMC_GATE_NR(element)])
14403         return MP_NO_ACTION;
14404     }
14405     else if (IS_EMC_GATE_GRAY(element))
14406     {
14407       if (!player->key[EMC_GATE_GRAY_NR(element)])
14408         return MP_NO_ACTION;
14409     }
14410     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14411     {
14412       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14413         return MP_NO_ACTION;
14414     }
14415     else if (element == EL_DC_GATE_WHITE ||
14416              element == EL_DC_GATE_WHITE_GRAY ||
14417              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14418     {
14419       if (player->num_white_keys == 0)
14420         return MP_NO_ACTION;
14421
14422       player->num_white_keys--;
14423     }
14424     else if (IS_SP_PORT(element))
14425     {
14426       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14427           element == EL_SP_GRAVITY_PORT_RIGHT ||
14428           element == EL_SP_GRAVITY_PORT_UP ||
14429           element == EL_SP_GRAVITY_PORT_DOWN)
14430         player->gravity = !player->gravity;
14431       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14432                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14433                element == EL_SP_GRAVITY_ON_PORT_UP ||
14434                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14435         player->gravity = TRUE;
14436       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14437                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14438                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14439                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14440         player->gravity = FALSE;
14441     }
14442
14443     // automatically move to the next field with double speed
14444     player->programmed_action = move_direction;
14445
14446     if (player->move_delay_reset_counter == 0)
14447     {
14448       player->move_delay_reset_counter = 2;     // two double speed steps
14449
14450       DOUBLE_PLAYER_SPEED(player);
14451     }
14452
14453     PlayLevelSoundAction(x, y, ACTION_PASSING);
14454   }
14455   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14456   {
14457     RemoveField(x, y);
14458
14459     if (mode != DF_SNAP)
14460     {
14461       GfxElement[x][y] = GFX_ELEMENT(element);
14462       player->is_digging = TRUE;
14463     }
14464
14465     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14466
14467     // use old behaviour for old levels (digging)
14468     if (!level.finish_dig_collect)
14469     {
14470       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14471                                           player->index_bit, dig_side);
14472
14473       // if digging triggered player relocation, finish digging tile
14474       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14475         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14476     }
14477
14478     if (mode == DF_SNAP)
14479     {
14480       if (level.block_snap_field)
14481         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14482       else
14483         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14484
14485       // use old behaviour for old levels (snapping)
14486       if (!level.finish_dig_collect)
14487         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14488                                             player->index_bit, dig_side);
14489     }
14490   }
14491   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14492   {
14493     RemoveField(x, y);
14494
14495     if (is_player && mode != DF_SNAP)
14496     {
14497       GfxElement[x][y] = element;
14498       player->is_collecting = TRUE;
14499     }
14500
14501     if (element == EL_SPEED_PILL)
14502     {
14503       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14504     }
14505     else if (element == EL_EXTRA_TIME && level.time > 0)
14506     {
14507       TimeLeft += level.extra_time;
14508
14509       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14510
14511       DisplayGameControlValues();
14512     }
14513     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14514     {
14515       player->shield_normal_time_left += level.shield_normal_time;
14516       if (element == EL_SHIELD_DEADLY)
14517         player->shield_deadly_time_left += level.shield_deadly_time;
14518     }
14519     else if (element == EL_DYNAMITE ||
14520              element == EL_EM_DYNAMITE ||
14521              element == EL_SP_DISK_RED)
14522     {
14523       if (player->inventory_size < MAX_INVENTORY_SIZE)
14524         player->inventory_element[player->inventory_size++] = element;
14525
14526       DrawGameDoorValues();
14527     }
14528     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14529     {
14530       player->dynabomb_count++;
14531       player->dynabombs_left++;
14532     }
14533     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14534     {
14535       player->dynabomb_size++;
14536     }
14537     else if (element == EL_DYNABOMB_INCREASE_POWER)
14538     {
14539       player->dynabomb_xl = TRUE;
14540     }
14541     else if (IS_KEY(element))
14542     {
14543       player->key[KEY_NR(element)] = TRUE;
14544
14545       DrawGameDoorValues();
14546     }
14547     else if (element == EL_DC_KEY_WHITE)
14548     {
14549       player->num_white_keys++;
14550
14551       // display white keys?
14552       // DrawGameDoorValues();
14553     }
14554     else if (IS_ENVELOPE(element))
14555     {
14556       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14557
14558       if (!wait_for_snapping)
14559         player->show_envelope = element;
14560     }
14561     else if (element == EL_EMC_LENSES)
14562     {
14563       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14564
14565       RedrawAllInvisibleElementsForLenses();
14566     }
14567     else if (element == EL_EMC_MAGNIFIER)
14568     {
14569       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14570
14571       RedrawAllInvisibleElementsForMagnifier();
14572     }
14573     else if (IS_DROPPABLE(element) ||
14574              IS_THROWABLE(element))     // can be collected and dropped
14575     {
14576       int i;
14577
14578       if (collect_count == 0)
14579         player->inventory_infinite_element = element;
14580       else
14581         for (i = 0; i < collect_count; i++)
14582           if (player->inventory_size < MAX_INVENTORY_SIZE)
14583             player->inventory_element[player->inventory_size++] = element;
14584
14585       DrawGameDoorValues();
14586     }
14587     else if (collect_count > 0)
14588     {
14589       game.gems_still_needed -= collect_count;
14590       if (game.gems_still_needed < 0)
14591         game.gems_still_needed = 0;
14592
14593       game.snapshot.collected_item = TRUE;
14594
14595       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14596
14597       DisplayGameControlValues();
14598     }
14599
14600     RaiseScoreElement(element);
14601     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14602
14603     // use old behaviour for old levels (collecting)
14604     if (!level.finish_dig_collect && is_player)
14605     {
14606       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14607                                           player->index_bit, dig_side);
14608
14609       // if collecting triggered player relocation, finish collecting tile
14610       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14611         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14612     }
14613
14614     if (mode == DF_SNAP)
14615     {
14616       if (level.block_snap_field)
14617         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14618       else
14619         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14620
14621       // use old behaviour for old levels (snapping)
14622       if (!level.finish_dig_collect)
14623         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14624                                             player->index_bit, dig_side);
14625     }
14626   }
14627   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14628   {
14629     if (mode == DF_SNAP && element != EL_BD_ROCK)
14630       return MP_NO_ACTION;
14631
14632     if (CAN_FALL(element) && dy)
14633       return MP_NO_ACTION;
14634
14635     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14636         !(element == EL_SPRING && level.use_spring_bug))
14637       return MP_NO_ACTION;
14638
14639     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14640         ((move_direction & MV_VERTICAL &&
14641           ((element_info[element].move_pattern & MV_LEFT &&
14642             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14643            (element_info[element].move_pattern & MV_RIGHT &&
14644             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14645          (move_direction & MV_HORIZONTAL &&
14646           ((element_info[element].move_pattern & MV_UP &&
14647             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14648            (element_info[element].move_pattern & MV_DOWN &&
14649             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14650       return MP_NO_ACTION;
14651
14652     // do not push elements already moving away faster than player
14653     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14654         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14655       return MP_NO_ACTION;
14656
14657     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14658     {
14659       if (player->push_delay_value == -1 || !player_was_pushing)
14660         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14661     }
14662     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14663     {
14664       if (player->push_delay_value == -1)
14665         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14666     }
14667     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14668     {
14669       if (!player->is_pushing)
14670         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14671     }
14672
14673     player->is_pushing = TRUE;
14674     player->is_active = TRUE;
14675
14676     if (!(IN_LEV_FIELD(nextx, nexty) &&
14677           (IS_FREE(nextx, nexty) ||
14678            (IS_SB_ELEMENT(element) &&
14679             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14680            (IS_CUSTOM_ELEMENT(element) &&
14681             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14682       return MP_NO_ACTION;
14683
14684     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14685       return MP_NO_ACTION;
14686
14687     if (player->push_delay == -1)       // new pushing; restart delay
14688       player->push_delay = 0;
14689
14690     if (player->push_delay < player->push_delay_value &&
14691         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14692         element != EL_SPRING && element != EL_BALLOON)
14693     {
14694       // make sure that there is no move delay before next try to push
14695       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14696         player->move_delay = 0;
14697
14698       return MP_NO_ACTION;
14699     }
14700
14701     if (IS_CUSTOM_ELEMENT(element) &&
14702         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14703     {
14704       if (!DigFieldByCE(nextx, nexty, element))
14705         return MP_NO_ACTION;
14706     }
14707
14708     if (IS_SB_ELEMENT(element))
14709     {
14710       boolean sokoban_task_solved = FALSE;
14711
14712       if (element == EL_SOKOBAN_FIELD_FULL)
14713       {
14714         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14715
14716         IncrementSokobanFieldsNeeded();
14717         IncrementSokobanObjectsNeeded();
14718       }
14719
14720       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14721       {
14722         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14723
14724         DecrementSokobanFieldsNeeded();
14725         DecrementSokobanObjectsNeeded();
14726
14727         // sokoban object was pushed from empty field to sokoban field
14728         if (Back[x][y] == EL_EMPTY)
14729           sokoban_task_solved = TRUE;
14730       }
14731
14732       Tile[x][y] = EL_SOKOBAN_OBJECT;
14733
14734       if (Back[x][y] == Back[nextx][nexty])
14735         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14736       else if (Back[x][y] != 0)
14737         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14738                                     ACTION_EMPTYING);
14739       else
14740         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14741                                     ACTION_FILLING);
14742
14743       if (sokoban_task_solved &&
14744           game.sokoban_fields_still_needed == 0 &&
14745           game.sokoban_objects_still_needed == 0 &&
14746           level.auto_exit_sokoban)
14747       {
14748         game.players_still_needed = 0;
14749
14750         LevelSolved();
14751
14752         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14753       }
14754     }
14755     else
14756       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14757
14758     InitMovingField(x, y, move_direction);
14759     GfxAction[x][y] = ACTION_PUSHING;
14760
14761     if (mode == DF_SNAP)
14762       ContinueMoving(x, y);
14763     else
14764       MovPos[x][y] = (dx != 0 ? dx : dy);
14765
14766     Pushed[x][y] = TRUE;
14767     Pushed[nextx][nexty] = TRUE;
14768
14769     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14770       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14771     else
14772       player->push_delay_value = -1;    // get new value later
14773
14774     // check for element change _after_ element has been pushed
14775     if (game.use_change_when_pushing_bug)
14776     {
14777       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14778                                  player->index_bit, dig_side);
14779       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14780                                           player->index_bit, dig_side);
14781     }
14782   }
14783   else if (IS_SWITCHABLE(element))
14784   {
14785     if (PLAYER_SWITCHING(player, x, y))
14786     {
14787       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14788                                           player->index_bit, dig_side);
14789
14790       return MP_ACTION;
14791     }
14792
14793     player->is_switching = TRUE;
14794     player->switch_x = x;
14795     player->switch_y = y;
14796
14797     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14798
14799     if (element == EL_ROBOT_WHEEL)
14800     {
14801       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14802
14803       game.robot_wheel_x = x;
14804       game.robot_wheel_y = y;
14805       game.robot_wheel_active = TRUE;
14806
14807       TEST_DrawLevelField(x, y);
14808     }
14809     else if (element == EL_SP_TERMINAL)
14810     {
14811       int xx, yy;
14812
14813       SCAN_PLAYFIELD(xx, yy)
14814       {
14815         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14816         {
14817           Bang(xx, yy);
14818         }
14819         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14820         {
14821           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14822
14823           ResetGfxAnimation(xx, yy);
14824           TEST_DrawLevelField(xx, yy);
14825         }
14826       }
14827     }
14828     else if (IS_BELT_SWITCH(element))
14829     {
14830       ToggleBeltSwitch(x, y);
14831     }
14832     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14833              element == EL_SWITCHGATE_SWITCH_DOWN ||
14834              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14835              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14836     {
14837       ToggleSwitchgateSwitch(x, y);
14838     }
14839     else if (element == EL_LIGHT_SWITCH ||
14840              element == EL_LIGHT_SWITCH_ACTIVE)
14841     {
14842       ToggleLightSwitch(x, y);
14843     }
14844     else if (element == EL_TIMEGATE_SWITCH ||
14845              element == EL_DC_TIMEGATE_SWITCH)
14846     {
14847       ActivateTimegateSwitch(x, y);
14848     }
14849     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14850              element == EL_BALLOON_SWITCH_RIGHT ||
14851              element == EL_BALLOON_SWITCH_UP    ||
14852              element == EL_BALLOON_SWITCH_DOWN  ||
14853              element == EL_BALLOON_SWITCH_NONE  ||
14854              element == EL_BALLOON_SWITCH_ANY)
14855     {
14856       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14857                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14858                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14859                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14860                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14861                              move_direction);
14862     }
14863     else if (element == EL_LAMP)
14864     {
14865       Tile[x][y] = EL_LAMP_ACTIVE;
14866       game.lights_still_needed--;
14867
14868       ResetGfxAnimation(x, y);
14869       TEST_DrawLevelField(x, y);
14870     }
14871     else if (element == EL_TIME_ORB_FULL)
14872     {
14873       Tile[x][y] = EL_TIME_ORB_EMPTY;
14874
14875       if (level.time > 0 || level.use_time_orb_bug)
14876       {
14877         TimeLeft += level.time_orb_time;
14878         game.no_time_limit = FALSE;
14879
14880         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14881
14882         DisplayGameControlValues();
14883       }
14884
14885       ResetGfxAnimation(x, y);
14886       TEST_DrawLevelField(x, y);
14887     }
14888     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14889              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14890     {
14891       int xx, yy;
14892
14893       game.ball_active = !game.ball_active;
14894
14895       SCAN_PLAYFIELD(xx, yy)
14896       {
14897         int e = Tile[xx][yy];
14898
14899         if (game.ball_active)
14900         {
14901           if (e == EL_EMC_MAGIC_BALL)
14902             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14903           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14904             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14905         }
14906         else
14907         {
14908           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14909             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14910           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14911             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14912         }
14913       }
14914     }
14915
14916     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14917                                         player->index_bit, dig_side);
14918
14919     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14920                                         player->index_bit, dig_side);
14921
14922     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14923                                         player->index_bit, dig_side);
14924
14925     return MP_ACTION;
14926   }
14927   else
14928   {
14929     if (!PLAYER_SWITCHING(player, x, y))
14930     {
14931       player->is_switching = TRUE;
14932       player->switch_x = x;
14933       player->switch_y = y;
14934
14935       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14936                                  player->index_bit, dig_side);
14937       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14938                                           player->index_bit, dig_side);
14939
14940       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14941                                  player->index_bit, dig_side);
14942       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14943                                           player->index_bit, dig_side);
14944     }
14945
14946     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14947                                player->index_bit, dig_side);
14948     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14949                                         player->index_bit, dig_side);
14950
14951     return MP_NO_ACTION;
14952   }
14953
14954   player->push_delay = -1;
14955
14956   if (is_player)                // function can also be called by EL_PENGUIN
14957   {
14958     if (Tile[x][y] != element)          // really digged/collected something
14959     {
14960       player->is_collecting = !player->is_digging;
14961       player->is_active = TRUE;
14962
14963       player->last_removed_element = element;
14964     }
14965   }
14966
14967   return MP_MOVING;
14968 }
14969
14970 static boolean DigFieldByCE(int x, int y, int digging_element)
14971 {
14972   int element = Tile[x][y];
14973
14974   if (!IS_FREE(x, y))
14975   {
14976     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14977                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14978                   ACTION_BREAKING);
14979
14980     // no element can dig solid indestructible elements
14981     if (IS_INDESTRUCTIBLE(element) &&
14982         !IS_DIGGABLE(element) &&
14983         !IS_COLLECTIBLE(element))
14984       return FALSE;
14985
14986     if (AmoebaNr[x][y] &&
14987         (element == EL_AMOEBA_FULL ||
14988          element == EL_BD_AMOEBA ||
14989          element == EL_AMOEBA_GROWING))
14990     {
14991       AmoebaCnt[AmoebaNr[x][y]]--;
14992       AmoebaCnt2[AmoebaNr[x][y]]--;
14993     }
14994
14995     if (IS_MOVING(x, y))
14996       RemoveMovingField(x, y);
14997     else
14998     {
14999       RemoveField(x, y);
15000       TEST_DrawLevelField(x, y);
15001     }
15002
15003     // if digged element was about to explode, prevent the explosion
15004     ExplodeField[x][y] = EX_TYPE_NONE;
15005
15006     PlayLevelSoundAction(x, y, action);
15007   }
15008
15009   Store[x][y] = EL_EMPTY;
15010
15011   // this makes it possible to leave the removed element again
15012   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15013     Store[x][y] = element;
15014
15015   return TRUE;
15016 }
15017
15018 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15019 {
15020   int jx = player->jx, jy = player->jy;
15021   int x = jx + dx, y = jy + dy;
15022   int snap_direction = (dx == -1 ? MV_LEFT  :
15023                         dx == +1 ? MV_RIGHT :
15024                         dy == -1 ? MV_UP    :
15025                         dy == +1 ? MV_DOWN  : MV_NONE);
15026   boolean can_continue_snapping = (level.continuous_snapping &&
15027                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15028
15029   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15030     return FALSE;
15031
15032   if (!player->active || !IN_LEV_FIELD(x, y))
15033     return FALSE;
15034
15035   if (dx && dy)
15036     return FALSE;
15037
15038   if (!dx && !dy)
15039   {
15040     if (player->MovPos == 0)
15041       player->is_pushing = FALSE;
15042
15043     player->is_snapping = FALSE;
15044
15045     if (player->MovPos == 0)
15046     {
15047       player->is_moving = FALSE;
15048       player->is_digging = FALSE;
15049       player->is_collecting = FALSE;
15050     }
15051
15052     return FALSE;
15053   }
15054
15055   // prevent snapping with already pressed snap key when not allowed
15056   if (player->is_snapping && !can_continue_snapping)
15057     return FALSE;
15058
15059   player->MovDir = snap_direction;
15060
15061   if (player->MovPos == 0)
15062   {
15063     player->is_moving = FALSE;
15064     player->is_digging = FALSE;
15065     player->is_collecting = FALSE;
15066   }
15067
15068   player->is_dropping = FALSE;
15069   player->is_dropping_pressed = FALSE;
15070   player->drop_pressed_delay = 0;
15071
15072   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15073     return FALSE;
15074
15075   player->is_snapping = TRUE;
15076   player->is_active = TRUE;
15077
15078   if (player->MovPos == 0)
15079   {
15080     player->is_moving = FALSE;
15081     player->is_digging = FALSE;
15082     player->is_collecting = FALSE;
15083   }
15084
15085   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15086     TEST_DrawLevelField(player->last_jx, player->last_jy);
15087
15088   TEST_DrawLevelField(x, y);
15089
15090   return TRUE;
15091 }
15092
15093 static boolean DropElement(struct PlayerInfo *player)
15094 {
15095   int old_element, new_element;
15096   int dropx = player->jx, dropy = player->jy;
15097   int drop_direction = player->MovDir;
15098   int drop_side = drop_direction;
15099   int drop_element = get_next_dropped_element(player);
15100
15101   /* do not drop an element on top of another element; when holding drop key
15102      pressed without moving, dropped element must move away before the next
15103      element can be dropped (this is especially important if the next element
15104      is dynamite, which can be placed on background for historical reasons) */
15105   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15106     return MP_ACTION;
15107
15108   if (IS_THROWABLE(drop_element))
15109   {
15110     dropx += GET_DX_FROM_DIR(drop_direction);
15111     dropy += GET_DY_FROM_DIR(drop_direction);
15112
15113     if (!IN_LEV_FIELD(dropx, dropy))
15114       return FALSE;
15115   }
15116
15117   old_element = Tile[dropx][dropy];     // old element at dropping position
15118   new_element = drop_element;           // default: no change when dropping
15119
15120   // check if player is active, not moving and ready to drop
15121   if (!player->active || player->MovPos || player->drop_delay > 0)
15122     return FALSE;
15123
15124   // check if player has anything that can be dropped
15125   if (new_element == EL_UNDEFINED)
15126     return FALSE;
15127
15128   // only set if player has anything that can be dropped
15129   player->is_dropping_pressed = TRUE;
15130
15131   // check if drop key was pressed long enough for EM style dynamite
15132   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15133     return FALSE;
15134
15135   // check if anything can be dropped at the current position
15136   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15137     return FALSE;
15138
15139   // collected custom elements can only be dropped on empty fields
15140   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15141     return FALSE;
15142
15143   if (old_element != EL_EMPTY)
15144     Back[dropx][dropy] = old_element;   // store old element on this field
15145
15146   ResetGfxAnimation(dropx, dropy);
15147   ResetRandomAnimationValue(dropx, dropy);
15148
15149   if (player->inventory_size > 0 ||
15150       player->inventory_infinite_element != EL_UNDEFINED)
15151   {
15152     if (player->inventory_size > 0)
15153     {
15154       player->inventory_size--;
15155
15156       DrawGameDoorValues();
15157
15158       if (new_element == EL_DYNAMITE)
15159         new_element = EL_DYNAMITE_ACTIVE;
15160       else if (new_element == EL_EM_DYNAMITE)
15161         new_element = EL_EM_DYNAMITE_ACTIVE;
15162       else if (new_element == EL_SP_DISK_RED)
15163         new_element = EL_SP_DISK_RED_ACTIVE;
15164     }
15165
15166     Tile[dropx][dropy] = new_element;
15167
15168     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15169       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15170                           el2img(Tile[dropx][dropy]), 0);
15171
15172     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15173
15174     // needed if previous element just changed to "empty" in the last frame
15175     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15176
15177     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15178                                player->index_bit, drop_side);
15179     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15180                                         CE_PLAYER_DROPS_X,
15181                                         player->index_bit, drop_side);
15182
15183     TestIfElementTouchesCustomElement(dropx, dropy);
15184   }
15185   else          // player is dropping a dyna bomb
15186   {
15187     player->dynabombs_left--;
15188
15189     Tile[dropx][dropy] = new_element;
15190
15191     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15192       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15193                           el2img(Tile[dropx][dropy]), 0);
15194
15195     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15196   }
15197
15198   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15199     InitField_WithBug1(dropx, dropy, FALSE);
15200
15201   new_element = Tile[dropx][dropy];     // element might have changed
15202
15203   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15204       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15205   {
15206     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15207       MovDir[dropx][dropy] = drop_direction;
15208
15209     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15210
15211     // do not cause impact style collision by dropping elements that can fall
15212     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15213   }
15214
15215   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15216   player->is_dropping = TRUE;
15217
15218   player->drop_pressed_delay = 0;
15219   player->is_dropping_pressed = FALSE;
15220
15221   player->drop_x = dropx;
15222   player->drop_y = dropy;
15223
15224   return TRUE;
15225 }
15226
15227 // ----------------------------------------------------------------------------
15228 // game sound playing functions
15229 // ----------------------------------------------------------------------------
15230
15231 static int *loop_sound_frame = NULL;
15232 static int *loop_sound_volume = NULL;
15233
15234 void InitPlayLevelSound(void)
15235 {
15236   int num_sounds = getSoundListSize();
15237
15238   checked_free(loop_sound_frame);
15239   checked_free(loop_sound_volume);
15240
15241   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15242   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15243 }
15244
15245 static void PlayLevelSound(int x, int y, int nr)
15246 {
15247   int sx = SCREENX(x), sy = SCREENY(y);
15248   int volume, stereo_position;
15249   int max_distance = 8;
15250   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15251
15252   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15253       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15254     return;
15255
15256   if (!IN_LEV_FIELD(x, y) ||
15257       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15258       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15259     return;
15260
15261   volume = SOUND_MAX_VOLUME;
15262
15263   if (!IN_SCR_FIELD(sx, sy))
15264   {
15265     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15266     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15267
15268     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15269   }
15270
15271   stereo_position = (SOUND_MAX_LEFT +
15272                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15273                      (SCR_FIELDX + 2 * max_distance));
15274
15275   if (IS_LOOP_SOUND(nr))
15276   {
15277     /* This assures that quieter loop sounds do not overwrite louder ones,
15278        while restarting sound volume comparison with each new game frame. */
15279
15280     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15281       return;
15282
15283     loop_sound_volume[nr] = volume;
15284     loop_sound_frame[nr] = FrameCounter;
15285   }
15286
15287   PlaySoundExt(nr, volume, stereo_position, type);
15288 }
15289
15290 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15291 {
15292   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15293                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15294                  y < LEVELY(BY1) ? LEVELY(BY1) :
15295                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15296                  sound_action);
15297 }
15298
15299 static void PlayLevelSoundAction(int x, int y, int action)
15300 {
15301   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15302 }
15303
15304 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15305 {
15306   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15307
15308   if (sound_effect != SND_UNDEFINED)
15309     PlayLevelSound(x, y, sound_effect);
15310 }
15311
15312 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15313                                               int action)
15314 {
15315   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15316
15317   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15318     PlayLevelSound(x, y, sound_effect);
15319 }
15320
15321 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15322 {
15323   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15324
15325   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15326     PlayLevelSound(x, y, sound_effect);
15327 }
15328
15329 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15330 {
15331   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15332
15333   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15334     StopSound(sound_effect);
15335 }
15336
15337 static int getLevelMusicNr(void)
15338 {
15339   if (levelset.music[level_nr] != MUS_UNDEFINED)
15340     return levelset.music[level_nr];            // from config file
15341   else
15342     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15343 }
15344
15345 static void FadeLevelSounds(void)
15346 {
15347   FadeSounds();
15348 }
15349
15350 static void FadeLevelMusic(void)
15351 {
15352   int music_nr = getLevelMusicNr();
15353   char *curr_music = getCurrentlyPlayingMusicFilename();
15354   char *next_music = getMusicInfoEntryFilename(music_nr);
15355
15356   if (!strEqual(curr_music, next_music))
15357     FadeMusic();
15358 }
15359
15360 void FadeLevelSoundsAndMusic(void)
15361 {
15362   FadeLevelSounds();
15363   FadeLevelMusic();
15364 }
15365
15366 static void PlayLevelMusic(void)
15367 {
15368   int music_nr = getLevelMusicNr();
15369   char *curr_music = getCurrentlyPlayingMusicFilename();
15370   char *next_music = getMusicInfoEntryFilename(music_nr);
15371
15372   if (!strEqual(curr_music, next_music))
15373     PlayMusicLoop(music_nr);
15374 }
15375
15376 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15377 {
15378   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15379   int offset = 0;
15380   int x = xx - offset;
15381   int y = yy - offset;
15382
15383   switch (sample)
15384   {
15385     case SOUND_blank:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15387       break;
15388
15389     case SOUND_roll:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15391       break;
15392
15393     case SOUND_stone:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15395       break;
15396
15397     case SOUND_nut:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15399       break;
15400
15401     case SOUND_crack:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15403       break;
15404
15405     case SOUND_bug:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15407       break;
15408
15409     case SOUND_tank:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15411       break;
15412
15413     case SOUND_android_clone:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15415       break;
15416
15417     case SOUND_android_move:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15419       break;
15420
15421     case SOUND_spring:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15423       break;
15424
15425     case SOUND_slurp:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15427       break;
15428
15429     case SOUND_eater:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15431       break;
15432
15433     case SOUND_eater_eat:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15435       break;
15436
15437     case SOUND_alien:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15439       break;
15440
15441     case SOUND_collect:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15443       break;
15444
15445     case SOUND_diamond:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15447       break;
15448
15449     case SOUND_squash:
15450       // !!! CHECK THIS !!!
15451 #if 1
15452       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15453 #else
15454       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15455 #endif
15456       break;
15457
15458     case SOUND_wonderfall:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15460       break;
15461
15462     case SOUND_drip:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15464       break;
15465
15466     case SOUND_push:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15468       break;
15469
15470     case SOUND_dirt:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15472       break;
15473
15474     case SOUND_acid:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15476       break;
15477
15478     case SOUND_ball:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15480       break;
15481
15482     case SOUND_slide:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15484       break;
15485
15486     case SOUND_wonder:
15487       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15488       break;
15489
15490     case SOUND_door:
15491       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15492       break;
15493
15494     case SOUND_exit_open:
15495       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15496       break;
15497
15498     case SOUND_exit_leave:
15499       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15500       break;
15501
15502     case SOUND_dynamite:
15503       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15504       break;
15505
15506     case SOUND_tick:
15507       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15508       break;
15509
15510     case SOUND_press:
15511       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15512       break;
15513
15514     case SOUND_wheel:
15515       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15516       break;
15517
15518     case SOUND_boom:
15519       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15520       break;
15521
15522     case SOUND_die:
15523       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15524       break;
15525
15526     case SOUND_time:
15527       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15528       break;
15529
15530     default:
15531       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15532       break;
15533   }
15534 }
15535
15536 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15537 {
15538   int element = map_element_SP_to_RND(element_sp);
15539   int action = map_action_SP_to_RND(action_sp);
15540   int offset = (setup.sp_show_border_elements ? 0 : 1);
15541   int x = xx - offset;
15542   int y = yy - offset;
15543
15544   PlayLevelSoundElementAction(x, y, element, action);
15545 }
15546
15547 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15548 {
15549   int element = map_element_MM_to_RND(element_mm);
15550   int action = map_action_MM_to_RND(action_mm);
15551   int offset = 0;
15552   int x = xx - offset;
15553   int y = yy - offset;
15554
15555   if (!IS_MM_ELEMENT(element))
15556     element = EL_MM_DEFAULT;
15557
15558   PlayLevelSoundElementAction(x, y, element, action);
15559 }
15560
15561 void PlaySound_MM(int sound_mm)
15562 {
15563   int sound = map_sound_MM_to_RND(sound_mm);
15564
15565   if (sound == SND_UNDEFINED)
15566     return;
15567
15568   PlaySound(sound);
15569 }
15570
15571 void PlaySoundLoop_MM(int sound_mm)
15572 {
15573   int sound = map_sound_MM_to_RND(sound_mm);
15574
15575   if (sound == SND_UNDEFINED)
15576     return;
15577
15578   PlaySoundLoop(sound);
15579 }
15580
15581 void StopSound_MM(int sound_mm)
15582 {
15583   int sound = map_sound_MM_to_RND(sound_mm);
15584
15585   if (sound == SND_UNDEFINED)
15586     return;
15587
15588   StopSound(sound);
15589 }
15590
15591 void RaiseScore(int value)
15592 {
15593   game.score += value;
15594
15595   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15596
15597   DisplayGameControlValues();
15598 }
15599
15600 void RaiseScoreElement(int element)
15601 {
15602   switch (element)
15603   {
15604     case EL_EMERALD:
15605     case EL_BD_DIAMOND:
15606     case EL_EMERALD_YELLOW:
15607     case EL_EMERALD_RED:
15608     case EL_EMERALD_PURPLE:
15609     case EL_SP_INFOTRON:
15610       RaiseScore(level.score[SC_EMERALD]);
15611       break;
15612     case EL_DIAMOND:
15613       RaiseScore(level.score[SC_DIAMOND]);
15614       break;
15615     case EL_CRYSTAL:
15616       RaiseScore(level.score[SC_CRYSTAL]);
15617       break;
15618     case EL_PEARL:
15619       RaiseScore(level.score[SC_PEARL]);
15620       break;
15621     case EL_BUG:
15622     case EL_BD_BUTTERFLY:
15623     case EL_SP_ELECTRON:
15624       RaiseScore(level.score[SC_BUG]);
15625       break;
15626     case EL_SPACESHIP:
15627     case EL_BD_FIREFLY:
15628     case EL_SP_SNIKSNAK:
15629       RaiseScore(level.score[SC_SPACESHIP]);
15630       break;
15631     case EL_YAMYAM:
15632     case EL_DARK_YAMYAM:
15633       RaiseScore(level.score[SC_YAMYAM]);
15634       break;
15635     case EL_ROBOT:
15636       RaiseScore(level.score[SC_ROBOT]);
15637       break;
15638     case EL_PACMAN:
15639       RaiseScore(level.score[SC_PACMAN]);
15640       break;
15641     case EL_NUT:
15642       RaiseScore(level.score[SC_NUT]);
15643       break;
15644     case EL_DYNAMITE:
15645     case EL_EM_DYNAMITE:
15646     case EL_SP_DISK_RED:
15647     case EL_DYNABOMB_INCREASE_NUMBER:
15648     case EL_DYNABOMB_INCREASE_SIZE:
15649     case EL_DYNABOMB_INCREASE_POWER:
15650       RaiseScore(level.score[SC_DYNAMITE]);
15651       break;
15652     case EL_SHIELD_NORMAL:
15653     case EL_SHIELD_DEADLY:
15654       RaiseScore(level.score[SC_SHIELD]);
15655       break;
15656     case EL_EXTRA_TIME:
15657       RaiseScore(level.extra_time_score);
15658       break;
15659     case EL_KEY_1:
15660     case EL_KEY_2:
15661     case EL_KEY_3:
15662     case EL_KEY_4:
15663     case EL_EM_KEY_1:
15664     case EL_EM_KEY_2:
15665     case EL_EM_KEY_3:
15666     case EL_EM_KEY_4:
15667     case EL_EMC_KEY_5:
15668     case EL_EMC_KEY_6:
15669     case EL_EMC_KEY_7:
15670     case EL_EMC_KEY_8:
15671     case EL_DC_KEY_WHITE:
15672       RaiseScore(level.score[SC_KEY]);
15673       break;
15674     default:
15675       RaiseScore(element_info[element].collect_score);
15676       break;
15677   }
15678 }
15679
15680 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15681 {
15682   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15683   {
15684     if (!quick_quit)
15685     {
15686       // prevent short reactivation of overlay buttons while closing door
15687       SetOverlayActive(FALSE);
15688
15689       // door may still be open due to skipped or envelope style request
15690       CloseDoor(DOOR_CLOSE_1);
15691     }
15692
15693     if (network.enabled)
15694       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15695     else
15696     {
15697       if (quick_quit)
15698         FadeSkipNextFadeIn();
15699
15700       SetGameStatus(GAME_MODE_MAIN);
15701
15702       DrawMainMenu();
15703     }
15704   }
15705   else          // continue playing the game
15706   {
15707     if (tape.playing && tape.deactivate_display)
15708       TapeDeactivateDisplayOff(TRUE);
15709
15710     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15711
15712     if (tape.playing && tape.deactivate_display)
15713       TapeDeactivateDisplayOn();
15714   }
15715 }
15716
15717 void RequestQuitGame(boolean escape_key_pressed)
15718 {
15719   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15720   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15721                         level_editor_test_game);
15722   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15723                           quick_quit);
15724
15725   RequestQuitGameExt(skip_request, quick_quit,
15726                      "Do you really want to quit the game?");
15727 }
15728
15729 void RequestRestartGame(char *message)
15730 {
15731   game.restart_game_message = NULL;
15732
15733   boolean has_started_game = hasStartedNetworkGame();
15734   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15735
15736   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15737   {
15738     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15739   }
15740   else
15741   {
15742     // needed in case of envelope request to close game panel
15743     CloseDoor(DOOR_CLOSE_1);
15744
15745     SetGameStatus(GAME_MODE_MAIN);
15746
15747     DrawMainMenu();
15748   }
15749 }
15750
15751 void CheckGameOver(void)
15752 {
15753   static boolean last_game_over = FALSE;
15754   static int game_over_delay = 0;
15755   int game_over_delay_value = 50;
15756   boolean game_over = checkGameFailed();
15757
15758   // do not handle game over if request dialog is already active
15759   if (game.request_active)
15760     return;
15761
15762   // do not ask to play again if game was never actually played
15763   if (!game.GamePlayed)
15764     return;
15765
15766   if (!game_over)
15767   {
15768     last_game_over = FALSE;
15769     game_over_delay = game_over_delay_value;
15770
15771     return;
15772   }
15773
15774   if (game_over_delay > 0)
15775   {
15776     game_over_delay--;
15777
15778     return;
15779   }
15780
15781   if (last_game_over != game_over)
15782     game.restart_game_message = (hasStartedNetworkGame() ?
15783                                  "Game over! Play it again?" :
15784                                  "Game over!");
15785
15786   last_game_over = game_over;
15787 }
15788
15789 boolean checkGameSolved(void)
15790 {
15791   // set for all game engines if level was solved
15792   return game.LevelSolved_GameEnd;
15793 }
15794
15795 boolean checkGameFailed(void)
15796 {
15797   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15798     return (game_em.game_over && !game_em.level_solved);
15799   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15800     return (game_sp.game_over && !game_sp.level_solved);
15801   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15802     return (game_mm.game_over && !game_mm.level_solved);
15803   else                          // GAME_ENGINE_TYPE_RND
15804     return (game.GameOver && !game.LevelSolved);
15805 }
15806
15807 boolean checkGameEnded(void)
15808 {
15809   return (checkGameSolved() || checkGameFailed());
15810 }
15811
15812
15813 // ----------------------------------------------------------------------------
15814 // random generator functions
15815 // ----------------------------------------------------------------------------
15816
15817 unsigned int InitEngineRandom_RND(int seed)
15818 {
15819   game.num_random_calls = 0;
15820
15821   return InitEngineRandom(seed);
15822 }
15823
15824 unsigned int RND(int max)
15825 {
15826   if (max > 0)
15827   {
15828     game.num_random_calls++;
15829
15830     return GetEngineRandom(max);
15831   }
15832
15833   return 0;
15834 }
15835
15836
15837 // ----------------------------------------------------------------------------
15838 // game engine snapshot handling functions
15839 // ----------------------------------------------------------------------------
15840
15841 struct EngineSnapshotInfo
15842 {
15843   // runtime values for custom element collect score
15844   int collect_score[NUM_CUSTOM_ELEMENTS];
15845
15846   // runtime values for group element choice position
15847   int choice_pos[NUM_GROUP_ELEMENTS];
15848
15849   // runtime values for belt position animations
15850   int belt_graphic[4][NUM_BELT_PARTS];
15851   int belt_anim_mode[4][NUM_BELT_PARTS];
15852 };
15853
15854 static struct EngineSnapshotInfo engine_snapshot_rnd;
15855 static char *snapshot_level_identifier = NULL;
15856 static int snapshot_level_nr = -1;
15857
15858 static void SaveEngineSnapshotValues_RND(void)
15859 {
15860   static int belt_base_active_element[4] =
15861   {
15862     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15863     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15864     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15865     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15866   };
15867   int i, j;
15868
15869   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15870   {
15871     int element = EL_CUSTOM_START + i;
15872
15873     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15874   }
15875
15876   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15877   {
15878     int element = EL_GROUP_START + i;
15879
15880     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15881   }
15882
15883   for (i = 0; i < 4; i++)
15884   {
15885     for (j = 0; j < NUM_BELT_PARTS; j++)
15886     {
15887       int element = belt_base_active_element[i] + j;
15888       int graphic = el2img(element);
15889       int anim_mode = graphic_info[graphic].anim_mode;
15890
15891       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15892       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15893     }
15894   }
15895 }
15896
15897 static void LoadEngineSnapshotValues_RND(void)
15898 {
15899   unsigned int num_random_calls = game.num_random_calls;
15900   int i, j;
15901
15902   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15903   {
15904     int element = EL_CUSTOM_START + i;
15905
15906     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15907   }
15908
15909   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15910   {
15911     int element = EL_GROUP_START + i;
15912
15913     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15914   }
15915
15916   for (i = 0; i < 4; i++)
15917   {
15918     for (j = 0; j < NUM_BELT_PARTS; j++)
15919     {
15920       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15921       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15922
15923       graphic_info[graphic].anim_mode = anim_mode;
15924     }
15925   }
15926
15927   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15928   {
15929     InitRND(tape.random_seed);
15930     for (i = 0; i < num_random_calls; i++)
15931       RND(1);
15932   }
15933
15934   if (game.num_random_calls != num_random_calls)
15935   {
15936     Error("number of random calls out of sync");
15937     Error("number of random calls should be %d", num_random_calls);
15938     Error("number of random calls is %d", game.num_random_calls);
15939
15940     Fail("this should not happen -- please debug");
15941   }
15942 }
15943
15944 void FreeEngineSnapshotSingle(void)
15945 {
15946   FreeSnapshotSingle();
15947
15948   setString(&snapshot_level_identifier, NULL);
15949   snapshot_level_nr = -1;
15950 }
15951
15952 void FreeEngineSnapshotList(void)
15953 {
15954   FreeSnapshotList();
15955 }
15956
15957 static ListNode *SaveEngineSnapshotBuffers(void)
15958 {
15959   ListNode *buffers = NULL;
15960
15961   // copy some special values to a structure better suited for the snapshot
15962
15963   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15964     SaveEngineSnapshotValues_RND();
15965   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15966     SaveEngineSnapshotValues_EM();
15967   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15968     SaveEngineSnapshotValues_SP(&buffers);
15969   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15970     SaveEngineSnapshotValues_MM(&buffers);
15971
15972   // save values stored in special snapshot structure
15973
15974   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15975     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15976   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15977     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15978   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15979     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15980   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15981     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15982
15983   // save further RND engine values
15984
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15988
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15994
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15998
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16003
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16022
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16025
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16029
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16032
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16039
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16042
16043 #if 0
16044   ListNode *node = engine_snapshot_list_rnd;
16045   int num_bytes = 0;
16046
16047   while (node != NULL)
16048   {
16049     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16050
16051     node = node->next;
16052   }
16053
16054   Debug("game:playing:SaveEngineSnapshotBuffers",
16055         "size of engine snapshot: %d bytes", num_bytes);
16056 #endif
16057
16058   return buffers;
16059 }
16060
16061 void SaveEngineSnapshotSingle(void)
16062 {
16063   ListNode *buffers = SaveEngineSnapshotBuffers();
16064
16065   // finally save all snapshot buffers to single snapshot
16066   SaveSnapshotSingle(buffers);
16067
16068   // save level identification information
16069   setString(&snapshot_level_identifier, leveldir_current->identifier);
16070   snapshot_level_nr = level_nr;
16071 }
16072
16073 boolean CheckSaveEngineSnapshotToList(void)
16074 {
16075   boolean save_snapshot =
16076     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16077      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16078       game.snapshot.changed_action) ||
16079      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16080       game.snapshot.collected_item));
16081
16082   game.snapshot.changed_action = FALSE;
16083   game.snapshot.collected_item = FALSE;
16084   game.snapshot.save_snapshot = save_snapshot;
16085
16086   return save_snapshot;
16087 }
16088
16089 void SaveEngineSnapshotToList(void)
16090 {
16091   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16092       tape.quick_resume)
16093     return;
16094
16095   ListNode *buffers = SaveEngineSnapshotBuffers();
16096
16097   // finally save all snapshot buffers to snapshot list
16098   SaveSnapshotToList(buffers);
16099 }
16100
16101 void SaveEngineSnapshotToListInitial(void)
16102 {
16103   FreeEngineSnapshotList();
16104
16105   SaveEngineSnapshotToList();
16106 }
16107
16108 static void LoadEngineSnapshotValues(void)
16109 {
16110   // restore special values from snapshot structure
16111
16112   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16113     LoadEngineSnapshotValues_RND();
16114   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16115     LoadEngineSnapshotValues_EM();
16116   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16117     LoadEngineSnapshotValues_SP();
16118   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16119     LoadEngineSnapshotValues_MM();
16120 }
16121
16122 void LoadEngineSnapshotSingle(void)
16123 {
16124   LoadSnapshotSingle();
16125
16126   LoadEngineSnapshotValues();
16127 }
16128
16129 static void LoadEngineSnapshot_Undo(int steps)
16130 {
16131   LoadSnapshotFromList_Older(steps);
16132
16133   LoadEngineSnapshotValues();
16134 }
16135
16136 static void LoadEngineSnapshot_Redo(int steps)
16137 {
16138   LoadSnapshotFromList_Newer(steps);
16139
16140   LoadEngineSnapshotValues();
16141 }
16142
16143 boolean CheckEngineSnapshotSingle(void)
16144 {
16145   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16146           snapshot_level_nr == level_nr);
16147 }
16148
16149 boolean CheckEngineSnapshotList(void)
16150 {
16151   return CheckSnapshotList();
16152 }
16153
16154
16155 // ---------- new game button stuff -------------------------------------------
16156
16157 static struct
16158 {
16159   int graphic;
16160   struct XY *pos;
16161   int gadget_id;
16162   boolean *setup_value;
16163   boolean allowed_on_tape;
16164   boolean is_touch_button;
16165   char *infotext;
16166 } gamebutton_info[NUM_GAME_BUTTONS] =
16167 {
16168   {
16169     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16170     GAME_CTRL_ID_STOP,                          NULL,
16171     TRUE, FALSE,                                "stop game"
16172   },
16173   {
16174     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16175     GAME_CTRL_ID_PAUSE,                         NULL,
16176     TRUE, FALSE,                                "pause game"
16177   },
16178   {
16179     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16180     GAME_CTRL_ID_PLAY,                          NULL,
16181     TRUE, FALSE,                                "play game"
16182   },
16183   {
16184     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16185     GAME_CTRL_ID_UNDO,                          NULL,
16186     TRUE, FALSE,                                "undo step"
16187   },
16188   {
16189     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16190     GAME_CTRL_ID_REDO,                          NULL,
16191     TRUE, FALSE,                                "redo step"
16192   },
16193   {
16194     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16195     GAME_CTRL_ID_SAVE,                          NULL,
16196     TRUE, FALSE,                                "save game"
16197   },
16198   {
16199     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16200     GAME_CTRL_ID_PAUSE2,                        NULL,
16201     TRUE, FALSE,                                "pause game"
16202   },
16203   {
16204     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16205     GAME_CTRL_ID_LOAD,                          NULL,
16206     TRUE, FALSE,                                "load game"
16207   },
16208   {
16209     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16210     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16211     FALSE, FALSE,                               "stop game"
16212   },
16213   {
16214     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16215     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16216     FALSE, FALSE,                               "pause game"
16217   },
16218   {
16219     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16220     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16221     FALSE, FALSE,                               "play game"
16222   },
16223   {
16224     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16225     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16226     FALSE, TRUE,                                "stop game"
16227   },
16228   {
16229     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16230     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16231     FALSE, TRUE,                                "pause game"
16232   },
16233   {
16234     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16235     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16236     TRUE, FALSE,                                "background music on/off"
16237   },
16238   {
16239     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16240     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16241     TRUE, FALSE,                                "sound loops on/off"
16242   },
16243   {
16244     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16245     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16246     TRUE, FALSE,                                "normal sounds on/off"
16247   },
16248   {
16249     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16250     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16251     FALSE, FALSE,                               "background music on/off"
16252   },
16253   {
16254     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16255     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16256     FALSE, FALSE,                               "sound loops on/off"
16257   },
16258   {
16259     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16260     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16261     FALSE, FALSE,                               "normal sounds on/off"
16262   }
16263 };
16264
16265 void CreateGameButtons(void)
16266 {
16267   int i;
16268
16269   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16270   {
16271     int graphic = gamebutton_info[i].graphic;
16272     struct GraphicInfo *gfx = &graphic_info[graphic];
16273     struct XY *pos = gamebutton_info[i].pos;
16274     struct GadgetInfo *gi;
16275     int button_type;
16276     boolean checked;
16277     unsigned int event_mask;
16278     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16279     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16280     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16281     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16282     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16283     int gd_x   = gfx->src_x;
16284     int gd_y   = gfx->src_y;
16285     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16286     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16287     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16288     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16289     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16290     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16291     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16292     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16293     int id = i;
16294
16295     if (gfx->bitmap == NULL)
16296     {
16297       game_gadget[id] = NULL;
16298
16299       continue;
16300     }
16301
16302     if (id == GAME_CTRL_ID_STOP ||
16303         id == GAME_CTRL_ID_PANEL_STOP ||
16304         id == GAME_CTRL_ID_TOUCH_STOP ||
16305         id == GAME_CTRL_ID_PLAY ||
16306         id == GAME_CTRL_ID_PANEL_PLAY ||
16307         id == GAME_CTRL_ID_SAVE ||
16308         id == GAME_CTRL_ID_LOAD)
16309     {
16310       button_type = GD_TYPE_NORMAL_BUTTON;
16311       checked = FALSE;
16312       event_mask = GD_EVENT_RELEASED;
16313     }
16314     else if (id == GAME_CTRL_ID_UNDO ||
16315              id == GAME_CTRL_ID_REDO)
16316     {
16317       button_type = GD_TYPE_NORMAL_BUTTON;
16318       checked = FALSE;
16319       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16320     }
16321     else
16322     {
16323       button_type = GD_TYPE_CHECK_BUTTON;
16324       checked = (gamebutton_info[i].setup_value != NULL ?
16325                  *gamebutton_info[i].setup_value : FALSE);
16326       event_mask = GD_EVENT_PRESSED;
16327     }
16328
16329     gi = CreateGadget(GDI_CUSTOM_ID, id,
16330                       GDI_IMAGE_ID, graphic,
16331                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16332                       GDI_X, base_x + x,
16333                       GDI_Y, base_y + y,
16334                       GDI_WIDTH, gfx->width,
16335                       GDI_HEIGHT, gfx->height,
16336                       GDI_TYPE, button_type,
16337                       GDI_STATE, GD_BUTTON_UNPRESSED,
16338                       GDI_CHECKED, checked,
16339                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16340                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16341                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16342                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16343                       GDI_DIRECT_DRAW, FALSE,
16344                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16345                       GDI_EVENT_MASK, event_mask,
16346                       GDI_CALLBACK_ACTION, HandleGameButtons,
16347                       GDI_END);
16348
16349     if (gi == NULL)
16350       Fail("cannot create gadget");
16351
16352     game_gadget[id] = gi;
16353   }
16354 }
16355
16356 void FreeGameButtons(void)
16357 {
16358   int i;
16359
16360   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16361     FreeGadget(game_gadget[i]);
16362 }
16363
16364 static void UnmapGameButtonsAtSamePosition(int id)
16365 {
16366   int i;
16367
16368   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16369     if (i != id &&
16370         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16371         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16372       UnmapGadget(game_gadget[i]);
16373 }
16374
16375 static void UnmapGameButtonsAtSamePosition_All(void)
16376 {
16377   if (setup.show_load_save_buttons)
16378   {
16379     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16380     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16381     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16382   }
16383   else if (setup.show_undo_redo_buttons)
16384   {
16385     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16386     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16387     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16388   }
16389   else
16390   {
16391     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16392     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16393     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16394
16395     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16396     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16397     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16398   }
16399 }
16400
16401 void MapLoadSaveButtons(void)
16402 {
16403   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16404   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16405
16406   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16407   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16408 }
16409
16410 void MapUndoRedoButtons(void)
16411 {
16412   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16413   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16414
16415   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16416   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16417 }
16418
16419 void ModifyPauseButtons(void)
16420 {
16421   static int ids[] =
16422   {
16423     GAME_CTRL_ID_PAUSE,
16424     GAME_CTRL_ID_PAUSE2,
16425     GAME_CTRL_ID_PANEL_PAUSE,
16426     GAME_CTRL_ID_TOUCH_PAUSE,
16427     -1
16428   };
16429   int i;
16430
16431   for (i = 0; ids[i] > -1; i++)
16432     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16433 }
16434
16435 static void MapGameButtonsExt(boolean on_tape)
16436 {
16437   int i;
16438
16439   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16440   {
16441     if ((i == GAME_CTRL_ID_UNDO ||
16442          i == GAME_CTRL_ID_REDO) &&
16443         game_status != GAME_MODE_PLAYING)
16444       continue;
16445
16446     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16447       MapGadget(game_gadget[i]);
16448   }
16449
16450   UnmapGameButtonsAtSamePosition_All();
16451
16452   RedrawGameButtons();
16453 }
16454
16455 static void UnmapGameButtonsExt(boolean on_tape)
16456 {
16457   int i;
16458
16459   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16460     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16461       UnmapGadget(game_gadget[i]);
16462 }
16463
16464 static void RedrawGameButtonsExt(boolean on_tape)
16465 {
16466   int i;
16467
16468   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16469     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16470       RedrawGadget(game_gadget[i]);
16471 }
16472
16473 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16474 {
16475   if (gi == NULL)
16476     return;
16477
16478   gi->checked = state;
16479 }
16480
16481 static void RedrawSoundButtonGadget(int id)
16482 {
16483   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16484              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16485              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16486              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16487              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16488              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16489              id);
16490
16491   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16492   RedrawGadget(game_gadget[id2]);
16493 }
16494
16495 void MapGameButtons(void)
16496 {
16497   MapGameButtonsExt(FALSE);
16498 }
16499
16500 void UnmapGameButtons(void)
16501 {
16502   UnmapGameButtonsExt(FALSE);
16503 }
16504
16505 void RedrawGameButtons(void)
16506 {
16507   RedrawGameButtonsExt(FALSE);
16508 }
16509
16510 void MapGameButtonsOnTape(void)
16511 {
16512   MapGameButtonsExt(TRUE);
16513 }
16514
16515 void UnmapGameButtonsOnTape(void)
16516 {
16517   UnmapGameButtonsExt(TRUE);
16518 }
16519
16520 void RedrawGameButtonsOnTape(void)
16521 {
16522   RedrawGameButtonsExt(TRUE);
16523 }
16524
16525 static void GameUndoRedoExt(void)
16526 {
16527   ClearPlayerAction();
16528
16529   tape.pausing = TRUE;
16530
16531   RedrawPlayfield();
16532   UpdateAndDisplayGameControlValues();
16533
16534   DrawCompleteVideoDisplay();
16535   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16536   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16537   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16538
16539   ModifyPauseButtons();
16540
16541   BackToFront();
16542 }
16543
16544 static void GameUndo(int steps)
16545 {
16546   if (!CheckEngineSnapshotList())
16547     return;
16548
16549   int tape_property_bits = tape.property_bits;
16550
16551   LoadEngineSnapshot_Undo(steps);
16552
16553   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16554
16555   GameUndoRedoExt();
16556 }
16557
16558 static void GameRedo(int steps)
16559 {
16560   if (!CheckEngineSnapshotList())
16561     return;
16562
16563   int tape_property_bits = tape.property_bits;
16564
16565   LoadEngineSnapshot_Redo(steps);
16566
16567   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16568
16569   GameUndoRedoExt();
16570 }
16571
16572 static void HandleGameButtonsExt(int id, int button)
16573 {
16574   static boolean game_undo_executed = FALSE;
16575   int steps = BUTTON_STEPSIZE(button);
16576   boolean handle_game_buttons =
16577     (game_status == GAME_MODE_PLAYING ||
16578      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16579
16580   if (!handle_game_buttons)
16581     return;
16582
16583   switch (id)
16584   {
16585     case GAME_CTRL_ID_STOP:
16586     case GAME_CTRL_ID_PANEL_STOP:
16587     case GAME_CTRL_ID_TOUCH_STOP:
16588       if (game_status == GAME_MODE_MAIN)
16589         break;
16590
16591       if (tape.playing)
16592         TapeStop();
16593       else
16594         RequestQuitGame(FALSE);
16595
16596       break;
16597
16598     case GAME_CTRL_ID_PAUSE:
16599     case GAME_CTRL_ID_PAUSE2:
16600     case GAME_CTRL_ID_PANEL_PAUSE:
16601     case GAME_CTRL_ID_TOUCH_PAUSE:
16602       if (network.enabled && game_status == GAME_MODE_PLAYING)
16603       {
16604         if (tape.pausing)
16605           SendToServer_ContinuePlaying();
16606         else
16607           SendToServer_PausePlaying();
16608       }
16609       else
16610         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16611
16612       game_undo_executed = FALSE;
16613
16614       break;
16615
16616     case GAME_CTRL_ID_PLAY:
16617     case GAME_CTRL_ID_PANEL_PLAY:
16618       if (game_status == GAME_MODE_MAIN)
16619       {
16620         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16621       }
16622       else if (tape.pausing)
16623       {
16624         if (network.enabled)
16625           SendToServer_ContinuePlaying();
16626         else
16627           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16628       }
16629       break;
16630
16631     case GAME_CTRL_ID_UNDO:
16632       // Important: When using "save snapshot when collecting an item" mode,
16633       // load last (current) snapshot for first "undo" after pressing "pause"
16634       // (else the last-but-one snapshot would be loaded, because the snapshot
16635       // pointer already points to the last snapshot when pressing "pause",
16636       // which is fine for "every step/move" mode, but not for "every collect")
16637       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16638           !game_undo_executed)
16639         steps--;
16640
16641       game_undo_executed = TRUE;
16642
16643       GameUndo(steps);
16644       break;
16645
16646     case GAME_CTRL_ID_REDO:
16647       GameRedo(steps);
16648       break;
16649
16650     case GAME_CTRL_ID_SAVE:
16651       TapeQuickSave();
16652       break;
16653
16654     case GAME_CTRL_ID_LOAD:
16655       TapeQuickLoad();
16656       break;
16657
16658     case SOUND_CTRL_ID_MUSIC:
16659     case SOUND_CTRL_ID_PANEL_MUSIC:
16660       if (setup.sound_music)
16661       { 
16662         setup.sound_music = FALSE;
16663
16664         FadeMusic();
16665       }
16666       else if (audio.music_available)
16667       { 
16668         setup.sound = setup.sound_music = TRUE;
16669
16670         SetAudioMode(setup.sound);
16671
16672         if (game_status == GAME_MODE_PLAYING)
16673           PlayLevelMusic();
16674       }
16675
16676       RedrawSoundButtonGadget(id);
16677
16678       break;
16679
16680     case SOUND_CTRL_ID_LOOPS:
16681     case SOUND_CTRL_ID_PANEL_LOOPS:
16682       if (setup.sound_loops)
16683         setup.sound_loops = FALSE;
16684       else if (audio.loops_available)
16685       {
16686         setup.sound = setup.sound_loops = TRUE;
16687
16688         SetAudioMode(setup.sound);
16689       }
16690
16691       RedrawSoundButtonGadget(id);
16692
16693       break;
16694
16695     case SOUND_CTRL_ID_SIMPLE:
16696     case SOUND_CTRL_ID_PANEL_SIMPLE:
16697       if (setup.sound_simple)
16698         setup.sound_simple = FALSE;
16699       else if (audio.sound_available)
16700       {
16701         setup.sound = setup.sound_simple = TRUE;
16702
16703         SetAudioMode(setup.sound);
16704       }
16705
16706       RedrawSoundButtonGadget(id);
16707
16708       break;
16709
16710     default:
16711       break;
16712   }
16713 }
16714
16715 static void HandleGameButtons(struct GadgetInfo *gi)
16716 {
16717   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16718 }
16719
16720 void HandleSoundButtonKeys(Key key)
16721 {
16722   if (key == setup.shortcut.sound_simple)
16723     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16724   else if (key == setup.shortcut.sound_loops)
16725     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16726   else if (key == setup.shortcut.sound_music)
16727     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16728 }