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