added playing score tape from score info page
[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 && !score_info_tape_play)
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 || score_info_tape_play)
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       DrawLevelGraphic(x, y, graphic, frame);
6084       DrawLevelElementThruMask(x, y, Back[x][y]);
6085     }
6086     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6087       DrawLevelGraphic(x, 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               DrawScreenGraphic(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       DrawLevelGraphic(x, 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       DrawLevelGraphic(x, 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       DrawLevelGraphic(x, 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         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9775       new_wall = TRUE;
9776     }
9777     if (unten_frei)
9778     {
9779       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9780       Store[ax][ay + 1] = element;
9781       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9782       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9783         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9784       new_wall = TRUE;
9785     }
9786   }
9787
9788   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9789       element == EL_EXPANDABLE_WALL_ANY ||
9790       element == EL_EXPANDABLE_WALL ||
9791       element == EL_BD_EXPANDABLE_WALL)
9792   {
9793     if (links_frei)
9794     {
9795       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9796       Store[ax - 1][ay] = element;
9797       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9798       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9799         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9800       new_wall = TRUE;
9801     }
9802
9803     if (rechts_frei)
9804     {
9805       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9806       Store[ax + 1][ay] = element;
9807       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9808       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9809         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9810       new_wall = TRUE;
9811     }
9812   }
9813
9814   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9815     TEST_DrawLevelField(ax, ay);
9816
9817   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9818     oben_massiv = TRUE;
9819   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9820     unten_massiv = TRUE;
9821   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9822     links_massiv = TRUE;
9823   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9824     rechts_massiv = TRUE;
9825
9826   if (((oben_massiv && unten_massiv) ||
9827        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9828        element == EL_EXPANDABLE_WALL) &&
9829       ((links_massiv && rechts_massiv) ||
9830        element == EL_EXPANDABLE_WALL_VERTICAL))
9831     Tile[ax][ay] = EL_WALL;
9832
9833   if (new_wall)
9834     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9835 }
9836
9837 static void MauerAblegerStahl(int ax, int ay)
9838 {
9839   int element = Tile[ax][ay];
9840   int graphic = el2img(element);
9841   boolean oben_frei = FALSE, unten_frei = FALSE;
9842   boolean links_frei = FALSE, rechts_frei = FALSE;
9843   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9844   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9845   boolean new_wall = FALSE;
9846
9847   if (IS_ANIMATED(graphic))
9848     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9849
9850   if (!MovDelay[ax][ay])        // start building new wall
9851     MovDelay[ax][ay] = 6;
9852
9853   if (MovDelay[ax][ay])         // wait some time before building new wall
9854   {
9855     MovDelay[ax][ay]--;
9856     if (MovDelay[ax][ay])
9857       return;
9858   }
9859
9860   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9861     oben_frei = TRUE;
9862   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9863     unten_frei = TRUE;
9864   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9865     links_frei = TRUE;
9866   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9867     rechts_frei = TRUE;
9868
9869   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9870       element == EL_EXPANDABLE_STEELWALL_ANY)
9871   {
9872     if (oben_frei)
9873     {
9874       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9875       Store[ax][ay - 1] = element;
9876       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9877       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9878         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9879       new_wall = TRUE;
9880     }
9881     if (unten_frei)
9882     {
9883       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9884       Store[ax][ay + 1] = element;
9885       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9886       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9887         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9888       new_wall = TRUE;
9889     }
9890   }
9891
9892   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9893       element == EL_EXPANDABLE_STEELWALL_ANY)
9894   {
9895     if (links_frei)
9896     {
9897       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9898       Store[ax - 1][ay] = element;
9899       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9900       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9901         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9902       new_wall = TRUE;
9903     }
9904
9905     if (rechts_frei)
9906     {
9907       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9908       Store[ax + 1][ay] = element;
9909       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9910       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9911         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9912       new_wall = TRUE;
9913     }
9914   }
9915
9916   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9917     oben_massiv = TRUE;
9918   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9919     unten_massiv = TRUE;
9920   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9921     links_massiv = TRUE;
9922   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9923     rechts_massiv = TRUE;
9924
9925   if (((oben_massiv && unten_massiv) ||
9926        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9927       ((links_massiv && rechts_massiv) ||
9928        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9929     Tile[ax][ay] = EL_STEELWALL;
9930
9931   if (new_wall)
9932     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9933 }
9934
9935 static void CheckForDragon(int x, int y)
9936 {
9937   int i, j;
9938   boolean dragon_found = FALSE;
9939   static int xy[4][2] =
9940   {
9941     { 0, -1 },
9942     { -1, 0 },
9943     { +1, 0 },
9944     { 0, +1 }
9945   };
9946
9947   for (i = 0; i < NUM_DIRECTIONS; i++)
9948   {
9949     for (j = 0; j < 4; j++)
9950     {
9951       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9952
9953       if (IN_LEV_FIELD(xx, yy) &&
9954           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9955       {
9956         if (Tile[xx][yy] == EL_DRAGON)
9957           dragon_found = TRUE;
9958       }
9959       else
9960         break;
9961     }
9962   }
9963
9964   if (!dragon_found)
9965   {
9966     for (i = 0; i < NUM_DIRECTIONS; i++)
9967     {
9968       for (j = 0; j < 3; j++)
9969       {
9970         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9971   
9972         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9973         {
9974           Tile[xx][yy] = EL_EMPTY;
9975           TEST_DrawLevelField(xx, yy);
9976         }
9977         else
9978           break;
9979       }
9980     }
9981   }
9982 }
9983
9984 static void InitBuggyBase(int x, int y)
9985 {
9986   int element = Tile[x][y];
9987   int activating_delay = FRAMES_PER_SECOND / 4;
9988
9989   ChangeDelay[x][y] =
9990     (element == EL_SP_BUGGY_BASE ?
9991      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9992      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9993      activating_delay :
9994      element == EL_SP_BUGGY_BASE_ACTIVE ?
9995      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9996 }
9997
9998 static void WarnBuggyBase(int x, int y)
9999 {
10000   int i;
10001   static int xy[4][2] =
10002   {
10003     { 0, -1 },
10004     { -1, 0 },
10005     { +1, 0 },
10006     { 0, +1 }
10007   };
10008
10009   for (i = 0; i < NUM_DIRECTIONS; i++)
10010   {
10011     int xx = x + xy[i][0];
10012     int yy = y + xy[i][1];
10013
10014     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10015     {
10016       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10017
10018       break;
10019     }
10020   }
10021 }
10022
10023 static void InitTrap(int x, int y)
10024 {
10025   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10026 }
10027
10028 static void ActivateTrap(int x, int y)
10029 {
10030   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10031 }
10032
10033 static void ChangeActiveTrap(int x, int y)
10034 {
10035   int graphic = IMG_TRAP_ACTIVE;
10036
10037   // if new animation frame was drawn, correct crumbled sand border
10038   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10039     TEST_DrawLevelFieldCrumbled(x, y);
10040 }
10041
10042 static int getSpecialActionElement(int element, int number, int base_element)
10043 {
10044   return (element != EL_EMPTY ? element :
10045           number != -1 ? base_element + number - 1 :
10046           EL_EMPTY);
10047 }
10048
10049 static int getModifiedActionNumber(int value_old, int operator, int operand,
10050                                    int value_min, int value_max)
10051 {
10052   int value_new = (operator == CA_MODE_SET      ? operand :
10053                    operator == CA_MODE_ADD      ? value_old + operand :
10054                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10055                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10056                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10057                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10058                    value_old);
10059
10060   return (value_new < value_min ? value_min :
10061           value_new > value_max ? value_max :
10062           value_new);
10063 }
10064
10065 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10066 {
10067   struct ElementInfo *ei = &element_info[element];
10068   struct ElementChangeInfo *change = &ei->change_page[page];
10069   int target_element = change->target_element;
10070   int action_type = change->action_type;
10071   int action_mode = change->action_mode;
10072   int action_arg = change->action_arg;
10073   int action_element = change->action_element;
10074   int i;
10075
10076   if (!change->has_action)
10077     return;
10078
10079   // ---------- determine action paramater values -----------------------------
10080
10081   int level_time_value =
10082     (level.time > 0 ? TimeLeft :
10083      TimePlayed);
10084
10085   int action_arg_element_raw =
10086     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10087      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10088      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10089      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10090      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10091      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10092      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10093      EL_EMPTY);
10094   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10095
10096   int action_arg_direction =
10097     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10098      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10099      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10100      change->actual_trigger_side :
10101      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10102      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10103      MV_NONE);
10104
10105   int action_arg_number_min =
10106     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10107      CA_ARG_MIN);
10108
10109   int action_arg_number_max =
10110     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10111      action_type == CA_SET_LEVEL_GEMS ? 999 :
10112      action_type == CA_SET_LEVEL_TIME ? 9999 :
10113      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10114      action_type == CA_SET_CE_VALUE ? 9999 :
10115      action_type == CA_SET_CE_SCORE ? 9999 :
10116      CA_ARG_MAX);
10117
10118   int action_arg_number_reset =
10119     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10120      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10121      action_type == CA_SET_LEVEL_TIME ? level.time :
10122      action_type == CA_SET_LEVEL_SCORE ? 0 :
10123      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10124      action_type == CA_SET_CE_SCORE ? 0 :
10125      0);
10126
10127   int action_arg_number =
10128     (action_arg <= CA_ARG_MAX ? action_arg :
10129      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10130      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10131      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10132      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10133      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10134      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10135      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10136      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10137      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10138      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10139      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10140      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10141      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10142      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10143      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10144      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10145      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10146      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10147      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10148      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10149      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10150      -1);
10151
10152   int action_arg_number_old =
10153     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10154      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10155      action_type == CA_SET_LEVEL_SCORE ? game.score :
10156      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10157      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10158      0);
10159
10160   int action_arg_number_new =
10161     getModifiedActionNumber(action_arg_number_old,
10162                             action_mode, action_arg_number,
10163                             action_arg_number_min, action_arg_number_max);
10164
10165   int trigger_player_bits =
10166     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10167      change->actual_trigger_player_bits : change->trigger_player);
10168
10169   int action_arg_player_bits =
10170     (action_arg >= CA_ARG_PLAYER_1 &&
10171      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10172      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10173      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10174      PLAYER_BITS_ANY);
10175
10176   // ---------- execute action  -----------------------------------------------
10177
10178   switch (action_type)
10179   {
10180     case CA_NO_ACTION:
10181     {
10182       return;
10183     }
10184
10185     // ---------- level actions  ----------------------------------------------
10186
10187     case CA_RESTART_LEVEL:
10188     {
10189       game.restart_level = TRUE;
10190
10191       break;
10192     }
10193
10194     case CA_SHOW_ENVELOPE:
10195     {
10196       int element = getSpecialActionElement(action_arg_element,
10197                                             action_arg_number, EL_ENVELOPE_1);
10198
10199       if (IS_ENVELOPE(element))
10200         local_player->show_envelope = element;
10201
10202       break;
10203     }
10204
10205     case CA_SET_LEVEL_TIME:
10206     {
10207       if (level.time > 0)       // only modify limited time value
10208       {
10209         TimeLeft = action_arg_number_new;
10210
10211         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10212
10213         DisplayGameControlValues();
10214
10215         if (!TimeLeft && setup.time_limit)
10216           for (i = 0; i < MAX_PLAYERS; i++)
10217             KillPlayer(&stored_player[i]);
10218       }
10219
10220       break;
10221     }
10222
10223     case CA_SET_LEVEL_SCORE:
10224     {
10225       game.score = action_arg_number_new;
10226
10227       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10228
10229       DisplayGameControlValues();
10230
10231       break;
10232     }
10233
10234     case CA_SET_LEVEL_GEMS:
10235     {
10236       game.gems_still_needed = action_arg_number_new;
10237
10238       game.snapshot.collected_item = TRUE;
10239
10240       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10241
10242       DisplayGameControlValues();
10243
10244       break;
10245     }
10246
10247     case CA_SET_LEVEL_WIND:
10248     {
10249       game.wind_direction = action_arg_direction;
10250
10251       break;
10252     }
10253
10254     case CA_SET_LEVEL_RANDOM_SEED:
10255     {
10256       // ensure that setting a new random seed while playing is predictable
10257       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10258
10259       break;
10260     }
10261
10262     // ---------- player actions  ---------------------------------------------
10263
10264     case CA_MOVE_PLAYER:
10265     case CA_MOVE_PLAYER_NEW:
10266     {
10267       // automatically move to the next field in specified direction
10268       for (i = 0; i < MAX_PLAYERS; i++)
10269         if (trigger_player_bits & (1 << i))
10270           if (action_type == CA_MOVE_PLAYER ||
10271               stored_player[i].MovPos == 0)
10272             stored_player[i].programmed_action = action_arg_direction;
10273
10274       break;
10275     }
10276
10277     case CA_EXIT_PLAYER:
10278     {
10279       for (i = 0; i < MAX_PLAYERS; i++)
10280         if (action_arg_player_bits & (1 << i))
10281           ExitPlayer(&stored_player[i]);
10282
10283       if (game.players_still_needed == 0)
10284         LevelSolved();
10285
10286       break;
10287     }
10288
10289     case CA_KILL_PLAYER:
10290     {
10291       for (i = 0; i < MAX_PLAYERS; i++)
10292         if (action_arg_player_bits & (1 << i))
10293           KillPlayer(&stored_player[i]);
10294
10295       break;
10296     }
10297
10298     case CA_SET_PLAYER_KEYS:
10299     {
10300       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10301       int element = getSpecialActionElement(action_arg_element,
10302                                             action_arg_number, EL_KEY_1);
10303
10304       if (IS_KEY(element))
10305       {
10306         for (i = 0; i < MAX_PLAYERS; i++)
10307         {
10308           if (trigger_player_bits & (1 << i))
10309           {
10310             stored_player[i].key[KEY_NR(element)] = key_state;
10311
10312             DrawGameDoorValues();
10313           }
10314         }
10315       }
10316
10317       break;
10318     }
10319
10320     case CA_SET_PLAYER_SPEED:
10321     {
10322       for (i = 0; i < MAX_PLAYERS; i++)
10323       {
10324         if (trigger_player_bits & (1 << i))
10325         {
10326           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10327
10328           if (action_arg == CA_ARG_SPEED_FASTER &&
10329               stored_player[i].cannot_move)
10330           {
10331             action_arg_number = STEPSIZE_VERY_SLOW;
10332           }
10333           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10334                    action_arg == CA_ARG_SPEED_FASTER)
10335           {
10336             action_arg_number = 2;
10337             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10338                            CA_MODE_MULTIPLY);
10339           }
10340           else if (action_arg == CA_ARG_NUMBER_RESET)
10341           {
10342             action_arg_number = level.initial_player_stepsize[i];
10343           }
10344
10345           move_stepsize =
10346             getModifiedActionNumber(move_stepsize,
10347                                     action_mode,
10348                                     action_arg_number,
10349                                     action_arg_number_min,
10350                                     action_arg_number_max);
10351
10352           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10353         }
10354       }
10355
10356       break;
10357     }
10358
10359     case CA_SET_PLAYER_SHIELD:
10360     {
10361       for (i = 0; i < MAX_PLAYERS; i++)
10362       {
10363         if (trigger_player_bits & (1 << i))
10364         {
10365           if (action_arg == CA_ARG_SHIELD_OFF)
10366           {
10367             stored_player[i].shield_normal_time_left = 0;
10368             stored_player[i].shield_deadly_time_left = 0;
10369           }
10370           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10371           {
10372             stored_player[i].shield_normal_time_left = 999999;
10373           }
10374           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10375           {
10376             stored_player[i].shield_normal_time_left = 999999;
10377             stored_player[i].shield_deadly_time_left = 999999;
10378           }
10379         }
10380       }
10381
10382       break;
10383     }
10384
10385     case CA_SET_PLAYER_GRAVITY:
10386     {
10387       for (i = 0; i < MAX_PLAYERS; i++)
10388       {
10389         if (trigger_player_bits & (1 << i))
10390         {
10391           stored_player[i].gravity =
10392             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10393              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10394              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10395              stored_player[i].gravity);
10396         }
10397       }
10398
10399       break;
10400     }
10401
10402     case CA_SET_PLAYER_ARTWORK:
10403     {
10404       for (i = 0; i < MAX_PLAYERS; i++)
10405       {
10406         if (trigger_player_bits & (1 << i))
10407         {
10408           int artwork_element = action_arg_element;
10409
10410           if (action_arg == CA_ARG_ELEMENT_RESET)
10411             artwork_element =
10412               (level.use_artwork_element[i] ? level.artwork_element[i] :
10413                stored_player[i].element_nr);
10414
10415           if (stored_player[i].artwork_element != artwork_element)
10416             stored_player[i].Frame = 0;
10417
10418           stored_player[i].artwork_element = artwork_element;
10419
10420           SetPlayerWaiting(&stored_player[i], FALSE);
10421
10422           // set number of special actions for bored and sleeping animation
10423           stored_player[i].num_special_action_bored =
10424             get_num_special_action(artwork_element,
10425                                    ACTION_BORING_1, ACTION_BORING_LAST);
10426           stored_player[i].num_special_action_sleeping =
10427             get_num_special_action(artwork_element,
10428                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10429         }
10430       }
10431
10432       break;
10433     }
10434
10435     case CA_SET_PLAYER_INVENTORY:
10436     {
10437       for (i = 0; i < MAX_PLAYERS; i++)
10438       {
10439         struct PlayerInfo *player = &stored_player[i];
10440         int j, k;
10441
10442         if (trigger_player_bits & (1 << i))
10443         {
10444           int inventory_element = action_arg_element;
10445
10446           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10447               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10448               action_arg == CA_ARG_ELEMENT_ACTION)
10449           {
10450             int element = inventory_element;
10451             int collect_count = element_info[element].collect_count_initial;
10452
10453             if (!IS_CUSTOM_ELEMENT(element))
10454               collect_count = 1;
10455
10456             if (collect_count == 0)
10457               player->inventory_infinite_element = element;
10458             else
10459               for (k = 0; k < collect_count; k++)
10460                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10461                   player->inventory_element[player->inventory_size++] =
10462                     element;
10463           }
10464           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10465                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10466                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10467           {
10468             if (player->inventory_infinite_element != EL_UNDEFINED &&
10469                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10470                                      action_arg_element_raw))
10471               player->inventory_infinite_element = EL_UNDEFINED;
10472
10473             for (k = 0, j = 0; j < player->inventory_size; j++)
10474             {
10475               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10476                                         action_arg_element_raw))
10477                 player->inventory_element[k++] = player->inventory_element[j];
10478             }
10479
10480             player->inventory_size = k;
10481           }
10482           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10483           {
10484             if (player->inventory_size > 0)
10485             {
10486               for (j = 0; j < player->inventory_size - 1; j++)
10487                 player->inventory_element[j] = player->inventory_element[j + 1];
10488
10489               player->inventory_size--;
10490             }
10491           }
10492           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10493           {
10494             if (player->inventory_size > 0)
10495               player->inventory_size--;
10496           }
10497           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10498           {
10499             player->inventory_infinite_element = EL_UNDEFINED;
10500             player->inventory_size = 0;
10501           }
10502           else if (action_arg == CA_ARG_INVENTORY_RESET)
10503           {
10504             player->inventory_infinite_element = EL_UNDEFINED;
10505             player->inventory_size = 0;
10506
10507             if (level.use_initial_inventory[i])
10508             {
10509               for (j = 0; j < level.initial_inventory_size[i]; j++)
10510               {
10511                 int element = level.initial_inventory_content[i][j];
10512                 int collect_count = element_info[element].collect_count_initial;
10513
10514                 if (!IS_CUSTOM_ELEMENT(element))
10515                   collect_count = 1;
10516
10517                 if (collect_count == 0)
10518                   player->inventory_infinite_element = element;
10519                 else
10520                   for (k = 0; k < collect_count; k++)
10521                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10522                       player->inventory_element[player->inventory_size++] =
10523                         element;
10524               }
10525             }
10526           }
10527         }
10528       }
10529
10530       break;
10531     }
10532
10533     // ---------- CE actions  -------------------------------------------------
10534
10535     case CA_SET_CE_VALUE:
10536     {
10537       int last_ce_value = CustomValue[x][y];
10538
10539       CustomValue[x][y] = action_arg_number_new;
10540
10541       if (CustomValue[x][y] != last_ce_value)
10542       {
10543         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10544         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10545
10546         if (CustomValue[x][y] == 0)
10547         {
10548           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10549           ChangeCount[x][y] = 0;        // allow at least one more change
10550
10551           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10552           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10553         }
10554       }
10555
10556       break;
10557     }
10558
10559     case CA_SET_CE_SCORE:
10560     {
10561       int last_ce_score = ei->collect_score;
10562
10563       ei->collect_score = action_arg_number_new;
10564
10565       if (ei->collect_score != last_ce_score)
10566       {
10567         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10568         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10569
10570         if (ei->collect_score == 0)
10571         {
10572           int xx, yy;
10573
10574           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10575           ChangeCount[x][y] = 0;        // allow at least one more change
10576
10577           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10578           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10579
10580           /*
10581             This is a very special case that seems to be a mixture between
10582             CheckElementChange() and CheckTriggeredElementChange(): while
10583             the first one only affects single elements that are triggered
10584             directly, the second one affects multiple elements in the playfield
10585             that are triggered indirectly by another element. This is a third
10586             case: Changing the CE score always affects multiple identical CEs,
10587             so every affected CE must be checked, not only the single CE for
10588             which the CE score was changed in the first place (as every instance
10589             of that CE shares the same CE score, and therefore also can change)!
10590           */
10591           SCAN_PLAYFIELD(xx, yy)
10592           {
10593             if (Tile[xx][yy] == element)
10594               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10595                                  CE_SCORE_GETS_ZERO);
10596           }
10597         }
10598       }
10599
10600       break;
10601     }
10602
10603     case CA_SET_CE_ARTWORK:
10604     {
10605       int artwork_element = action_arg_element;
10606       boolean reset_frame = FALSE;
10607       int xx, yy;
10608
10609       if (action_arg == CA_ARG_ELEMENT_RESET)
10610         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10611                            element);
10612
10613       if (ei->gfx_element != artwork_element)
10614         reset_frame = TRUE;
10615
10616       ei->gfx_element = artwork_element;
10617
10618       SCAN_PLAYFIELD(xx, yy)
10619       {
10620         if (Tile[xx][yy] == element)
10621         {
10622           if (reset_frame)
10623           {
10624             ResetGfxAnimation(xx, yy);
10625             ResetRandomAnimationValue(xx, yy);
10626           }
10627
10628           TEST_DrawLevelField(xx, yy);
10629         }
10630       }
10631
10632       break;
10633     }
10634
10635     // ---------- engine actions  ---------------------------------------------
10636
10637     case CA_SET_ENGINE_SCAN_MODE:
10638     {
10639       InitPlayfieldScanMode(action_arg);
10640
10641       break;
10642     }
10643
10644     default:
10645       break;
10646   }
10647 }
10648
10649 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10650 {
10651   int old_element = Tile[x][y];
10652   int new_element = GetElementFromGroupElement(element);
10653   int previous_move_direction = MovDir[x][y];
10654   int last_ce_value = CustomValue[x][y];
10655   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10656   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10657   boolean add_player_onto_element = (new_element_is_player &&
10658                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10659                                      IS_WALKABLE(old_element));
10660
10661   if (!add_player_onto_element)
10662   {
10663     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10664       RemoveMovingField(x, y);
10665     else
10666       RemoveField(x, y);
10667
10668     Tile[x][y] = new_element;
10669
10670     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10671       MovDir[x][y] = previous_move_direction;
10672
10673     if (element_info[new_element].use_last_ce_value)
10674       CustomValue[x][y] = last_ce_value;
10675
10676     InitField_WithBug1(x, y, FALSE);
10677
10678     new_element = Tile[x][y];   // element may have changed
10679
10680     ResetGfxAnimation(x, y);
10681     ResetRandomAnimationValue(x, y);
10682
10683     TEST_DrawLevelField(x, y);
10684
10685     if (GFX_CRUMBLED(new_element))
10686       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10687   }
10688
10689   // check if element under the player changes from accessible to unaccessible
10690   // (needed for special case of dropping element which then changes)
10691   // (must be checked after creating new element for walkable group elements)
10692   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10693       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10694   {
10695     Bang(x, y);
10696
10697     return;
10698   }
10699
10700   // "ChangeCount" not set yet to allow "entered by player" change one time
10701   if (new_element_is_player)
10702     RelocatePlayer(x, y, new_element);
10703
10704   if (is_change)
10705     ChangeCount[x][y]++;        // count number of changes in the same frame
10706
10707   TestIfBadThingTouchesPlayer(x, y);
10708   TestIfPlayerTouchesCustomElement(x, y);
10709   TestIfElementTouchesCustomElement(x, y);
10710 }
10711
10712 static void CreateField(int x, int y, int element)
10713 {
10714   CreateFieldExt(x, y, element, FALSE);
10715 }
10716
10717 static void CreateElementFromChange(int x, int y, int element)
10718 {
10719   element = GET_VALID_RUNTIME_ELEMENT(element);
10720
10721   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10722   {
10723     int old_element = Tile[x][y];
10724
10725     // prevent changed element from moving in same engine frame
10726     // unless both old and new element can either fall or move
10727     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10728         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10729       Stop[x][y] = TRUE;
10730   }
10731
10732   CreateFieldExt(x, y, element, TRUE);
10733 }
10734
10735 static boolean ChangeElement(int x, int y, int element, int page)
10736 {
10737   struct ElementInfo *ei = &element_info[element];
10738   struct ElementChangeInfo *change = &ei->change_page[page];
10739   int ce_value = CustomValue[x][y];
10740   int ce_score = ei->collect_score;
10741   int target_element;
10742   int old_element = Tile[x][y];
10743
10744   // always use default change event to prevent running into a loop
10745   if (ChangeEvent[x][y] == -1)
10746     ChangeEvent[x][y] = CE_DELAY;
10747
10748   if (ChangeEvent[x][y] == CE_DELAY)
10749   {
10750     // reset actual trigger element, trigger player and action element
10751     change->actual_trigger_element = EL_EMPTY;
10752     change->actual_trigger_player = EL_EMPTY;
10753     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10754     change->actual_trigger_side = CH_SIDE_NONE;
10755     change->actual_trigger_ce_value = 0;
10756     change->actual_trigger_ce_score = 0;
10757   }
10758
10759   // do not change elements more than a specified maximum number of changes
10760   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10761     return FALSE;
10762
10763   ChangeCount[x][y]++;          // count number of changes in the same frame
10764
10765   if (change->explode)
10766   {
10767     Bang(x, y);
10768
10769     return TRUE;
10770   }
10771
10772   if (change->use_target_content)
10773   {
10774     boolean complete_replace = TRUE;
10775     boolean can_replace[3][3];
10776     int xx, yy;
10777
10778     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10779     {
10780       boolean is_empty;
10781       boolean is_walkable;
10782       boolean is_diggable;
10783       boolean is_collectible;
10784       boolean is_removable;
10785       boolean is_destructible;
10786       int ex = x + xx - 1;
10787       int ey = y + yy - 1;
10788       int content_element = change->target_content.e[xx][yy];
10789       int e;
10790
10791       can_replace[xx][yy] = TRUE;
10792
10793       if (ex == x && ey == y)   // do not check changing element itself
10794         continue;
10795
10796       if (content_element == EL_EMPTY_SPACE)
10797       {
10798         can_replace[xx][yy] = FALSE;    // do not replace border with space
10799
10800         continue;
10801       }
10802
10803       if (!IN_LEV_FIELD(ex, ey))
10804       {
10805         can_replace[xx][yy] = FALSE;
10806         complete_replace = FALSE;
10807
10808         continue;
10809       }
10810
10811       e = Tile[ex][ey];
10812
10813       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10814         e = MovingOrBlocked2Element(ex, ey);
10815
10816       is_empty = (IS_FREE(ex, ey) ||
10817                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10818
10819       is_walkable     = (is_empty || IS_WALKABLE(e));
10820       is_diggable     = (is_empty || IS_DIGGABLE(e));
10821       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10822       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10823       is_removable    = (is_diggable || is_collectible);
10824
10825       can_replace[xx][yy] =
10826         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10827           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10828           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10829           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10830           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10831           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10832          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10833
10834       if (!can_replace[xx][yy])
10835         complete_replace = FALSE;
10836     }
10837
10838     if (!change->only_if_complete || complete_replace)
10839     {
10840       boolean something_has_changed = FALSE;
10841
10842       if (change->only_if_complete && change->use_random_replace &&
10843           RND(100) < change->random_percentage)
10844         return FALSE;
10845
10846       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10847       {
10848         int ex = x + xx - 1;
10849         int ey = y + yy - 1;
10850         int content_element;
10851
10852         if (can_replace[xx][yy] && (!change->use_random_replace ||
10853                                     RND(100) < change->random_percentage))
10854         {
10855           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10856             RemoveMovingField(ex, ey);
10857
10858           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10859
10860           content_element = change->target_content.e[xx][yy];
10861           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10862                                               ce_value, ce_score);
10863
10864           CreateElementFromChange(ex, ey, target_element);
10865
10866           something_has_changed = TRUE;
10867
10868           // for symmetry reasons, freeze newly created border elements
10869           if (ex != x || ey != y)
10870             Stop[ex][ey] = TRUE;        // no more moving in this frame
10871         }
10872       }
10873
10874       if (something_has_changed)
10875       {
10876         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10877         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10878       }
10879     }
10880   }
10881   else
10882   {
10883     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10884                                         ce_value, ce_score);
10885
10886     if (element == EL_DIAGONAL_GROWING ||
10887         element == EL_DIAGONAL_SHRINKING)
10888     {
10889       target_element = Store[x][y];
10890
10891       Store[x][y] = EL_EMPTY;
10892     }
10893
10894     // special case: element changes to player (and may be kept if walkable)
10895     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10896       CreateElementFromChange(x, y, EL_EMPTY);
10897
10898     CreateElementFromChange(x, y, target_element);
10899
10900     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10901     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10902   }
10903
10904   // this uses direct change before indirect change
10905   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10906
10907   return TRUE;
10908 }
10909
10910 static void HandleElementChange(int x, int y, int page)
10911 {
10912   int element = MovingOrBlocked2Element(x, y);
10913   struct ElementInfo *ei = &element_info[element];
10914   struct ElementChangeInfo *change = &ei->change_page[page];
10915   boolean handle_action_before_change = FALSE;
10916
10917 #ifdef DEBUG
10918   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10919       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10920   {
10921     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10922           x, y, element, element_info[element].token_name);
10923     Debug("game:playing:HandleElementChange", "This should never happen!");
10924   }
10925 #endif
10926
10927   // this can happen with classic bombs on walkable, changing elements
10928   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10929   {
10930     return;
10931   }
10932
10933   if (ChangeDelay[x][y] == 0)           // initialize element change
10934   {
10935     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10936
10937     if (change->can_change)
10938     {
10939       // !!! not clear why graphic animation should be reset at all here !!!
10940       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10941       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10942
10943       /*
10944         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10945
10946         When using an animation frame delay of 1 (this only happens with
10947         "sp_zonk.moving.left/right" in the classic graphics), the default
10948         (non-moving) animation shows wrong animation frames (while the
10949         moving animation, like "sp_zonk.moving.left/right", is correct,
10950         so this graphical bug never shows up with the classic graphics).
10951         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10952         be drawn instead of the correct frames 0,1,2,3. This is caused by
10953         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10954         an element change: First when the change delay ("ChangeDelay[][]")
10955         counter has reached zero after decrementing, then a second time in
10956         the next frame (after "GfxFrame[][]" was already incremented) when
10957         "ChangeDelay[][]" is reset to the initial delay value again.
10958
10959         This causes frame 0 to be drawn twice, while the last frame won't
10960         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10961
10962         As some animations may already be cleverly designed around this bug
10963         (at least the "Snake Bite" snake tail animation does this), it cannot
10964         simply be fixed here without breaking such existing animations.
10965         Unfortunately, it cannot easily be detected if a graphics set was
10966         designed "before" or "after" the bug was fixed. As a workaround,
10967         a new graphics set option "game.graphics_engine_version" was added
10968         to be able to specify the game's major release version for which the
10969         graphics set was designed, which can then be used to decide if the
10970         bugfix should be used (version 4 and above) or not (version 3 or
10971         below, or if no version was specified at all, as with old sets).
10972
10973         (The wrong/fixed animation frames can be tested with the test level set
10974         "test_gfxframe" and level "000", which contains a specially prepared
10975         custom element at level position (x/y) == (11/9) which uses the zonk
10976         animation mentioned above. Using "game.graphics_engine_version: 4"
10977         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10978         This can also be seen from the debug output for this test element.)
10979       */
10980
10981       // when a custom element is about to change (for example by change delay),
10982       // do not reset graphic animation when the custom element is moving
10983       if (game.graphics_engine_version < 4 &&
10984           !IS_MOVING(x, y))
10985       {
10986         ResetGfxAnimation(x, y);
10987         ResetRandomAnimationValue(x, y);
10988       }
10989
10990       if (change->pre_change_function)
10991         change->pre_change_function(x, y);
10992     }
10993   }
10994
10995   ChangeDelay[x][y]--;
10996
10997   if (ChangeDelay[x][y] != 0)           // continue element change
10998   {
10999     if (change->can_change)
11000     {
11001       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11002
11003       if (IS_ANIMATED(graphic))
11004         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11005
11006       if (change->change_function)
11007         change->change_function(x, y);
11008     }
11009   }
11010   else                                  // finish element change
11011   {
11012     if (ChangePage[x][y] != -1)         // remember page from delayed change
11013     {
11014       page = ChangePage[x][y];
11015       ChangePage[x][y] = -1;
11016
11017       change = &ei->change_page[page];
11018     }
11019
11020     if (IS_MOVING(x, y))                // never change a running system ;-)
11021     {
11022       ChangeDelay[x][y] = 1;            // try change after next move step
11023       ChangePage[x][y] = page;          // remember page to use for change
11024
11025       return;
11026     }
11027
11028     // special case: set new level random seed before changing element
11029     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11030       handle_action_before_change = TRUE;
11031
11032     if (change->has_action && handle_action_before_change)
11033       ExecuteCustomElementAction(x, y, element, page);
11034
11035     if (change->can_change)
11036     {
11037       if (ChangeElement(x, y, element, page))
11038       {
11039         if (change->post_change_function)
11040           change->post_change_function(x, y);
11041       }
11042     }
11043
11044     if (change->has_action && !handle_action_before_change)
11045       ExecuteCustomElementAction(x, y, element, page);
11046   }
11047 }
11048
11049 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11050                                               int trigger_element,
11051                                               int trigger_event,
11052                                               int trigger_player,
11053                                               int trigger_side,
11054                                               int trigger_page)
11055 {
11056   boolean change_done_any = FALSE;
11057   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11058   int i;
11059
11060   if (!(trigger_events[trigger_element][trigger_event]))
11061     return FALSE;
11062
11063   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11064
11065   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11066   {
11067     int element = EL_CUSTOM_START + i;
11068     boolean change_done = FALSE;
11069     int p;
11070
11071     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11072         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11073       continue;
11074
11075     for (p = 0; p < element_info[element].num_change_pages; p++)
11076     {
11077       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11078
11079       if (change->can_change_or_has_action &&
11080           change->has_event[trigger_event] &&
11081           change->trigger_side & trigger_side &&
11082           change->trigger_player & trigger_player &&
11083           change->trigger_page & trigger_page_bits &&
11084           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11085       {
11086         change->actual_trigger_element = trigger_element;
11087         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11088         change->actual_trigger_player_bits = trigger_player;
11089         change->actual_trigger_side = trigger_side;
11090         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11091         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11092
11093         if ((change->can_change && !change_done) || change->has_action)
11094         {
11095           int x, y;
11096
11097           SCAN_PLAYFIELD(x, y)
11098           {
11099             if (Tile[x][y] == element)
11100             {
11101               if (change->can_change && !change_done)
11102               {
11103                 // if element already changed in this frame, not only prevent
11104                 // another element change (checked in ChangeElement()), but
11105                 // also prevent additional element actions for this element
11106
11107                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11108                     !level.use_action_after_change_bug)
11109                   continue;
11110
11111                 ChangeDelay[x][y] = 1;
11112                 ChangeEvent[x][y] = trigger_event;
11113
11114                 HandleElementChange(x, y, p);
11115               }
11116               else if (change->has_action)
11117               {
11118                 // if element already changed in this frame, not only prevent
11119                 // another element change (checked in ChangeElement()), but
11120                 // also prevent additional element actions for this element
11121
11122                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11123                     !level.use_action_after_change_bug)
11124                   continue;
11125
11126                 ExecuteCustomElementAction(x, y, element, p);
11127                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11128               }
11129             }
11130           }
11131
11132           if (change->can_change)
11133           {
11134             change_done = TRUE;
11135             change_done_any = TRUE;
11136           }
11137         }
11138       }
11139     }
11140   }
11141
11142   RECURSION_LOOP_DETECTION_END();
11143
11144   return change_done_any;
11145 }
11146
11147 static boolean CheckElementChangeExt(int x, int y,
11148                                      int element,
11149                                      int trigger_element,
11150                                      int trigger_event,
11151                                      int trigger_player,
11152                                      int trigger_side)
11153 {
11154   boolean change_done = FALSE;
11155   int p;
11156
11157   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11158       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11159     return FALSE;
11160
11161   if (Tile[x][y] == EL_BLOCKED)
11162   {
11163     Blocked2Moving(x, y, &x, &y);
11164     element = Tile[x][y];
11165   }
11166
11167   // check if element has already changed or is about to change after moving
11168   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11169        Tile[x][y] != element) ||
11170
11171       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11172        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11173         ChangePage[x][y] != -1)))
11174     return FALSE;
11175
11176   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11177
11178   for (p = 0; p < element_info[element].num_change_pages; p++)
11179   {
11180     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11181
11182     /* check trigger element for all events where the element that is checked
11183        for changing interacts with a directly adjacent element -- this is
11184        different to element changes that affect other elements to change on the
11185        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11186     boolean check_trigger_element =
11187       (trigger_event == CE_NEXT_TO_X ||
11188        trigger_event == CE_TOUCHING_X ||
11189        trigger_event == CE_HITTING_X ||
11190        trigger_event == CE_HIT_BY_X ||
11191        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11192
11193     if (change->can_change_or_has_action &&
11194         change->has_event[trigger_event] &&
11195         change->trigger_side & trigger_side &&
11196         change->trigger_player & trigger_player &&
11197         (!check_trigger_element ||
11198          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11199     {
11200       change->actual_trigger_element = trigger_element;
11201       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11202       change->actual_trigger_player_bits = trigger_player;
11203       change->actual_trigger_side = trigger_side;
11204       change->actual_trigger_ce_value = CustomValue[x][y];
11205       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11206
11207       // special case: trigger element not at (x,y) position for some events
11208       if (check_trigger_element)
11209       {
11210         static struct
11211         {
11212           int dx, dy;
11213         } move_xy[] =
11214           {
11215             {  0,  0 },
11216             { -1,  0 },
11217             { +1,  0 },
11218             {  0,  0 },
11219             {  0, -1 },
11220             {  0,  0 }, { 0, 0 }, { 0, 0 },
11221             {  0, +1 }
11222           };
11223
11224         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11225         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11226
11227         change->actual_trigger_ce_value = CustomValue[xx][yy];
11228         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11229       }
11230
11231       if (change->can_change && !change_done)
11232       {
11233         ChangeDelay[x][y] = 1;
11234         ChangeEvent[x][y] = trigger_event;
11235
11236         HandleElementChange(x, y, p);
11237
11238         change_done = TRUE;
11239       }
11240       else if (change->has_action)
11241       {
11242         ExecuteCustomElementAction(x, y, element, p);
11243         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11244       }
11245     }
11246   }
11247
11248   RECURSION_LOOP_DETECTION_END();
11249
11250   return change_done;
11251 }
11252
11253 static void PlayPlayerSound(struct PlayerInfo *player)
11254 {
11255   int jx = player->jx, jy = player->jy;
11256   int sound_element = player->artwork_element;
11257   int last_action = player->last_action_waiting;
11258   int action = player->action_waiting;
11259
11260   if (player->is_waiting)
11261   {
11262     if (action != last_action)
11263       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11264     else
11265       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11266   }
11267   else
11268   {
11269     if (action != last_action)
11270       StopSound(element_info[sound_element].sound[last_action]);
11271
11272     if (last_action == ACTION_SLEEPING)
11273       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11274   }
11275 }
11276
11277 static void PlayAllPlayersSound(void)
11278 {
11279   int i;
11280
11281   for (i = 0; i < MAX_PLAYERS; i++)
11282     if (stored_player[i].active)
11283       PlayPlayerSound(&stored_player[i]);
11284 }
11285
11286 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11287 {
11288   boolean last_waiting = player->is_waiting;
11289   int move_dir = player->MovDir;
11290
11291   player->dir_waiting = move_dir;
11292   player->last_action_waiting = player->action_waiting;
11293
11294   if (is_waiting)
11295   {
11296     if (!last_waiting)          // not waiting -> waiting
11297     {
11298       player->is_waiting = TRUE;
11299
11300       player->frame_counter_bored =
11301         FrameCounter +
11302         game.player_boring_delay_fixed +
11303         GetSimpleRandom(game.player_boring_delay_random);
11304       player->frame_counter_sleeping =
11305         FrameCounter +
11306         game.player_sleeping_delay_fixed +
11307         GetSimpleRandom(game.player_sleeping_delay_random);
11308
11309       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11310     }
11311
11312     if (game.player_sleeping_delay_fixed +
11313         game.player_sleeping_delay_random > 0 &&
11314         player->anim_delay_counter == 0 &&
11315         player->post_delay_counter == 0 &&
11316         FrameCounter >= player->frame_counter_sleeping)
11317       player->is_sleeping = TRUE;
11318     else if (game.player_boring_delay_fixed +
11319              game.player_boring_delay_random > 0 &&
11320              FrameCounter >= player->frame_counter_bored)
11321       player->is_bored = TRUE;
11322
11323     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11324                               player->is_bored ? ACTION_BORING :
11325                               ACTION_WAITING);
11326
11327     if (player->is_sleeping && player->use_murphy)
11328     {
11329       // special case for sleeping Murphy when leaning against non-free tile
11330
11331       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11332           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11333            !IS_MOVING(player->jx - 1, player->jy)))
11334         move_dir = MV_LEFT;
11335       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11336                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11337                 !IS_MOVING(player->jx + 1, player->jy)))
11338         move_dir = MV_RIGHT;
11339       else
11340         player->is_sleeping = FALSE;
11341
11342       player->dir_waiting = move_dir;
11343     }
11344
11345     if (player->is_sleeping)
11346     {
11347       if (player->num_special_action_sleeping > 0)
11348       {
11349         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11350         {
11351           int last_special_action = player->special_action_sleeping;
11352           int num_special_action = player->num_special_action_sleeping;
11353           int special_action =
11354             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11355              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11356              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11357              last_special_action + 1 : ACTION_SLEEPING);
11358           int special_graphic =
11359             el_act_dir2img(player->artwork_element, special_action, move_dir);
11360
11361           player->anim_delay_counter =
11362             graphic_info[special_graphic].anim_delay_fixed +
11363             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11364           player->post_delay_counter =
11365             graphic_info[special_graphic].post_delay_fixed +
11366             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11367
11368           player->special_action_sleeping = special_action;
11369         }
11370
11371         if (player->anim_delay_counter > 0)
11372         {
11373           player->action_waiting = player->special_action_sleeping;
11374           player->anim_delay_counter--;
11375         }
11376         else if (player->post_delay_counter > 0)
11377         {
11378           player->post_delay_counter--;
11379         }
11380       }
11381     }
11382     else if (player->is_bored)
11383     {
11384       if (player->num_special_action_bored > 0)
11385       {
11386         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11387         {
11388           int special_action =
11389             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11390           int special_graphic =
11391             el_act_dir2img(player->artwork_element, special_action, move_dir);
11392
11393           player->anim_delay_counter =
11394             graphic_info[special_graphic].anim_delay_fixed +
11395             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11396           player->post_delay_counter =
11397             graphic_info[special_graphic].post_delay_fixed +
11398             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11399
11400           player->special_action_bored = special_action;
11401         }
11402
11403         if (player->anim_delay_counter > 0)
11404         {
11405           player->action_waiting = player->special_action_bored;
11406           player->anim_delay_counter--;
11407         }
11408         else if (player->post_delay_counter > 0)
11409         {
11410           player->post_delay_counter--;
11411         }
11412       }
11413     }
11414   }
11415   else if (last_waiting)        // waiting -> not waiting
11416   {
11417     player->is_waiting = FALSE;
11418     player->is_bored = FALSE;
11419     player->is_sleeping = FALSE;
11420
11421     player->frame_counter_bored = -1;
11422     player->frame_counter_sleeping = -1;
11423
11424     player->anim_delay_counter = 0;
11425     player->post_delay_counter = 0;
11426
11427     player->dir_waiting = player->MovDir;
11428     player->action_waiting = ACTION_DEFAULT;
11429
11430     player->special_action_bored = ACTION_DEFAULT;
11431     player->special_action_sleeping = ACTION_DEFAULT;
11432   }
11433 }
11434
11435 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11436 {
11437   if ((!player->is_moving  && player->was_moving) ||
11438       (player->MovPos == 0 && player->was_moving) ||
11439       (player->is_snapping && !player->was_snapping) ||
11440       (player->is_dropping && !player->was_dropping))
11441   {
11442     if (!CheckSaveEngineSnapshotToList())
11443       return;
11444
11445     player->was_moving = FALSE;
11446     player->was_snapping = TRUE;
11447     player->was_dropping = TRUE;
11448   }
11449   else
11450   {
11451     if (player->is_moving)
11452       player->was_moving = TRUE;
11453
11454     if (!player->is_snapping)
11455       player->was_snapping = FALSE;
11456
11457     if (!player->is_dropping)
11458       player->was_dropping = FALSE;
11459   }
11460
11461   static struct MouseActionInfo mouse_action_last = { 0 };
11462   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11463   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11464
11465   if (new_released)
11466     CheckSaveEngineSnapshotToList();
11467
11468   mouse_action_last = mouse_action;
11469 }
11470
11471 static void CheckSingleStepMode(struct PlayerInfo *player)
11472 {
11473   if (tape.single_step && tape.recording && !tape.pausing)
11474   {
11475     // as it is called "single step mode", just return to pause mode when the
11476     // player stopped moving after one tile (or never starts moving at all)
11477     // (reverse logic needed here in case single step mode used in team mode)
11478     if (player->is_moving ||
11479         player->is_pushing ||
11480         player->is_dropping_pressed ||
11481         player->effective_mouse_action.button)
11482       game.enter_single_step_mode = FALSE;
11483   }
11484
11485   CheckSaveEngineSnapshot(player);
11486 }
11487
11488 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11489 {
11490   int left      = player_action & JOY_LEFT;
11491   int right     = player_action & JOY_RIGHT;
11492   int up        = player_action & JOY_UP;
11493   int down      = player_action & JOY_DOWN;
11494   int button1   = player_action & JOY_BUTTON_1;
11495   int button2   = player_action & JOY_BUTTON_2;
11496   int dx        = (left ? -1 : right ? 1 : 0);
11497   int dy        = (up   ? -1 : down  ? 1 : 0);
11498
11499   if (!player->active || tape.pausing)
11500     return 0;
11501
11502   if (player_action)
11503   {
11504     if (button1)
11505       SnapField(player, dx, dy);
11506     else
11507     {
11508       if (button2)
11509         DropElement(player);
11510
11511       MovePlayer(player, dx, dy);
11512     }
11513
11514     CheckSingleStepMode(player);
11515
11516     SetPlayerWaiting(player, FALSE);
11517
11518     return player_action;
11519   }
11520   else
11521   {
11522     // no actions for this player (no input at player's configured device)
11523
11524     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11525     SnapField(player, 0, 0);
11526     CheckGravityMovementWhenNotMoving(player);
11527
11528     if (player->MovPos == 0)
11529       SetPlayerWaiting(player, TRUE);
11530
11531     if (player->MovPos == 0)    // needed for tape.playing
11532       player->is_moving = FALSE;
11533
11534     player->is_dropping = FALSE;
11535     player->is_dropping_pressed = FALSE;
11536     player->drop_pressed_delay = 0;
11537
11538     CheckSingleStepMode(player);
11539
11540     return 0;
11541   }
11542 }
11543
11544 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11545                                          byte *tape_action)
11546 {
11547   if (!tape.use_mouse_actions)
11548     return;
11549
11550   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11551   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11552   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11553 }
11554
11555 static void SetTapeActionFromMouseAction(byte *tape_action,
11556                                          struct MouseActionInfo *mouse_action)
11557 {
11558   if (!tape.use_mouse_actions)
11559     return;
11560
11561   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11562   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11563   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11564 }
11565
11566 static void CheckLevelSolved(void)
11567 {
11568   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11569   {
11570     if (game_em.level_solved &&
11571         !game_em.game_over)                             // game won
11572     {
11573       LevelSolved();
11574
11575       game_em.game_over = TRUE;
11576
11577       game.all_players_gone = TRUE;
11578     }
11579
11580     if (game_em.game_over)                              // game lost
11581       game.all_players_gone = TRUE;
11582   }
11583   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11584   {
11585     if (game_sp.level_solved &&
11586         !game_sp.game_over)                             // game won
11587     {
11588       LevelSolved();
11589
11590       game_sp.game_over = TRUE;
11591
11592       game.all_players_gone = TRUE;
11593     }
11594
11595     if (game_sp.game_over)                              // game lost
11596       game.all_players_gone = TRUE;
11597   }
11598   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11599   {
11600     if (game_mm.level_solved &&
11601         !game_mm.game_over)                             // game won
11602     {
11603       LevelSolved();
11604
11605       game_mm.game_over = TRUE;
11606
11607       game.all_players_gone = TRUE;
11608     }
11609
11610     if (game_mm.game_over)                              // game lost
11611       game.all_players_gone = TRUE;
11612   }
11613 }
11614
11615 static void CheckLevelTime_StepCounter(void)
11616 {
11617   int i;
11618
11619   TimePlayed++;
11620
11621   if (TimeLeft > 0)
11622   {
11623     TimeLeft--;
11624
11625     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11626       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11627
11628     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11629
11630     DisplayGameControlValues();
11631
11632     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11633       for (i = 0; i < MAX_PLAYERS; i++)
11634         KillPlayer(&stored_player[i]);
11635   }
11636   else if (game.no_time_limit && !game.all_players_gone)
11637   {
11638     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11639
11640     DisplayGameControlValues();
11641   }
11642 }
11643
11644 static void CheckLevelTime(void)
11645 {
11646   int i;
11647
11648   if (TimeFrames >= FRAMES_PER_SECOND)
11649   {
11650     TimeFrames = 0;
11651     TapeTime++;
11652
11653     for (i = 0; i < MAX_PLAYERS; i++)
11654     {
11655       struct PlayerInfo *player = &stored_player[i];
11656
11657       if (SHIELD_ON(player))
11658       {
11659         player->shield_normal_time_left--;
11660
11661         if (player->shield_deadly_time_left > 0)
11662           player->shield_deadly_time_left--;
11663       }
11664     }
11665
11666     if (!game.LevelSolved && !level.use_step_counter)
11667     {
11668       TimePlayed++;
11669
11670       if (TimeLeft > 0)
11671       {
11672         TimeLeft--;
11673
11674         if (TimeLeft <= 10 && setup.time_limit)
11675           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11676
11677         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11678            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11679
11680         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11681
11682         if (!TimeLeft && setup.time_limit)
11683         {
11684           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11685             game_em.lev->killed_out_of_time = TRUE;
11686           else
11687             for (i = 0; i < MAX_PLAYERS; i++)
11688               KillPlayer(&stored_player[i]);
11689         }
11690       }
11691       else if (game.no_time_limit && !game.all_players_gone)
11692       {
11693         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11694       }
11695
11696       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11697     }
11698
11699     if (tape.recording || tape.playing)
11700       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11701   }
11702
11703   if (tape.recording || tape.playing)
11704     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11705
11706   UpdateAndDisplayGameControlValues();
11707 }
11708
11709 void AdvanceFrameAndPlayerCounters(int player_nr)
11710 {
11711   int i;
11712
11713   // advance frame counters (global frame counter and time frame counter)
11714   FrameCounter++;
11715   TimeFrames++;
11716
11717   // advance player counters (counters for move delay, move animation etc.)
11718   for (i = 0; i < MAX_PLAYERS; i++)
11719   {
11720     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11721     int move_delay_value = stored_player[i].move_delay_value;
11722     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11723
11724     if (!advance_player_counters)       // not all players may be affected
11725       continue;
11726
11727     if (move_frames == 0)       // less than one move per game frame
11728     {
11729       int stepsize = TILEX / move_delay_value;
11730       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11731       int count = (stored_player[i].is_moving ?
11732                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11733
11734       if (count % delay == 0)
11735         move_frames = 1;
11736     }
11737
11738     stored_player[i].Frame += move_frames;
11739
11740     if (stored_player[i].MovPos != 0)
11741       stored_player[i].StepFrame += move_frames;
11742
11743     if (stored_player[i].move_delay > 0)
11744       stored_player[i].move_delay--;
11745
11746     // due to bugs in previous versions, counter must count up, not down
11747     if (stored_player[i].push_delay != -1)
11748       stored_player[i].push_delay++;
11749
11750     if (stored_player[i].drop_delay > 0)
11751       stored_player[i].drop_delay--;
11752
11753     if (stored_player[i].is_dropping_pressed)
11754       stored_player[i].drop_pressed_delay++;
11755   }
11756 }
11757
11758 void StartGameActions(boolean init_network_game, boolean record_tape,
11759                       int random_seed)
11760 {
11761   unsigned int new_random_seed = InitRND(random_seed);
11762
11763   if (record_tape)
11764     TapeStartRecording(new_random_seed);
11765
11766   if (setup.auto_pause_on_start && !tape.pausing)
11767     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11768
11769   if (init_network_game)
11770   {
11771     SendToServer_LevelFile();
11772     SendToServer_StartPlaying();
11773
11774     return;
11775   }
11776
11777   InitGame();
11778 }
11779
11780 static void GameActionsExt(void)
11781 {
11782 #if 0
11783   static unsigned int game_frame_delay = 0;
11784 #endif
11785   unsigned int game_frame_delay_value;
11786   byte *recorded_player_action;
11787   byte summarized_player_action = 0;
11788   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11789   int i;
11790
11791   // detect endless loops, caused by custom element programming
11792   if (recursion_loop_detected && recursion_loop_depth == 0)
11793   {
11794     char *message = getStringCat3("Internal Error! Element ",
11795                                   EL_NAME(recursion_loop_element),
11796                                   " caused endless loop! Quit the game?");
11797
11798     Warn("element '%s' caused endless loop in game engine",
11799          EL_NAME(recursion_loop_element));
11800
11801     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11802
11803     recursion_loop_detected = FALSE;    // if game should be continued
11804
11805     free(message);
11806
11807     return;
11808   }
11809
11810   if (game.restart_level)
11811     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11812
11813   CheckLevelSolved();
11814
11815   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11816     GameWon();
11817
11818   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11819     TapeStop();
11820
11821   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11822     return;
11823
11824   game_frame_delay_value =
11825     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11826
11827   if (tape.playing && tape.warp_forward && !tape.pausing)
11828     game_frame_delay_value = 0;
11829
11830   SetVideoFrameDelay(game_frame_delay_value);
11831
11832   // (de)activate virtual buttons depending on current game status
11833   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11834   {
11835     if (game.all_players_gone)  // if no players there to be controlled anymore
11836       SetOverlayActive(FALSE);
11837     else if (!tape.playing)     // if game continues after tape stopped playing
11838       SetOverlayActive(TRUE);
11839   }
11840
11841 #if 0
11842 #if 0
11843   // ---------- main game synchronization point ----------
11844
11845   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11846
11847   Debug("game:playing:skip", "skip == %d", skip);
11848
11849 #else
11850   // ---------- main game synchronization point ----------
11851
11852   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11853 #endif
11854 #endif
11855
11856   if (network_playing && !network_player_action_received)
11857   {
11858     // try to get network player actions in time
11859
11860     // last chance to get network player actions without main loop delay
11861     HandleNetworking();
11862
11863     // game was quit by network peer
11864     if (game_status != GAME_MODE_PLAYING)
11865       return;
11866
11867     // check if network player actions still missing and game still running
11868     if (!network_player_action_received && !checkGameEnded())
11869       return;           // failed to get network player actions in time
11870
11871     // do not yet reset "network_player_action_received" (for tape.pausing)
11872   }
11873
11874   if (tape.pausing)
11875     return;
11876
11877   // at this point we know that we really continue executing the game
11878
11879   network_player_action_received = FALSE;
11880
11881   // when playing tape, read previously recorded player input from tape data
11882   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11883
11884   local_player->effective_mouse_action = local_player->mouse_action;
11885
11886   if (recorded_player_action != NULL)
11887     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11888                                  recorded_player_action);
11889
11890   // TapePlayAction() may return NULL when toggling to "pause before death"
11891   if (tape.pausing)
11892     return;
11893
11894   if (tape.set_centered_player)
11895   {
11896     game.centered_player_nr_next = tape.centered_player_nr_next;
11897     game.set_centered_player = TRUE;
11898   }
11899
11900   for (i = 0; i < MAX_PLAYERS; i++)
11901   {
11902     summarized_player_action |= stored_player[i].action;
11903
11904     if (!network_playing && (game.team_mode || tape.playing))
11905       stored_player[i].effective_action = stored_player[i].action;
11906   }
11907
11908   if (network_playing && !checkGameEnded())
11909     SendToServer_MovePlayer(summarized_player_action);
11910
11911   // summarize all actions at local players mapped input device position
11912   // (this allows using different input devices in single player mode)
11913   if (!network.enabled && !game.team_mode)
11914     stored_player[map_player_action[local_player->index_nr]].effective_action =
11915       summarized_player_action;
11916
11917   // summarize all actions at centered player in local team mode
11918   if (tape.recording &&
11919       setup.team_mode && !network.enabled &&
11920       setup.input_on_focus &&
11921       game.centered_player_nr != -1)
11922   {
11923     for (i = 0; i < MAX_PLAYERS; i++)
11924       stored_player[map_player_action[i]].effective_action =
11925         (i == game.centered_player_nr ? summarized_player_action : 0);
11926   }
11927
11928   if (recorded_player_action != NULL)
11929     for (i = 0; i < MAX_PLAYERS; i++)
11930       stored_player[i].effective_action = recorded_player_action[i];
11931
11932   for (i = 0; i < MAX_PLAYERS; i++)
11933   {
11934     tape_action[i] = stored_player[i].effective_action;
11935
11936     /* (this may happen in the RND game engine if a player was not present on
11937        the playfield on level start, but appeared later from a custom element */
11938     if (setup.team_mode &&
11939         tape.recording &&
11940         tape_action[i] &&
11941         !tape.player_participates[i])
11942       tape.player_participates[i] = TRUE;
11943   }
11944
11945   SetTapeActionFromMouseAction(tape_action,
11946                                &local_player->effective_mouse_action);
11947
11948   // only record actions from input devices, but not programmed actions
11949   if (tape.recording)
11950     TapeRecordAction(tape_action);
11951
11952   // remember if game was played (especially after tape stopped playing)
11953   if (!tape.playing && summarized_player_action)
11954     game.GamePlayed = TRUE;
11955
11956 #if USE_NEW_PLAYER_ASSIGNMENTS
11957   // !!! also map player actions in single player mode !!!
11958   // if (game.team_mode)
11959   if (1)
11960   {
11961     byte mapped_action[MAX_PLAYERS];
11962
11963 #if DEBUG_PLAYER_ACTIONS
11964     for (i = 0; i < MAX_PLAYERS; i++)
11965       DebugContinued("", "%d, ", stored_player[i].effective_action);
11966 #endif
11967
11968     for (i = 0; i < MAX_PLAYERS; i++)
11969       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11970
11971     for (i = 0; i < MAX_PLAYERS; i++)
11972       stored_player[i].effective_action = mapped_action[i];
11973
11974 #if DEBUG_PLAYER_ACTIONS
11975     DebugContinued("", "=> ");
11976     for (i = 0; i < MAX_PLAYERS; i++)
11977       DebugContinued("", "%d, ", stored_player[i].effective_action);
11978     DebugContinued("game:playing:player", "\n");
11979 #endif
11980   }
11981 #if DEBUG_PLAYER_ACTIONS
11982   else
11983   {
11984     for (i = 0; i < MAX_PLAYERS; i++)
11985       DebugContinued("", "%d, ", stored_player[i].effective_action);
11986     DebugContinued("game:playing:player", "\n");
11987   }
11988 #endif
11989 #endif
11990
11991   for (i = 0; i < MAX_PLAYERS; i++)
11992   {
11993     // allow engine snapshot in case of changed movement attempt
11994     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11995         (stored_player[i].effective_action & KEY_MOTION))
11996       game.snapshot.changed_action = TRUE;
11997
11998     // allow engine snapshot in case of snapping/dropping attempt
11999     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12000         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12001       game.snapshot.changed_action = TRUE;
12002
12003     game.snapshot.last_action[i] = stored_player[i].effective_action;
12004   }
12005
12006   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12007   {
12008     GameActions_EM_Main();
12009   }
12010   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12011   {
12012     GameActions_SP_Main();
12013   }
12014   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12015   {
12016     GameActions_MM_Main();
12017   }
12018   else
12019   {
12020     GameActions_RND_Main();
12021   }
12022
12023   BlitScreenToBitmap(backbuffer);
12024
12025   CheckLevelSolved();
12026   CheckLevelTime();
12027
12028   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12029
12030   if (global.show_frames_per_second)
12031   {
12032     static unsigned int fps_counter = 0;
12033     static int fps_frames = 0;
12034     unsigned int fps_delay_ms = Counter() - fps_counter;
12035
12036     fps_frames++;
12037
12038     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12039     {
12040       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12041
12042       fps_frames = 0;
12043       fps_counter = Counter();
12044
12045       // always draw FPS to screen after FPS value was updated
12046       redraw_mask |= REDRAW_FPS;
12047     }
12048
12049     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12050     if (GetDrawDeactivationMask() == REDRAW_NONE)
12051       redraw_mask |= REDRAW_FPS;
12052   }
12053 }
12054
12055 static void GameActions_CheckSaveEngineSnapshot(void)
12056 {
12057   if (!game.snapshot.save_snapshot)
12058     return;
12059
12060   // clear flag for saving snapshot _before_ saving snapshot
12061   game.snapshot.save_snapshot = FALSE;
12062
12063   SaveEngineSnapshotToList();
12064 }
12065
12066 void GameActions(void)
12067 {
12068   GameActionsExt();
12069
12070   GameActions_CheckSaveEngineSnapshot();
12071 }
12072
12073 void GameActions_EM_Main(void)
12074 {
12075   byte effective_action[MAX_PLAYERS];
12076   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12077   int i;
12078
12079   for (i = 0; i < MAX_PLAYERS; i++)
12080     effective_action[i] = stored_player[i].effective_action;
12081
12082   GameActions_EM(effective_action, warp_mode);
12083 }
12084
12085 void GameActions_SP_Main(void)
12086 {
12087   byte effective_action[MAX_PLAYERS];
12088   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12089   int i;
12090
12091   for (i = 0; i < MAX_PLAYERS; i++)
12092     effective_action[i] = stored_player[i].effective_action;
12093
12094   GameActions_SP(effective_action, warp_mode);
12095
12096   for (i = 0; i < MAX_PLAYERS; i++)
12097   {
12098     if (stored_player[i].force_dropping)
12099       stored_player[i].action |= KEY_BUTTON_DROP;
12100
12101     stored_player[i].force_dropping = FALSE;
12102   }
12103 }
12104
12105 void GameActions_MM_Main(void)
12106 {
12107   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12108
12109   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12110 }
12111
12112 void GameActions_RND_Main(void)
12113 {
12114   GameActions_RND();
12115 }
12116
12117 void GameActions_RND(void)
12118 {
12119   static struct MouseActionInfo mouse_action_last = { 0 };
12120   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12121   int magic_wall_x = 0, magic_wall_y = 0;
12122   int i, x, y, element, graphic, last_gfx_frame;
12123
12124   InitPlayfieldScanModeVars();
12125
12126   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12127   {
12128     SCAN_PLAYFIELD(x, y)
12129     {
12130       ChangeCount[x][y] = 0;
12131       ChangeEvent[x][y] = -1;
12132     }
12133   }
12134
12135   if (game.set_centered_player)
12136   {
12137     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12138
12139     // switching to "all players" only possible if all players fit to screen
12140     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12141     {
12142       game.centered_player_nr_next = game.centered_player_nr;
12143       game.set_centered_player = FALSE;
12144     }
12145
12146     // do not switch focus to non-existing (or non-active) player
12147     if (game.centered_player_nr_next >= 0 &&
12148         !stored_player[game.centered_player_nr_next].active)
12149     {
12150       game.centered_player_nr_next = game.centered_player_nr;
12151       game.set_centered_player = FALSE;
12152     }
12153   }
12154
12155   if (game.set_centered_player &&
12156       ScreenMovPos == 0)        // screen currently aligned at tile position
12157   {
12158     int sx, sy;
12159
12160     if (game.centered_player_nr_next == -1)
12161     {
12162       setScreenCenteredToAllPlayers(&sx, &sy);
12163     }
12164     else
12165     {
12166       sx = stored_player[game.centered_player_nr_next].jx;
12167       sy = stored_player[game.centered_player_nr_next].jy;
12168     }
12169
12170     game.centered_player_nr = game.centered_player_nr_next;
12171     game.set_centered_player = FALSE;
12172
12173     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12174     DrawGameDoorValues();
12175   }
12176
12177   // check single step mode (set flag and clear again if any player is active)
12178   game.enter_single_step_mode =
12179     (tape.single_step && tape.recording && !tape.pausing);
12180
12181   for (i = 0; i < MAX_PLAYERS; i++)
12182   {
12183     int actual_player_action = stored_player[i].effective_action;
12184
12185 #if 1
12186     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12187        - rnd_equinox_tetrachloride 048
12188        - rnd_equinox_tetrachloride_ii 096
12189        - rnd_emanuel_schmieg 002
12190        - doctor_sloan_ww 001, 020
12191     */
12192     if (stored_player[i].MovPos == 0)
12193       CheckGravityMovement(&stored_player[i]);
12194 #endif
12195
12196     // overwrite programmed action with tape action
12197     if (stored_player[i].programmed_action)
12198       actual_player_action = stored_player[i].programmed_action;
12199
12200     PlayerActions(&stored_player[i], actual_player_action);
12201
12202     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12203   }
12204
12205   // single step pause mode may already have been toggled by "ScrollPlayer()"
12206   if (game.enter_single_step_mode && !tape.pausing)
12207     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12208
12209   ScrollScreen(NULL, SCROLL_GO_ON);
12210
12211   /* for backwards compatibility, the following code emulates a fixed bug that
12212      occured when pushing elements (causing elements that just made their last
12213      pushing step to already (if possible) make their first falling step in the
12214      same game frame, which is bad); this code is also needed to use the famous
12215      "spring push bug" which is used in older levels and might be wanted to be
12216      used also in newer levels, but in this case the buggy pushing code is only
12217      affecting the "spring" element and no other elements */
12218
12219   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12220   {
12221     for (i = 0; i < MAX_PLAYERS; i++)
12222     {
12223       struct PlayerInfo *player = &stored_player[i];
12224       int x = player->jx;
12225       int y = player->jy;
12226
12227       if (player->active && player->is_pushing && player->is_moving &&
12228           IS_MOVING(x, y) &&
12229           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12230            Tile[x][y] == EL_SPRING))
12231       {
12232         ContinueMoving(x, y);
12233
12234         // continue moving after pushing (this is actually a bug)
12235         if (!IS_MOVING(x, y))
12236           Stop[x][y] = FALSE;
12237       }
12238     }
12239   }
12240
12241   SCAN_PLAYFIELD(x, y)
12242   {
12243     Last[x][y] = Tile[x][y];
12244
12245     ChangeCount[x][y] = 0;
12246     ChangeEvent[x][y] = -1;
12247
12248     // this must be handled before main playfield loop
12249     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12250     {
12251       MovDelay[x][y]--;
12252       if (MovDelay[x][y] <= 0)
12253         RemoveField(x, y);
12254     }
12255
12256     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12257     {
12258       MovDelay[x][y]--;
12259       if (MovDelay[x][y] <= 0)
12260       {
12261         int element = Store[x][y];
12262         int move_direction = MovDir[x][y];
12263         int player_index_bit = Store2[x][y];
12264
12265         Store[x][y] = 0;
12266         Store2[x][y] = 0;
12267
12268         RemoveField(x, y);
12269         TEST_DrawLevelField(x, y);
12270
12271         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12272
12273         if (IS_ENVELOPE(element))
12274           local_player->show_envelope = element;
12275       }
12276     }
12277
12278 #if DEBUG
12279     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12280     {
12281       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12282             x, y);
12283       Debug("game:playing:GameActions_RND", "This should never happen!");
12284
12285       ChangePage[x][y] = -1;
12286     }
12287 #endif
12288
12289     Stop[x][y] = FALSE;
12290     if (WasJustMoving[x][y] > 0)
12291       WasJustMoving[x][y]--;
12292     if (WasJustFalling[x][y] > 0)
12293       WasJustFalling[x][y]--;
12294     if (CheckCollision[x][y] > 0)
12295       CheckCollision[x][y]--;
12296     if (CheckImpact[x][y] > 0)
12297       CheckImpact[x][y]--;
12298
12299     GfxFrame[x][y]++;
12300
12301     /* reset finished pushing action (not done in ContinueMoving() to allow
12302        continuous pushing animation for elements with zero push delay) */
12303     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12304     {
12305       ResetGfxAnimation(x, y);
12306       TEST_DrawLevelField(x, y);
12307     }
12308
12309 #if DEBUG
12310     if (IS_BLOCKED(x, y))
12311     {
12312       int oldx, oldy;
12313
12314       Blocked2Moving(x, y, &oldx, &oldy);
12315       if (!IS_MOVING(oldx, oldy))
12316       {
12317         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12318         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12319         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12320         Debug("game:playing:GameActions_RND", "This should never happen!");
12321       }
12322     }
12323 #endif
12324   }
12325
12326   if (mouse_action.button)
12327   {
12328     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12329     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12330
12331     x = mouse_action.lx;
12332     y = mouse_action.ly;
12333     element = Tile[x][y];
12334
12335     if (new_button)
12336     {
12337       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12338       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12339                                          ch_button);
12340     }
12341
12342     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12343     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12344                                        ch_button);
12345
12346     if (level.use_step_counter)
12347     {
12348       boolean counted_click = FALSE;
12349
12350       // element clicked that can change when clicked/pressed
12351       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12352           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12353            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12354         counted_click = TRUE;
12355
12356       // element clicked that can trigger change when clicked/pressed
12357       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12358           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12359         counted_click = TRUE;
12360
12361       if (new_button && counted_click)
12362         CheckLevelTime_StepCounter();
12363     }
12364   }
12365
12366   SCAN_PLAYFIELD(x, y)
12367   {
12368     element = Tile[x][y];
12369     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12370     last_gfx_frame = GfxFrame[x][y];
12371
12372     if (element == EL_EMPTY)
12373       graphic = el2img(GfxElementEmpty[x][y]);
12374
12375     ResetGfxFrame(x, y);
12376
12377     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12378       DrawLevelGraphicAnimation(x, y, graphic);
12379
12380     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12381         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12382       ResetRandomAnimationValue(x, y);
12383
12384     SetRandomAnimationValue(x, y);
12385
12386     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12387
12388     if (IS_INACTIVE(element))
12389     {
12390       if (IS_ANIMATED(graphic))
12391         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12392
12393       continue;
12394     }
12395
12396     // this may take place after moving, so 'element' may have changed
12397     if (IS_CHANGING(x, y) &&
12398         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12399     {
12400       int page = element_info[element].event_page_nr[CE_DELAY];
12401
12402       HandleElementChange(x, y, page);
12403
12404       element = Tile[x][y];
12405       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12406     }
12407
12408     CheckNextToConditions(x, y);
12409
12410     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12411     {
12412       StartMoving(x, y);
12413
12414       element = Tile[x][y];
12415       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12416
12417       if (IS_ANIMATED(graphic) &&
12418           !IS_MOVING(x, y) &&
12419           !Stop[x][y])
12420         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12421
12422       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12423         TEST_DrawTwinkleOnField(x, y);
12424     }
12425     else if (element == EL_ACID)
12426     {
12427       if (!Stop[x][y])
12428         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12429     }
12430     else if ((element == EL_EXIT_OPEN ||
12431               element == EL_EM_EXIT_OPEN ||
12432               element == EL_SP_EXIT_OPEN ||
12433               element == EL_STEEL_EXIT_OPEN ||
12434               element == EL_EM_STEEL_EXIT_OPEN ||
12435               element == EL_SP_TERMINAL ||
12436               element == EL_SP_TERMINAL_ACTIVE ||
12437               element == EL_EXTRA_TIME ||
12438               element == EL_SHIELD_NORMAL ||
12439               element == EL_SHIELD_DEADLY) &&
12440              IS_ANIMATED(graphic))
12441       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12442     else if (IS_MOVING(x, y))
12443       ContinueMoving(x, y);
12444     else if (IS_ACTIVE_BOMB(element))
12445       CheckDynamite(x, y);
12446     else if (element == EL_AMOEBA_GROWING)
12447       AmoebaGrowing(x, y);
12448     else if (element == EL_AMOEBA_SHRINKING)
12449       AmoebaShrinking(x, y);
12450
12451 #if !USE_NEW_AMOEBA_CODE
12452     else if (IS_AMOEBALIVE(element))
12453       AmoebaReproduce(x, y);
12454 #endif
12455
12456     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12457       Life(x, y);
12458     else if (element == EL_EXIT_CLOSED)
12459       CheckExit(x, y);
12460     else if (element == EL_EM_EXIT_CLOSED)
12461       CheckExitEM(x, y);
12462     else if (element == EL_STEEL_EXIT_CLOSED)
12463       CheckExitSteel(x, y);
12464     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12465       CheckExitSteelEM(x, y);
12466     else if (element == EL_SP_EXIT_CLOSED)
12467       CheckExitSP(x, y);
12468     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12469              element == EL_EXPANDABLE_STEELWALL_GROWING)
12470       MauerWaechst(x, y);
12471     else if (element == EL_EXPANDABLE_WALL ||
12472              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12473              element == EL_EXPANDABLE_WALL_VERTICAL ||
12474              element == EL_EXPANDABLE_WALL_ANY ||
12475              element == EL_BD_EXPANDABLE_WALL)
12476       MauerAbleger(x, y);
12477     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12478              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12479              element == EL_EXPANDABLE_STEELWALL_ANY)
12480       MauerAblegerStahl(x, y);
12481     else if (element == EL_FLAMES)
12482       CheckForDragon(x, y);
12483     else if (element == EL_EXPLOSION)
12484       ; // drawing of correct explosion animation is handled separately
12485     else if (element == EL_ELEMENT_SNAPPING ||
12486              element == EL_DIAGONAL_SHRINKING ||
12487              element == EL_DIAGONAL_GROWING)
12488     {
12489       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12490
12491       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12492     }
12493     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12494       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12495
12496     if (IS_BELT_ACTIVE(element))
12497       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12498
12499     if (game.magic_wall_active)
12500     {
12501       int jx = local_player->jx, jy = local_player->jy;
12502
12503       // play the element sound at the position nearest to the player
12504       if ((element == EL_MAGIC_WALL_FULL ||
12505            element == EL_MAGIC_WALL_ACTIVE ||
12506            element == EL_MAGIC_WALL_EMPTYING ||
12507            element == EL_BD_MAGIC_WALL_FULL ||
12508            element == EL_BD_MAGIC_WALL_ACTIVE ||
12509            element == EL_BD_MAGIC_WALL_EMPTYING ||
12510            element == EL_DC_MAGIC_WALL_FULL ||
12511            element == EL_DC_MAGIC_WALL_ACTIVE ||
12512            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12513           ABS(x - jx) + ABS(y - jy) <
12514           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12515       {
12516         magic_wall_x = x;
12517         magic_wall_y = y;
12518       }
12519     }
12520   }
12521
12522 #if USE_NEW_AMOEBA_CODE
12523   // new experimental amoeba growth stuff
12524   if (!(FrameCounter % 8))
12525   {
12526     static unsigned int random = 1684108901;
12527
12528     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12529     {
12530       x = RND(lev_fieldx);
12531       y = RND(lev_fieldy);
12532       element = Tile[x][y];
12533
12534       if (!IS_PLAYER(x,y) &&
12535           (element == EL_EMPTY ||
12536            CAN_GROW_INTO(element) ||
12537            element == EL_QUICKSAND_EMPTY ||
12538            element == EL_QUICKSAND_FAST_EMPTY ||
12539            element == EL_ACID_SPLASH_LEFT ||
12540            element == EL_ACID_SPLASH_RIGHT))
12541       {
12542         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12543             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12544             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12545             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12546           Tile[x][y] = EL_AMOEBA_DROP;
12547       }
12548
12549       random = random * 129 + 1;
12550     }
12551   }
12552 #endif
12553
12554   game.explosions_delayed = FALSE;
12555
12556   SCAN_PLAYFIELD(x, y)
12557   {
12558     element = Tile[x][y];
12559
12560     if (ExplodeField[x][y])
12561       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12562     else if (element == EL_EXPLOSION)
12563       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12564
12565     ExplodeField[x][y] = EX_TYPE_NONE;
12566   }
12567
12568   game.explosions_delayed = TRUE;
12569
12570   if (game.magic_wall_active)
12571   {
12572     if (!(game.magic_wall_time_left % 4))
12573     {
12574       int element = Tile[magic_wall_x][magic_wall_y];
12575
12576       if (element == EL_BD_MAGIC_WALL_FULL ||
12577           element == EL_BD_MAGIC_WALL_ACTIVE ||
12578           element == EL_BD_MAGIC_WALL_EMPTYING)
12579         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12580       else if (element == EL_DC_MAGIC_WALL_FULL ||
12581                element == EL_DC_MAGIC_WALL_ACTIVE ||
12582                element == EL_DC_MAGIC_WALL_EMPTYING)
12583         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12584       else
12585         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12586     }
12587
12588     if (game.magic_wall_time_left > 0)
12589     {
12590       game.magic_wall_time_left--;
12591
12592       if (!game.magic_wall_time_left)
12593       {
12594         SCAN_PLAYFIELD(x, y)
12595         {
12596           element = Tile[x][y];
12597
12598           if (element == EL_MAGIC_WALL_ACTIVE ||
12599               element == EL_MAGIC_WALL_FULL)
12600           {
12601             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12602             TEST_DrawLevelField(x, y);
12603           }
12604           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12605                    element == EL_BD_MAGIC_WALL_FULL)
12606           {
12607             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12608             TEST_DrawLevelField(x, y);
12609           }
12610           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12611                    element == EL_DC_MAGIC_WALL_FULL)
12612           {
12613             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12614             TEST_DrawLevelField(x, y);
12615           }
12616         }
12617
12618         game.magic_wall_active = FALSE;
12619       }
12620     }
12621   }
12622
12623   if (game.light_time_left > 0)
12624   {
12625     game.light_time_left--;
12626
12627     if (game.light_time_left == 0)
12628       RedrawAllLightSwitchesAndInvisibleElements();
12629   }
12630
12631   if (game.timegate_time_left > 0)
12632   {
12633     game.timegate_time_left--;
12634
12635     if (game.timegate_time_left == 0)
12636       CloseAllOpenTimegates();
12637   }
12638
12639   if (game.lenses_time_left > 0)
12640   {
12641     game.lenses_time_left--;
12642
12643     if (game.lenses_time_left == 0)
12644       RedrawAllInvisibleElementsForLenses();
12645   }
12646
12647   if (game.magnify_time_left > 0)
12648   {
12649     game.magnify_time_left--;
12650
12651     if (game.magnify_time_left == 0)
12652       RedrawAllInvisibleElementsForMagnifier();
12653   }
12654
12655   for (i = 0; i < MAX_PLAYERS; i++)
12656   {
12657     struct PlayerInfo *player = &stored_player[i];
12658
12659     if (SHIELD_ON(player))
12660     {
12661       if (player->shield_deadly_time_left)
12662         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12663       else if (player->shield_normal_time_left)
12664         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12665     }
12666   }
12667
12668 #if USE_DELAYED_GFX_REDRAW
12669   SCAN_PLAYFIELD(x, y)
12670   {
12671     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12672     {
12673       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12674          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12675
12676       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12677         DrawLevelField(x, y);
12678
12679       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12680         DrawLevelFieldCrumbled(x, y);
12681
12682       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12683         DrawLevelFieldCrumbledNeighbours(x, y);
12684
12685       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12686         DrawTwinkleOnField(x, y);
12687     }
12688
12689     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12690   }
12691 #endif
12692
12693   DrawAllPlayers();
12694   PlayAllPlayersSound();
12695
12696   for (i = 0; i < MAX_PLAYERS; i++)
12697   {
12698     struct PlayerInfo *player = &stored_player[i];
12699
12700     if (player->show_envelope != 0 && (!player->active ||
12701                                        player->MovPos == 0))
12702     {
12703       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12704
12705       player->show_envelope = 0;
12706     }
12707   }
12708
12709   // use random number generator in every frame to make it less predictable
12710   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12711     RND(1);
12712
12713   mouse_action_last = mouse_action;
12714 }
12715
12716 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12717 {
12718   int min_x = x, min_y = y, max_x = x, max_y = y;
12719   int scr_fieldx = getScreenFieldSizeX();
12720   int scr_fieldy = getScreenFieldSizeY();
12721   int i;
12722
12723   for (i = 0; i < MAX_PLAYERS; i++)
12724   {
12725     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12726
12727     if (!stored_player[i].active || &stored_player[i] == player)
12728       continue;
12729
12730     min_x = MIN(min_x, jx);
12731     min_y = MIN(min_y, jy);
12732     max_x = MAX(max_x, jx);
12733     max_y = MAX(max_y, jy);
12734   }
12735
12736   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12737 }
12738
12739 static boolean AllPlayersInVisibleScreen(void)
12740 {
12741   int i;
12742
12743   for (i = 0; i < MAX_PLAYERS; i++)
12744   {
12745     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12746
12747     if (!stored_player[i].active)
12748       continue;
12749
12750     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12751       return FALSE;
12752   }
12753
12754   return TRUE;
12755 }
12756
12757 void ScrollLevel(int dx, int dy)
12758 {
12759   int scroll_offset = 2 * TILEX_VAR;
12760   int x, y;
12761
12762   BlitBitmap(drawto_field, drawto_field,
12763              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12764              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12765              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12766              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12767              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12768              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12769
12770   if (dx != 0)
12771   {
12772     x = (dx == 1 ? BX1 : BX2);
12773     for (y = BY1; y <= BY2; y++)
12774       DrawScreenField(x, y);
12775   }
12776
12777   if (dy != 0)
12778   {
12779     y = (dy == 1 ? BY1 : BY2);
12780     for (x = BX1; x <= BX2; x++)
12781       DrawScreenField(x, y);
12782   }
12783
12784   redraw_mask |= REDRAW_FIELD;
12785 }
12786
12787 static boolean canFallDown(struct PlayerInfo *player)
12788 {
12789   int jx = player->jx, jy = player->jy;
12790
12791   return (IN_LEV_FIELD(jx, jy + 1) &&
12792           (IS_FREE(jx, jy + 1) ||
12793            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12794           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12795           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12796 }
12797
12798 static boolean canPassField(int x, int y, int move_dir)
12799 {
12800   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12801   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12802   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12803   int nextx = x + dx;
12804   int nexty = y + dy;
12805   int element = Tile[x][y];
12806
12807   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12808           !CAN_MOVE(element) &&
12809           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12810           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12811           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12812 }
12813
12814 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12815 {
12816   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12817   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12818   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12819   int newx = x + dx;
12820   int newy = y + dy;
12821
12822   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12823           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12824           (IS_DIGGABLE(Tile[newx][newy]) ||
12825            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12826            canPassField(newx, newy, move_dir)));
12827 }
12828
12829 static void CheckGravityMovement(struct PlayerInfo *player)
12830 {
12831   if (player->gravity && !player->programmed_action)
12832   {
12833     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12834     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12835     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12836     int jx = player->jx, jy = player->jy;
12837     boolean player_is_moving_to_valid_field =
12838       (!player_is_snapping &&
12839        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12840         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12841     boolean player_can_fall_down = canFallDown(player);
12842
12843     if (player_can_fall_down &&
12844         !player_is_moving_to_valid_field)
12845       player->programmed_action = MV_DOWN;
12846   }
12847 }
12848
12849 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12850 {
12851   return CheckGravityMovement(player);
12852
12853   if (player->gravity && !player->programmed_action)
12854   {
12855     int jx = player->jx, jy = player->jy;
12856     boolean field_under_player_is_free =
12857       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12858     boolean player_is_standing_on_valid_field =
12859       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12860        (IS_WALKABLE(Tile[jx][jy]) &&
12861         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12862
12863     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12864       player->programmed_action = MV_DOWN;
12865   }
12866 }
12867
12868 /*
12869   MovePlayerOneStep()
12870   -----------------------------------------------------------------------------
12871   dx, dy:               direction (non-diagonal) to try to move the player to
12872   real_dx, real_dy:     direction as read from input device (can be diagonal)
12873 */
12874
12875 boolean MovePlayerOneStep(struct PlayerInfo *player,
12876                           int dx, int dy, int real_dx, int real_dy)
12877 {
12878   int jx = player->jx, jy = player->jy;
12879   int new_jx = jx + dx, new_jy = jy + dy;
12880   int can_move;
12881   boolean player_can_move = !player->cannot_move;
12882
12883   if (!player->active || (!dx && !dy))
12884     return MP_NO_ACTION;
12885
12886   player->MovDir = (dx < 0 ? MV_LEFT :
12887                     dx > 0 ? MV_RIGHT :
12888                     dy < 0 ? MV_UP :
12889                     dy > 0 ? MV_DOWN :  MV_NONE);
12890
12891   if (!IN_LEV_FIELD(new_jx, new_jy))
12892     return MP_NO_ACTION;
12893
12894   if (!player_can_move)
12895   {
12896     if (player->MovPos == 0)
12897     {
12898       player->is_moving = FALSE;
12899       player->is_digging = FALSE;
12900       player->is_collecting = FALSE;
12901       player->is_snapping = FALSE;
12902       player->is_pushing = FALSE;
12903     }
12904   }
12905
12906   if (!network.enabled && game.centered_player_nr == -1 &&
12907       !AllPlayersInSight(player, new_jx, new_jy))
12908     return MP_NO_ACTION;
12909
12910   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12911   if (can_move != MP_MOVING)
12912     return can_move;
12913
12914   // check if DigField() has caused relocation of the player
12915   if (player->jx != jx || player->jy != jy)
12916     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12917
12918   StorePlayer[jx][jy] = 0;
12919   player->last_jx = jx;
12920   player->last_jy = jy;
12921   player->jx = new_jx;
12922   player->jy = new_jy;
12923   StorePlayer[new_jx][new_jy] = player->element_nr;
12924
12925   if (player->move_delay_value_next != -1)
12926   {
12927     player->move_delay_value = player->move_delay_value_next;
12928     player->move_delay_value_next = -1;
12929   }
12930
12931   player->MovPos =
12932     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12933
12934   player->step_counter++;
12935
12936   PlayerVisit[jx][jy] = FrameCounter;
12937
12938   player->is_moving = TRUE;
12939
12940 #if 1
12941   // should better be called in MovePlayer(), but this breaks some tapes
12942   ScrollPlayer(player, SCROLL_INIT);
12943 #endif
12944
12945   return MP_MOVING;
12946 }
12947
12948 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12949 {
12950   int jx = player->jx, jy = player->jy;
12951   int old_jx = jx, old_jy = jy;
12952   int moved = MP_NO_ACTION;
12953
12954   if (!player->active)
12955     return FALSE;
12956
12957   if (!dx && !dy)
12958   {
12959     if (player->MovPos == 0)
12960     {
12961       player->is_moving = FALSE;
12962       player->is_digging = FALSE;
12963       player->is_collecting = FALSE;
12964       player->is_snapping = FALSE;
12965       player->is_pushing = FALSE;
12966     }
12967
12968     return FALSE;
12969   }
12970
12971   if (player->move_delay > 0)
12972     return FALSE;
12973
12974   player->move_delay = -1;              // set to "uninitialized" value
12975
12976   // store if player is automatically moved to next field
12977   player->is_auto_moving = (player->programmed_action != MV_NONE);
12978
12979   // remove the last programmed player action
12980   player->programmed_action = 0;
12981
12982   if (player->MovPos)
12983   {
12984     // should only happen if pre-1.2 tape recordings are played
12985     // this is only for backward compatibility
12986
12987     int original_move_delay_value = player->move_delay_value;
12988
12989 #if DEBUG
12990     Debug("game:playing:MovePlayer",
12991           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12992           tape.counter);
12993 #endif
12994
12995     // scroll remaining steps with finest movement resolution
12996     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12997
12998     while (player->MovPos)
12999     {
13000       ScrollPlayer(player, SCROLL_GO_ON);
13001       ScrollScreen(NULL, SCROLL_GO_ON);
13002
13003       AdvanceFrameAndPlayerCounters(player->index_nr);
13004
13005       DrawAllPlayers();
13006       BackToFront_WithFrameDelay(0);
13007     }
13008
13009     player->move_delay_value = original_move_delay_value;
13010   }
13011
13012   player->is_active = FALSE;
13013
13014   if (player->last_move_dir & MV_HORIZONTAL)
13015   {
13016     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13017       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13018   }
13019   else
13020   {
13021     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13022       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13023   }
13024
13025   if (!moved && !player->is_active)
13026   {
13027     player->is_moving = FALSE;
13028     player->is_digging = FALSE;
13029     player->is_collecting = FALSE;
13030     player->is_snapping = FALSE;
13031     player->is_pushing = FALSE;
13032   }
13033
13034   jx = player->jx;
13035   jy = player->jy;
13036
13037   if (moved & MP_MOVING && !ScreenMovPos &&
13038       (player->index_nr == game.centered_player_nr ||
13039        game.centered_player_nr == -1))
13040   {
13041     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13042
13043     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13044     {
13045       // actual player has left the screen -- scroll in that direction
13046       if (jx != old_jx)         // player has moved horizontally
13047         scroll_x += (jx - old_jx);
13048       else                      // player has moved vertically
13049         scroll_y += (jy - old_jy);
13050     }
13051     else
13052     {
13053       int offset_raw = game.scroll_delay_value;
13054
13055       if (jx != old_jx)         // player has moved horizontally
13056       {
13057         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13058         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13059         int new_scroll_x = jx - MIDPOSX + offset_x;
13060
13061         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13062             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13063           scroll_x = new_scroll_x;
13064
13065         // don't scroll over playfield boundaries
13066         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13067
13068         // don't scroll more than one field at a time
13069         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13070
13071         // don't scroll against the player's moving direction
13072         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13073             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13074           scroll_x = old_scroll_x;
13075       }
13076       else                      // player has moved vertically
13077       {
13078         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13079         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13080         int new_scroll_y = jy - MIDPOSY + offset_y;
13081
13082         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13083             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13084           scroll_y = new_scroll_y;
13085
13086         // don't scroll over playfield boundaries
13087         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13088
13089         // don't scroll more than one field at a time
13090         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13091
13092         // don't scroll against the player's moving direction
13093         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13094             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13095           scroll_y = old_scroll_y;
13096       }
13097     }
13098
13099     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13100     {
13101       if (!network.enabled && game.centered_player_nr == -1 &&
13102           !AllPlayersInVisibleScreen())
13103       {
13104         scroll_x = old_scroll_x;
13105         scroll_y = old_scroll_y;
13106       }
13107       else
13108       {
13109         ScrollScreen(player, SCROLL_INIT);
13110         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13111       }
13112     }
13113   }
13114
13115   player->StepFrame = 0;
13116
13117   if (moved & MP_MOVING)
13118   {
13119     if (old_jx != jx && old_jy == jy)
13120       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13121     else if (old_jx == jx && old_jy != jy)
13122       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13123
13124     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13125
13126     player->last_move_dir = player->MovDir;
13127     player->is_moving = TRUE;
13128     player->is_snapping = FALSE;
13129     player->is_switching = FALSE;
13130     player->is_dropping = FALSE;
13131     player->is_dropping_pressed = FALSE;
13132     player->drop_pressed_delay = 0;
13133
13134 #if 0
13135     // should better be called here than above, but this breaks some tapes
13136     ScrollPlayer(player, SCROLL_INIT);
13137 #endif
13138   }
13139   else
13140   {
13141     CheckGravityMovementWhenNotMoving(player);
13142
13143     player->is_moving = FALSE;
13144
13145     /* at this point, the player is allowed to move, but cannot move right now
13146        (e.g. because of something blocking the way) -- ensure that the player
13147        is also allowed to move in the next frame (in old versions before 3.1.1,
13148        the player was forced to wait again for eight frames before next try) */
13149
13150     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13151       player->move_delay = 0;   // allow direct movement in the next frame
13152   }
13153
13154   if (player->move_delay == -1)         // not yet initialized by DigField()
13155     player->move_delay = player->move_delay_value;
13156
13157   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13158   {
13159     TestIfPlayerTouchesBadThing(jx, jy);
13160     TestIfPlayerTouchesCustomElement(jx, jy);
13161   }
13162
13163   if (!player->active)
13164     RemovePlayer(player);
13165
13166   return moved;
13167 }
13168
13169 void ScrollPlayer(struct PlayerInfo *player, int mode)
13170 {
13171   int jx = player->jx, jy = player->jy;
13172   int last_jx = player->last_jx, last_jy = player->last_jy;
13173   int move_stepsize = TILEX / player->move_delay_value;
13174
13175   if (!player->active)
13176     return;
13177
13178   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13179     return;
13180
13181   if (mode == SCROLL_INIT)
13182   {
13183     player->actual_frame_counter = FrameCounter;
13184     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13185
13186     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13187         Tile[last_jx][last_jy] == EL_EMPTY)
13188     {
13189       int last_field_block_delay = 0;   // start with no blocking at all
13190       int block_delay_adjustment = player->block_delay_adjustment;
13191
13192       // if player blocks last field, add delay for exactly one move
13193       if (player->block_last_field)
13194       {
13195         last_field_block_delay += player->move_delay_value;
13196
13197         // when blocking enabled, prevent moving up despite gravity
13198         if (player->gravity && player->MovDir == MV_UP)
13199           block_delay_adjustment = -1;
13200       }
13201
13202       // add block delay adjustment (also possible when not blocking)
13203       last_field_block_delay += block_delay_adjustment;
13204
13205       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13206       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13207     }
13208
13209     if (player->MovPos != 0)    // player has not yet reached destination
13210       return;
13211   }
13212   else if (!FrameReached(&player->actual_frame_counter, 1))
13213     return;
13214
13215   if (player->MovPos != 0)
13216   {
13217     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13218     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13219
13220     // before DrawPlayer() to draw correct player graphic for this case
13221     if (player->MovPos == 0)
13222       CheckGravityMovement(player);
13223   }
13224
13225   if (player->MovPos == 0)      // player reached destination field
13226   {
13227     if (player->move_delay_reset_counter > 0)
13228     {
13229       player->move_delay_reset_counter--;
13230
13231       if (player->move_delay_reset_counter == 0)
13232       {
13233         // continue with normal speed after quickly moving through gate
13234         HALVE_PLAYER_SPEED(player);
13235
13236         // be able to make the next move without delay
13237         player->move_delay = 0;
13238       }
13239     }
13240
13241     player->last_jx = jx;
13242     player->last_jy = jy;
13243
13244     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13245         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13246         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13247         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13248         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13249         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13250         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13251         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13252     {
13253       ExitPlayer(player);
13254
13255       if (game.players_still_needed == 0 &&
13256           (game.friends_still_needed == 0 ||
13257            IS_SP_ELEMENT(Tile[jx][jy])))
13258         LevelSolved();
13259     }
13260
13261     // this breaks one level: "machine", level 000
13262     {
13263       int move_direction = player->MovDir;
13264       int enter_side = MV_DIR_OPPOSITE(move_direction);
13265       int leave_side = move_direction;
13266       int old_jx = last_jx;
13267       int old_jy = last_jy;
13268       int old_element = Tile[old_jx][old_jy];
13269       int new_element = Tile[jx][jy];
13270
13271       if (IS_CUSTOM_ELEMENT(old_element))
13272         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13273                                    CE_LEFT_BY_PLAYER,
13274                                    player->index_bit, leave_side);
13275
13276       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13277                                           CE_PLAYER_LEAVES_X,
13278                                           player->index_bit, leave_side);
13279
13280       if (IS_CUSTOM_ELEMENT(new_element))
13281         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13282                                    player->index_bit, enter_side);
13283
13284       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13285                                           CE_PLAYER_ENTERS_X,
13286                                           player->index_bit, enter_side);
13287
13288       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13289                                         CE_MOVE_OF_X, move_direction);
13290     }
13291
13292     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13293     {
13294       TestIfPlayerTouchesBadThing(jx, jy);
13295       TestIfPlayerTouchesCustomElement(jx, jy);
13296
13297       /* needed because pushed element has not yet reached its destination,
13298          so it would trigger a change event at its previous field location */
13299       if (!player->is_pushing)
13300         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13301
13302       if (level.finish_dig_collect &&
13303           (player->is_digging || player->is_collecting))
13304       {
13305         int last_element = player->last_removed_element;
13306         int move_direction = player->MovDir;
13307         int enter_side = MV_DIR_OPPOSITE(move_direction);
13308         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13309                             CE_PLAYER_COLLECTS_X);
13310
13311         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13312                                             player->index_bit, enter_side);
13313
13314         player->last_removed_element = EL_UNDEFINED;
13315       }
13316
13317       if (!player->active)
13318         RemovePlayer(player);
13319     }
13320
13321     if (level.use_step_counter)
13322       CheckLevelTime_StepCounter();
13323
13324     if (tape.single_step && tape.recording && !tape.pausing &&
13325         !player->programmed_action)
13326       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13327
13328     if (!player->programmed_action)
13329       CheckSaveEngineSnapshot(player);
13330   }
13331 }
13332
13333 void ScrollScreen(struct PlayerInfo *player, int mode)
13334 {
13335   static unsigned int screen_frame_counter = 0;
13336
13337   if (mode == SCROLL_INIT)
13338   {
13339     // set scrolling step size according to actual player's moving speed
13340     ScrollStepSize = TILEX / player->move_delay_value;
13341
13342     screen_frame_counter = FrameCounter;
13343     ScreenMovDir = player->MovDir;
13344     ScreenMovPos = player->MovPos;
13345     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13346     return;
13347   }
13348   else if (!FrameReached(&screen_frame_counter, 1))
13349     return;
13350
13351   if (ScreenMovPos)
13352   {
13353     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13354     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13355     redraw_mask |= REDRAW_FIELD;
13356   }
13357   else
13358     ScreenMovDir = MV_NONE;
13359 }
13360
13361 void CheckNextToConditions(int x, int y)
13362 {
13363   int element = Tile[x][y];
13364
13365   if (IS_PLAYER(x, y))
13366     TestIfPlayerNextToCustomElement(x, y);
13367
13368   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13369       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13370     TestIfElementNextToCustomElement(x, y);
13371 }
13372
13373 void TestIfPlayerNextToCustomElement(int x, int y)
13374 {
13375   static int xy[4][2] =
13376   {
13377     { 0, -1 },
13378     { -1, 0 },
13379     { +1, 0 },
13380     { 0, +1 }
13381   };
13382   static int trigger_sides[4][2] =
13383   {
13384     // center side       border side
13385     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13386     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13387     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13388     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13389   };
13390   int i;
13391
13392   if (!IS_PLAYER(x, y))
13393     return;
13394
13395   struct PlayerInfo *player = PLAYERINFO(x, y);
13396
13397   if (player->is_moving)
13398     return;
13399
13400   for (i = 0; i < NUM_DIRECTIONS; i++)
13401   {
13402     int xx = x + xy[i][0];
13403     int yy = y + xy[i][1];
13404     int border_side = trigger_sides[i][1];
13405     int border_element;
13406
13407     if (!IN_LEV_FIELD(xx, yy))
13408       continue;
13409
13410     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13411       continue;         // center and border element not connected
13412
13413     border_element = Tile[xx][yy];
13414
13415     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13416                                player->index_bit, border_side);
13417     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13418                                         CE_PLAYER_NEXT_TO_X,
13419                                         player->index_bit, border_side);
13420
13421     /* use player element that is initially defined in the level playfield,
13422        not the player element that corresponds to the runtime player number
13423        (example: a level that contains EL_PLAYER_3 as the only player would
13424        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13425
13426     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13427                              CE_NEXT_TO_X, border_side);
13428   }
13429 }
13430
13431 void TestIfPlayerTouchesCustomElement(int x, int y)
13432 {
13433   static int xy[4][2] =
13434   {
13435     { 0, -1 },
13436     { -1, 0 },
13437     { +1, 0 },
13438     { 0, +1 }
13439   };
13440   static int trigger_sides[4][2] =
13441   {
13442     // center side       border side
13443     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13444     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13445     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13446     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13447   };
13448   static int touch_dir[4] =
13449   {
13450     MV_LEFT | MV_RIGHT,
13451     MV_UP   | MV_DOWN,
13452     MV_UP   | MV_DOWN,
13453     MV_LEFT | MV_RIGHT
13454   };
13455   int center_element = Tile[x][y];      // should always be non-moving!
13456   int i;
13457
13458   for (i = 0; i < NUM_DIRECTIONS; i++)
13459   {
13460     int xx = x + xy[i][0];
13461     int yy = y + xy[i][1];
13462     int center_side = trigger_sides[i][0];
13463     int border_side = trigger_sides[i][1];
13464     int border_element;
13465
13466     if (!IN_LEV_FIELD(xx, yy))
13467       continue;
13468
13469     if (IS_PLAYER(x, y))                // player found at center element
13470     {
13471       struct PlayerInfo *player = PLAYERINFO(x, y);
13472
13473       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13474         border_element = Tile[xx][yy];          // may be moving!
13475       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13476         border_element = Tile[xx][yy];
13477       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13478         border_element = MovingOrBlocked2Element(xx, yy);
13479       else
13480         continue;               // center and border element do not touch
13481
13482       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13483                                  player->index_bit, border_side);
13484       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13485                                           CE_PLAYER_TOUCHES_X,
13486                                           player->index_bit, border_side);
13487
13488       {
13489         /* use player element that is initially defined in the level playfield,
13490            not the player element that corresponds to the runtime player number
13491            (example: a level that contains EL_PLAYER_3 as the only player would
13492            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13493         int player_element = PLAYERINFO(x, y)->initial_element;
13494
13495         CheckElementChangeBySide(xx, yy, border_element, player_element,
13496                                  CE_TOUCHING_X, border_side);
13497       }
13498     }
13499     else if (IS_PLAYER(xx, yy))         // player found at border element
13500     {
13501       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13502
13503       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13504       {
13505         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13506           continue;             // center and border element do not touch
13507       }
13508
13509       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13510                                  player->index_bit, center_side);
13511       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13512                                           CE_PLAYER_TOUCHES_X,
13513                                           player->index_bit, center_side);
13514
13515       {
13516         /* use player element that is initially defined in the level playfield,
13517            not the player element that corresponds to the runtime player number
13518            (example: a level that contains EL_PLAYER_3 as the only player would
13519            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13520         int player_element = PLAYERINFO(xx, yy)->initial_element;
13521
13522         CheckElementChangeBySide(x, y, center_element, player_element,
13523                                  CE_TOUCHING_X, center_side);
13524       }
13525
13526       break;
13527     }
13528   }
13529 }
13530
13531 void TestIfElementNextToCustomElement(int x, int y)
13532 {
13533   static int xy[4][2] =
13534   {
13535     { 0, -1 },
13536     { -1, 0 },
13537     { +1, 0 },
13538     { 0, +1 }
13539   };
13540   static int trigger_sides[4][2] =
13541   {
13542     // center side      border side
13543     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13544     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13545     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13546     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13547   };
13548   int center_element = Tile[x][y];      // should always be non-moving!
13549   int i;
13550
13551   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13552     return;
13553
13554   for (i = 0; i < NUM_DIRECTIONS; i++)
13555   {
13556     int xx = x + xy[i][0];
13557     int yy = y + xy[i][1];
13558     int border_side = trigger_sides[i][1];
13559     int border_element;
13560
13561     if (!IN_LEV_FIELD(xx, yy))
13562       continue;
13563
13564     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13565       continue;                 // center and border element not connected
13566
13567     border_element = Tile[xx][yy];
13568
13569     // check for change of center element (but change it only once)
13570     if (CheckElementChangeBySide(x, y, center_element, border_element,
13571                                  CE_NEXT_TO_X, border_side))
13572       break;
13573   }
13574 }
13575
13576 void TestIfElementTouchesCustomElement(int x, int y)
13577 {
13578   static int xy[4][2] =
13579   {
13580     { 0, -1 },
13581     { -1, 0 },
13582     { +1, 0 },
13583     { 0, +1 }
13584   };
13585   static int trigger_sides[4][2] =
13586   {
13587     // center side      border side
13588     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13589     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13590     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13591     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13592   };
13593   static int touch_dir[4] =
13594   {
13595     MV_LEFT | MV_RIGHT,
13596     MV_UP   | MV_DOWN,
13597     MV_UP   | MV_DOWN,
13598     MV_LEFT | MV_RIGHT
13599   };
13600   boolean change_center_element = FALSE;
13601   int center_element = Tile[x][y];      // should always be non-moving!
13602   int border_element_old[NUM_DIRECTIONS];
13603   int i;
13604
13605   for (i = 0; i < NUM_DIRECTIONS; i++)
13606   {
13607     int xx = x + xy[i][0];
13608     int yy = y + xy[i][1];
13609     int border_element;
13610
13611     border_element_old[i] = -1;
13612
13613     if (!IN_LEV_FIELD(xx, yy))
13614       continue;
13615
13616     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13617       border_element = Tile[xx][yy];    // may be moving!
13618     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13619       border_element = Tile[xx][yy];
13620     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13621       border_element = MovingOrBlocked2Element(xx, yy);
13622     else
13623       continue;                 // center and border element do not touch
13624
13625     border_element_old[i] = border_element;
13626   }
13627
13628   for (i = 0; i < NUM_DIRECTIONS; i++)
13629   {
13630     int xx = x + xy[i][0];
13631     int yy = y + xy[i][1];
13632     int center_side = trigger_sides[i][0];
13633     int border_element = border_element_old[i];
13634
13635     if (border_element == -1)
13636       continue;
13637
13638     // check for change of border element
13639     CheckElementChangeBySide(xx, yy, border_element, center_element,
13640                              CE_TOUCHING_X, center_side);
13641
13642     // (center element cannot be player, so we dont have to check this here)
13643   }
13644
13645   for (i = 0; i < NUM_DIRECTIONS; i++)
13646   {
13647     int xx = x + xy[i][0];
13648     int yy = y + xy[i][1];
13649     int border_side = trigger_sides[i][1];
13650     int border_element = border_element_old[i];
13651
13652     if (border_element == -1)
13653       continue;
13654
13655     // check for change of center element (but change it only once)
13656     if (!change_center_element)
13657       change_center_element =
13658         CheckElementChangeBySide(x, y, center_element, border_element,
13659                                  CE_TOUCHING_X, border_side);
13660
13661     if (IS_PLAYER(xx, yy))
13662     {
13663       /* use player element that is initially defined in the level playfield,
13664          not the player element that corresponds to the runtime player number
13665          (example: a level that contains EL_PLAYER_3 as the only player would
13666          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13667       int player_element = PLAYERINFO(xx, yy)->initial_element;
13668
13669       CheckElementChangeBySide(x, y, center_element, player_element,
13670                                CE_TOUCHING_X, border_side);
13671     }
13672   }
13673 }
13674
13675 void TestIfElementHitsCustomElement(int x, int y, int direction)
13676 {
13677   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13678   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13679   int hitx = x + dx, hity = y + dy;
13680   int hitting_element = Tile[x][y];
13681   int touched_element;
13682
13683   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13684     return;
13685
13686   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13687                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13688
13689   if (IN_LEV_FIELD(hitx, hity))
13690   {
13691     int opposite_direction = MV_DIR_OPPOSITE(direction);
13692     int hitting_side = direction;
13693     int touched_side = opposite_direction;
13694     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13695                           MovDir[hitx][hity] != direction ||
13696                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13697
13698     object_hit = TRUE;
13699
13700     if (object_hit)
13701     {
13702       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13703                                CE_HITTING_X, touched_side);
13704
13705       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13706                                CE_HIT_BY_X, hitting_side);
13707
13708       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13709                                CE_HIT_BY_SOMETHING, opposite_direction);
13710
13711       if (IS_PLAYER(hitx, hity))
13712       {
13713         /* use player element that is initially defined in the level playfield,
13714            not the player element that corresponds to the runtime player number
13715            (example: a level that contains EL_PLAYER_3 as the only player would
13716            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13717         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13718
13719         CheckElementChangeBySide(x, y, hitting_element, player_element,
13720                                  CE_HITTING_X, touched_side);
13721       }
13722     }
13723   }
13724
13725   // "hitting something" is also true when hitting the playfield border
13726   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13727                            CE_HITTING_SOMETHING, direction);
13728 }
13729
13730 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13731 {
13732   int i, kill_x = -1, kill_y = -1;
13733
13734   int bad_element = -1;
13735   static int test_xy[4][2] =
13736   {
13737     { 0, -1 },
13738     { -1, 0 },
13739     { +1, 0 },
13740     { 0, +1 }
13741   };
13742   static int test_dir[4] =
13743   {
13744     MV_UP,
13745     MV_LEFT,
13746     MV_RIGHT,
13747     MV_DOWN
13748   };
13749
13750   for (i = 0; i < NUM_DIRECTIONS; i++)
13751   {
13752     int test_x, test_y, test_move_dir, test_element;
13753
13754     test_x = good_x + test_xy[i][0];
13755     test_y = good_y + test_xy[i][1];
13756
13757     if (!IN_LEV_FIELD(test_x, test_y))
13758       continue;
13759
13760     test_move_dir =
13761       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13762
13763     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13764
13765     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13766        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13767     */
13768     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13769         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13770     {
13771       kill_x = test_x;
13772       kill_y = test_y;
13773       bad_element = test_element;
13774
13775       break;
13776     }
13777   }
13778
13779   if (kill_x != -1 || kill_y != -1)
13780   {
13781     if (IS_PLAYER(good_x, good_y))
13782     {
13783       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13784
13785       if (player->shield_deadly_time_left > 0 &&
13786           !IS_INDESTRUCTIBLE(bad_element))
13787         Bang(kill_x, kill_y);
13788       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13789         KillPlayer(player);
13790     }
13791     else
13792       Bang(good_x, good_y);
13793   }
13794 }
13795
13796 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13797 {
13798   int i, kill_x = -1, kill_y = -1;
13799   int bad_element = Tile[bad_x][bad_y];
13800   static int test_xy[4][2] =
13801   {
13802     { 0, -1 },
13803     { -1, 0 },
13804     { +1, 0 },
13805     { 0, +1 }
13806   };
13807   static int touch_dir[4] =
13808   {
13809     MV_LEFT | MV_RIGHT,
13810     MV_UP   | MV_DOWN,
13811     MV_UP   | MV_DOWN,
13812     MV_LEFT | MV_RIGHT
13813   };
13814   static int test_dir[4] =
13815   {
13816     MV_UP,
13817     MV_LEFT,
13818     MV_RIGHT,
13819     MV_DOWN
13820   };
13821
13822   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13823     return;
13824
13825   for (i = 0; i < NUM_DIRECTIONS; i++)
13826   {
13827     int test_x, test_y, test_move_dir, test_element;
13828
13829     test_x = bad_x + test_xy[i][0];
13830     test_y = bad_y + test_xy[i][1];
13831
13832     if (!IN_LEV_FIELD(test_x, test_y))
13833       continue;
13834
13835     test_move_dir =
13836       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13837
13838     test_element = Tile[test_x][test_y];
13839
13840     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13841        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13842     */
13843     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13844         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13845     {
13846       // good thing is player or penguin that does not move away
13847       if (IS_PLAYER(test_x, test_y))
13848       {
13849         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13850
13851         if (bad_element == EL_ROBOT && player->is_moving)
13852           continue;     // robot does not kill player if he is moving
13853
13854         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13855         {
13856           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13857             continue;           // center and border element do not touch
13858         }
13859
13860         kill_x = test_x;
13861         kill_y = test_y;
13862
13863         break;
13864       }
13865       else if (test_element == EL_PENGUIN)
13866       {
13867         kill_x = test_x;
13868         kill_y = test_y;
13869
13870         break;
13871       }
13872     }
13873   }
13874
13875   if (kill_x != -1 || kill_y != -1)
13876   {
13877     if (IS_PLAYER(kill_x, kill_y))
13878     {
13879       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13880
13881       if (player->shield_deadly_time_left > 0 &&
13882           !IS_INDESTRUCTIBLE(bad_element))
13883         Bang(bad_x, bad_y);
13884       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13885         KillPlayer(player);
13886     }
13887     else
13888       Bang(kill_x, kill_y);
13889   }
13890 }
13891
13892 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13893 {
13894   int bad_element = Tile[bad_x][bad_y];
13895   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13896   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13897   int test_x = bad_x + dx, test_y = bad_y + dy;
13898   int test_move_dir, test_element;
13899   int kill_x = -1, kill_y = -1;
13900
13901   if (!IN_LEV_FIELD(test_x, test_y))
13902     return;
13903
13904   test_move_dir =
13905     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13906
13907   test_element = Tile[test_x][test_y];
13908
13909   if (test_move_dir != bad_move_dir)
13910   {
13911     // good thing can be player or penguin that does not move away
13912     if (IS_PLAYER(test_x, test_y))
13913     {
13914       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13915
13916       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13917          player as being hit when he is moving towards the bad thing, because
13918          the "get hit by" condition would be lost after the player stops) */
13919       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13920         return;         // player moves away from bad thing
13921
13922       kill_x = test_x;
13923       kill_y = test_y;
13924     }
13925     else if (test_element == EL_PENGUIN)
13926     {
13927       kill_x = test_x;
13928       kill_y = test_y;
13929     }
13930   }
13931
13932   if (kill_x != -1 || kill_y != -1)
13933   {
13934     if (IS_PLAYER(kill_x, kill_y))
13935     {
13936       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13937
13938       if (player->shield_deadly_time_left > 0 &&
13939           !IS_INDESTRUCTIBLE(bad_element))
13940         Bang(bad_x, bad_y);
13941       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13942         KillPlayer(player);
13943     }
13944     else
13945       Bang(kill_x, kill_y);
13946   }
13947 }
13948
13949 void TestIfPlayerTouchesBadThing(int x, int y)
13950 {
13951   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13952 }
13953
13954 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13955 {
13956   TestIfGoodThingHitsBadThing(x, y, move_dir);
13957 }
13958
13959 void TestIfBadThingTouchesPlayer(int x, int y)
13960 {
13961   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13962 }
13963
13964 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13965 {
13966   TestIfBadThingHitsGoodThing(x, y, move_dir);
13967 }
13968
13969 void TestIfFriendTouchesBadThing(int x, int y)
13970 {
13971   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13972 }
13973
13974 void TestIfBadThingTouchesFriend(int x, int y)
13975 {
13976   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13977 }
13978
13979 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13980 {
13981   int i, kill_x = bad_x, kill_y = bad_y;
13982   static int xy[4][2] =
13983   {
13984     { 0, -1 },
13985     { -1, 0 },
13986     { +1, 0 },
13987     { 0, +1 }
13988   };
13989
13990   for (i = 0; i < NUM_DIRECTIONS; i++)
13991   {
13992     int x, y, element;
13993
13994     x = bad_x + xy[i][0];
13995     y = bad_y + xy[i][1];
13996     if (!IN_LEV_FIELD(x, y))
13997       continue;
13998
13999     element = Tile[x][y];
14000     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14001         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14002     {
14003       kill_x = x;
14004       kill_y = y;
14005       break;
14006     }
14007   }
14008
14009   if (kill_x != bad_x || kill_y != bad_y)
14010     Bang(bad_x, bad_y);
14011 }
14012
14013 void KillPlayer(struct PlayerInfo *player)
14014 {
14015   int jx = player->jx, jy = player->jy;
14016
14017   if (!player->active)
14018     return;
14019
14020 #if 0
14021   Debug("game:playing:KillPlayer",
14022         "0: killed == %d, active == %d, reanimated == %d",
14023         player->killed, player->active, player->reanimated);
14024 #endif
14025
14026   /* the following code was introduced to prevent an infinite loop when calling
14027      -> Bang()
14028      -> CheckTriggeredElementChangeExt()
14029      -> ExecuteCustomElementAction()
14030      -> KillPlayer()
14031      -> (infinitely repeating the above sequence of function calls)
14032      which occurs when killing the player while having a CE with the setting
14033      "kill player X when explosion of <player X>"; the solution using a new
14034      field "player->killed" was chosen for backwards compatibility, although
14035      clever use of the fields "player->active" etc. would probably also work */
14036 #if 1
14037   if (player->killed)
14038     return;
14039 #endif
14040
14041   player->killed = TRUE;
14042
14043   // remove accessible field at the player's position
14044   Tile[jx][jy] = EL_EMPTY;
14045
14046   // deactivate shield (else Bang()/Explode() would not work right)
14047   player->shield_normal_time_left = 0;
14048   player->shield_deadly_time_left = 0;
14049
14050 #if 0
14051   Debug("game:playing:KillPlayer",
14052         "1: killed == %d, active == %d, reanimated == %d",
14053         player->killed, player->active, player->reanimated);
14054 #endif
14055
14056   Bang(jx, jy);
14057
14058 #if 0
14059   Debug("game:playing:KillPlayer",
14060         "2: killed == %d, active == %d, reanimated == %d",
14061         player->killed, player->active, player->reanimated);
14062 #endif
14063
14064   if (player->reanimated)       // killed player may have been reanimated
14065     player->killed = player->reanimated = FALSE;
14066   else
14067     BuryPlayer(player);
14068 }
14069
14070 static void KillPlayerUnlessEnemyProtected(int x, int y)
14071 {
14072   if (!PLAYER_ENEMY_PROTECTED(x, y))
14073     KillPlayer(PLAYERINFO(x, y));
14074 }
14075
14076 static void KillPlayerUnlessExplosionProtected(int x, int y)
14077 {
14078   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14079     KillPlayer(PLAYERINFO(x, y));
14080 }
14081
14082 void BuryPlayer(struct PlayerInfo *player)
14083 {
14084   int jx = player->jx, jy = player->jy;
14085
14086   if (!player->active)
14087     return;
14088
14089   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14090   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14091
14092   RemovePlayer(player);
14093
14094   player->buried = TRUE;
14095
14096   if (game.all_players_gone)
14097     game.GameOver = TRUE;
14098 }
14099
14100 void RemovePlayer(struct PlayerInfo *player)
14101 {
14102   int jx = player->jx, jy = player->jy;
14103   int i, found = FALSE;
14104
14105   player->present = FALSE;
14106   player->active = FALSE;
14107
14108   // required for some CE actions (even if the player is not active anymore)
14109   player->MovPos = 0;
14110
14111   if (!ExplodeField[jx][jy])
14112     StorePlayer[jx][jy] = 0;
14113
14114   if (player->is_moving)
14115     TEST_DrawLevelField(player->last_jx, player->last_jy);
14116
14117   for (i = 0; i < MAX_PLAYERS; i++)
14118     if (stored_player[i].active)
14119       found = TRUE;
14120
14121   if (!found)
14122   {
14123     game.all_players_gone = TRUE;
14124     game.GameOver = TRUE;
14125   }
14126
14127   game.exit_x = game.robot_wheel_x = jx;
14128   game.exit_y = game.robot_wheel_y = jy;
14129 }
14130
14131 void ExitPlayer(struct PlayerInfo *player)
14132 {
14133   DrawPlayer(player);   // needed here only to cleanup last field
14134   RemovePlayer(player);
14135
14136   if (game.players_still_needed > 0)
14137     game.players_still_needed--;
14138 }
14139
14140 static void SetFieldForSnapping(int x, int y, int element, int direction,
14141                                 int player_index_bit)
14142 {
14143   struct ElementInfo *ei = &element_info[element];
14144   int direction_bit = MV_DIR_TO_BIT(direction);
14145   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14146   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14147                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14148
14149   Tile[x][y] = EL_ELEMENT_SNAPPING;
14150   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14151   MovDir[x][y] = direction;
14152   Store[x][y] = element;
14153   Store2[x][y] = player_index_bit;
14154
14155   ResetGfxAnimation(x, y);
14156
14157   GfxElement[x][y] = element;
14158   GfxAction[x][y] = action;
14159   GfxDir[x][y] = direction;
14160   GfxFrame[x][y] = -1;
14161 }
14162
14163 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14164                                    int player_index_bit)
14165 {
14166   TestIfElementTouchesCustomElement(x, y);      // for empty space
14167
14168   if (level.finish_dig_collect)
14169   {
14170     int dig_side = MV_DIR_OPPOSITE(direction);
14171     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14172                         CE_PLAYER_COLLECTS_X);
14173
14174     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14175                                         player_index_bit, dig_side);
14176     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14177                                         player_index_bit, dig_side);
14178   }
14179 }
14180
14181 /*
14182   =============================================================================
14183   checkDiagonalPushing()
14184   -----------------------------------------------------------------------------
14185   check if diagonal input device direction results in pushing of object
14186   (by checking if the alternative direction is walkable, diggable, ...)
14187   =============================================================================
14188 */
14189
14190 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14191                                     int x, int y, int real_dx, int real_dy)
14192 {
14193   int jx, jy, dx, dy, xx, yy;
14194
14195   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14196     return TRUE;
14197
14198   // diagonal direction: check alternative direction
14199   jx = player->jx;
14200   jy = player->jy;
14201   dx = x - jx;
14202   dy = y - jy;
14203   xx = jx + (dx == 0 ? real_dx : 0);
14204   yy = jy + (dy == 0 ? real_dy : 0);
14205
14206   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14207 }
14208
14209 /*
14210   =============================================================================
14211   DigField()
14212   -----------------------------------------------------------------------------
14213   x, y:                 field next to player (non-diagonal) to try to dig to
14214   real_dx, real_dy:     direction as read from input device (can be diagonal)
14215   =============================================================================
14216 */
14217
14218 static int DigField(struct PlayerInfo *player,
14219                     int oldx, int oldy, int x, int y,
14220                     int real_dx, int real_dy, int mode)
14221 {
14222   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14223   boolean player_was_pushing = player->is_pushing;
14224   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14225   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14226   int jx = oldx, jy = oldy;
14227   int dx = x - jx, dy = y - jy;
14228   int nextx = x + dx, nexty = y + dy;
14229   int move_direction = (dx == -1 ? MV_LEFT  :
14230                         dx == +1 ? MV_RIGHT :
14231                         dy == -1 ? MV_UP    :
14232                         dy == +1 ? MV_DOWN  : MV_NONE);
14233   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14234   int dig_side = MV_DIR_OPPOSITE(move_direction);
14235   int old_element = Tile[jx][jy];
14236   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14237   int collect_count;
14238
14239   if (is_player)                // function can also be called by EL_PENGUIN
14240   {
14241     if (player->MovPos == 0)
14242     {
14243       player->is_digging = FALSE;
14244       player->is_collecting = FALSE;
14245     }
14246
14247     if (player->MovPos == 0)    // last pushing move finished
14248       player->is_pushing = FALSE;
14249
14250     if (mode == DF_NO_PUSH)     // player just stopped pushing
14251     {
14252       player->is_switching = FALSE;
14253       player->push_delay = -1;
14254
14255       return MP_NO_ACTION;
14256     }
14257   }
14258   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14259     old_element = Back[jx][jy];
14260
14261   // in case of element dropped at player position, check background
14262   else if (Back[jx][jy] != EL_EMPTY &&
14263            game.engine_version >= VERSION_IDENT(2,2,0,0))
14264     old_element = Back[jx][jy];
14265
14266   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14267     return MP_NO_ACTION;        // field has no opening in this direction
14268
14269   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14270     return MP_NO_ACTION;        // field has no opening in this direction
14271
14272   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14273   {
14274     SplashAcid(x, y);
14275
14276     Tile[jx][jy] = player->artwork_element;
14277     InitMovingField(jx, jy, MV_DOWN);
14278     Store[jx][jy] = EL_ACID;
14279     ContinueMoving(jx, jy);
14280     BuryPlayer(player);
14281
14282     return MP_DONT_RUN_INTO;
14283   }
14284
14285   if (player_can_move && DONT_RUN_INTO(element))
14286   {
14287     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14288
14289     return MP_DONT_RUN_INTO;
14290   }
14291
14292   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14293     return MP_NO_ACTION;
14294
14295   collect_count = element_info[element].collect_count_initial;
14296
14297   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14298     return MP_NO_ACTION;
14299
14300   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14301     player_can_move = player_can_move_or_snap;
14302
14303   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14304       game.engine_version >= VERSION_IDENT(2,2,0,0))
14305   {
14306     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14307                                player->index_bit, dig_side);
14308     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14309                                         player->index_bit, dig_side);
14310
14311     if (element == EL_DC_LANDMINE)
14312       Bang(x, y);
14313
14314     if (Tile[x][y] != element)          // field changed by snapping
14315       return MP_ACTION;
14316
14317     return MP_NO_ACTION;
14318   }
14319
14320   if (player->gravity && is_player && !player->is_auto_moving &&
14321       canFallDown(player) && move_direction != MV_DOWN &&
14322       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14323     return MP_NO_ACTION;        // player cannot walk here due to gravity
14324
14325   if (player_can_move &&
14326       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14327   {
14328     int sound_element = SND_ELEMENT(element);
14329     int sound_action = ACTION_WALKING;
14330
14331     if (IS_RND_GATE(element))
14332     {
14333       if (!player->key[RND_GATE_NR(element)])
14334         return MP_NO_ACTION;
14335     }
14336     else if (IS_RND_GATE_GRAY(element))
14337     {
14338       if (!player->key[RND_GATE_GRAY_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14342     {
14343       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (element == EL_EXIT_OPEN ||
14347              element == EL_EM_EXIT_OPEN ||
14348              element == EL_EM_EXIT_OPENING ||
14349              element == EL_STEEL_EXIT_OPEN ||
14350              element == EL_EM_STEEL_EXIT_OPEN ||
14351              element == EL_EM_STEEL_EXIT_OPENING ||
14352              element == EL_SP_EXIT_OPEN ||
14353              element == EL_SP_EXIT_OPENING)
14354     {
14355       sound_action = ACTION_PASSING;    // player is passing exit
14356     }
14357     else if (element == EL_EMPTY)
14358     {
14359       sound_action = ACTION_MOVING;             // nothing to walk on
14360     }
14361
14362     // play sound from background or player, whatever is available
14363     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14364       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14365     else
14366       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14367   }
14368   else if (player_can_move &&
14369            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14370   {
14371     if (!ACCESS_FROM(element, opposite_direction))
14372       return MP_NO_ACTION;      // field not accessible from this direction
14373
14374     if (CAN_MOVE(element))      // only fixed elements can be passed!
14375       return MP_NO_ACTION;
14376
14377     if (IS_EM_GATE(element))
14378     {
14379       if (!player->key[EM_GATE_NR(element)])
14380         return MP_NO_ACTION;
14381     }
14382     else if (IS_EM_GATE_GRAY(element))
14383     {
14384       if (!player->key[EM_GATE_GRAY_NR(element)])
14385         return MP_NO_ACTION;
14386     }
14387     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14388     {
14389       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14390         return MP_NO_ACTION;
14391     }
14392     else if (IS_EMC_GATE(element))
14393     {
14394       if (!player->key[EMC_GATE_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EMC_GATE_GRAY(element))
14398     {
14399       if (!player->key[EMC_GATE_GRAY_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14403     {
14404       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (element == EL_DC_GATE_WHITE ||
14408              element == EL_DC_GATE_WHITE_GRAY ||
14409              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14410     {
14411       if (player->num_white_keys == 0)
14412         return MP_NO_ACTION;
14413
14414       player->num_white_keys--;
14415     }
14416     else if (IS_SP_PORT(element))
14417     {
14418       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14419           element == EL_SP_GRAVITY_PORT_RIGHT ||
14420           element == EL_SP_GRAVITY_PORT_UP ||
14421           element == EL_SP_GRAVITY_PORT_DOWN)
14422         player->gravity = !player->gravity;
14423       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14424                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14425                element == EL_SP_GRAVITY_ON_PORT_UP ||
14426                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14427         player->gravity = TRUE;
14428       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14429                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14430                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14431                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14432         player->gravity = FALSE;
14433     }
14434
14435     // automatically move to the next field with double speed
14436     player->programmed_action = move_direction;
14437
14438     if (player->move_delay_reset_counter == 0)
14439     {
14440       player->move_delay_reset_counter = 2;     // two double speed steps
14441
14442       DOUBLE_PLAYER_SPEED(player);
14443     }
14444
14445     PlayLevelSoundAction(x, y, ACTION_PASSING);
14446   }
14447   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14448   {
14449     RemoveField(x, y);
14450
14451     if (mode != DF_SNAP)
14452     {
14453       GfxElement[x][y] = GFX_ELEMENT(element);
14454       player->is_digging = TRUE;
14455     }
14456
14457     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14458
14459     // use old behaviour for old levels (digging)
14460     if (!level.finish_dig_collect)
14461     {
14462       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14463                                           player->index_bit, dig_side);
14464
14465       // if digging triggered player relocation, finish digging tile
14466       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14467         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14468     }
14469
14470     if (mode == DF_SNAP)
14471     {
14472       if (level.block_snap_field)
14473         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14474       else
14475         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14476
14477       // use old behaviour for old levels (snapping)
14478       if (!level.finish_dig_collect)
14479         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14480                                             player->index_bit, dig_side);
14481     }
14482   }
14483   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14484   {
14485     RemoveField(x, y);
14486
14487     if (is_player && mode != DF_SNAP)
14488     {
14489       GfxElement[x][y] = element;
14490       player->is_collecting = TRUE;
14491     }
14492
14493     if (element == EL_SPEED_PILL)
14494     {
14495       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14496     }
14497     else if (element == EL_EXTRA_TIME && level.time > 0)
14498     {
14499       TimeLeft += level.extra_time;
14500
14501       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14502
14503       DisplayGameControlValues();
14504     }
14505     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14506     {
14507       player->shield_normal_time_left += level.shield_normal_time;
14508       if (element == EL_SHIELD_DEADLY)
14509         player->shield_deadly_time_left += level.shield_deadly_time;
14510     }
14511     else if (element == EL_DYNAMITE ||
14512              element == EL_EM_DYNAMITE ||
14513              element == EL_SP_DISK_RED)
14514     {
14515       if (player->inventory_size < MAX_INVENTORY_SIZE)
14516         player->inventory_element[player->inventory_size++] = element;
14517
14518       DrawGameDoorValues();
14519     }
14520     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14521     {
14522       player->dynabomb_count++;
14523       player->dynabombs_left++;
14524     }
14525     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14526     {
14527       player->dynabomb_size++;
14528     }
14529     else if (element == EL_DYNABOMB_INCREASE_POWER)
14530     {
14531       player->dynabomb_xl = TRUE;
14532     }
14533     else if (IS_KEY(element))
14534     {
14535       player->key[KEY_NR(element)] = TRUE;
14536
14537       DrawGameDoorValues();
14538     }
14539     else if (element == EL_DC_KEY_WHITE)
14540     {
14541       player->num_white_keys++;
14542
14543       // display white keys?
14544       // DrawGameDoorValues();
14545     }
14546     else if (IS_ENVELOPE(element))
14547     {
14548       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14549
14550       if (!wait_for_snapping)
14551         player->show_envelope = element;
14552     }
14553     else if (element == EL_EMC_LENSES)
14554     {
14555       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14556
14557       RedrawAllInvisibleElementsForLenses();
14558     }
14559     else if (element == EL_EMC_MAGNIFIER)
14560     {
14561       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14562
14563       RedrawAllInvisibleElementsForMagnifier();
14564     }
14565     else if (IS_DROPPABLE(element) ||
14566              IS_THROWABLE(element))     // can be collected and dropped
14567     {
14568       int i;
14569
14570       if (collect_count == 0)
14571         player->inventory_infinite_element = element;
14572       else
14573         for (i = 0; i < collect_count; i++)
14574           if (player->inventory_size < MAX_INVENTORY_SIZE)
14575             player->inventory_element[player->inventory_size++] = element;
14576
14577       DrawGameDoorValues();
14578     }
14579     else if (collect_count > 0)
14580     {
14581       game.gems_still_needed -= collect_count;
14582       if (game.gems_still_needed < 0)
14583         game.gems_still_needed = 0;
14584
14585       game.snapshot.collected_item = TRUE;
14586
14587       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14588
14589       DisplayGameControlValues();
14590     }
14591
14592     RaiseScoreElement(element);
14593     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14594
14595     // use old behaviour for old levels (collecting)
14596     if (!level.finish_dig_collect && is_player)
14597     {
14598       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14599                                           player->index_bit, dig_side);
14600
14601       // if collecting triggered player relocation, finish collecting tile
14602       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14603         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14604     }
14605
14606     if (mode == DF_SNAP)
14607     {
14608       if (level.block_snap_field)
14609         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14610       else
14611         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14612
14613       // use old behaviour for old levels (snapping)
14614       if (!level.finish_dig_collect)
14615         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14616                                             player->index_bit, dig_side);
14617     }
14618   }
14619   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14620   {
14621     if (mode == DF_SNAP && element != EL_BD_ROCK)
14622       return MP_NO_ACTION;
14623
14624     if (CAN_FALL(element) && dy)
14625       return MP_NO_ACTION;
14626
14627     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14628         !(element == EL_SPRING && level.use_spring_bug))
14629       return MP_NO_ACTION;
14630
14631     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14632         ((move_direction & MV_VERTICAL &&
14633           ((element_info[element].move_pattern & MV_LEFT &&
14634             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14635            (element_info[element].move_pattern & MV_RIGHT &&
14636             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14637          (move_direction & MV_HORIZONTAL &&
14638           ((element_info[element].move_pattern & MV_UP &&
14639             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14640            (element_info[element].move_pattern & MV_DOWN &&
14641             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14642       return MP_NO_ACTION;
14643
14644     // do not push elements already moving away faster than player
14645     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14646         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14647       return MP_NO_ACTION;
14648
14649     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14650     {
14651       if (player->push_delay_value == -1 || !player_was_pushing)
14652         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14653     }
14654     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14655     {
14656       if (player->push_delay_value == -1)
14657         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14658     }
14659     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14660     {
14661       if (!player->is_pushing)
14662         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14663     }
14664
14665     player->is_pushing = TRUE;
14666     player->is_active = TRUE;
14667
14668     if (!(IN_LEV_FIELD(nextx, nexty) &&
14669           (IS_FREE(nextx, nexty) ||
14670            (IS_SB_ELEMENT(element) &&
14671             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14672            (IS_CUSTOM_ELEMENT(element) &&
14673             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14674       return MP_NO_ACTION;
14675
14676     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14677       return MP_NO_ACTION;
14678
14679     if (player->push_delay == -1)       // new pushing; restart delay
14680       player->push_delay = 0;
14681
14682     if (player->push_delay < player->push_delay_value &&
14683         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14684         element != EL_SPRING && element != EL_BALLOON)
14685     {
14686       // make sure that there is no move delay before next try to push
14687       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14688         player->move_delay = 0;
14689
14690       return MP_NO_ACTION;
14691     }
14692
14693     if (IS_CUSTOM_ELEMENT(element) &&
14694         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14695     {
14696       if (!DigFieldByCE(nextx, nexty, element))
14697         return MP_NO_ACTION;
14698     }
14699
14700     if (IS_SB_ELEMENT(element))
14701     {
14702       boolean sokoban_task_solved = FALSE;
14703
14704       if (element == EL_SOKOBAN_FIELD_FULL)
14705       {
14706         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14707
14708         IncrementSokobanFieldsNeeded();
14709         IncrementSokobanObjectsNeeded();
14710       }
14711
14712       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14713       {
14714         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14715
14716         DecrementSokobanFieldsNeeded();
14717         DecrementSokobanObjectsNeeded();
14718
14719         // sokoban object was pushed from empty field to sokoban field
14720         if (Back[x][y] == EL_EMPTY)
14721           sokoban_task_solved = TRUE;
14722       }
14723
14724       Tile[x][y] = EL_SOKOBAN_OBJECT;
14725
14726       if (Back[x][y] == Back[nextx][nexty])
14727         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14728       else if (Back[x][y] != 0)
14729         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14730                                     ACTION_EMPTYING);
14731       else
14732         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14733                                     ACTION_FILLING);
14734
14735       if (sokoban_task_solved &&
14736           game.sokoban_fields_still_needed == 0 &&
14737           game.sokoban_objects_still_needed == 0 &&
14738           level.auto_exit_sokoban)
14739       {
14740         game.players_still_needed = 0;
14741
14742         LevelSolved();
14743
14744         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14745       }
14746     }
14747     else
14748       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14749
14750     InitMovingField(x, y, move_direction);
14751     GfxAction[x][y] = ACTION_PUSHING;
14752
14753     if (mode == DF_SNAP)
14754       ContinueMoving(x, y);
14755     else
14756       MovPos[x][y] = (dx != 0 ? dx : dy);
14757
14758     Pushed[x][y] = TRUE;
14759     Pushed[nextx][nexty] = TRUE;
14760
14761     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14762       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14763     else
14764       player->push_delay_value = -1;    // get new value later
14765
14766     // check for element change _after_ element has been pushed
14767     if (game.use_change_when_pushing_bug)
14768     {
14769       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14770                                  player->index_bit, dig_side);
14771       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14772                                           player->index_bit, dig_side);
14773     }
14774   }
14775   else if (IS_SWITCHABLE(element))
14776   {
14777     if (PLAYER_SWITCHING(player, x, y))
14778     {
14779       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14780                                           player->index_bit, dig_side);
14781
14782       return MP_ACTION;
14783     }
14784
14785     player->is_switching = TRUE;
14786     player->switch_x = x;
14787     player->switch_y = y;
14788
14789     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14790
14791     if (element == EL_ROBOT_WHEEL)
14792     {
14793       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14794
14795       game.robot_wheel_x = x;
14796       game.robot_wheel_y = y;
14797       game.robot_wheel_active = TRUE;
14798
14799       TEST_DrawLevelField(x, y);
14800     }
14801     else if (element == EL_SP_TERMINAL)
14802     {
14803       int xx, yy;
14804
14805       SCAN_PLAYFIELD(xx, yy)
14806       {
14807         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14808         {
14809           Bang(xx, yy);
14810         }
14811         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14812         {
14813           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14814
14815           ResetGfxAnimation(xx, yy);
14816           TEST_DrawLevelField(xx, yy);
14817         }
14818       }
14819     }
14820     else if (IS_BELT_SWITCH(element))
14821     {
14822       ToggleBeltSwitch(x, y);
14823     }
14824     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14825              element == EL_SWITCHGATE_SWITCH_DOWN ||
14826              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14827              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14828     {
14829       ToggleSwitchgateSwitch(x, y);
14830     }
14831     else if (element == EL_LIGHT_SWITCH ||
14832              element == EL_LIGHT_SWITCH_ACTIVE)
14833     {
14834       ToggleLightSwitch(x, y);
14835     }
14836     else if (element == EL_TIMEGATE_SWITCH ||
14837              element == EL_DC_TIMEGATE_SWITCH)
14838     {
14839       ActivateTimegateSwitch(x, y);
14840     }
14841     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14842              element == EL_BALLOON_SWITCH_RIGHT ||
14843              element == EL_BALLOON_SWITCH_UP    ||
14844              element == EL_BALLOON_SWITCH_DOWN  ||
14845              element == EL_BALLOON_SWITCH_NONE  ||
14846              element == EL_BALLOON_SWITCH_ANY)
14847     {
14848       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14849                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14850                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14851                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14852                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14853                              move_direction);
14854     }
14855     else if (element == EL_LAMP)
14856     {
14857       Tile[x][y] = EL_LAMP_ACTIVE;
14858       game.lights_still_needed--;
14859
14860       ResetGfxAnimation(x, y);
14861       TEST_DrawLevelField(x, y);
14862     }
14863     else if (element == EL_TIME_ORB_FULL)
14864     {
14865       Tile[x][y] = EL_TIME_ORB_EMPTY;
14866
14867       if (level.time > 0 || level.use_time_orb_bug)
14868       {
14869         TimeLeft += level.time_orb_time;
14870         game.no_time_limit = FALSE;
14871
14872         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14873
14874         DisplayGameControlValues();
14875       }
14876
14877       ResetGfxAnimation(x, y);
14878       TEST_DrawLevelField(x, y);
14879     }
14880     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14881              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14882     {
14883       int xx, yy;
14884
14885       game.ball_active = !game.ball_active;
14886
14887       SCAN_PLAYFIELD(xx, yy)
14888       {
14889         int e = Tile[xx][yy];
14890
14891         if (game.ball_active)
14892         {
14893           if (e == EL_EMC_MAGIC_BALL)
14894             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14895           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14896             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14897         }
14898         else
14899         {
14900           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14901             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14902           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14903             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14904         }
14905       }
14906     }
14907
14908     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14909                                         player->index_bit, dig_side);
14910
14911     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14912                                         player->index_bit, dig_side);
14913
14914     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14915                                         player->index_bit, dig_side);
14916
14917     return MP_ACTION;
14918   }
14919   else
14920   {
14921     if (!PLAYER_SWITCHING(player, x, y))
14922     {
14923       player->is_switching = TRUE;
14924       player->switch_x = x;
14925       player->switch_y = y;
14926
14927       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14928                                  player->index_bit, dig_side);
14929       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14930                                           player->index_bit, dig_side);
14931
14932       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14933                                  player->index_bit, dig_side);
14934       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14935                                           player->index_bit, dig_side);
14936     }
14937
14938     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14939                                player->index_bit, dig_side);
14940     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14941                                         player->index_bit, dig_side);
14942
14943     return MP_NO_ACTION;
14944   }
14945
14946   player->push_delay = -1;
14947
14948   if (is_player)                // function can also be called by EL_PENGUIN
14949   {
14950     if (Tile[x][y] != element)          // really digged/collected something
14951     {
14952       player->is_collecting = !player->is_digging;
14953       player->is_active = TRUE;
14954
14955       player->last_removed_element = element;
14956     }
14957   }
14958
14959   return MP_MOVING;
14960 }
14961
14962 static boolean DigFieldByCE(int x, int y, int digging_element)
14963 {
14964   int element = Tile[x][y];
14965
14966   if (!IS_FREE(x, y))
14967   {
14968     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14969                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14970                   ACTION_BREAKING);
14971
14972     // no element can dig solid indestructible elements
14973     if (IS_INDESTRUCTIBLE(element) &&
14974         !IS_DIGGABLE(element) &&
14975         !IS_COLLECTIBLE(element))
14976       return FALSE;
14977
14978     if (AmoebaNr[x][y] &&
14979         (element == EL_AMOEBA_FULL ||
14980          element == EL_BD_AMOEBA ||
14981          element == EL_AMOEBA_GROWING))
14982     {
14983       AmoebaCnt[AmoebaNr[x][y]]--;
14984       AmoebaCnt2[AmoebaNr[x][y]]--;
14985     }
14986
14987     if (IS_MOVING(x, y))
14988       RemoveMovingField(x, y);
14989     else
14990     {
14991       RemoveField(x, y);
14992       TEST_DrawLevelField(x, y);
14993     }
14994
14995     // if digged element was about to explode, prevent the explosion
14996     ExplodeField[x][y] = EX_TYPE_NONE;
14997
14998     PlayLevelSoundAction(x, y, action);
14999   }
15000
15001   Store[x][y] = EL_EMPTY;
15002
15003   // this makes it possible to leave the removed element again
15004   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15005     Store[x][y] = element;
15006
15007   return TRUE;
15008 }
15009
15010 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15011 {
15012   int jx = player->jx, jy = player->jy;
15013   int x = jx + dx, y = jy + dy;
15014   int snap_direction = (dx == -1 ? MV_LEFT  :
15015                         dx == +1 ? MV_RIGHT :
15016                         dy == -1 ? MV_UP    :
15017                         dy == +1 ? MV_DOWN  : MV_NONE);
15018   boolean can_continue_snapping = (level.continuous_snapping &&
15019                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15020
15021   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15022     return FALSE;
15023
15024   if (!player->active || !IN_LEV_FIELD(x, y))
15025     return FALSE;
15026
15027   if (dx && dy)
15028     return FALSE;
15029
15030   if (!dx && !dy)
15031   {
15032     if (player->MovPos == 0)
15033       player->is_pushing = FALSE;
15034
15035     player->is_snapping = FALSE;
15036
15037     if (player->MovPos == 0)
15038     {
15039       player->is_moving = FALSE;
15040       player->is_digging = FALSE;
15041       player->is_collecting = FALSE;
15042     }
15043
15044     return FALSE;
15045   }
15046
15047   // prevent snapping with already pressed snap key when not allowed
15048   if (player->is_snapping && !can_continue_snapping)
15049     return FALSE;
15050
15051   player->MovDir = snap_direction;
15052
15053   if (player->MovPos == 0)
15054   {
15055     player->is_moving = FALSE;
15056     player->is_digging = FALSE;
15057     player->is_collecting = FALSE;
15058   }
15059
15060   player->is_dropping = FALSE;
15061   player->is_dropping_pressed = FALSE;
15062   player->drop_pressed_delay = 0;
15063
15064   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15065     return FALSE;
15066
15067   player->is_snapping = TRUE;
15068   player->is_active = TRUE;
15069
15070   if (player->MovPos == 0)
15071   {
15072     player->is_moving = FALSE;
15073     player->is_digging = FALSE;
15074     player->is_collecting = FALSE;
15075   }
15076
15077   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15078     TEST_DrawLevelField(player->last_jx, player->last_jy);
15079
15080   TEST_DrawLevelField(x, y);
15081
15082   return TRUE;
15083 }
15084
15085 static boolean DropElement(struct PlayerInfo *player)
15086 {
15087   int old_element, new_element;
15088   int dropx = player->jx, dropy = player->jy;
15089   int drop_direction = player->MovDir;
15090   int drop_side = drop_direction;
15091   int drop_element = get_next_dropped_element(player);
15092
15093   /* do not drop an element on top of another element; when holding drop key
15094      pressed without moving, dropped element must move away before the next
15095      element can be dropped (this is especially important if the next element
15096      is dynamite, which can be placed on background for historical reasons) */
15097   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15098     return MP_ACTION;
15099
15100   if (IS_THROWABLE(drop_element))
15101   {
15102     dropx += GET_DX_FROM_DIR(drop_direction);
15103     dropy += GET_DY_FROM_DIR(drop_direction);
15104
15105     if (!IN_LEV_FIELD(dropx, dropy))
15106       return FALSE;
15107   }
15108
15109   old_element = Tile[dropx][dropy];     // old element at dropping position
15110   new_element = drop_element;           // default: no change when dropping
15111
15112   // check if player is active, not moving and ready to drop
15113   if (!player->active || player->MovPos || player->drop_delay > 0)
15114     return FALSE;
15115
15116   // check if player has anything that can be dropped
15117   if (new_element == EL_UNDEFINED)
15118     return FALSE;
15119
15120   // only set if player has anything that can be dropped
15121   player->is_dropping_pressed = TRUE;
15122
15123   // check if drop key was pressed long enough for EM style dynamite
15124   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15125     return FALSE;
15126
15127   // check if anything can be dropped at the current position
15128   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15129     return FALSE;
15130
15131   // collected custom elements can only be dropped on empty fields
15132   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15133     return FALSE;
15134
15135   if (old_element != EL_EMPTY)
15136     Back[dropx][dropy] = old_element;   // store old element on this field
15137
15138   ResetGfxAnimation(dropx, dropy);
15139   ResetRandomAnimationValue(dropx, dropy);
15140
15141   if (player->inventory_size > 0 ||
15142       player->inventory_infinite_element != EL_UNDEFINED)
15143   {
15144     if (player->inventory_size > 0)
15145     {
15146       player->inventory_size--;
15147
15148       DrawGameDoorValues();
15149
15150       if (new_element == EL_DYNAMITE)
15151         new_element = EL_DYNAMITE_ACTIVE;
15152       else if (new_element == EL_EM_DYNAMITE)
15153         new_element = EL_EM_DYNAMITE_ACTIVE;
15154       else if (new_element == EL_SP_DISK_RED)
15155         new_element = EL_SP_DISK_RED_ACTIVE;
15156     }
15157
15158     Tile[dropx][dropy] = new_element;
15159
15160     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15161       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15162                           el2img(Tile[dropx][dropy]), 0);
15163
15164     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15165
15166     // needed if previous element just changed to "empty" in the last frame
15167     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15168
15169     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15170                                player->index_bit, drop_side);
15171     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15172                                         CE_PLAYER_DROPS_X,
15173                                         player->index_bit, drop_side);
15174
15175     TestIfElementTouchesCustomElement(dropx, dropy);
15176   }
15177   else          // player is dropping a dyna bomb
15178   {
15179     player->dynabombs_left--;
15180
15181     Tile[dropx][dropy] = new_element;
15182
15183     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15184       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15185                           el2img(Tile[dropx][dropy]), 0);
15186
15187     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15188   }
15189
15190   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15191     InitField_WithBug1(dropx, dropy, FALSE);
15192
15193   new_element = Tile[dropx][dropy];     // element might have changed
15194
15195   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15196       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15197   {
15198     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15199       MovDir[dropx][dropy] = drop_direction;
15200
15201     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15202
15203     // do not cause impact style collision by dropping elements that can fall
15204     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15205   }
15206
15207   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15208   player->is_dropping = TRUE;
15209
15210   player->drop_pressed_delay = 0;
15211   player->is_dropping_pressed = FALSE;
15212
15213   player->drop_x = dropx;
15214   player->drop_y = dropy;
15215
15216   return TRUE;
15217 }
15218
15219 // ----------------------------------------------------------------------------
15220 // game sound playing functions
15221 // ----------------------------------------------------------------------------
15222
15223 static int *loop_sound_frame = NULL;
15224 static int *loop_sound_volume = NULL;
15225
15226 void InitPlayLevelSound(void)
15227 {
15228   int num_sounds = getSoundListSize();
15229
15230   checked_free(loop_sound_frame);
15231   checked_free(loop_sound_volume);
15232
15233   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15234   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15235 }
15236
15237 static void PlayLevelSound(int x, int y, int nr)
15238 {
15239   int sx = SCREENX(x), sy = SCREENY(y);
15240   int volume, stereo_position;
15241   int max_distance = 8;
15242   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15243
15244   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15245       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15246     return;
15247
15248   if (!IN_LEV_FIELD(x, y) ||
15249       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15250       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15251     return;
15252
15253   volume = SOUND_MAX_VOLUME;
15254
15255   if (!IN_SCR_FIELD(sx, sy))
15256   {
15257     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15258     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15259
15260     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15261   }
15262
15263   stereo_position = (SOUND_MAX_LEFT +
15264                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15265                      (SCR_FIELDX + 2 * max_distance));
15266
15267   if (IS_LOOP_SOUND(nr))
15268   {
15269     /* This assures that quieter loop sounds do not overwrite louder ones,
15270        while restarting sound volume comparison with each new game frame. */
15271
15272     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15273       return;
15274
15275     loop_sound_volume[nr] = volume;
15276     loop_sound_frame[nr] = FrameCounter;
15277   }
15278
15279   PlaySoundExt(nr, volume, stereo_position, type);
15280 }
15281
15282 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15283 {
15284   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15285                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15286                  y < LEVELY(BY1) ? LEVELY(BY1) :
15287                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15288                  sound_action);
15289 }
15290
15291 static void PlayLevelSoundAction(int x, int y, int action)
15292 {
15293   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15294 }
15295
15296 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15297 {
15298   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15299
15300   if (sound_effect != SND_UNDEFINED)
15301     PlayLevelSound(x, y, sound_effect);
15302 }
15303
15304 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15305                                               int action)
15306 {
15307   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15308
15309   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15310     PlayLevelSound(x, y, sound_effect);
15311 }
15312
15313 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15314 {
15315   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].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 StopLevelSoundActionIfLoop(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     StopSound(sound_effect);
15327 }
15328
15329 static int getLevelMusicNr(void)
15330 {
15331   if (levelset.music[level_nr] != MUS_UNDEFINED)
15332     return levelset.music[level_nr];            // from config file
15333   else
15334     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15335 }
15336
15337 static void FadeLevelSounds(void)
15338 {
15339   FadeSounds();
15340 }
15341
15342 static void FadeLevelMusic(void)
15343 {
15344   int music_nr = getLevelMusicNr();
15345   char *curr_music = getCurrentlyPlayingMusicFilename();
15346   char *next_music = getMusicInfoEntryFilename(music_nr);
15347
15348   if (!strEqual(curr_music, next_music))
15349     FadeMusic();
15350 }
15351
15352 void FadeLevelSoundsAndMusic(void)
15353 {
15354   FadeLevelSounds();
15355   FadeLevelMusic();
15356 }
15357
15358 static void PlayLevelMusic(void)
15359 {
15360   int music_nr = getLevelMusicNr();
15361   char *curr_music = getCurrentlyPlayingMusicFilename();
15362   char *next_music = getMusicInfoEntryFilename(music_nr);
15363
15364   if (!strEqual(curr_music, next_music))
15365     PlayMusicLoop(music_nr);
15366 }
15367
15368 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15369 {
15370   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15371   int offset = 0;
15372   int x = xx - offset;
15373   int y = yy - offset;
15374
15375   switch (sample)
15376   {
15377     case SOUND_blank:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15379       break;
15380
15381     case SOUND_roll:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15383       break;
15384
15385     case SOUND_stone:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15387       break;
15388
15389     case SOUND_nut:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15391       break;
15392
15393     case SOUND_crack:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15395       break;
15396
15397     case SOUND_bug:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15399       break;
15400
15401     case SOUND_tank:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15403       break;
15404
15405     case SOUND_android_clone:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15407       break;
15408
15409     case SOUND_android_move:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15411       break;
15412
15413     case SOUND_spring:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15415       break;
15416
15417     case SOUND_slurp:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15419       break;
15420
15421     case SOUND_eater:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15423       break;
15424
15425     case SOUND_eater_eat:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15427       break;
15428
15429     case SOUND_alien:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15431       break;
15432
15433     case SOUND_collect:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15435       break;
15436
15437     case SOUND_diamond:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15439       break;
15440
15441     case SOUND_squash:
15442       // !!! CHECK THIS !!!
15443 #if 1
15444       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15445 #else
15446       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15447 #endif
15448       break;
15449
15450     case SOUND_wonderfall:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15452       break;
15453
15454     case SOUND_drip:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15456       break;
15457
15458     case SOUND_push:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15460       break;
15461
15462     case SOUND_dirt:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15464       break;
15465
15466     case SOUND_acid:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15468       break;
15469
15470     case SOUND_ball:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15472       break;
15473
15474     case SOUND_slide:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15476       break;
15477
15478     case SOUND_wonder:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15480       break;
15481
15482     case SOUND_door:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15484       break;
15485
15486     case SOUND_exit_open:
15487       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15488       break;
15489
15490     case SOUND_exit_leave:
15491       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15492       break;
15493
15494     case SOUND_dynamite:
15495       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15496       break;
15497
15498     case SOUND_tick:
15499       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15500       break;
15501
15502     case SOUND_press:
15503       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15504       break;
15505
15506     case SOUND_wheel:
15507       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15508       break;
15509
15510     case SOUND_boom:
15511       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15512       break;
15513
15514     case SOUND_die:
15515       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15516       break;
15517
15518     case SOUND_time:
15519       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15520       break;
15521
15522     default:
15523       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15524       break;
15525   }
15526 }
15527
15528 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15529 {
15530   int element = map_element_SP_to_RND(element_sp);
15531   int action = map_action_SP_to_RND(action_sp);
15532   int offset = (setup.sp_show_border_elements ? 0 : 1);
15533   int x = xx - offset;
15534   int y = yy - offset;
15535
15536   PlayLevelSoundElementAction(x, y, element, action);
15537 }
15538
15539 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15540 {
15541   int element = map_element_MM_to_RND(element_mm);
15542   int action = map_action_MM_to_RND(action_mm);
15543   int offset = 0;
15544   int x = xx - offset;
15545   int y = yy - offset;
15546
15547   if (!IS_MM_ELEMENT(element))
15548     element = EL_MM_DEFAULT;
15549
15550   PlayLevelSoundElementAction(x, y, element, action);
15551 }
15552
15553 void PlaySound_MM(int sound_mm)
15554 {
15555   int sound = map_sound_MM_to_RND(sound_mm);
15556
15557   if (sound == SND_UNDEFINED)
15558     return;
15559
15560   PlaySound(sound);
15561 }
15562
15563 void PlaySoundLoop_MM(int sound_mm)
15564 {
15565   int sound = map_sound_MM_to_RND(sound_mm);
15566
15567   if (sound == SND_UNDEFINED)
15568     return;
15569
15570   PlaySoundLoop(sound);
15571 }
15572
15573 void StopSound_MM(int sound_mm)
15574 {
15575   int sound = map_sound_MM_to_RND(sound_mm);
15576
15577   if (sound == SND_UNDEFINED)
15578     return;
15579
15580   StopSound(sound);
15581 }
15582
15583 void RaiseScore(int value)
15584 {
15585   game.score += value;
15586
15587   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15588
15589   DisplayGameControlValues();
15590 }
15591
15592 void RaiseScoreElement(int element)
15593 {
15594   switch (element)
15595   {
15596     case EL_EMERALD:
15597     case EL_BD_DIAMOND:
15598     case EL_EMERALD_YELLOW:
15599     case EL_EMERALD_RED:
15600     case EL_EMERALD_PURPLE:
15601     case EL_SP_INFOTRON:
15602       RaiseScore(level.score[SC_EMERALD]);
15603       break;
15604     case EL_DIAMOND:
15605       RaiseScore(level.score[SC_DIAMOND]);
15606       break;
15607     case EL_CRYSTAL:
15608       RaiseScore(level.score[SC_CRYSTAL]);
15609       break;
15610     case EL_PEARL:
15611       RaiseScore(level.score[SC_PEARL]);
15612       break;
15613     case EL_BUG:
15614     case EL_BD_BUTTERFLY:
15615     case EL_SP_ELECTRON:
15616       RaiseScore(level.score[SC_BUG]);
15617       break;
15618     case EL_SPACESHIP:
15619     case EL_BD_FIREFLY:
15620     case EL_SP_SNIKSNAK:
15621       RaiseScore(level.score[SC_SPACESHIP]);
15622       break;
15623     case EL_YAMYAM:
15624     case EL_DARK_YAMYAM:
15625       RaiseScore(level.score[SC_YAMYAM]);
15626       break;
15627     case EL_ROBOT:
15628       RaiseScore(level.score[SC_ROBOT]);
15629       break;
15630     case EL_PACMAN:
15631       RaiseScore(level.score[SC_PACMAN]);
15632       break;
15633     case EL_NUT:
15634       RaiseScore(level.score[SC_NUT]);
15635       break;
15636     case EL_DYNAMITE:
15637     case EL_EM_DYNAMITE:
15638     case EL_SP_DISK_RED:
15639     case EL_DYNABOMB_INCREASE_NUMBER:
15640     case EL_DYNABOMB_INCREASE_SIZE:
15641     case EL_DYNABOMB_INCREASE_POWER:
15642       RaiseScore(level.score[SC_DYNAMITE]);
15643       break;
15644     case EL_SHIELD_NORMAL:
15645     case EL_SHIELD_DEADLY:
15646       RaiseScore(level.score[SC_SHIELD]);
15647       break;
15648     case EL_EXTRA_TIME:
15649       RaiseScore(level.extra_time_score);
15650       break;
15651     case EL_KEY_1:
15652     case EL_KEY_2:
15653     case EL_KEY_3:
15654     case EL_KEY_4:
15655     case EL_EM_KEY_1:
15656     case EL_EM_KEY_2:
15657     case EL_EM_KEY_3:
15658     case EL_EM_KEY_4:
15659     case EL_EMC_KEY_5:
15660     case EL_EMC_KEY_6:
15661     case EL_EMC_KEY_7:
15662     case EL_EMC_KEY_8:
15663     case EL_DC_KEY_WHITE:
15664       RaiseScore(level.score[SC_KEY]);
15665       break;
15666     default:
15667       RaiseScore(element_info[element].collect_score);
15668       break;
15669   }
15670 }
15671
15672 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15673 {
15674   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15675   {
15676     if (!quick_quit)
15677     {
15678       // prevent short reactivation of overlay buttons while closing door
15679       SetOverlayActive(FALSE);
15680
15681       // door may still be open due to skipped or envelope style request
15682       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15683     }
15684
15685     if (network.enabled)
15686       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15687     else
15688     {
15689       if (quick_quit)
15690         FadeSkipNextFadeIn();
15691
15692       SetGameStatus(GAME_MODE_MAIN);
15693
15694       DrawMainMenu();
15695     }
15696   }
15697   else          // continue playing the game
15698   {
15699     if (tape.playing && tape.deactivate_display)
15700       TapeDeactivateDisplayOff(TRUE);
15701
15702     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15703
15704     if (tape.playing && tape.deactivate_display)
15705       TapeDeactivateDisplayOn();
15706   }
15707 }
15708
15709 void RequestQuitGame(boolean escape_key_pressed)
15710 {
15711   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15712   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15713                         level_editor_test_game);
15714   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15715                           quick_quit || score_info_tape_play);
15716
15717   RequestQuitGameExt(skip_request, quick_quit,
15718                      "Do you really want to quit the game?");
15719 }
15720
15721 void RequestRestartGame(char *message)
15722 {
15723   game.restart_game_message = NULL;
15724
15725   boolean has_started_game = hasStartedNetworkGame();
15726   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15727
15728   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15729   {
15730     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15731   }
15732   else
15733   {
15734     // needed in case of envelope request to close game panel
15735     CloseDoor(DOOR_CLOSE_1);
15736
15737     SetGameStatus(GAME_MODE_MAIN);
15738
15739     DrawMainMenu();
15740   }
15741 }
15742
15743 void CheckGameOver(void)
15744 {
15745   static boolean last_game_over = FALSE;
15746   static int game_over_delay = 0;
15747   int game_over_delay_value = 50;
15748   boolean game_over = checkGameFailed();
15749
15750   // do not handle game over if request dialog is already active
15751   if (game.request_active)
15752     return;
15753
15754   // do not ask to play again if game was never actually played
15755   if (!game.GamePlayed)
15756     return;
15757
15758   if (!game_over)
15759   {
15760     last_game_over = FALSE;
15761     game_over_delay = game_over_delay_value;
15762
15763     return;
15764   }
15765
15766   if (game_over_delay > 0)
15767   {
15768     game_over_delay--;
15769
15770     return;
15771   }
15772
15773   if (last_game_over != game_over)
15774     game.restart_game_message = (hasStartedNetworkGame() ?
15775                                  "Game over! Play it again?" :
15776                                  "Game over!");
15777
15778   last_game_over = game_over;
15779 }
15780
15781 boolean checkGameSolved(void)
15782 {
15783   // set for all game engines if level was solved
15784   return game.LevelSolved_GameEnd;
15785 }
15786
15787 boolean checkGameFailed(void)
15788 {
15789   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15790     return (game_em.game_over && !game_em.level_solved);
15791   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15792     return (game_sp.game_over && !game_sp.level_solved);
15793   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15794     return (game_mm.game_over && !game_mm.level_solved);
15795   else                          // GAME_ENGINE_TYPE_RND
15796     return (game.GameOver && !game.LevelSolved);
15797 }
15798
15799 boolean checkGameEnded(void)
15800 {
15801   return (checkGameSolved() || checkGameFailed());
15802 }
15803
15804
15805 // ----------------------------------------------------------------------------
15806 // random generator functions
15807 // ----------------------------------------------------------------------------
15808
15809 unsigned int InitEngineRandom_RND(int seed)
15810 {
15811   game.num_random_calls = 0;
15812
15813   return InitEngineRandom(seed);
15814 }
15815
15816 unsigned int RND(int max)
15817 {
15818   if (max > 0)
15819   {
15820     game.num_random_calls++;
15821
15822     return GetEngineRandom(max);
15823   }
15824
15825   return 0;
15826 }
15827
15828
15829 // ----------------------------------------------------------------------------
15830 // game engine snapshot handling functions
15831 // ----------------------------------------------------------------------------
15832
15833 struct EngineSnapshotInfo
15834 {
15835   // runtime values for custom element collect score
15836   int collect_score[NUM_CUSTOM_ELEMENTS];
15837
15838   // runtime values for group element choice position
15839   int choice_pos[NUM_GROUP_ELEMENTS];
15840
15841   // runtime values for belt position animations
15842   int belt_graphic[4][NUM_BELT_PARTS];
15843   int belt_anim_mode[4][NUM_BELT_PARTS];
15844 };
15845
15846 static struct EngineSnapshotInfo engine_snapshot_rnd;
15847 static char *snapshot_level_identifier = NULL;
15848 static int snapshot_level_nr = -1;
15849
15850 static void SaveEngineSnapshotValues_RND(void)
15851 {
15852   static int belt_base_active_element[4] =
15853   {
15854     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15855     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15856     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15857     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15858   };
15859   int i, j;
15860
15861   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15862   {
15863     int element = EL_CUSTOM_START + i;
15864
15865     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15866   }
15867
15868   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15869   {
15870     int element = EL_GROUP_START + i;
15871
15872     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15873   }
15874
15875   for (i = 0; i < 4; i++)
15876   {
15877     for (j = 0; j < NUM_BELT_PARTS; j++)
15878     {
15879       int element = belt_base_active_element[i] + j;
15880       int graphic = el2img(element);
15881       int anim_mode = graphic_info[graphic].anim_mode;
15882
15883       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15884       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15885     }
15886   }
15887 }
15888
15889 static void LoadEngineSnapshotValues_RND(void)
15890 {
15891   unsigned int num_random_calls = game.num_random_calls;
15892   int i, j;
15893
15894   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15895   {
15896     int element = EL_CUSTOM_START + i;
15897
15898     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15899   }
15900
15901   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15902   {
15903     int element = EL_GROUP_START + i;
15904
15905     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15906   }
15907
15908   for (i = 0; i < 4; i++)
15909   {
15910     for (j = 0; j < NUM_BELT_PARTS; j++)
15911     {
15912       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15913       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15914
15915       graphic_info[graphic].anim_mode = anim_mode;
15916     }
15917   }
15918
15919   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15920   {
15921     InitRND(tape.random_seed);
15922     for (i = 0; i < num_random_calls; i++)
15923       RND(1);
15924   }
15925
15926   if (game.num_random_calls != num_random_calls)
15927   {
15928     Error("number of random calls out of sync");
15929     Error("number of random calls should be %d", num_random_calls);
15930     Error("number of random calls is %d", game.num_random_calls);
15931
15932     Fail("this should not happen -- please debug");
15933   }
15934 }
15935
15936 void FreeEngineSnapshotSingle(void)
15937 {
15938   FreeSnapshotSingle();
15939
15940   setString(&snapshot_level_identifier, NULL);
15941   snapshot_level_nr = -1;
15942 }
15943
15944 void FreeEngineSnapshotList(void)
15945 {
15946   FreeSnapshotList();
15947 }
15948
15949 static ListNode *SaveEngineSnapshotBuffers(void)
15950 {
15951   ListNode *buffers = NULL;
15952
15953   // copy some special values to a structure better suited for the snapshot
15954
15955   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15956     SaveEngineSnapshotValues_RND();
15957   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15958     SaveEngineSnapshotValues_EM();
15959   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15960     SaveEngineSnapshotValues_SP(&buffers);
15961   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15962     SaveEngineSnapshotValues_MM(&buffers);
15963
15964   // save values stored in special snapshot structure
15965
15966   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15967     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15968   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15969     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15970   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15971     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15972   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15973     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15974
15975   // save further RND engine values
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15980
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15986
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15992
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15995
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16014
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16017
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16021
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16024
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16031
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16034
16035 #if 0
16036   ListNode *node = engine_snapshot_list_rnd;
16037   int num_bytes = 0;
16038
16039   while (node != NULL)
16040   {
16041     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16042
16043     node = node->next;
16044   }
16045
16046   Debug("game:playing:SaveEngineSnapshotBuffers",
16047         "size of engine snapshot: %d bytes", num_bytes);
16048 #endif
16049
16050   return buffers;
16051 }
16052
16053 void SaveEngineSnapshotSingle(void)
16054 {
16055   ListNode *buffers = SaveEngineSnapshotBuffers();
16056
16057   // finally save all snapshot buffers to single snapshot
16058   SaveSnapshotSingle(buffers);
16059
16060   // save level identification information
16061   setString(&snapshot_level_identifier, leveldir_current->identifier);
16062   snapshot_level_nr = level_nr;
16063 }
16064
16065 boolean CheckSaveEngineSnapshotToList(void)
16066 {
16067   boolean save_snapshot =
16068     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16069      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16070       game.snapshot.changed_action) ||
16071      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16072       game.snapshot.collected_item));
16073
16074   game.snapshot.changed_action = FALSE;
16075   game.snapshot.collected_item = FALSE;
16076   game.snapshot.save_snapshot = save_snapshot;
16077
16078   return save_snapshot;
16079 }
16080
16081 void SaveEngineSnapshotToList(void)
16082 {
16083   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16084       tape.quick_resume)
16085     return;
16086
16087   ListNode *buffers = SaveEngineSnapshotBuffers();
16088
16089   // finally save all snapshot buffers to snapshot list
16090   SaveSnapshotToList(buffers);
16091 }
16092
16093 void SaveEngineSnapshotToListInitial(void)
16094 {
16095   FreeEngineSnapshotList();
16096
16097   SaveEngineSnapshotToList();
16098 }
16099
16100 static void LoadEngineSnapshotValues(void)
16101 {
16102   // restore special values from snapshot structure
16103
16104   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16105     LoadEngineSnapshotValues_RND();
16106   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16107     LoadEngineSnapshotValues_EM();
16108   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16109     LoadEngineSnapshotValues_SP();
16110   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16111     LoadEngineSnapshotValues_MM();
16112 }
16113
16114 void LoadEngineSnapshotSingle(void)
16115 {
16116   LoadSnapshotSingle();
16117
16118   LoadEngineSnapshotValues();
16119 }
16120
16121 static void LoadEngineSnapshot_Undo(int steps)
16122 {
16123   LoadSnapshotFromList_Older(steps);
16124
16125   LoadEngineSnapshotValues();
16126 }
16127
16128 static void LoadEngineSnapshot_Redo(int steps)
16129 {
16130   LoadSnapshotFromList_Newer(steps);
16131
16132   LoadEngineSnapshotValues();
16133 }
16134
16135 boolean CheckEngineSnapshotSingle(void)
16136 {
16137   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16138           snapshot_level_nr == level_nr);
16139 }
16140
16141 boolean CheckEngineSnapshotList(void)
16142 {
16143   return CheckSnapshotList();
16144 }
16145
16146
16147 // ---------- new game button stuff -------------------------------------------
16148
16149 static struct
16150 {
16151   int graphic;
16152   struct XY *pos;
16153   int gadget_id;
16154   boolean *setup_value;
16155   boolean allowed_on_tape;
16156   boolean is_touch_button;
16157   char *infotext;
16158 } gamebutton_info[NUM_GAME_BUTTONS] =
16159 {
16160   {
16161     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16162     GAME_CTRL_ID_STOP,                          NULL,
16163     TRUE, FALSE,                                "stop game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16167     GAME_CTRL_ID_PAUSE,                         NULL,
16168     TRUE, FALSE,                                "pause game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16172     GAME_CTRL_ID_PLAY,                          NULL,
16173     TRUE, FALSE,                                "play game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16177     GAME_CTRL_ID_UNDO,                          NULL,
16178     TRUE, FALSE,                                "undo step"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16182     GAME_CTRL_ID_REDO,                          NULL,
16183     TRUE, FALSE,                                "redo step"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16187     GAME_CTRL_ID_SAVE,                          NULL,
16188     TRUE, FALSE,                                "save game"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16192     GAME_CTRL_ID_PAUSE2,                        NULL,
16193     TRUE, FALSE,                                "pause game"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16197     GAME_CTRL_ID_LOAD,                          NULL,
16198     TRUE, FALSE,                                "load game"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16202     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16203     FALSE, FALSE,                               "stop game"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16207     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16208     FALSE, FALSE,                               "pause game"
16209   },
16210   {
16211     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16212     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16213     FALSE, FALSE,                               "play game"
16214   },
16215   {
16216     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16217     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16218     FALSE, TRUE,                                "stop game"
16219   },
16220   {
16221     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16222     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16223     FALSE, TRUE,                                "pause game"
16224   },
16225   {
16226     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16227     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16228     TRUE, FALSE,                                "background music on/off"
16229   },
16230   {
16231     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16232     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16233     TRUE, FALSE,                                "sound loops on/off"
16234   },
16235   {
16236     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16237     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16238     TRUE, FALSE,                                "normal sounds on/off"
16239   },
16240   {
16241     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16242     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16243     FALSE, FALSE,                               "background music on/off"
16244   },
16245   {
16246     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16247     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16248     FALSE, FALSE,                               "sound loops on/off"
16249   },
16250   {
16251     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16252     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16253     FALSE, FALSE,                               "normal sounds on/off"
16254   }
16255 };
16256
16257 void CreateGameButtons(void)
16258 {
16259   int i;
16260
16261   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16262   {
16263     int graphic = gamebutton_info[i].graphic;
16264     struct GraphicInfo *gfx = &graphic_info[graphic];
16265     struct XY *pos = gamebutton_info[i].pos;
16266     struct GadgetInfo *gi;
16267     int button_type;
16268     boolean checked;
16269     unsigned int event_mask;
16270     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16271     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16272     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16273     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16274     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16275     int gd_x   = gfx->src_x;
16276     int gd_y   = gfx->src_y;
16277     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16278     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16279     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16280     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16281     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16282     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16283     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16284     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16285     int id = i;
16286
16287     if (gfx->bitmap == NULL)
16288     {
16289       game_gadget[id] = NULL;
16290
16291       continue;
16292     }
16293
16294     if (id == GAME_CTRL_ID_STOP ||
16295         id == GAME_CTRL_ID_PANEL_STOP ||
16296         id == GAME_CTRL_ID_TOUCH_STOP ||
16297         id == GAME_CTRL_ID_PLAY ||
16298         id == GAME_CTRL_ID_PANEL_PLAY ||
16299         id == GAME_CTRL_ID_SAVE ||
16300         id == GAME_CTRL_ID_LOAD)
16301     {
16302       button_type = GD_TYPE_NORMAL_BUTTON;
16303       checked = FALSE;
16304       event_mask = GD_EVENT_RELEASED;
16305     }
16306     else if (id == GAME_CTRL_ID_UNDO ||
16307              id == GAME_CTRL_ID_REDO)
16308     {
16309       button_type = GD_TYPE_NORMAL_BUTTON;
16310       checked = FALSE;
16311       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16312     }
16313     else
16314     {
16315       button_type = GD_TYPE_CHECK_BUTTON;
16316       checked = (gamebutton_info[i].setup_value != NULL ?
16317                  *gamebutton_info[i].setup_value : FALSE);
16318       event_mask = GD_EVENT_PRESSED;
16319     }
16320
16321     gi = CreateGadget(GDI_CUSTOM_ID, id,
16322                       GDI_IMAGE_ID, graphic,
16323                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16324                       GDI_X, base_x + x,
16325                       GDI_Y, base_y + y,
16326                       GDI_WIDTH, gfx->width,
16327                       GDI_HEIGHT, gfx->height,
16328                       GDI_TYPE, button_type,
16329                       GDI_STATE, GD_BUTTON_UNPRESSED,
16330                       GDI_CHECKED, checked,
16331                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16332                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16333                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16334                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16335                       GDI_DIRECT_DRAW, FALSE,
16336                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16337                       GDI_EVENT_MASK, event_mask,
16338                       GDI_CALLBACK_ACTION, HandleGameButtons,
16339                       GDI_END);
16340
16341     if (gi == NULL)
16342       Fail("cannot create gadget");
16343
16344     game_gadget[id] = gi;
16345   }
16346 }
16347
16348 void FreeGameButtons(void)
16349 {
16350   int i;
16351
16352   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16353     FreeGadget(game_gadget[i]);
16354 }
16355
16356 static void UnmapGameButtonsAtSamePosition(int id)
16357 {
16358   int i;
16359
16360   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16361     if (i != id &&
16362         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16363         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16364       UnmapGadget(game_gadget[i]);
16365 }
16366
16367 static void UnmapGameButtonsAtSamePosition_All(void)
16368 {
16369   if (setup.show_load_save_buttons)
16370   {
16371     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16374   }
16375   else if (setup.show_undo_redo_buttons)
16376   {
16377     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16378     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16379     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16380   }
16381   else
16382   {
16383     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16384     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16385     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16386
16387     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16388     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16389     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16390   }
16391 }
16392
16393 void MapLoadSaveButtons(void)
16394 {
16395   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16396   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16397
16398   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16399   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16400 }
16401
16402 void MapUndoRedoButtons(void)
16403 {
16404   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16405   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16406
16407   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16408   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16409 }
16410
16411 void ModifyPauseButtons(void)
16412 {
16413   static int ids[] =
16414   {
16415     GAME_CTRL_ID_PAUSE,
16416     GAME_CTRL_ID_PAUSE2,
16417     GAME_CTRL_ID_PANEL_PAUSE,
16418     GAME_CTRL_ID_TOUCH_PAUSE,
16419     -1
16420   };
16421   int i;
16422
16423   for (i = 0; ids[i] > -1; i++)
16424     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16425 }
16426
16427 static void MapGameButtonsExt(boolean on_tape)
16428 {
16429   int i;
16430
16431   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16432   {
16433     if ((i == GAME_CTRL_ID_UNDO ||
16434          i == GAME_CTRL_ID_REDO) &&
16435         game_status != GAME_MODE_PLAYING)
16436       continue;
16437
16438     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16439       MapGadget(game_gadget[i]);
16440   }
16441
16442   UnmapGameButtonsAtSamePosition_All();
16443
16444   RedrawGameButtons();
16445 }
16446
16447 static void UnmapGameButtonsExt(boolean on_tape)
16448 {
16449   int i;
16450
16451   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16452     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16453       UnmapGadget(game_gadget[i]);
16454 }
16455
16456 static void RedrawGameButtonsExt(boolean on_tape)
16457 {
16458   int i;
16459
16460   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16461     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16462       RedrawGadget(game_gadget[i]);
16463 }
16464
16465 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16466 {
16467   if (gi == NULL)
16468     return;
16469
16470   gi->checked = state;
16471 }
16472
16473 static void RedrawSoundButtonGadget(int id)
16474 {
16475   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16476              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16477              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16478              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16479              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16480              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16481              id);
16482
16483   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16484   RedrawGadget(game_gadget[id2]);
16485 }
16486
16487 void MapGameButtons(void)
16488 {
16489   MapGameButtonsExt(FALSE);
16490 }
16491
16492 void UnmapGameButtons(void)
16493 {
16494   UnmapGameButtonsExt(FALSE);
16495 }
16496
16497 void RedrawGameButtons(void)
16498 {
16499   RedrawGameButtonsExt(FALSE);
16500 }
16501
16502 void MapGameButtonsOnTape(void)
16503 {
16504   MapGameButtonsExt(TRUE);
16505 }
16506
16507 void UnmapGameButtonsOnTape(void)
16508 {
16509   UnmapGameButtonsExt(TRUE);
16510 }
16511
16512 void RedrawGameButtonsOnTape(void)
16513 {
16514   RedrawGameButtonsExt(TRUE);
16515 }
16516
16517 static void GameUndoRedoExt(void)
16518 {
16519   ClearPlayerAction();
16520
16521   tape.pausing = TRUE;
16522
16523   RedrawPlayfield();
16524   UpdateAndDisplayGameControlValues();
16525
16526   DrawCompleteVideoDisplay();
16527   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16528   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16529   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16530
16531   ModifyPauseButtons();
16532
16533   BackToFront();
16534 }
16535
16536 static void GameUndo(int steps)
16537 {
16538   if (!CheckEngineSnapshotList())
16539     return;
16540
16541   int tape_property_bits = tape.property_bits;
16542
16543   LoadEngineSnapshot_Undo(steps);
16544
16545   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16546
16547   GameUndoRedoExt();
16548 }
16549
16550 static void GameRedo(int steps)
16551 {
16552   if (!CheckEngineSnapshotList())
16553     return;
16554
16555   int tape_property_bits = tape.property_bits;
16556
16557   LoadEngineSnapshot_Redo(steps);
16558
16559   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16560
16561   GameUndoRedoExt();
16562 }
16563
16564 static void HandleGameButtonsExt(int id, int button)
16565 {
16566   static boolean game_undo_executed = FALSE;
16567   int steps = BUTTON_STEPSIZE(button);
16568   boolean handle_game_buttons =
16569     (game_status == GAME_MODE_PLAYING ||
16570      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16571
16572   if (!handle_game_buttons)
16573     return;
16574
16575   switch (id)
16576   {
16577     case GAME_CTRL_ID_STOP:
16578     case GAME_CTRL_ID_PANEL_STOP:
16579     case GAME_CTRL_ID_TOUCH_STOP:
16580       TapeStopGame();
16581
16582       break;
16583
16584     case GAME_CTRL_ID_PAUSE:
16585     case GAME_CTRL_ID_PAUSE2:
16586     case GAME_CTRL_ID_PANEL_PAUSE:
16587     case GAME_CTRL_ID_TOUCH_PAUSE:
16588       if (network.enabled && game_status == GAME_MODE_PLAYING)
16589       {
16590         if (tape.pausing)
16591           SendToServer_ContinuePlaying();
16592         else
16593           SendToServer_PausePlaying();
16594       }
16595       else
16596         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16597
16598       game_undo_executed = FALSE;
16599
16600       break;
16601
16602     case GAME_CTRL_ID_PLAY:
16603     case GAME_CTRL_ID_PANEL_PLAY:
16604       if (game_status == GAME_MODE_MAIN)
16605       {
16606         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16607       }
16608       else if (tape.pausing)
16609       {
16610         if (network.enabled)
16611           SendToServer_ContinuePlaying();
16612         else
16613           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16614       }
16615       break;
16616
16617     case GAME_CTRL_ID_UNDO:
16618       // Important: When using "save snapshot when collecting an item" mode,
16619       // load last (current) snapshot for first "undo" after pressing "pause"
16620       // (else the last-but-one snapshot would be loaded, because the snapshot
16621       // pointer already points to the last snapshot when pressing "pause",
16622       // which is fine for "every step/move" mode, but not for "every collect")
16623       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16624           !game_undo_executed)
16625         steps--;
16626
16627       game_undo_executed = TRUE;
16628
16629       GameUndo(steps);
16630       break;
16631
16632     case GAME_CTRL_ID_REDO:
16633       GameRedo(steps);
16634       break;
16635
16636     case GAME_CTRL_ID_SAVE:
16637       TapeQuickSave();
16638       break;
16639
16640     case GAME_CTRL_ID_LOAD:
16641       TapeQuickLoad();
16642       break;
16643
16644     case SOUND_CTRL_ID_MUSIC:
16645     case SOUND_CTRL_ID_PANEL_MUSIC:
16646       if (setup.sound_music)
16647       { 
16648         setup.sound_music = FALSE;
16649
16650         FadeMusic();
16651       }
16652       else if (audio.music_available)
16653       { 
16654         setup.sound = setup.sound_music = TRUE;
16655
16656         SetAudioMode(setup.sound);
16657
16658         if (game_status == GAME_MODE_PLAYING)
16659           PlayLevelMusic();
16660       }
16661
16662       RedrawSoundButtonGadget(id);
16663
16664       break;
16665
16666     case SOUND_CTRL_ID_LOOPS:
16667     case SOUND_CTRL_ID_PANEL_LOOPS:
16668       if (setup.sound_loops)
16669         setup.sound_loops = FALSE;
16670       else if (audio.loops_available)
16671       {
16672         setup.sound = setup.sound_loops = TRUE;
16673
16674         SetAudioMode(setup.sound);
16675       }
16676
16677       RedrawSoundButtonGadget(id);
16678
16679       break;
16680
16681     case SOUND_CTRL_ID_SIMPLE:
16682     case SOUND_CTRL_ID_PANEL_SIMPLE:
16683       if (setup.sound_simple)
16684         setup.sound_simple = FALSE;
16685       else if (audio.sound_available)
16686       {
16687         setup.sound = setup.sound_simple = TRUE;
16688
16689         SetAudioMode(setup.sound);
16690       }
16691
16692       RedrawSoundButtonGadget(id);
16693
16694       break;
16695
16696     default:
16697       break;
16698   }
16699 }
16700
16701 static void HandleGameButtons(struct GadgetInfo *gi)
16702 {
16703   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16704 }
16705
16706 void HandleSoundButtonKeys(Key key)
16707 {
16708   if (key == setup.shortcut.sound_simple)
16709     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16710   else if (key == setup.shortcut.sound_loops)
16711     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16712   else if (key == setup.shortcut.sound_music)
16713     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16714 }