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