efc81e4e56214c58ac33a36004e828df3303d4e9
[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
2032       break;
2033   }
2034
2035   if (!init_game)
2036     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2037 }
2038
2039 static void InitField_WithBug1(int x, int y, boolean init_game)
2040 {
2041   InitField(x, y, init_game);
2042
2043   // not needed to call InitMovDir() -- already done by InitField()!
2044   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2045       CAN_MOVE(Tile[x][y]))
2046     InitMovDir(x, y);
2047 }
2048
2049 static void InitField_WithBug2(int x, int y, boolean init_game)
2050 {
2051   int old_element = Tile[x][y];
2052
2053   InitField(x, y, init_game);
2054
2055   // not needed to call InitMovDir() -- already done by InitField()!
2056   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2057       CAN_MOVE(old_element) &&
2058       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2059     InitMovDir(x, y);
2060
2061   /* this case is in fact a combination of not less than three bugs:
2062      first, it calls InitMovDir() for elements that can move, although this is
2063      already done by InitField(); then, it checks the element that was at this
2064      field _before_ the call to InitField() (which can change it); lastly, it
2065      was not called for "mole with direction" elements, which were treated as
2066      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2067   */
2068 }
2069
2070 static int get_key_element_from_nr(int key_nr)
2071 {
2072   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2073                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2074                           EL_EM_KEY_1 : EL_KEY_1);
2075
2076   return key_base_element + key_nr;
2077 }
2078
2079 static int get_next_dropped_element(struct PlayerInfo *player)
2080 {
2081   return (player->inventory_size > 0 ?
2082           player->inventory_element[player->inventory_size - 1] :
2083           player->inventory_infinite_element != EL_UNDEFINED ?
2084           player->inventory_infinite_element :
2085           player->dynabombs_left > 0 ?
2086           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2087           EL_UNDEFINED);
2088 }
2089
2090 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2091 {
2092   // pos >= 0: get element from bottom of the stack;
2093   // pos <  0: get element from top of the stack
2094
2095   if (pos < 0)
2096   {
2097     int min_inventory_size = -pos;
2098     int inventory_pos = player->inventory_size - min_inventory_size;
2099     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2100
2101     return (player->inventory_size >= min_inventory_size ?
2102             player->inventory_element[inventory_pos] :
2103             player->inventory_infinite_element != EL_UNDEFINED ?
2104             player->inventory_infinite_element :
2105             player->dynabombs_left >= min_dynabombs_left ?
2106             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2107             EL_UNDEFINED);
2108   }
2109   else
2110   {
2111     int min_dynabombs_left = pos + 1;
2112     int min_inventory_size = pos + 1 - player->dynabombs_left;
2113     int inventory_pos = pos - player->dynabombs_left;
2114
2115     return (player->inventory_infinite_element != EL_UNDEFINED ?
2116             player->inventory_infinite_element :
2117             player->dynabombs_left >= min_dynabombs_left ?
2118             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2119             player->inventory_size >= min_inventory_size ?
2120             player->inventory_element[inventory_pos] :
2121             EL_UNDEFINED);
2122   }
2123 }
2124
2125 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2126 {
2127   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2128   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2129   int compare_result;
2130
2131   if (gpo1->sort_priority != gpo2->sort_priority)
2132     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2133   else
2134     compare_result = gpo1->nr - gpo2->nr;
2135
2136   return compare_result;
2137 }
2138
2139 int getPlayerInventorySize(int player_nr)
2140 {
2141   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2142     return game_em.ply[player_nr]->dynamite;
2143   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2144     return game_sp.red_disk_count;
2145   else
2146     return stored_player[player_nr].inventory_size;
2147 }
2148
2149 static void InitGameControlValues(void)
2150 {
2151   int i;
2152
2153   for (i = 0; game_panel_controls[i].nr != -1; i++)
2154   {
2155     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2156     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2157     struct TextPosInfo *pos = gpc->pos;
2158     int nr = gpc->nr;
2159     int type = gpc->type;
2160
2161     if (nr != i)
2162     {
2163       Error("'game_panel_controls' structure corrupted at %d", i);
2164
2165       Fail("this should not happen -- please debug");
2166     }
2167
2168     // force update of game controls after initialization
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     // determine panel value width for later calculation of alignment
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     // fill structure for game panel draw order
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   // sort game panel controls according to sort_priority and control number
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 static void UpdatePlayfieldElementCount(void)
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   // first check if it is needed at all to calculate playfield element count
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Tile[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 static void UpdateGameControlValues(void)
2224 {
2225   int i, k;
2226   int time = (game.LevelSolved ?
2227               game.LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               game_em.lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               game_sp.time_played :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.energy_left :
2234               game.no_time_limit ? TimePlayed : TimeLeft);
2235   int score = (game.LevelSolved ?
2236                game.LevelSolved_CountingScore :
2237                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                game_em.lev->score :
2239                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                game_sp.score :
2241                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                game_mm.score :
2243                game.score);
2244   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->gems_needed :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.infotrons_still_needed :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.kettles_still_needed :
2250               game.gems_still_needed);
2251   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2252                      game_em.lev->gems_needed > 0 :
2253                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2254                      game_sp.infotrons_still_needed > 0 :
2255                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2256                      game_mm.kettles_still_needed > 0 ||
2257                      game_mm.lights_still_needed > 0 :
2258                      game.gems_still_needed > 0 ||
2259                      game.sokoban_fields_still_needed > 0 ||
2260                      game.sokoban_objects_still_needed > 0 ||
2261                      game.lights_still_needed > 0);
2262   int health = (game.LevelSolved ?
2263                 game.LevelSolved_CountingHealth :
2264                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265                 MM_HEALTH(game_mm.laser_overload_value) :
2266                 game.health);
2267   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2268
2269   UpdatePlayfieldElementCount();
2270
2271   // update game panel control values
2272
2273   // used instead of "level_nr" (for network games)
2274   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2275   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2276
2277   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2278   for (i = 0; i < MAX_NUM_KEYS; i++)
2279     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2280   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2281   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2282
2283   if (game.centered_player_nr == -1)
2284   {
2285     for (i = 0; i < MAX_PLAYERS; i++)
2286     {
2287       // only one player in Supaplex game engine
2288       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2289         break;
2290
2291       for (k = 0; k < MAX_NUM_KEYS; k++)
2292       {
2293         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2294         {
2295           if (game_em.ply[i]->keys & (1 << k))
2296             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2297               get_key_element_from_nr(k);
2298         }
2299         else if (stored_player[i].key[k])
2300           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2301             get_key_element_from_nr(k);
2302       }
2303
2304       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305         getPlayerInventorySize(i);
2306
2307       if (stored_player[i].num_white_keys > 0)
2308         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2309           EL_DC_KEY_WHITE;
2310
2311       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312         stored_player[i].num_white_keys;
2313     }
2314   }
2315   else
2316   {
2317     int player_nr = game.centered_player_nr;
2318
2319     for (k = 0; k < MAX_NUM_KEYS; k++)
2320     {
2321       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2322       {
2323         if (game_em.ply[player_nr]->keys & (1 << k))
2324           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325             get_key_element_from_nr(k);
2326       }
2327       else if (stored_player[player_nr].key[k])
2328         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329           get_key_element_from_nr(k);
2330     }
2331
2332     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2333       getPlayerInventorySize(player_nr);
2334
2335     if (stored_player[player_nr].num_white_keys > 0)
2336       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2337
2338     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2339       stored_player[player_nr].num_white_keys;
2340   }
2341
2342   // re-arrange keys on game panel, if needed or if defined by style settings
2343   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2344   {
2345     int nr = GAME_PANEL_KEY_1 + i;
2346     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2347     struct TextPosInfo *pos = gpc->pos;
2348
2349     // skip check if key is not in the player's inventory
2350     if (gpc->value == EL_EMPTY)
2351       continue;
2352
2353     // check if keys should be arranged on panel from left to right
2354     if (pos->style == STYLE_LEFTMOST_POSITION)
2355     {
2356       // check previous key positions (left from current key)
2357       for (k = 0; k < i; k++)
2358       {
2359         int nr_new = GAME_PANEL_KEY_1 + k;
2360
2361         if (game_panel_controls[nr_new].value == EL_EMPTY)
2362         {
2363           game_panel_controls[nr_new].value = gpc->value;
2364           gpc->value = EL_EMPTY;
2365
2366           break;
2367         }
2368       }
2369     }
2370
2371     // check if "undefined" keys can be placed at some other position
2372     if (pos->x == -1 && pos->y == -1)
2373     {
2374       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2375
2376       // 1st try: display key at the same position as normal or EM keys
2377       if (game_panel_controls[nr_new].value == EL_EMPTY)
2378       {
2379         game_panel_controls[nr_new].value = gpc->value;
2380       }
2381       else
2382       {
2383         // 2nd try: display key at the next free position in the key panel
2384         for (k = 0; k < STD_NUM_KEYS; k++)
2385         {
2386           nr_new = GAME_PANEL_KEY_1 + k;
2387
2388           if (game_panel_controls[nr_new].value == EL_EMPTY)
2389           {
2390             game_panel_controls[nr_new].value = gpc->value;
2391
2392             break;
2393           }
2394         }
2395       }
2396     }
2397   }
2398
2399   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2400   {
2401     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2402       get_inventory_element_from_pos(local_player, i);
2403     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2404       get_inventory_element_from_pos(local_player, -i - 1);
2405   }
2406
2407   game_panel_controls[GAME_PANEL_SCORE].value = score;
2408   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2409
2410   game_panel_controls[GAME_PANEL_TIME].value = time;
2411
2412   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2413   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2414   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2415
2416   if (level.time == 0)
2417     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2418   else
2419     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2420
2421   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2422   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2423
2424   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2425
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2427     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2428      EL_EMPTY);
2429   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2430     local_player->shield_normal_time_left;
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2432     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2433      EL_EMPTY);
2434   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2435     local_player->shield_deadly_time_left;
2436
2437   game_panel_controls[GAME_PANEL_EXIT].value =
2438     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2439
2440   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2441     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2442   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2443     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2444      EL_EMC_MAGIC_BALL_SWITCH);
2445
2446   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2447     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2448   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2449     game.light_time_left;
2450
2451   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2452     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2453   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2454     game.timegate_time_left;
2455
2456   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2457     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2458
2459   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2460     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2461   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2462     game.lenses_time_left;
2463
2464   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2465     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2466   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2467     game.magnify_time_left;
2468
2469   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2470     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2471      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2472      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2473      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2474      EL_BALLOON_SWITCH_NONE);
2475
2476   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2477     local_player->dynabomb_count;
2478   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2479     local_player->dynabomb_size;
2480   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2481     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2482
2483   game_panel_controls[GAME_PANEL_PENGUINS].value =
2484     game.friends_still_needed;
2485
2486   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2487     game.sokoban_objects_still_needed;
2488   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2489     game.sokoban_fields_still_needed;
2490
2491   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2492     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2493
2494   for (i = 0; i < NUM_BELTS; i++)
2495   {
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2497       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2498        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2499     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2500       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2501   }
2502
2503   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2504     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2505   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2506     game.magic_wall_time_left;
2507
2508   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2509     local_player->gravity;
2510
2511   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2512     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2513
2514   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2515     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2516       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2517        game.panel.element[i].id : EL_UNDEFINED);
2518
2519   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2520     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2521       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2522        element_info[game.panel.element_count[i].id].element_count : 0);
2523
2524   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2525     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2526       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2527        element_info[game.panel.ce_score[i].id].collect_score : 0);
2528
2529   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2530     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2531       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2532        element_info[game.panel.ce_score_element[i].id].collect_score :
2533        EL_UNDEFINED);
2534
2535   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2536   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2537   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2538
2539   // update game panel control frames
2540
2541   for (i = 0; game_panel_controls[i].nr != -1; i++)
2542   {
2543     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2544
2545     if (gpc->type == TYPE_ELEMENT)
2546     {
2547       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2548       {
2549         int last_anim_random_frame = gfx.anim_random_frame;
2550         int element = gpc->value;
2551         int graphic = el2panelimg(element);
2552         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2553                                sync_random_frame : INIT_GFX_RANDOM());
2554
2555         if (gpc->value != gpc->last_value)
2556         {
2557           gpc->gfx_frame = 0;
2558           gpc->gfx_random = init_gfx_random;
2559         }
2560         else
2561         {
2562           gpc->gfx_frame++;
2563
2564           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2565               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2566             gpc->gfx_random = init_gfx_random;
2567         }
2568
2569         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2570           gfx.anim_random_frame = gpc->gfx_random;
2571
2572         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2573           gpc->gfx_frame = element_info[element].collect_score;
2574
2575         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = last_anim_random_frame;
2579       }
2580     }
2581     else if (gpc->type == TYPE_GRAPHIC)
2582     {
2583       if (gpc->graphic != IMG_UNDEFINED)
2584       {
2585         int last_anim_random_frame = gfx.anim_random_frame;
2586         int graphic = gpc->graphic;
2587         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2588                                sync_random_frame : INIT_GFX_RANDOM());
2589
2590         if (gpc->value != gpc->last_value)
2591         {
2592           gpc->gfx_frame = 0;
2593           gpc->gfx_random = init_gfx_random;
2594         }
2595         else
2596         {
2597           gpc->gfx_frame++;
2598
2599           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2600               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2601             gpc->gfx_random = init_gfx_random;
2602         }
2603
2604         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2605           gfx.anim_random_frame = gpc->gfx_random;
2606
2607         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2608
2609         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2610           gfx.anim_random_frame = last_anim_random_frame;
2611       }
2612     }
2613   }
2614 }
2615
2616 static void DisplayGameControlValues(void)
2617 {
2618   boolean redraw_panel = FALSE;
2619   int i;
2620
2621   for (i = 0; game_panel_controls[i].nr != -1; i++)
2622   {
2623     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2624
2625     if (PANEL_DEACTIVATED(gpc->pos))
2626       continue;
2627
2628     if (gpc->value == gpc->last_value &&
2629         gpc->frame == gpc->last_frame)
2630       continue;
2631
2632     redraw_panel = TRUE;
2633   }
2634
2635   if (!redraw_panel)
2636     return;
2637
2638   // copy default game door content to main double buffer
2639
2640   // !!! CHECK AGAIN !!!
2641   SetPanelBackground();
2642   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2643   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2644
2645   // redraw game control buttons
2646   RedrawGameButtons();
2647
2648   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2649
2650   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2651   {
2652     int nr = game_panel_order[i].nr;
2653     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2654     struct TextPosInfo *pos = gpc->pos;
2655     int type = gpc->type;
2656     int value = gpc->value;
2657     int frame = gpc->frame;
2658     int size = pos->size;
2659     int font = pos->font;
2660     boolean draw_masked = pos->draw_masked;
2661     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2662
2663     if (PANEL_DEACTIVATED(pos))
2664       continue;
2665
2666     if (pos->class == get_hash_from_key("extra_panel_items") &&
2667         !setup.prefer_extra_panel_items)
2668       continue;
2669
2670     gpc->last_value = value;
2671     gpc->last_frame = frame;
2672
2673     if (type == TYPE_INTEGER)
2674     {
2675       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2676           nr == GAME_PANEL_TIME)
2677       {
2678         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2679
2680         if (use_dynamic_size)           // use dynamic number of digits
2681         {
2682           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2683           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2684           int size2 = size1 + 1;
2685           int font1 = pos->font;
2686           int font2 = pos->font_alt;
2687
2688           size = (value < value_change ? size1 : size2);
2689           font = (value < value_change ? font1 : font2);
2690         }
2691       }
2692
2693       // correct text size if "digits" is zero or less
2694       if (size <= 0)
2695         size = strlen(int2str(value, size));
2696
2697       // dynamically correct text alignment
2698       pos->width = size * getFontWidth(font);
2699
2700       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701                   int2str(value, size), font, mask_mode);
2702     }
2703     else if (type == TYPE_ELEMENT)
2704     {
2705       int element, graphic;
2706       Bitmap *src_bitmap;
2707       int src_x, src_y;
2708       int width, height;
2709       int dst_x = PANEL_XPOS(pos);
2710       int dst_y = PANEL_YPOS(pos);
2711
2712       if (value != EL_UNDEFINED && value != EL_EMPTY)
2713       {
2714         element = value;
2715         graphic = el2panelimg(value);
2716
2717 #if 0
2718         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2719               element, EL_NAME(element), size);
2720 #endif
2721
2722         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2723           size = TILESIZE;
2724
2725         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2726                               &src_x, &src_y);
2727
2728         width  = graphic_info[graphic].width  * size / TILESIZE;
2729         height = graphic_info[graphic].height * size / TILESIZE;
2730
2731         if (draw_masked)
2732           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2733                            dst_x, dst_y);
2734         else
2735           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2736                      dst_x, dst_y);
2737       }
2738     }
2739     else if (type == TYPE_GRAPHIC)
2740     {
2741       int graphic        = gpc->graphic;
2742       int graphic_active = gpc->graphic_active;
2743       Bitmap *src_bitmap;
2744       int src_x, src_y;
2745       int width, height;
2746       int dst_x = PANEL_XPOS(pos);
2747       int dst_y = PANEL_YPOS(pos);
2748       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2749                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2750
2751       if (graphic != IMG_UNDEFINED && !skip)
2752       {
2753         if (pos->style == STYLE_REVERSE)
2754           value = 100 - value;
2755
2756         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2757
2758         if (pos->direction & MV_HORIZONTAL)
2759         {
2760           width  = graphic_info[graphic_active].width * value / 100;
2761           height = graphic_info[graphic_active].height;
2762
2763           if (pos->direction == MV_LEFT)
2764           {
2765             src_x += graphic_info[graphic_active].width - width;
2766             dst_x += graphic_info[graphic_active].width - width;
2767           }
2768         }
2769         else
2770         {
2771           width  = graphic_info[graphic_active].width;
2772           height = graphic_info[graphic_active].height * value / 100;
2773
2774           if (pos->direction == MV_UP)
2775           {
2776             src_y += graphic_info[graphic_active].height - height;
2777             dst_y += graphic_info[graphic_active].height - height;
2778           }
2779         }
2780
2781         if (draw_masked)
2782           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2783                            dst_x, dst_y);
2784         else
2785           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2786                      dst_x, dst_y);
2787
2788         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2789
2790         if (pos->direction & MV_HORIZONTAL)
2791         {
2792           if (pos->direction == MV_RIGHT)
2793           {
2794             src_x += width;
2795             dst_x += width;
2796           }
2797           else
2798           {
2799             dst_x = PANEL_XPOS(pos);
2800           }
2801
2802           width = graphic_info[graphic].width - width;
2803         }
2804         else
2805         {
2806           if (pos->direction == MV_DOWN)
2807           {
2808             src_y += height;
2809             dst_y += height;
2810           }
2811           else
2812           {
2813             dst_y = PANEL_YPOS(pos);
2814           }
2815
2816           height = graphic_info[graphic].height - height;
2817         }
2818
2819         if (draw_masked)
2820           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2821                            dst_x, dst_y);
2822         else
2823           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2824                      dst_x, dst_y);
2825       }
2826     }
2827     else if (type == TYPE_STRING)
2828     {
2829       boolean active = (value != 0);
2830       char *state_normal = "off";
2831       char *state_active = "on";
2832       char *state = (active ? state_active : state_normal);
2833       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2834                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2835                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2836                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2837
2838       if (nr == GAME_PANEL_GRAVITY_STATE)
2839       {
2840         int font1 = pos->font;          // (used for normal state)
2841         int font2 = pos->font_alt;      // (used for active state)
2842
2843         font = (active ? font2 : font1);
2844       }
2845
2846       if (s != NULL)
2847       {
2848         char *s_cut;
2849
2850         if (size <= 0)
2851         {
2852           // don't truncate output if "chars" is zero or less
2853           size = strlen(s);
2854
2855           // dynamically correct text alignment
2856           pos->width = size * getFontWidth(font);
2857         }
2858
2859         s_cut = getStringCopyN(s, size);
2860
2861         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2862                     s_cut, font, mask_mode);
2863
2864         free(s_cut);
2865       }
2866     }
2867
2868     redraw_mask |= REDRAW_DOOR_1;
2869   }
2870
2871   SetGameStatus(GAME_MODE_PLAYING);
2872 }
2873
2874 void UpdateAndDisplayGameControlValues(void)
2875 {
2876   if (tape.deactivate_display)
2877     return;
2878
2879   UpdateGameControlValues();
2880   DisplayGameControlValues();
2881 }
2882
2883 void UpdateGameDoorValues(void)
2884 {
2885   UpdateGameControlValues();
2886 }
2887
2888 void DrawGameDoorValues(void)
2889 {
2890   DisplayGameControlValues();
2891 }
2892
2893
2894 // ============================================================================
2895 // InitGameEngine()
2896 // ----------------------------------------------------------------------------
2897 // initialize game engine due to level / tape version number
2898 // ============================================================================
2899
2900 static void InitGameEngine(void)
2901 {
2902   int i, j, k, l, x, y;
2903
2904   // set game engine from tape file when re-playing, else from level file
2905   game.engine_version = (tape.playing ? tape.engine_version :
2906                          level.game_version);
2907
2908   // set single or multi-player game mode (needed for re-playing tapes)
2909   game.team_mode = setup.team_mode;
2910
2911   if (tape.playing)
2912   {
2913     int num_players = 0;
2914
2915     for (i = 0; i < MAX_PLAYERS; i++)
2916       if (tape.player_participates[i])
2917         num_players++;
2918
2919     // multi-player tapes contain input data for more than one player
2920     game.team_mode = (num_players > 1);
2921   }
2922
2923 #if 0
2924   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2925         level.game_version);
2926   Debug("game:init:level", "          tape.file_version   == %06d",
2927         tape.file_version);
2928   Debug("game:init:level", "          tape.game_version   == %06d",
2929         tape.game_version);
2930   Debug("game:init:level", "          tape.engine_version == %06d",
2931         tape.engine_version);
2932   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2933         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2934 #endif
2935
2936   // --------------------------------------------------------------------------
2937   // set flags for bugs and changes according to active game engine version
2938   // --------------------------------------------------------------------------
2939
2940   /*
2941     Summary of bugfix:
2942     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2943
2944     Bug was introduced in version:
2945     2.0.1
2946
2947     Bug was fixed in version:
2948     4.2.0.0
2949
2950     Description:
2951     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2952     but the property "can fall" was missing, which caused some levels to be
2953     unsolvable. This was fixed in version 4.2.0.0.
2954
2955     Affected levels/tapes:
2956     An example for a tape that was fixed by this bugfix is tape 029 from the
2957     level set "rnd_sam_bateman".
2958     The wrong behaviour will still be used for all levels or tapes that were
2959     created/recorded with it. An example for this is tape 023 from the level
2960     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2961   */
2962
2963   boolean use_amoeba_dropping_cannot_fall_bug =
2964     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2965       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2966      (tape.playing &&
2967       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2968       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2969
2970   /*
2971     Summary of bugfix/change:
2972     Fixed move speed of elements entering or leaving magic wall.
2973
2974     Fixed/changed in version:
2975     2.0.1
2976
2977     Description:
2978     Before 2.0.1, move speed of elements entering or leaving magic wall was
2979     twice as fast as it is now.
2980     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2981
2982     Affected levels/tapes:
2983     The first condition is generally needed for all levels/tapes before version
2984     2.0.1, which might use the old behaviour before it was changed; known tapes
2985     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2986     The second condition is an exception from the above case and is needed for
2987     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2988     above, but before it was known that this change would break tapes like the
2989     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2990     although the engine version while recording maybe was before 2.0.1. There
2991     are a lot of tapes that are affected by this exception, like tape 006 from
2992     the level set "rnd_conor_mancone".
2993   */
2994
2995   boolean use_old_move_stepsize_for_magic_wall =
2996     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2997      !(tape.playing &&
2998        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2999        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3000
3001   /*
3002     Summary of bugfix/change:
3003     Fixed handling for custom elements that change when pushed by the player.
3004
3005     Fixed/changed in version:
3006     3.1.0
3007
3008     Description:
3009     Before 3.1.0, custom elements that "change when pushing" changed directly
3010     after the player started pushing them (until then handled in "DigField()").
3011     Since 3.1.0, these custom elements are not changed until the "pushing"
3012     move of the element is finished (now handled in "ContinueMoving()").
3013
3014     Affected levels/tapes:
3015     The first condition is generally needed for all levels/tapes before version
3016     3.1.0, which might use the old behaviour before it was changed; known tapes
3017     that are affected are some tapes from the level set "Walpurgis Gardens" by
3018     Jamie Cullen.
3019     The second condition is an exception from the above case and is needed for
3020     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3021     above (including some development versions of 3.1.0), but before it was
3022     known that this change would break tapes like the above and was fixed in
3023     3.1.1, so that the changed behaviour was active although the engine version
3024     while recording maybe was before 3.1.0. There is at least one tape that is
3025     affected by this exception, which is the tape for the one-level set "Bug
3026     Machine" by Juergen Bonhagen.
3027   */
3028
3029   game.use_change_when_pushing_bug =
3030     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3031      !(tape.playing &&
3032        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3033        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3034
3035   /*
3036     Summary of bugfix/change:
3037     Fixed handling for blocking the field the player leaves when moving.
3038
3039     Fixed/changed in version:
3040     3.1.1
3041
3042     Description:
3043     Before 3.1.1, when "block last field when moving" was enabled, the field
3044     the player is leaving when moving was blocked for the time of the move,
3045     and was directly unblocked afterwards. This resulted in the last field
3046     being blocked for exactly one less than the number of frames of one player
3047     move. Additionally, even when blocking was disabled, the last field was
3048     blocked for exactly one frame.
3049     Since 3.1.1, due to changes in player movement handling, the last field
3050     is not blocked at all when blocking is disabled. When blocking is enabled,
3051     the last field is blocked for exactly the number of frames of one player
3052     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3053     last field is blocked for exactly one more than the number of frames of
3054     one player move.
3055
3056     Affected levels/tapes:
3057     (!!! yet to be determined -- probably many !!!)
3058   */
3059
3060   game.use_block_last_field_bug =
3061     (game.engine_version < VERSION_IDENT(3,1,1,0));
3062
3063   /* various special flags and settings for native Emerald Mine game engine */
3064
3065   game_em.use_single_button =
3066     (game.engine_version > VERSION_IDENT(4,0,0,2));
3067
3068   game_em.use_snap_key_bug =
3069     (game.engine_version < VERSION_IDENT(4,0,1,0));
3070
3071   game_em.use_random_bug =
3072     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3073
3074   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3075
3076   game_em.use_old_explosions            = use_old_em_engine;
3077   game_em.use_old_android               = use_old_em_engine;
3078   game_em.use_old_push_elements         = use_old_em_engine;
3079   game_em.use_old_push_into_acid        = use_old_em_engine;
3080
3081   game_em.use_wrap_around               = !use_old_em_engine;
3082
3083   // --------------------------------------------------------------------------
3084
3085   // set maximal allowed number of custom element changes per game frame
3086   game.max_num_changes_per_frame = 1;
3087
3088   // default scan direction: scan playfield from top/left to bottom/right
3089   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3090
3091   // dynamically adjust element properties according to game engine version
3092   InitElementPropertiesEngine(game.engine_version);
3093
3094   // ---------- initialize special element properties -------------------------
3095
3096   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3097   if (use_amoeba_dropping_cannot_fall_bug)
3098     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3099
3100   // ---------- initialize player's initial move delay ------------------------
3101
3102   // dynamically adjust player properties according to level information
3103   for (i = 0; i < MAX_PLAYERS; i++)
3104     game.initial_move_delay_value[i] =
3105       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3106
3107   // dynamically adjust player properties according to game engine version
3108   for (i = 0; i < MAX_PLAYERS; i++)
3109     game.initial_move_delay[i] =
3110       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3111        game.initial_move_delay_value[i] : 0);
3112
3113   // ---------- initialize player's initial push delay ------------------------
3114
3115   // dynamically adjust player properties according to game engine version
3116   game.initial_push_delay_value =
3117     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3118
3119   // ---------- initialize changing elements ----------------------------------
3120
3121   // initialize changing elements information
3122   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123   {
3124     struct ElementInfo *ei = &element_info[i];
3125
3126     // this pointer might have been changed in the level editor
3127     ei->change = &ei->change_page[0];
3128
3129     if (!IS_CUSTOM_ELEMENT(i))
3130     {
3131       ei->change->target_element = EL_EMPTY_SPACE;
3132       ei->change->delay_fixed = 0;
3133       ei->change->delay_random = 0;
3134       ei->change->delay_frames = 1;
3135     }
3136
3137     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3138     {
3139       ei->has_change_event[j] = FALSE;
3140
3141       ei->event_page_nr[j] = 0;
3142       ei->event_page[j] = &ei->change_page[0];
3143     }
3144   }
3145
3146   // add changing elements from pre-defined list
3147   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3150     struct ElementInfo *ei = &element_info[ch_delay->element];
3151
3152     ei->change->target_element       = ch_delay->target_element;
3153     ei->change->delay_fixed          = ch_delay->change_delay;
3154
3155     ei->change->pre_change_function  = ch_delay->pre_change_function;
3156     ei->change->change_function      = ch_delay->change_function;
3157     ei->change->post_change_function = ch_delay->post_change_function;
3158
3159     ei->change->can_change = TRUE;
3160     ei->change->can_change_or_has_action = TRUE;
3161
3162     ei->has_change_event[CE_DELAY] = TRUE;
3163
3164     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3165     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3166   }
3167
3168   // ---------- initialize internal run-time variables ------------------------
3169
3170   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3171   {
3172     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3173
3174     for (j = 0; j < ei->num_change_pages; j++)
3175     {
3176       ei->change_page[j].can_change_or_has_action =
3177         (ei->change_page[j].can_change |
3178          ei->change_page[j].has_action);
3179     }
3180   }
3181
3182   // add change events from custom element configuration
3183   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3184   {
3185     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3186
3187     for (j = 0; j < ei->num_change_pages; j++)
3188     {
3189       if (!ei->change_page[j].can_change_or_has_action)
3190         continue;
3191
3192       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3193       {
3194         // only add event page for the first page found with this event
3195         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3196         {
3197           ei->has_change_event[k] = TRUE;
3198
3199           ei->event_page_nr[k] = j;
3200           ei->event_page[k] = &ei->change_page[j];
3201         }
3202       }
3203     }
3204   }
3205
3206   // ---------- initialize reference elements in change conditions ------------
3207
3208   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3209   {
3210     int element = EL_CUSTOM_START + i;
3211     struct ElementInfo *ei = &element_info[element];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       int trigger_element = ei->change_page[j].initial_trigger_element;
3216
3217       if (trigger_element >= EL_PREV_CE_8 &&
3218           trigger_element <= EL_NEXT_CE_8)
3219         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3220
3221       ei->change_page[j].trigger_element = trigger_element;
3222     }
3223   }
3224
3225   // ---------- initialize run-time trigger player and element ----------------
3226
3227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3228   {
3229     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3234       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3235       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3236       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3237       ei->change_page[j].actual_trigger_ce_value = 0;
3238       ei->change_page[j].actual_trigger_ce_score = 0;
3239     }
3240   }
3241
3242   // ---------- initialize trigger events -------------------------------------
3243
3244   // initialize trigger events information
3245   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3246     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3247       trigger_events[i][j] = FALSE;
3248
3249   // add trigger events from element change event properties
3250   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3251   {
3252     struct ElementInfo *ei = &element_info[i];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       if (!ei->change_page[j].can_change_or_has_action)
3257         continue;
3258
3259       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3260       {
3261         int trigger_element = ei->change_page[j].trigger_element;
3262
3263         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3264         {
3265           if (ei->change_page[j].has_event[k])
3266           {
3267             if (IS_GROUP_ELEMENT(trigger_element))
3268             {
3269               struct ElementGroupInfo *group =
3270                 element_info[trigger_element].group;
3271
3272               for (l = 0; l < group->num_elements_resolved; l++)
3273                 trigger_events[group->element_resolved[l]][k] = TRUE;
3274             }
3275             else if (trigger_element == EL_ANY_ELEMENT)
3276               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3277                 trigger_events[l][k] = TRUE;
3278             else
3279               trigger_events[trigger_element][k] = TRUE;
3280           }
3281         }
3282       }
3283     }
3284   }
3285
3286   // ---------- initialize push delay -----------------------------------------
3287
3288   // initialize push delay values to default
3289   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3290   {
3291     if (!IS_CUSTOM_ELEMENT(i))
3292     {
3293       // set default push delay values (corrected since version 3.0.7-1)
3294       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3295       {
3296         element_info[i].push_delay_fixed = 2;
3297         element_info[i].push_delay_random = 8;
3298       }
3299       else
3300       {
3301         element_info[i].push_delay_fixed = 8;
3302         element_info[i].push_delay_random = 8;
3303       }
3304     }
3305   }
3306
3307   // set push delay value for certain elements from pre-defined list
3308   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3309   {
3310     int e = push_delay_list[i].element;
3311
3312     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3313     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3314   }
3315
3316   // set push delay value for Supaplex elements for newer engine versions
3317   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3318   {
3319     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320     {
3321       if (IS_SP_ELEMENT(i))
3322       {
3323         // set SP push delay to just enough to push under a falling zonk
3324         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3325
3326         element_info[i].push_delay_fixed  = delay;
3327         element_info[i].push_delay_random = 0;
3328       }
3329     }
3330   }
3331
3332   // ---------- initialize move stepsize --------------------------------------
3333
3334   // initialize move stepsize values to default
3335   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3336     if (!IS_CUSTOM_ELEMENT(i))
3337       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3338
3339   // set move stepsize value for certain elements from pre-defined list
3340   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3341   {
3342     int e = move_stepsize_list[i].element;
3343
3344     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3345
3346     // set move stepsize value for certain elements for older engine versions
3347     if (use_old_move_stepsize_for_magic_wall)
3348     {
3349       if (e == EL_MAGIC_WALL_FILLING ||
3350           e == EL_MAGIC_WALL_EMPTYING ||
3351           e == EL_BD_MAGIC_WALL_FILLING ||
3352           e == EL_BD_MAGIC_WALL_EMPTYING)
3353         element_info[e].move_stepsize *= 2;
3354     }
3355   }
3356
3357   // ---------- initialize collect score --------------------------------------
3358
3359   // initialize collect score values for custom elements from initial value
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361     if (IS_CUSTOM_ELEMENT(i))
3362       element_info[i].collect_score = element_info[i].collect_score_initial;
3363
3364   // ---------- initialize collect count --------------------------------------
3365
3366   // initialize collect count values for non-custom elements
3367   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3368     if (!IS_CUSTOM_ELEMENT(i))
3369       element_info[i].collect_count_initial = 0;
3370
3371   // add collect count values for all elements from pre-defined list
3372   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3373     element_info[collect_count_list[i].element].collect_count_initial =
3374       collect_count_list[i].count;
3375
3376   // ---------- initialize access direction -----------------------------------
3377
3378   // initialize access direction values to default (access from every side)
3379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3380     if (!IS_CUSTOM_ELEMENT(i))
3381       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3382
3383   // set access direction value for certain elements from pre-defined list
3384   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3385     element_info[access_direction_list[i].element].access_direction =
3386       access_direction_list[i].direction;
3387
3388   // ---------- initialize explosion content ----------------------------------
3389   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3390   {
3391     if (IS_CUSTOM_ELEMENT(i))
3392       continue;
3393
3394     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3395     {
3396       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3397
3398       element_info[i].content.e[x][y] =
3399         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3400          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3401          i == EL_PLAYER_3 ? EL_EMERALD :
3402          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3403          i == EL_MOLE ? EL_EMERALD_RED :
3404          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3405          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3406          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3407          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3408          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3409          i == EL_WALL_EMERALD ? EL_EMERALD :
3410          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3411          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3412          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3413          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3414          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3415          i == EL_WALL_PEARL ? EL_PEARL :
3416          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3417          EL_EMPTY);
3418     }
3419   }
3420
3421   // ---------- initialize recursion detection --------------------------------
3422   recursion_loop_depth = 0;
3423   recursion_loop_detected = FALSE;
3424   recursion_loop_element = EL_UNDEFINED;
3425
3426   // ---------- initialize graphics engine ------------------------------------
3427   game.scroll_delay_value =
3428     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3429      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3430      !setup.forced_scroll_delay           ? 0 :
3431      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3432   game.scroll_delay_value =
3433     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3434
3435   // ---------- initialize game engine snapshots ------------------------------
3436   for (i = 0; i < MAX_PLAYERS; i++)
3437     game.snapshot.last_action[i] = 0;
3438   game.snapshot.changed_action = FALSE;
3439   game.snapshot.collected_item = FALSE;
3440   game.snapshot.mode =
3441     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3442      SNAPSHOT_MODE_EVERY_STEP :
3443      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3444      SNAPSHOT_MODE_EVERY_MOVE :
3445      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3446      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3447   game.snapshot.save_snapshot = FALSE;
3448
3449   // ---------- initialize level time for Supaplex engine ---------------------
3450   // Supaplex levels with time limit currently unsupported -- should be added
3451   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3452     level.time = 0;
3453
3454   // ---------- initialize flags for handling game actions --------------------
3455
3456   // set flags for game actions to default values
3457   game.use_key_actions = TRUE;
3458   game.use_mouse_actions = FALSE;
3459
3460   // when using Mirror Magic game engine, handle mouse events only
3461   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3462   {
3463     game.use_key_actions = FALSE;
3464     game.use_mouse_actions = TRUE;
3465   }
3466
3467   // check for custom elements with mouse click events
3468   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3469   {
3470     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3471     {
3472       int element = EL_CUSTOM_START + i;
3473
3474       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3475           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3476           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3477           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3478         game.use_mouse_actions = TRUE;
3479     }
3480   }
3481 }
3482
3483 static int get_num_special_action(int element, int action_first,
3484                                   int action_last)
3485 {
3486   int num_special_action = 0;
3487   int i, j;
3488
3489   for (i = action_first; i <= action_last; i++)
3490   {
3491     boolean found = FALSE;
3492
3493     for (j = 0; j < NUM_DIRECTIONS; j++)
3494       if (el_act_dir2img(element, i, j) !=
3495           el_act_dir2img(element, ACTION_DEFAULT, j))
3496         found = TRUE;
3497
3498     if (found)
3499       num_special_action++;
3500     else
3501       break;
3502   }
3503
3504   return num_special_action;
3505 }
3506
3507
3508 // ============================================================================
3509 // InitGame()
3510 // ----------------------------------------------------------------------------
3511 // initialize and start new game
3512 // ============================================================================
3513
3514 #if DEBUG_INIT_PLAYER
3515 static void DebugPrintPlayerStatus(char *message)
3516 {
3517   int i;
3518
3519   if (!options.debug)
3520     return;
3521
3522   Debug("game:init:player", "%s:", message);
3523
3524   for (i = 0; i < MAX_PLAYERS; i++)
3525   {
3526     struct PlayerInfo *player = &stored_player[i];
3527
3528     Debug("game:init:player",
3529           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3530           i + 1,
3531           player->present,
3532           player->connected,
3533           player->connected_locally,
3534           player->connected_network,
3535           player->active,
3536           (local_player == player ? " (local player)" : ""));
3537   }
3538 }
3539 #endif
3540
3541 void InitGame(void)
3542 {
3543   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3544   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3545   int fade_mask = REDRAW_FIELD;
3546
3547   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3548   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3549   int initial_move_dir = MV_DOWN;
3550   int i, j, x, y;
3551
3552   // required here to update video display before fading (FIX THIS)
3553   DrawMaskedBorder(REDRAW_DOOR_2);
3554
3555   if (!game.restart_level)
3556     CloseDoor(DOOR_CLOSE_1);
3557
3558   SetGameStatus(GAME_MODE_PLAYING);
3559
3560   if (level_editor_test_game)
3561     FadeSkipNextFadeOut();
3562   else
3563     FadeSetEnterScreen();
3564
3565   if (CheckFadeAll())
3566     fade_mask = REDRAW_ALL;
3567
3568   FadeLevelSoundsAndMusic();
3569
3570   ExpireSoundLoops(TRUE);
3571
3572   FadeOut(fade_mask);
3573
3574   if (level_editor_test_game)
3575     FadeSkipNextFadeIn();
3576
3577   // needed if different viewport properties defined for playing
3578   ChangeViewportPropertiesIfNeeded();
3579
3580   ClearField();
3581
3582   DrawCompleteVideoDisplay();
3583
3584   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3585
3586   InitGameEngine();
3587   InitGameControlValues();
3588
3589   if (tape.recording)
3590   {
3591     // initialize tape actions from game when recording tape
3592     tape.use_key_actions   = game.use_key_actions;
3593     tape.use_mouse_actions = game.use_mouse_actions;
3594
3595     // initialize visible playfield size when recording tape (for team mode)
3596     tape.scr_fieldx = SCR_FIELDX;
3597     tape.scr_fieldy = SCR_FIELDY;
3598   }
3599
3600   // don't play tapes over network
3601   network_playing = (network.enabled && !tape.playing);
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     player->index_nr = i;
3608     player->index_bit = (1 << i);
3609     player->element_nr = EL_PLAYER_1 + i;
3610
3611     player->present = FALSE;
3612     player->active = FALSE;
3613     player->mapped = FALSE;
3614
3615     player->killed = FALSE;
3616     player->reanimated = FALSE;
3617     player->buried = FALSE;
3618
3619     player->action = 0;
3620     player->effective_action = 0;
3621     player->programmed_action = 0;
3622     player->snap_action = 0;
3623
3624     player->mouse_action.lx = 0;
3625     player->mouse_action.ly = 0;
3626     player->mouse_action.button = 0;
3627     player->mouse_action.button_hint = 0;
3628
3629     player->effective_mouse_action.lx = 0;
3630     player->effective_mouse_action.ly = 0;
3631     player->effective_mouse_action.button = 0;
3632     player->effective_mouse_action.button_hint = 0;
3633
3634     for (j = 0; j < MAX_NUM_KEYS; j++)
3635       player->key[j] = FALSE;
3636
3637     player->num_white_keys = 0;
3638
3639     player->dynabomb_count = 0;
3640     player->dynabomb_size = 1;
3641     player->dynabombs_left = 0;
3642     player->dynabomb_xl = FALSE;
3643
3644     player->MovDir = initial_move_dir;
3645     player->MovPos = 0;
3646     player->GfxPos = 0;
3647     player->GfxDir = initial_move_dir;
3648     player->GfxAction = ACTION_DEFAULT;
3649     player->Frame = 0;
3650     player->StepFrame = 0;
3651
3652     player->initial_element = player->element_nr;
3653     player->artwork_element =
3654       (level.use_artwork_element[i] ? level.artwork_element[i] :
3655        player->element_nr);
3656     player->use_murphy = FALSE;
3657
3658     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3659     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3660
3661     player->gravity = level.initial_player_gravity[i];
3662
3663     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3664
3665     player->actual_frame_counter = 0;
3666
3667     player->step_counter = 0;
3668
3669     player->last_move_dir = initial_move_dir;
3670
3671     player->is_active = FALSE;
3672
3673     player->is_waiting = FALSE;
3674     player->is_moving = FALSE;
3675     player->is_auto_moving = FALSE;
3676     player->is_digging = FALSE;
3677     player->is_snapping = FALSE;
3678     player->is_collecting = FALSE;
3679     player->is_pushing = FALSE;
3680     player->is_switching = FALSE;
3681     player->is_dropping = FALSE;
3682     player->is_dropping_pressed = FALSE;
3683
3684     player->is_bored = FALSE;
3685     player->is_sleeping = FALSE;
3686
3687     player->was_waiting = TRUE;
3688     player->was_moving = FALSE;
3689     player->was_snapping = FALSE;
3690     player->was_dropping = FALSE;
3691
3692     player->force_dropping = FALSE;
3693
3694     player->frame_counter_bored = -1;
3695     player->frame_counter_sleeping = -1;
3696
3697     player->anim_delay_counter = 0;
3698     player->post_delay_counter = 0;
3699
3700     player->dir_waiting = initial_move_dir;
3701     player->action_waiting = ACTION_DEFAULT;
3702     player->last_action_waiting = ACTION_DEFAULT;
3703     player->special_action_bored = ACTION_DEFAULT;
3704     player->special_action_sleeping = ACTION_DEFAULT;
3705
3706     player->switch_x = -1;
3707     player->switch_y = -1;
3708
3709     player->drop_x = -1;
3710     player->drop_y = -1;
3711
3712     player->show_envelope = 0;
3713
3714     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3715
3716     player->push_delay       = -1;      // initialized when pushing starts
3717     player->push_delay_value = game.initial_push_delay_value;
3718
3719     player->drop_delay = 0;
3720     player->drop_pressed_delay = 0;
3721
3722     player->last_jx = -1;
3723     player->last_jy = -1;
3724     player->jx = -1;
3725     player->jy = -1;
3726
3727     player->shield_normal_time_left = 0;
3728     player->shield_deadly_time_left = 0;
3729
3730     player->last_removed_element = EL_UNDEFINED;
3731
3732     player->inventory_infinite_element = EL_UNDEFINED;
3733     player->inventory_size = 0;
3734
3735     if (level.use_initial_inventory[i])
3736     {
3737       for (j = 0; j < level.initial_inventory_size[i]; j++)
3738       {
3739         int element = level.initial_inventory_content[i][j];
3740         int collect_count = element_info[element].collect_count_initial;
3741         int k;
3742
3743         if (!IS_CUSTOM_ELEMENT(element))
3744           collect_count = 1;
3745
3746         if (collect_count == 0)
3747           player->inventory_infinite_element = element;
3748         else
3749           for (k = 0; k < collect_count; k++)
3750             if (player->inventory_size < MAX_INVENTORY_SIZE)
3751               player->inventory_element[player->inventory_size++] = element;
3752       }
3753     }
3754
3755     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3756     SnapField(player, 0, 0);
3757
3758     map_player_action[i] = i;
3759   }
3760
3761   network_player_action_received = FALSE;
3762
3763   // initial null action
3764   if (network_playing)
3765     SendToServer_MovePlayer(MV_NONE);
3766
3767   FrameCounter = 0;
3768   TimeFrames = 0;
3769   TimePlayed = 0;
3770   TimeLeft = level.time;
3771   TapeTime = 0;
3772
3773   ScreenMovDir = MV_NONE;
3774   ScreenMovPos = 0;
3775   ScreenGfxPos = 0;
3776
3777   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3778
3779   game.robot_wheel_x = -1;
3780   game.robot_wheel_y = -1;
3781
3782   game.exit_x = -1;
3783   game.exit_y = -1;
3784
3785   game.all_players_gone = FALSE;
3786
3787   game.LevelSolved = FALSE;
3788   game.GameOver = FALSE;
3789
3790   game.GamePlayed = !tape.playing;
3791
3792   game.LevelSolved_GameWon = FALSE;
3793   game.LevelSolved_GameEnd = FALSE;
3794   game.LevelSolved_SaveTape = FALSE;
3795   game.LevelSolved_SaveScore = FALSE;
3796
3797   game.LevelSolved_CountingTime = 0;
3798   game.LevelSolved_CountingScore = 0;
3799   game.LevelSolved_CountingHealth = 0;
3800
3801   game.panel.active = TRUE;
3802
3803   game.no_time_limit = (level.time == 0);
3804
3805   game.yamyam_content_nr = 0;
3806   game.robot_wheel_active = FALSE;
3807   game.magic_wall_active = FALSE;
3808   game.magic_wall_time_left = 0;
3809   game.light_time_left = 0;
3810   game.timegate_time_left = 0;
3811   game.switchgate_pos = 0;
3812   game.wind_direction = level.wind_direction_initial;
3813
3814   game.time_final = 0;
3815   game.score_time_final = 0;
3816
3817   game.score = 0;
3818   game.score_final = 0;
3819
3820   game.health = MAX_HEALTH;
3821   game.health_final = MAX_HEALTH;
3822
3823   game.gems_still_needed = level.gems_needed;
3824   game.sokoban_fields_still_needed = 0;
3825   game.sokoban_objects_still_needed = 0;
3826   game.lights_still_needed = 0;
3827   game.players_still_needed = 0;
3828   game.friends_still_needed = 0;
3829
3830   game.lenses_time_left = 0;
3831   game.magnify_time_left = 0;
3832
3833   game.ball_active = level.ball_active_initial;
3834   game.ball_content_nr = 0;
3835
3836   game.explosions_delayed = TRUE;
3837
3838   game.envelope_active = FALSE;
3839
3840   for (i = 0; i < NUM_BELTS; i++)
3841   {
3842     game.belt_dir[i] = MV_NONE;
3843     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3844   }
3845
3846   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3847     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3848
3849 #if DEBUG_INIT_PLAYER
3850   DebugPrintPlayerStatus("Player status at level initialization");
3851 #endif
3852
3853   SCAN_PLAYFIELD(x, y)
3854   {
3855     Tile[x][y] = Last[x][y] = level.field[x][y];
3856     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3857     ChangeDelay[x][y] = 0;
3858     ChangePage[x][y] = -1;
3859     CustomValue[x][y] = 0;              // initialized in InitField()
3860     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3861     AmoebaNr[x][y] = 0;
3862     WasJustMoving[x][y] = 0;
3863     WasJustFalling[x][y] = 0;
3864     CheckCollision[x][y] = 0;
3865     CheckImpact[x][y] = 0;
3866     Stop[x][y] = FALSE;
3867     Pushed[x][y] = FALSE;
3868
3869     ChangeCount[x][y] = 0;
3870     ChangeEvent[x][y] = -1;
3871
3872     ExplodePhase[x][y] = 0;
3873     ExplodeDelay[x][y] = 0;
3874     ExplodeField[x][y] = EX_TYPE_NONE;
3875
3876     RunnerVisit[x][y] = 0;
3877     PlayerVisit[x][y] = 0;
3878
3879     GfxFrame[x][y] = 0;
3880     GfxRandom[x][y] = INIT_GFX_RANDOM();
3881     GfxElement[x][y] = EL_UNDEFINED;
3882     GfxAction[x][y] = ACTION_DEFAULT;
3883     GfxDir[x][y] = MV_NONE;
3884     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3885   }
3886
3887   SCAN_PLAYFIELD(x, y)
3888   {
3889     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3890       emulate_bd = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3916
3917   // initialize type of slippery elements
3918   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3919   {
3920     if (!IS_CUSTOM_ELEMENT(i))
3921     {
3922       // default: elements slip down either to the left or right randomly
3923       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3924
3925       // SP style elements prefer to slip down on the left side
3926       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3927         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3928
3929       // BD style elements prefer to slip down on the left side
3930       if (game.emulation == EMU_BOULDERDASH)
3931         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3932     }
3933   }
3934
3935   // initialize explosion and ignition delay
3936   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3937   {
3938     if (!IS_CUSTOM_ELEMENT(i))
3939     {
3940       int num_phase = 8;
3941       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3942                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3943                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3944       int last_phase = (num_phase + 1) * delay;
3945       int half_phase = (num_phase / 2) * delay;
3946
3947       element_info[i].explosion_delay = last_phase - 1;
3948       element_info[i].ignition_delay = half_phase;
3949
3950       if (i == EL_BLACK_ORB)
3951         element_info[i].ignition_delay = 1;
3952     }
3953   }
3954
3955   // correct non-moving belts to start moving left
3956   for (i = 0; i < NUM_BELTS; i++)
3957     if (game.belt_dir[i] == MV_NONE)
3958       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3959
3960 #if USE_NEW_PLAYER_ASSIGNMENTS
3961   // use preferred player also in local single-player mode
3962   if (!network.enabled && !game.team_mode)
3963   {
3964     int new_index_nr = setup.network_player_nr;
3965
3966     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3967     {
3968       for (i = 0; i < MAX_PLAYERS; i++)
3969         stored_player[i].connected_locally = FALSE;
3970
3971       stored_player[new_index_nr].connected_locally = TRUE;
3972     }
3973   }
3974
3975   for (i = 0; i < MAX_PLAYERS; i++)
3976   {
3977     stored_player[i].connected = FALSE;
3978
3979     // in network game mode, the local player might not be the first player
3980     if (stored_player[i].connected_locally)
3981       local_player = &stored_player[i];
3982   }
3983
3984   if (!network.enabled)
3985     local_player->connected = TRUE;
3986
3987   if (tape.playing)
3988   {
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       stored_player[i].connected = tape.player_participates[i];
3991   }
3992   else if (network.enabled)
3993   {
3994     // add team mode players connected over the network (needed for correct
3995     // assignment of player figures from level to locally playing players)
3996
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].connected_network)
3999         stored_player[i].connected = TRUE;
4000   }
4001   else if (game.team_mode)
4002   {
4003     // try to guess locally connected team mode players (needed for correct
4004     // assignment of player figures from level to locally playing players)
4005
4006     for (i = 0; i < MAX_PLAYERS; i++)
4007       if (setup.input[i].use_joystick ||
4008           setup.input[i].key.left != KSYM_UNDEFINED)
4009         stored_player[i].connected = TRUE;
4010   }
4011
4012 #if DEBUG_INIT_PLAYER
4013   DebugPrintPlayerStatus("Player status after level initialization");
4014 #endif
4015
4016 #if DEBUG_INIT_PLAYER
4017   Debug("game:init:player", "Reassigning players ...");
4018 #endif
4019
4020   // check if any connected player was not found in playfield
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     if (player->connected && !player->present)
4026     {
4027       struct PlayerInfo *field_player = NULL;
4028
4029 #if DEBUG_INIT_PLAYER
4030       Debug("game:init:player",
4031             "- looking for field player for player %d ...", i + 1);
4032 #endif
4033
4034       // assign first free player found that is present in the playfield
4035
4036       // first try: look for unmapped playfield player that is not connected
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped &&
4041             !stored_player[j].connected)
4042           field_player = &stored_player[j];
4043
4044       // second try: look for *any* unmapped playfield player
4045       for (j = 0; j < MAX_PLAYERS; j++)
4046         if (field_player == NULL &&
4047             stored_player[j].present &&
4048             !stored_player[j].mapped)
4049           field_player = &stored_player[j];
4050
4051       if (field_player != NULL)
4052       {
4053         int jx = field_player->jx, jy = field_player->jy;
4054
4055 #if DEBUG_INIT_PLAYER
4056         Debug("game:init:player", "- found player %d",
4057               field_player->index_nr + 1);
4058 #endif
4059
4060         player->present = FALSE;
4061         player->active = FALSE;
4062
4063         field_player->present = TRUE;
4064         field_player->active = TRUE;
4065
4066         /*
4067         player->initial_element = field_player->initial_element;
4068         player->artwork_element = field_player->artwork_element;
4069
4070         player->block_last_field       = field_player->block_last_field;
4071         player->block_delay_adjustment = field_player->block_delay_adjustment;
4072         */
4073
4074         StorePlayer[jx][jy] = field_player->element_nr;
4075
4076         field_player->jx = field_player->last_jx = jx;
4077         field_player->jy = field_player->last_jy = jy;
4078
4079         if (local_player == player)
4080           local_player = field_player;
4081
4082         map_player_action[field_player->index_nr] = i;
4083
4084         field_player->mapped = TRUE;
4085
4086 #if DEBUG_INIT_PLAYER
4087         Debug("game:init:player", "- map_player_action[%d] == %d",
4088               field_player->index_nr + 1, i + 1);
4089 #endif
4090       }
4091     }
4092
4093     if (player->connected && player->present)
4094       player->mapped = TRUE;
4095   }
4096
4097 #if DEBUG_INIT_PLAYER
4098   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4099 #endif
4100
4101 #else
4102
4103   // check if any connected player was not found in playfield
4104   for (i = 0; i < MAX_PLAYERS; i++)
4105   {
4106     struct PlayerInfo *player = &stored_player[i];
4107
4108     if (player->connected && !player->present)
4109     {
4110       for (j = 0; j < MAX_PLAYERS; j++)
4111       {
4112         struct PlayerInfo *field_player = &stored_player[j];
4113         int jx = field_player->jx, jy = field_player->jy;
4114
4115         // assign first free player found that is present in the playfield
4116         if (field_player->present && !field_player->connected)
4117         {
4118           player->present = TRUE;
4119           player->active = TRUE;
4120
4121           field_player->present = FALSE;
4122           field_player->active = FALSE;
4123
4124           player->initial_element = field_player->initial_element;
4125           player->artwork_element = field_player->artwork_element;
4126
4127           player->block_last_field       = field_player->block_last_field;
4128           player->block_delay_adjustment = field_player->block_delay_adjustment;
4129
4130           StorePlayer[jx][jy] = player->element_nr;
4131
4132           player->jx = player->last_jx = jx;
4133           player->jy = player->last_jy = jy;
4134
4135           break;
4136         }
4137       }
4138     }
4139   }
4140 #endif
4141
4142 #if 0
4143   Debug("game:init:player", "local_player->present == %d",
4144         local_player->present);
4145 #endif
4146
4147   // set focus to local player for network games, else to all players
4148   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4149   game.centered_player_nr_next = game.centered_player_nr;
4150   game.set_centered_player = FALSE;
4151   game.set_centered_player_wrap = FALSE;
4152
4153   if (network_playing && tape.recording)
4154   {
4155     // store client dependent player focus when recording network games
4156     tape.centered_player_nr_next = game.centered_player_nr_next;
4157     tape.set_centered_player = TRUE;
4158   }
4159
4160   if (tape.playing)
4161   {
4162     // when playing a tape, eliminate all players who do not participate
4163
4164 #if USE_NEW_PLAYER_ASSIGNMENTS
4165
4166     if (!game.team_mode)
4167     {
4168       for (i = 0; i < MAX_PLAYERS; i++)
4169       {
4170         if (stored_player[i].active &&
4171             !tape.player_participates[map_player_action[i]])
4172         {
4173           struct PlayerInfo *player = &stored_player[i];
4174           int jx = player->jx, jy = player->jy;
4175
4176 #if DEBUG_INIT_PLAYER
4177           Debug("game:init:player", "Removing player %d at (%d, %d)",
4178                 i + 1, jx, jy);
4179 #endif
4180
4181           player->active = FALSE;
4182           StorePlayer[jx][jy] = 0;
4183           Tile[jx][jy] = EL_EMPTY;
4184         }
4185       }
4186     }
4187
4188 #else
4189
4190     for (i = 0; i < MAX_PLAYERS; i++)
4191     {
4192       if (stored_player[i].active &&
4193           !tape.player_participates[i])
4194       {
4195         struct PlayerInfo *player = &stored_player[i];
4196         int jx = player->jx, jy = player->jy;
4197
4198         player->active = FALSE;
4199         StorePlayer[jx][jy] = 0;
4200         Tile[jx][jy] = EL_EMPTY;
4201       }
4202     }
4203 #endif
4204   }
4205   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4206   {
4207     // when in single player mode, eliminate all but the local player
4208
4209     for (i = 0; i < MAX_PLAYERS; i++)
4210     {
4211       struct PlayerInfo *player = &stored_player[i];
4212
4213       if (player->active && player != local_player)
4214       {
4215         int jx = player->jx, jy = player->jy;
4216
4217         player->active = FALSE;
4218         player->present = FALSE;
4219
4220         StorePlayer[jx][jy] = 0;
4221         Tile[jx][jy] = EL_EMPTY;
4222       }
4223     }
4224   }
4225
4226   for (i = 0; i < MAX_PLAYERS; i++)
4227     if (stored_player[i].active)
4228       game.players_still_needed++;
4229
4230   if (level.solved_by_one_player)
4231     game.players_still_needed = 1;
4232
4233   // when recording the game, store which players take part in the game
4234   if (tape.recording)
4235   {
4236 #if USE_NEW_PLAYER_ASSIGNMENTS
4237     for (i = 0; i < MAX_PLAYERS; i++)
4238       if (stored_player[i].connected)
4239         tape.player_participates[i] = TRUE;
4240 #else
4241     for (i = 0; i < MAX_PLAYERS; i++)
4242       if (stored_player[i].active)
4243         tape.player_participates[i] = TRUE;
4244 #endif
4245   }
4246
4247 #if DEBUG_INIT_PLAYER
4248   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4249 #endif
4250
4251   if (BorderElement == EL_EMPTY)
4252   {
4253     SBX_Left = 0;
4254     SBX_Right = lev_fieldx - SCR_FIELDX;
4255     SBY_Upper = 0;
4256     SBY_Lower = lev_fieldy - SCR_FIELDY;
4257   }
4258   else
4259   {
4260     SBX_Left = -1;
4261     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4262     SBY_Upper = -1;
4263     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4264   }
4265
4266   if (full_lev_fieldx <= SCR_FIELDX)
4267     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4268   if (full_lev_fieldy <= SCR_FIELDY)
4269     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4270
4271   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4272     SBX_Left--;
4273   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4274     SBY_Upper--;
4275
4276   // if local player not found, look for custom element that might create
4277   // the player (make some assumptions about the right custom element)
4278   if (!local_player->present)
4279   {
4280     int start_x = 0, start_y = 0;
4281     int found_rating = 0;
4282     int found_element = EL_UNDEFINED;
4283     int player_nr = local_player->index_nr;
4284
4285     SCAN_PLAYFIELD(x, y)
4286     {
4287       int element = Tile[x][y];
4288       int content;
4289       int xx, yy;
4290       boolean is_player;
4291
4292       if (level.use_start_element[player_nr] &&
4293           level.start_element[player_nr] == element &&
4294           found_rating < 4)
4295       {
4296         start_x = x;
4297         start_y = y;
4298
4299         found_rating = 4;
4300         found_element = element;
4301       }
4302
4303       if (!IS_CUSTOM_ELEMENT(element))
4304         continue;
4305
4306       if (CAN_CHANGE(element))
4307       {
4308         for (i = 0; i < element_info[element].num_change_pages; i++)
4309         {
4310           // check for player created from custom element as single target
4311           content = element_info[element].change_page[i].target_element;
4312           is_player = IS_PLAYER_ELEMENT(content);
4313
4314           if (is_player && (found_rating < 3 ||
4315                             (found_rating == 3 && element < found_element)))
4316           {
4317             start_x = x;
4318             start_y = y;
4319
4320             found_rating = 3;
4321             found_element = element;
4322           }
4323         }
4324       }
4325
4326       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4327       {
4328         // check for player created from custom element as explosion content
4329         content = element_info[element].content.e[xx][yy];
4330         is_player = IS_PLAYER_ELEMENT(content);
4331
4332         if (is_player && (found_rating < 2 ||
4333                           (found_rating == 2 && element < found_element)))
4334         {
4335           start_x = x + xx - 1;
4336           start_y = y + yy - 1;
4337
4338           found_rating = 2;
4339           found_element = element;
4340         }
4341
4342         if (!CAN_CHANGE(element))
4343           continue;
4344
4345         for (i = 0; i < element_info[element].num_change_pages; i++)
4346         {
4347           // check for player created from custom element as extended target
4348           content =
4349             element_info[element].change_page[i].target_content.e[xx][yy];
4350
4351           is_player = IS_PLAYER_ELEMENT(content);
4352
4353           if (is_player && (found_rating < 1 ||
4354                             (found_rating == 1 && element < found_element)))
4355           {
4356             start_x = x + xx - 1;
4357             start_y = y + yy - 1;
4358
4359             found_rating = 1;
4360             found_element = element;
4361           }
4362         }
4363       }
4364     }
4365
4366     scroll_x = SCROLL_POSITION_X(start_x);
4367     scroll_y = SCROLL_POSITION_Y(start_y);
4368   }
4369   else
4370   {
4371     scroll_x = SCROLL_POSITION_X(local_player->jx);
4372     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4373   }
4374
4375   // !!! FIX THIS (START) !!!
4376   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4377   {
4378     InitGameEngine_EM();
4379   }
4380   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4381   {
4382     InitGameEngine_SP();
4383   }
4384   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4385   {
4386     InitGameEngine_MM();
4387   }
4388   else
4389   {
4390     DrawLevel(REDRAW_FIELD);
4391     DrawAllPlayers();
4392
4393     // after drawing the level, correct some elements
4394     if (game.timegate_time_left == 0)
4395       CloseAllOpenTimegates();
4396   }
4397
4398   // blit playfield from scroll buffer to normal back buffer for fading in
4399   BlitScreenToBitmap(backbuffer);
4400   // !!! FIX THIS (END) !!!
4401
4402   DrawMaskedBorder(fade_mask);
4403
4404   FadeIn(fade_mask);
4405
4406 #if 1
4407   // full screen redraw is required at this point in the following cases:
4408   // - special editor door undrawn when game was started from level editor
4409   // - drawing area (playfield) was changed and has to be removed completely
4410   redraw_mask = REDRAW_ALL;
4411   BackToFront();
4412 #endif
4413
4414   if (!game.restart_level)
4415   {
4416     // copy default game door content to main double buffer
4417
4418     // !!! CHECK AGAIN !!!
4419     SetPanelBackground();
4420     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4421     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4422   }
4423
4424   SetPanelBackground();
4425   SetDrawBackgroundMask(REDRAW_DOOR_1);
4426
4427   UpdateAndDisplayGameControlValues();
4428
4429   if (!game.restart_level)
4430   {
4431     UnmapGameButtons();
4432     UnmapTapeButtons();
4433
4434     FreeGameButtons();
4435     CreateGameButtons();
4436
4437     MapGameButtons();
4438     MapTapeButtons();
4439
4440     // copy actual game door content to door double buffer for OpenDoor()
4441     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4442
4443     OpenDoor(DOOR_OPEN_ALL);
4444
4445     KeyboardAutoRepeatOffUnlessAutoplay();
4446
4447 #if DEBUG_INIT_PLAYER
4448     DebugPrintPlayerStatus("Player status (final)");
4449 #endif
4450   }
4451
4452   UnmapAllGadgets();
4453
4454   MapGameButtons();
4455   MapTapeButtons();
4456
4457   if (!game.restart_level && !tape.playing)
4458   {
4459     LevelStats_incPlayed(level_nr);
4460
4461     SaveLevelSetup_SeriesInfo();
4462   }
4463
4464   game.restart_level = FALSE;
4465   game.restart_game_message = NULL;
4466
4467   game.request_active = FALSE;
4468   game.request_active_or_moving = FALSE;
4469
4470   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4471     InitGameActions_MM();
4472
4473   SaveEngineSnapshotToListInitial();
4474
4475   if (!game.restart_level)
4476   {
4477     PlaySound(SND_GAME_STARTING);
4478
4479     if (setup.sound_music)
4480       PlayLevelMusic();
4481   }
4482 }
4483
4484 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4485                         int actual_player_x, int actual_player_y)
4486 {
4487   // this is used for non-R'n'D game engines to update certain engine values
4488
4489   // needed to determine if sounds are played within the visible screen area
4490   scroll_x = actual_scroll_x;
4491   scroll_y = actual_scroll_y;
4492
4493   // needed to get player position for "follow finger" playing input method
4494   local_player->jx = actual_player_x;
4495   local_player->jy = actual_player_y;
4496 }
4497
4498 void InitMovDir(int x, int y)
4499 {
4500   int i, element = Tile[x][y];
4501   static int xy[4][2] =
4502   {
4503     {  0, +1 },
4504     { +1,  0 },
4505     {  0, -1 },
4506     { -1,  0 }
4507   };
4508   static int direction[3][4] =
4509   {
4510     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4511     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4512     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4513   };
4514
4515   switch (element)
4516   {
4517     case EL_BUG_RIGHT:
4518     case EL_BUG_UP:
4519     case EL_BUG_LEFT:
4520     case EL_BUG_DOWN:
4521       Tile[x][y] = EL_BUG;
4522       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4523       break;
4524
4525     case EL_SPACESHIP_RIGHT:
4526     case EL_SPACESHIP_UP:
4527     case EL_SPACESHIP_LEFT:
4528     case EL_SPACESHIP_DOWN:
4529       Tile[x][y] = EL_SPACESHIP;
4530       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4531       break;
4532
4533     case EL_BD_BUTTERFLY_RIGHT:
4534     case EL_BD_BUTTERFLY_UP:
4535     case EL_BD_BUTTERFLY_LEFT:
4536     case EL_BD_BUTTERFLY_DOWN:
4537       Tile[x][y] = EL_BD_BUTTERFLY;
4538       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4539       break;
4540
4541     case EL_BD_FIREFLY_RIGHT:
4542     case EL_BD_FIREFLY_UP:
4543     case EL_BD_FIREFLY_LEFT:
4544     case EL_BD_FIREFLY_DOWN:
4545       Tile[x][y] = EL_BD_FIREFLY;
4546       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4547       break;
4548
4549     case EL_PACMAN_RIGHT:
4550     case EL_PACMAN_UP:
4551     case EL_PACMAN_LEFT:
4552     case EL_PACMAN_DOWN:
4553       Tile[x][y] = EL_PACMAN;
4554       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4555       break;
4556
4557     case EL_YAMYAM_LEFT:
4558     case EL_YAMYAM_RIGHT:
4559     case EL_YAMYAM_UP:
4560     case EL_YAMYAM_DOWN:
4561       Tile[x][y] = EL_YAMYAM;
4562       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4563       break;
4564
4565     case EL_SP_SNIKSNAK:
4566       MovDir[x][y] = MV_UP;
4567       break;
4568
4569     case EL_SP_ELECTRON:
4570       MovDir[x][y] = MV_LEFT;
4571       break;
4572
4573     case EL_MOLE_LEFT:
4574     case EL_MOLE_RIGHT:
4575     case EL_MOLE_UP:
4576     case EL_MOLE_DOWN:
4577       Tile[x][y] = EL_MOLE;
4578       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4579       break;
4580
4581     case EL_SPRING_LEFT:
4582     case EL_SPRING_RIGHT:
4583       Tile[x][y] = EL_SPRING;
4584       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4585       break;
4586
4587     default:
4588       if (IS_CUSTOM_ELEMENT(element))
4589       {
4590         struct ElementInfo *ei = &element_info[element];
4591         int move_direction_initial = ei->move_direction_initial;
4592         int move_pattern = ei->move_pattern;
4593
4594         if (move_direction_initial == MV_START_PREVIOUS)
4595         {
4596           if (MovDir[x][y] != MV_NONE)
4597             return;
4598
4599           move_direction_initial = MV_START_AUTOMATIC;
4600         }
4601
4602         if (move_direction_initial == MV_START_RANDOM)
4603           MovDir[x][y] = 1 << RND(4);
4604         else if (move_direction_initial & MV_ANY_DIRECTION)
4605           MovDir[x][y] = move_direction_initial;
4606         else if (move_pattern == MV_ALL_DIRECTIONS ||
4607                  move_pattern == MV_TURNING_LEFT ||
4608                  move_pattern == MV_TURNING_RIGHT ||
4609                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4610                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4611                  move_pattern == MV_TURNING_RANDOM)
4612           MovDir[x][y] = 1 << RND(4);
4613         else if (move_pattern == MV_HORIZONTAL)
4614           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4615         else if (move_pattern == MV_VERTICAL)
4616           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4617         else if (move_pattern & MV_ANY_DIRECTION)
4618           MovDir[x][y] = element_info[element].move_pattern;
4619         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4620                  move_pattern == MV_ALONG_RIGHT_SIDE)
4621         {
4622           // use random direction as default start direction
4623           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4624             MovDir[x][y] = 1 << RND(4);
4625
4626           for (i = 0; i < NUM_DIRECTIONS; i++)
4627           {
4628             int x1 = x + xy[i][0];
4629             int y1 = y + xy[i][1];
4630
4631             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4632             {
4633               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4634                 MovDir[x][y] = direction[0][i];
4635               else
4636                 MovDir[x][y] = direction[1][i];
4637
4638               break;
4639             }
4640           }
4641         }                
4642       }
4643       else
4644       {
4645         MovDir[x][y] = 1 << RND(4);
4646
4647         if (element != EL_BUG &&
4648             element != EL_SPACESHIP &&
4649             element != EL_BD_BUTTERFLY &&
4650             element != EL_BD_FIREFLY)
4651           break;
4652
4653         for (i = 0; i < NUM_DIRECTIONS; i++)
4654         {
4655           int x1 = x + xy[i][0];
4656           int y1 = y + xy[i][1];
4657
4658           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4659           {
4660             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4661             {
4662               MovDir[x][y] = direction[0][i];
4663               break;
4664             }
4665             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4666                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4667             {
4668               MovDir[x][y] = direction[1][i];
4669               break;
4670             }
4671           }
4672         }
4673       }
4674       break;
4675   }
4676
4677   GfxDir[x][y] = MovDir[x][y];
4678 }
4679
4680 void InitAmoebaNr(int x, int y)
4681 {
4682   int i;
4683   int group_nr = AmoebaNeighbourNr(x, y);
4684
4685   if (group_nr == 0)
4686   {
4687     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4688     {
4689       if (AmoebaCnt[i] == 0)
4690       {
4691         group_nr = i;
4692         break;
4693       }
4694     }
4695   }
4696
4697   AmoebaNr[x][y] = group_nr;
4698   AmoebaCnt[group_nr]++;
4699   AmoebaCnt2[group_nr]++;
4700 }
4701
4702 static void LevelSolved_SetFinalGameValues(void)
4703 {
4704   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4705   game.score_time_final = (level.use_step_counter ? TimePlayed :
4706                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4707
4708   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4709                       game_em.lev->score :
4710                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4711                       game_mm.score :
4712                       game.score);
4713
4714   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4715                        MM_HEALTH(game_mm.laser_overload_value) :
4716                        game.health);
4717
4718   game.LevelSolved_CountingTime = game.time_final;
4719   game.LevelSolved_CountingScore = game.score_final;
4720   game.LevelSolved_CountingHealth = game.health_final;
4721 }
4722
4723 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4724 {
4725   game.LevelSolved_CountingTime = time;
4726   game.LevelSolved_CountingScore = score;
4727   game.LevelSolved_CountingHealth = health;
4728
4729   game_panel_controls[GAME_PANEL_TIME].value = time;
4730   game_panel_controls[GAME_PANEL_SCORE].value = score;
4731   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4732
4733   DisplayGameControlValues();
4734 }
4735
4736 static void LevelSolved(void)
4737 {
4738   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4739       game.players_still_needed > 0)
4740     return;
4741
4742   game.LevelSolved = TRUE;
4743   game.GameOver = TRUE;
4744
4745   // needed here to display correct panel values while player walks into exit
4746   LevelSolved_SetFinalGameValues();
4747 }
4748
4749 void GameWon(void)
4750 {
4751   static int time_count_steps;
4752   static int time, time_final;
4753   static float score, score_final; // needed for time score < 10 for 10 seconds
4754   static int health, health_final;
4755   static int game_over_delay_1 = 0;
4756   static int game_over_delay_2 = 0;
4757   static int game_over_delay_3 = 0;
4758   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4759   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4760
4761   if (!game.LevelSolved_GameWon)
4762   {
4763     int i;
4764
4765     // do not start end game actions before the player stops moving (to exit)
4766     if (local_player->active && local_player->MovPos)
4767       return;
4768
4769     // calculate final game values after player finished walking into exit
4770     LevelSolved_SetFinalGameValues();
4771
4772     game.LevelSolved_GameWon = TRUE;
4773     game.LevelSolved_SaveTape = tape.recording;
4774     game.LevelSolved_SaveScore = !tape.playing;
4775
4776     if (!tape.playing)
4777     {
4778       LevelStats_incSolved(level_nr);
4779
4780       SaveLevelSetup_SeriesInfo();
4781     }
4782
4783     if (tape.auto_play)         // tape might already be stopped here
4784       tape.auto_play_level_solved = TRUE;
4785
4786     TapeStop();
4787
4788     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4789     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4790     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4791
4792     time = time_final = game.time_final;
4793     score = score_final = game.score_final;
4794     health = health_final = game.health_final;
4795
4796     // update game panel values before (delayed) counting of score (if any)
4797     LevelSolved_DisplayFinalGameValues(time, score, health);
4798
4799     // if level has time score defined, calculate new final game values
4800     if (time_score > 0)
4801     {
4802       int time_final_max = 999;
4803       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4804       int time_frames = 0;
4805       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4806       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4807
4808       if (TimeLeft > 0)
4809       {
4810         time_final = 0;
4811         time_frames = time_frames_left;
4812       }
4813       else if (game.no_time_limit && TimePlayed < time_final_max)
4814       {
4815         time_final = time_final_max;
4816         time_frames = time_frames_final_max - time_frames_played;
4817       }
4818
4819       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4820
4821       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4822
4823       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4824       {
4825         health_final = 0;
4826         score_final += health * time_score;
4827       }
4828
4829       game.score_final = score_final;
4830       game.health_final = health_final;
4831     }
4832
4833     // if not counting score after game, immediately update game panel values
4834     if (level_editor_test_game || !setup.count_score_after_game)
4835     {
4836       time = time_final;
4837       score = score_final;
4838
4839       LevelSolved_DisplayFinalGameValues(time, score, health);
4840     }
4841
4842     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4843     {
4844       // check if last player has left the level
4845       if (game.exit_x >= 0 &&
4846           game.exit_y >= 0)
4847       {
4848         int x = game.exit_x;
4849         int y = game.exit_y;
4850         int element = Tile[x][y];
4851
4852         // close exit door after last player
4853         if ((game.all_players_gone &&
4854              (element == EL_EXIT_OPEN ||
4855               element == EL_SP_EXIT_OPEN ||
4856               element == EL_STEEL_EXIT_OPEN)) ||
4857             element == EL_EM_EXIT_OPEN ||
4858             element == EL_EM_STEEL_EXIT_OPEN)
4859         {
4860
4861           Tile[x][y] =
4862             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4863              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4864              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4865              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4866              EL_EM_STEEL_EXIT_CLOSING);
4867
4868           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4869         }
4870
4871         // player disappears
4872         DrawLevelField(x, y);
4873       }
4874
4875       for (i = 0; i < MAX_PLAYERS; i++)
4876       {
4877         struct PlayerInfo *player = &stored_player[i];
4878
4879         if (player->present)
4880         {
4881           RemovePlayer(player);
4882
4883           // player disappears
4884           DrawLevelField(player->jx, player->jy);
4885         }
4886       }
4887     }
4888
4889     PlaySound(SND_GAME_WINNING);
4890   }
4891
4892   if (setup.count_score_after_game)
4893   {
4894     if (time != time_final)
4895     {
4896       if (game_over_delay_1 > 0)
4897       {
4898         game_over_delay_1--;
4899
4900         return;
4901       }
4902
4903       int time_to_go = ABS(time_final - time);
4904       int time_count_dir = (time < time_final ? +1 : -1);
4905
4906       if (time_to_go < time_count_steps)
4907         time_count_steps = 1;
4908
4909       time  += time_count_steps * time_count_dir;
4910       score += time_count_steps * time_score;
4911
4912       // set final score to correct rounding differences after counting score
4913       if (time == time_final)
4914         score = score_final;
4915
4916       LevelSolved_DisplayFinalGameValues(time, score, health);
4917
4918       if (time == time_final)
4919         StopSound(SND_GAME_LEVELTIME_BONUS);
4920       else if (setup.sound_loops)
4921         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4922       else
4923         PlaySound(SND_GAME_LEVELTIME_BONUS);
4924
4925       return;
4926     }
4927
4928     if (health != health_final)
4929     {
4930       if (game_over_delay_2 > 0)
4931       {
4932         game_over_delay_2--;
4933
4934         return;
4935       }
4936
4937       int health_count_dir = (health < health_final ? +1 : -1);
4938
4939       health += health_count_dir;
4940       score  += time_score;
4941
4942       LevelSolved_DisplayFinalGameValues(time, score, health);
4943
4944       if (health == health_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
4955   game.panel.active = FALSE;
4956
4957   if (game_over_delay_3 > 0)
4958   {
4959     game_over_delay_3--;
4960
4961     return;
4962   }
4963
4964   GameEnd();
4965 }
4966
4967 void GameEnd(void)
4968 {
4969   // used instead of "level_nr" (needed for network games)
4970   int last_level_nr = levelset.level_nr;
4971   boolean tape_saved = FALSE;
4972
4973   game.LevelSolved_GameEnd = TRUE;
4974
4975   if (game.LevelSolved_SaveTape)
4976   {
4977     // make sure that request dialog to save tape does not open door again
4978     if (!global.use_envelope_request)
4979       CloseDoor(DOOR_CLOSE_1);
4980
4981     // ask to save tape
4982     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4983
4984     // set unique basename for score tape (also saved in high score table)
4985     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4986   }
4987
4988   // if no tape is to be saved, close both doors simultaneously
4989   CloseDoor(DOOR_CLOSE_ALL);
4990
4991   if (level_editor_test_game)
4992   {
4993     SetGameStatus(GAME_MODE_MAIN);
4994
4995     DrawMainMenu();
4996
4997     return;
4998   }
4999
5000   if (!game.LevelSolved_SaveScore)
5001   {
5002     SetGameStatus(GAME_MODE_MAIN);
5003
5004     DrawMainMenu();
5005
5006     return;
5007   }
5008
5009   if (level_nr == leveldir_current->handicap_level)
5010   {
5011     leveldir_current->handicap_level++;
5012
5013     SaveLevelSetup_SeriesInfo();
5014   }
5015
5016   // save score and score tape before potentially erasing tape below
5017   NewHighScore(last_level_nr, tape_saved);
5018
5019   if (setup.increment_levels &&
5020       level_nr < leveldir_current->last_level &&
5021       !network_playing)
5022   {
5023     level_nr++;         // advance to next level
5024     TapeErase();        // start with empty tape
5025
5026     if (setup.auto_play_next_level)
5027     {
5028       LoadLevel(level_nr);
5029
5030       SaveLevelSetup_SeriesInfo();
5031     }
5032   }
5033
5034   if (scores.last_added >= 0 && setup.show_scores_after_game)
5035   {
5036     SetGameStatus(GAME_MODE_SCORES);
5037
5038     DrawHallOfFame(last_level_nr);
5039   }
5040   else if (setup.auto_play_next_level && setup.increment_levels &&
5041            last_level_nr < leveldir_current->last_level &&
5042            !network_playing)
5043   {
5044     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5045   }
5046   else
5047   {
5048     SetGameStatus(GAME_MODE_MAIN);
5049
5050     DrawMainMenu();
5051   }
5052 }
5053
5054 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5055                          boolean one_score_entry_per_name)
5056 {
5057   int i;
5058
5059   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5060     return -1;
5061
5062   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5063   {
5064     struct ScoreEntry *entry = &list->entry[i];
5065     boolean score_is_better = (new_entry->score >  entry->score);
5066     boolean score_is_equal  = (new_entry->score == entry->score);
5067     boolean time_is_better  = (new_entry->time  <  entry->time);
5068     boolean time_is_equal   = (new_entry->time  == entry->time);
5069     boolean better_by_score = (score_is_better ||
5070                                (score_is_equal && time_is_better));
5071     boolean better_by_time  = (time_is_better ||
5072                                (time_is_equal && score_is_better));
5073     boolean is_better = (level.rate_time_over_score ? better_by_time :
5074                          better_by_score);
5075     boolean entry_is_empty = (entry->score == 0 &&
5076                               entry->time == 0);
5077
5078     // prevent adding server score entries if also existing in local score file
5079     // (special case: historic score entries have an empty tape basename entry)
5080     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5081         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5082       return -1;
5083
5084     if (is_better || entry_is_empty)
5085     {
5086       // player has made it to the hall of fame
5087
5088       if (i < MAX_SCORE_ENTRIES - 1)
5089       {
5090         int m = MAX_SCORE_ENTRIES - 1;
5091         int l;
5092
5093         if (one_score_entry_per_name)
5094         {
5095           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5096             if (strEqual(list->entry[l].name, new_entry->name))
5097               m = l;
5098
5099           if (m == i)   // player's new highscore overwrites his old one
5100             goto put_into_list;
5101         }
5102
5103         for (l = m; l > i; l--)
5104           list->entry[l] = list->entry[l - 1];
5105       }
5106
5107       put_into_list:
5108
5109       *entry = *new_entry;
5110
5111       return i;
5112     }
5113     else if (one_score_entry_per_name &&
5114              strEqual(entry->name, new_entry->name))
5115     {
5116       // player already in high score list with better score or time
5117
5118       return -1;
5119     }
5120   }
5121
5122   return -1;
5123 }
5124
5125 void NewHighScore(int level_nr, boolean tape_saved)
5126 {
5127   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5128   boolean one_per_name = FALSE;
5129
5130   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5131   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5132
5133   new_entry.score = game.score_final;
5134   new_entry.time = game.score_time_final;
5135
5136   LoadScore(level_nr);
5137
5138   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5139
5140   if (scores.last_added < 0)
5141     return;
5142
5143   SaveScore(level_nr);
5144
5145   // store last added local score entry (before merging server scores)
5146   scores.last_added_local = scores.last_added;
5147
5148   if (!game.LevelSolved_SaveTape)
5149     return;
5150
5151   SaveScoreTape(level_nr);
5152
5153   if (setup.ask_for_using_api_server)
5154   {
5155     setup.use_api_server =
5156       Request("Upload your score and tape to the high score server?", REQ_ASK);
5157
5158     if (!setup.use_api_server)
5159       Request("Not using high score server! Use setup menu to enable again!",
5160               REQ_CONFIRM);
5161
5162     runtime.use_api_server = setup.use_api_server;
5163
5164     // after asking for using API server once, do not ask again
5165     setup.ask_for_using_api_server = FALSE;
5166
5167     SaveSetup_ServerSetup();
5168   }
5169
5170   SaveServerScore(level_nr, tape_saved);
5171 }
5172
5173 void MergeServerScore(void)
5174 {
5175   struct ScoreEntry last_added_entry;
5176   boolean one_per_name = FALSE;
5177   int i;
5178
5179   if (scores.last_added >= 0)
5180     last_added_entry = scores.entry[scores.last_added];
5181
5182   for (i = 0; i < server_scores.num_entries; i++)
5183   {
5184     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5185
5186     if (pos >= 0 && pos <= scores.last_added)
5187       scores.last_added++;
5188   }
5189
5190   if (scores.last_added >= MAX_SCORE_ENTRIES)
5191   {
5192     scores.last_added = MAX_SCORE_ENTRIES - 1;
5193     scores.force_last_added = TRUE;
5194
5195     scores.entry[scores.last_added] = last_added_entry;
5196   }
5197 }
5198
5199 static int getElementMoveStepsizeExt(int x, int y, int direction)
5200 {
5201   int element = Tile[x][y];
5202   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5203   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5204   int horiz_move = (dx != 0);
5205   int sign = (horiz_move ? dx : dy);
5206   int step = sign * element_info[element].move_stepsize;
5207
5208   // special values for move stepsize for spring and things on conveyor belt
5209   if (horiz_move)
5210   {
5211     if (CAN_FALL(element) &&
5212         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5213       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5214     else if (element == EL_SPRING)
5215       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5216   }
5217
5218   return step;
5219 }
5220
5221 static int getElementMoveStepsize(int x, int y)
5222 {
5223   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5224 }
5225
5226 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5227 {
5228   if (player->GfxAction != action || player->GfxDir != dir)
5229   {
5230     player->GfxAction = action;
5231     player->GfxDir = dir;
5232     player->Frame = 0;
5233     player->StepFrame = 0;
5234   }
5235 }
5236
5237 static void ResetGfxFrame(int x, int y)
5238 {
5239   // profiling showed that "autotest" spends 10~20% of its time in this function
5240   if (DrawingDeactivatedField())
5241     return;
5242
5243   int element = Tile[x][y];
5244   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5245
5246   if (graphic_info[graphic].anim_global_sync)
5247     GfxFrame[x][y] = FrameCounter;
5248   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5249     GfxFrame[x][y] = CustomValue[x][y];
5250   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5251     GfxFrame[x][y] = element_info[element].collect_score;
5252   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5253     GfxFrame[x][y] = ChangeDelay[x][y];
5254 }
5255
5256 static void ResetGfxAnimation(int x, int y)
5257 {
5258   GfxAction[x][y] = ACTION_DEFAULT;
5259   GfxDir[x][y] = MovDir[x][y];
5260   GfxFrame[x][y] = 0;
5261
5262   ResetGfxFrame(x, y);
5263 }
5264
5265 static void ResetRandomAnimationValue(int x, int y)
5266 {
5267   GfxRandom[x][y] = INIT_GFX_RANDOM();
5268 }
5269
5270 static void InitMovingField(int x, int y, int direction)
5271 {
5272   int element = Tile[x][y];
5273   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5274   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5275   int newx = x + dx;
5276   int newy = y + dy;
5277   boolean is_moving_before, is_moving_after;
5278
5279   // check if element was/is moving or being moved before/after mode change
5280   is_moving_before = (WasJustMoving[x][y] != 0);
5281   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5282
5283   // reset animation only for moving elements which change direction of moving
5284   // or which just started or stopped moving
5285   // (else CEs with property "can move" / "not moving" are reset each frame)
5286   if (is_moving_before != is_moving_after ||
5287       direction != MovDir[x][y])
5288     ResetGfxAnimation(x, y);
5289
5290   MovDir[x][y] = direction;
5291   GfxDir[x][y] = direction;
5292
5293   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5294                      direction == MV_DOWN && CAN_FALL(element) ?
5295                      ACTION_FALLING : ACTION_MOVING);
5296
5297   // this is needed for CEs with property "can move" / "not moving"
5298
5299   if (is_moving_after)
5300   {
5301     if (Tile[newx][newy] == EL_EMPTY)
5302       Tile[newx][newy] = EL_BLOCKED;
5303
5304     MovDir[newx][newy] = MovDir[x][y];
5305
5306     CustomValue[newx][newy] = CustomValue[x][y];
5307
5308     GfxFrame[newx][newy] = GfxFrame[x][y];
5309     GfxRandom[newx][newy] = GfxRandom[x][y];
5310     GfxAction[newx][newy] = GfxAction[x][y];
5311     GfxDir[newx][newy] = GfxDir[x][y];
5312   }
5313 }
5314
5315 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5316 {
5317   int direction = MovDir[x][y];
5318   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5319   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5320
5321   *goes_to_x = newx;
5322   *goes_to_y = newy;
5323 }
5324
5325 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5326 {
5327   int oldx = x, oldy = y;
5328   int direction = MovDir[x][y];
5329
5330   if (direction == MV_LEFT)
5331     oldx++;
5332   else if (direction == MV_RIGHT)
5333     oldx--;
5334   else if (direction == MV_UP)
5335     oldy++;
5336   else if (direction == MV_DOWN)
5337     oldy--;
5338
5339   *comes_from_x = oldx;
5340   *comes_from_y = oldy;
5341 }
5342
5343 static int MovingOrBlocked2Element(int x, int y)
5344 {
5345   int element = Tile[x][y];
5346
5347   if (element == EL_BLOCKED)
5348   {
5349     int oldx, oldy;
5350
5351     Blocked2Moving(x, y, &oldx, &oldy);
5352     return Tile[oldx][oldy];
5353   }
5354   else
5355     return element;
5356 }
5357
5358 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5359 {
5360   // like MovingOrBlocked2Element(), but if element is moving
5361   // and (x,y) is the field the moving element is just leaving,
5362   // return EL_BLOCKED instead of the element value
5363   int element = Tile[x][y];
5364
5365   if (IS_MOVING(x, y))
5366   {
5367     if (element == EL_BLOCKED)
5368     {
5369       int oldx, oldy;
5370
5371       Blocked2Moving(x, y, &oldx, &oldy);
5372       return Tile[oldx][oldy];
5373     }
5374     else
5375       return EL_BLOCKED;
5376   }
5377   else
5378     return element;
5379 }
5380
5381 static void RemoveField(int x, int y)
5382 {
5383   Tile[x][y] = EL_EMPTY;
5384
5385   MovPos[x][y] = 0;
5386   MovDir[x][y] = 0;
5387   MovDelay[x][y] = 0;
5388
5389   CustomValue[x][y] = 0;
5390
5391   AmoebaNr[x][y] = 0;
5392   ChangeDelay[x][y] = 0;
5393   ChangePage[x][y] = -1;
5394   Pushed[x][y] = FALSE;
5395
5396   GfxElement[x][y] = EL_UNDEFINED;
5397   GfxAction[x][y] = ACTION_DEFAULT;
5398   GfxDir[x][y] = MV_NONE;
5399 }
5400
5401 static void RemoveMovingField(int x, int y)
5402 {
5403   int oldx = x, oldy = y, newx = x, newy = y;
5404   int element = Tile[x][y];
5405   int next_element = EL_UNDEFINED;
5406
5407   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5408     return;
5409
5410   if (IS_MOVING(x, y))
5411   {
5412     Moving2Blocked(x, y, &newx, &newy);
5413
5414     if (Tile[newx][newy] != EL_BLOCKED)
5415     {
5416       // element is moving, but target field is not free (blocked), but
5417       // already occupied by something different (example: acid pool);
5418       // in this case, only remove the moving field, but not the target
5419
5420       RemoveField(oldx, oldy);
5421
5422       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5423
5424       TEST_DrawLevelField(oldx, oldy);
5425
5426       return;
5427     }
5428   }
5429   else if (element == EL_BLOCKED)
5430   {
5431     Blocked2Moving(x, y, &oldx, &oldy);
5432     if (!IS_MOVING(oldx, oldy))
5433       return;
5434   }
5435
5436   if (element == EL_BLOCKED &&
5437       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5438        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5439        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5440        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5441        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5442        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5443     next_element = get_next_element(Tile[oldx][oldy]);
5444
5445   RemoveField(oldx, oldy);
5446   RemoveField(newx, newy);
5447
5448   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5449
5450   if (next_element != EL_UNDEFINED)
5451     Tile[oldx][oldy] = next_element;
5452
5453   TEST_DrawLevelField(oldx, oldy);
5454   TEST_DrawLevelField(newx, newy);
5455 }
5456
5457 void DrawDynamite(int x, int y)
5458 {
5459   int sx = SCREENX(x), sy = SCREENY(y);
5460   int graphic = el2img(Tile[x][y]);
5461   int frame;
5462
5463   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5464     return;
5465
5466   if (IS_WALKABLE_INSIDE(Back[x][y]))
5467     return;
5468
5469   if (Back[x][y])
5470     DrawLevelElement(x, y, Back[x][y]);
5471   else if (Store[x][y])
5472     DrawLevelElement(x, y, Store[x][y]);
5473   else if (game.use_masked_elements)
5474     DrawLevelElement(x, y, EL_EMPTY);
5475
5476   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5477
5478   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5479     DrawGraphicThruMask(sx, sy, graphic, frame);
5480   else
5481     DrawGraphic(sx, sy, graphic, frame);
5482 }
5483
5484 static void CheckDynamite(int x, int y)
5485 {
5486   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5487   {
5488     MovDelay[x][y]--;
5489
5490     if (MovDelay[x][y] != 0)
5491     {
5492       DrawDynamite(x, y);
5493       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5494
5495       return;
5496     }
5497   }
5498
5499   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5500
5501   Bang(x, y);
5502 }
5503
5504 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5505 {
5506   boolean num_checked_players = 0;
5507   int i;
5508
5509   for (i = 0; i < MAX_PLAYERS; i++)
5510   {
5511     if (stored_player[i].active)
5512     {
5513       int sx = stored_player[i].jx;
5514       int sy = stored_player[i].jy;
5515
5516       if (num_checked_players == 0)
5517       {
5518         *sx1 = *sx2 = sx;
5519         *sy1 = *sy2 = sy;
5520       }
5521       else
5522       {
5523         *sx1 = MIN(*sx1, sx);
5524         *sy1 = MIN(*sy1, sy);
5525         *sx2 = MAX(*sx2, sx);
5526         *sy2 = MAX(*sy2, sy);
5527       }
5528
5529       num_checked_players++;
5530     }
5531   }
5532 }
5533
5534 static boolean checkIfAllPlayersFitToScreen_RND(void)
5535 {
5536   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5537
5538   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5539
5540   return (sx2 - sx1 < SCR_FIELDX &&
5541           sy2 - sy1 < SCR_FIELDY);
5542 }
5543
5544 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5545 {
5546   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5547
5548   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5549
5550   *sx = (sx1 + sx2) / 2;
5551   *sy = (sy1 + sy2) / 2;
5552 }
5553
5554 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5555                                boolean center_screen, boolean quick_relocation)
5556 {
5557   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5558   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5559   boolean no_delay = (tape.warp_forward);
5560   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5561   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5562   int new_scroll_x, new_scroll_y;
5563
5564   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5565   {
5566     // case 1: quick relocation inside visible screen (without scrolling)
5567
5568     RedrawPlayfield();
5569
5570     return;
5571   }
5572
5573   if (!level.shifted_relocation || center_screen)
5574   {
5575     // relocation _with_ centering of screen
5576
5577     new_scroll_x = SCROLL_POSITION_X(x);
5578     new_scroll_y = SCROLL_POSITION_Y(y);
5579   }
5580   else
5581   {
5582     // relocation _without_ centering of screen
5583
5584     int center_scroll_x = SCROLL_POSITION_X(old_x);
5585     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5586     int offset_x = x + (scroll_x - center_scroll_x);
5587     int offset_y = y + (scroll_y - center_scroll_y);
5588
5589     // for new screen position, apply previous offset to center position
5590     new_scroll_x = SCROLL_POSITION_X(offset_x);
5591     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5592   }
5593
5594   if (quick_relocation)
5595   {
5596     // case 2: quick relocation (redraw without visible scrolling)
5597
5598     scroll_x = new_scroll_x;
5599     scroll_y = new_scroll_y;
5600
5601     RedrawPlayfield();
5602
5603     return;
5604   }
5605
5606   // case 3: visible relocation (with scrolling to new position)
5607
5608   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5609
5610   SetVideoFrameDelay(wait_delay_value);
5611
5612   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5613   {
5614     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5615     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5616
5617     if (dx == 0 && dy == 0)             // no scrolling needed at all
5618       break;
5619
5620     scroll_x -= dx;
5621     scroll_y -= dy;
5622
5623     // set values for horizontal/vertical screen scrolling (half tile size)
5624     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5625     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5626     int pos_x = dx * TILEX / 2;
5627     int pos_y = dy * TILEY / 2;
5628     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5629     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5630
5631     ScrollLevel(dx, dy);
5632     DrawAllPlayers();
5633
5634     // scroll in two steps of half tile size to make things smoother
5635     BlitScreenToBitmapExt_RND(window, fx, fy);
5636
5637     // scroll second step to align at full tile size
5638     BlitScreenToBitmap(window);
5639   }
5640
5641   DrawAllPlayers();
5642   BackToFront();
5643
5644   SetVideoFrameDelay(frame_delay_value_old);
5645 }
5646
5647 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5648 {
5649   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5650   int player_nr = GET_PLAYER_NR(el_player);
5651   struct PlayerInfo *player = &stored_player[player_nr];
5652   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5653   boolean no_delay = (tape.warp_forward);
5654   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5655   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5656   int old_jx = player->jx;
5657   int old_jy = player->jy;
5658   int old_element = Tile[old_jx][old_jy];
5659   int element = Tile[jx][jy];
5660   boolean player_relocated = (old_jx != jx || old_jy != jy);
5661
5662   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5663   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5664   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5665   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5666   int leave_side_horiz = move_dir_horiz;
5667   int leave_side_vert  = move_dir_vert;
5668   int enter_side = enter_side_horiz | enter_side_vert;
5669   int leave_side = leave_side_horiz | leave_side_vert;
5670
5671   if (player->buried)           // do not reanimate dead player
5672     return;
5673
5674   if (!player_relocated)        // no need to relocate the player
5675     return;
5676
5677   if (IS_PLAYER(jx, jy))        // player already placed at new position
5678   {
5679     RemoveField(jx, jy);        // temporarily remove newly placed player
5680     DrawLevelField(jx, jy);
5681   }
5682
5683   if (player->present)
5684   {
5685     while (player->MovPos)
5686     {
5687       ScrollPlayer(player, SCROLL_GO_ON);
5688       ScrollScreen(NULL, SCROLL_GO_ON);
5689
5690       AdvanceFrameAndPlayerCounters(player->index_nr);
5691
5692       DrawPlayer(player);
5693
5694       BackToFront_WithFrameDelay(wait_delay_value);
5695     }
5696
5697     DrawPlayer(player);         // needed here only to cleanup last field
5698     DrawLevelField(player->jx, player->jy);     // remove player graphic
5699
5700     player->is_moving = FALSE;
5701   }
5702
5703   if (IS_CUSTOM_ELEMENT(old_element))
5704     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5705                                CE_LEFT_BY_PLAYER,
5706                                player->index_bit, leave_side);
5707
5708   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5709                                       CE_PLAYER_LEAVES_X,
5710                                       player->index_bit, leave_side);
5711
5712   Tile[jx][jy] = el_player;
5713   InitPlayerField(jx, jy, el_player, TRUE);
5714
5715   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5716      possible that the relocation target field did not contain a player element,
5717      but a walkable element, to which the new player was relocated -- in this
5718      case, restore that (already initialized!) element on the player field */
5719   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5720   {
5721     Tile[jx][jy] = element;     // restore previously existing element
5722   }
5723
5724   // only visually relocate centered player
5725   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5726                      FALSE, level.instant_relocation);
5727
5728   TestIfPlayerTouchesBadThing(jx, jy);
5729   TestIfPlayerTouchesCustomElement(jx, jy);
5730
5731   if (IS_CUSTOM_ELEMENT(element))
5732     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5733                                player->index_bit, enter_side);
5734
5735   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5736                                       player->index_bit, enter_side);
5737
5738   if (player->is_switching)
5739   {
5740     /* ensure that relocation while still switching an element does not cause
5741        a new element to be treated as also switched directly after relocation
5742        (this is important for teleporter switches that teleport the player to
5743        a place where another teleporter switch is in the same direction, which
5744        would then incorrectly be treated as immediately switched before the
5745        direction key that caused the switch was released) */
5746
5747     player->switch_x += jx - old_jx;
5748     player->switch_y += jy - old_jy;
5749   }
5750 }
5751
5752 static void Explode(int ex, int ey, int phase, int mode)
5753 {
5754   int x, y;
5755   int last_phase;
5756   int border_element;
5757
5758   // !!! eliminate this variable !!!
5759   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5760
5761   if (game.explosions_delayed)
5762   {
5763     ExplodeField[ex][ey] = mode;
5764     return;
5765   }
5766
5767   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5768   {
5769     int center_element = Tile[ex][ey];
5770     int artwork_element, explosion_element;     // set these values later
5771
5772     // remove things displayed in background while burning dynamite
5773     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5774       Back[ex][ey] = 0;
5775
5776     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5777     {
5778       // put moving element to center field (and let it explode there)
5779       center_element = MovingOrBlocked2Element(ex, ey);
5780       RemoveMovingField(ex, ey);
5781       Tile[ex][ey] = center_element;
5782     }
5783
5784     // now "center_element" is finally determined -- set related values now
5785     artwork_element = center_element;           // for custom player artwork
5786     explosion_element = center_element;         // for custom player artwork
5787
5788     if (IS_PLAYER(ex, ey))
5789     {
5790       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5791
5792       artwork_element = stored_player[player_nr].artwork_element;
5793
5794       if (level.use_explosion_element[player_nr])
5795       {
5796         explosion_element = level.explosion_element[player_nr];
5797         artwork_element = explosion_element;
5798       }
5799     }
5800
5801     if (mode == EX_TYPE_NORMAL ||
5802         mode == EX_TYPE_CENTER ||
5803         mode == EX_TYPE_CROSS)
5804       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5805
5806     last_phase = element_info[explosion_element].explosion_delay + 1;
5807
5808     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5809     {
5810       int xx = x - ex + 1;
5811       int yy = y - ey + 1;
5812       int element;
5813
5814       if (!IN_LEV_FIELD(x, y) ||
5815           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5816           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5817         continue;
5818
5819       element = Tile[x][y];
5820
5821       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5822       {
5823         element = MovingOrBlocked2Element(x, y);
5824
5825         if (!IS_EXPLOSION_PROOF(element))
5826           RemoveMovingField(x, y);
5827       }
5828
5829       // indestructible elements can only explode in center (but not flames)
5830       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5831                                            mode == EX_TYPE_BORDER)) ||
5832           element == EL_FLAMES)
5833         continue;
5834
5835       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5836          behaviour, for example when touching a yamyam that explodes to rocks
5837          with active deadly shield, a rock is created under the player !!! */
5838       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5839 #if 0
5840       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5841           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5842            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5843 #else
5844       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5845 #endif
5846       {
5847         if (IS_ACTIVE_BOMB(element))
5848         {
5849           // re-activate things under the bomb like gate or penguin
5850           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5851           Back[x][y] = 0;
5852         }
5853
5854         continue;
5855       }
5856
5857       // save walkable background elements while explosion on same tile
5858       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5859           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5860         Back[x][y] = element;
5861
5862       // ignite explodable elements reached by other explosion
5863       if (element == EL_EXPLOSION)
5864         element = Store2[x][y];
5865
5866       if (AmoebaNr[x][y] &&
5867           (element == EL_AMOEBA_FULL ||
5868            element == EL_BD_AMOEBA ||
5869            element == EL_AMOEBA_GROWING))
5870       {
5871         AmoebaCnt[AmoebaNr[x][y]]--;
5872         AmoebaCnt2[AmoebaNr[x][y]]--;
5873       }
5874
5875       RemoveField(x, y);
5876
5877       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5878       {
5879         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5880
5881         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5882
5883         if (PLAYERINFO(ex, ey)->use_murphy)
5884           Store[x][y] = EL_EMPTY;
5885       }
5886
5887       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5888       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5889       else if (IS_PLAYER_ELEMENT(center_element))
5890         Store[x][y] = EL_EMPTY;
5891       else if (center_element == EL_YAMYAM)
5892         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5893       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5894         Store[x][y] = element_info[center_element].content.e[xx][yy];
5895 #if 1
5896       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5897       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5898       // otherwise) -- FIX THIS !!!
5899       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5900         Store[x][y] = element_info[element].content.e[1][1];
5901 #else
5902       else if (!CAN_EXPLODE(element))
5903         Store[x][y] = element_info[element].content.e[1][1];
5904 #endif
5905       else
5906         Store[x][y] = EL_EMPTY;
5907
5908       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5909           center_element == EL_AMOEBA_TO_DIAMOND)
5910         Store2[x][y] = element;
5911
5912       Tile[x][y] = EL_EXPLOSION;
5913       GfxElement[x][y] = artwork_element;
5914
5915       ExplodePhase[x][y] = 1;
5916       ExplodeDelay[x][y] = last_phase;
5917
5918       Stop[x][y] = TRUE;
5919     }
5920
5921     if (center_element == EL_YAMYAM)
5922       game.yamyam_content_nr =
5923         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5924
5925     return;
5926   }
5927
5928   if (Stop[ex][ey])
5929     return;
5930
5931   x = ex;
5932   y = ey;
5933
5934   if (phase == 1)
5935     GfxFrame[x][y] = 0;         // restart explosion animation
5936
5937   last_phase = ExplodeDelay[x][y];
5938
5939   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5940
5941   // this can happen if the player leaves an explosion just in time
5942   if (GfxElement[x][y] == EL_UNDEFINED)
5943     GfxElement[x][y] = EL_EMPTY;
5944
5945   border_element = Store2[x][y];
5946   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5947     border_element = StorePlayer[x][y];
5948
5949   if (phase == element_info[border_element].ignition_delay ||
5950       phase == last_phase)
5951   {
5952     boolean border_explosion = FALSE;
5953
5954     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5955         !PLAYER_EXPLOSION_PROTECTED(x, y))
5956     {
5957       KillPlayerUnlessExplosionProtected(x, y);
5958       border_explosion = TRUE;
5959     }
5960     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5961     {
5962       Tile[x][y] = Store2[x][y];
5963       Store2[x][y] = 0;
5964       Bang(x, y);
5965       border_explosion = TRUE;
5966     }
5967     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5968     {
5969       AmoebaToDiamond(x, y);
5970       Store2[x][y] = 0;
5971       border_explosion = TRUE;
5972     }
5973
5974     // if an element just explodes due to another explosion (chain-reaction),
5975     // do not immediately end the new explosion when it was the last frame of
5976     // the explosion (as it would be done in the following "if"-statement!)
5977     if (border_explosion && phase == last_phase)
5978       return;
5979   }
5980
5981   if (phase == last_phase)
5982   {
5983     int element;
5984
5985     element = Tile[x][y] = Store[x][y];
5986     Store[x][y] = Store2[x][y] = 0;
5987     GfxElement[x][y] = EL_UNDEFINED;
5988
5989     // player can escape from explosions and might therefore be still alive
5990     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5991         element <= EL_PLAYER_IS_EXPLODING_4)
5992     {
5993       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5994       int explosion_element = EL_PLAYER_1 + player_nr;
5995       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5996       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5997
5998       if (level.use_explosion_element[player_nr])
5999         explosion_element = level.explosion_element[player_nr];
6000
6001       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6002                     element_info[explosion_element].content.e[xx][yy]);
6003     }
6004
6005     // restore probably existing indestructible background element
6006     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6007       element = Tile[x][y] = Back[x][y];
6008     Back[x][y] = 0;
6009
6010     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6011     GfxDir[x][y] = MV_NONE;
6012     ChangeDelay[x][y] = 0;
6013     ChangePage[x][y] = -1;
6014
6015     CustomValue[x][y] = 0;
6016
6017     InitField_WithBug2(x, y, FALSE);
6018
6019     TEST_DrawLevelField(x, y);
6020
6021     TestIfElementTouchesCustomElement(x, y);
6022
6023     if (GFX_CRUMBLED(element))
6024       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6025
6026     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6027       StorePlayer[x][y] = 0;
6028
6029     if (IS_PLAYER_ELEMENT(element))
6030       RelocatePlayer(x, y, element);
6031   }
6032   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6033   {
6034     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6035     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6036
6037     if (phase == delay)
6038       TEST_DrawLevelFieldCrumbled(x, y);
6039
6040     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6041     {
6042       DrawLevelElement(x, y, Back[x][y]);
6043       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6044     }
6045     else if (IS_WALKABLE_UNDER(Back[x][y]))
6046     {
6047       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6048       DrawLevelElementThruMask(x, y, Back[x][y]);
6049     }
6050     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6051       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6052   }
6053 }
6054
6055 static void DynaExplode(int ex, int ey)
6056 {
6057   int i, j;
6058   int dynabomb_element = Tile[ex][ey];
6059   int dynabomb_size = 1;
6060   boolean dynabomb_xl = FALSE;
6061   struct PlayerInfo *player;
6062   static int xy[4][2] =
6063   {
6064     { 0, -1 },
6065     { -1, 0 },
6066     { +1, 0 },
6067     { 0, +1 }
6068   };
6069
6070   if (IS_ACTIVE_BOMB(dynabomb_element))
6071   {
6072     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6073     dynabomb_size = player->dynabomb_size;
6074     dynabomb_xl = player->dynabomb_xl;
6075     player->dynabombs_left++;
6076   }
6077
6078   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6079
6080   for (i = 0; i < NUM_DIRECTIONS; i++)
6081   {
6082     for (j = 1; j <= dynabomb_size; j++)
6083     {
6084       int x = ex + j * xy[i][0];
6085       int y = ey + j * xy[i][1];
6086       int element;
6087
6088       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6089         break;
6090
6091       element = Tile[x][y];
6092
6093       // do not restart explosions of fields with active bombs
6094       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6095         continue;
6096
6097       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6098
6099       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6100           !IS_DIGGABLE(element) && !dynabomb_xl)
6101         break;
6102     }
6103   }
6104 }
6105
6106 void Bang(int x, int y)
6107 {
6108   int element = MovingOrBlocked2Element(x, y);
6109   int explosion_type = EX_TYPE_NORMAL;
6110
6111   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6112   {
6113     struct PlayerInfo *player = PLAYERINFO(x, y);
6114
6115     element = Tile[x][y] = player->initial_element;
6116
6117     if (level.use_explosion_element[player->index_nr])
6118     {
6119       int explosion_element = level.explosion_element[player->index_nr];
6120
6121       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6122         explosion_type = EX_TYPE_CROSS;
6123       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6124         explosion_type = EX_TYPE_CENTER;
6125     }
6126   }
6127
6128   switch (element)
6129   {
6130     case EL_BUG:
6131     case EL_SPACESHIP:
6132     case EL_BD_BUTTERFLY:
6133     case EL_BD_FIREFLY:
6134     case EL_YAMYAM:
6135     case EL_DARK_YAMYAM:
6136     case EL_ROBOT:
6137     case EL_PACMAN:
6138     case EL_MOLE:
6139       RaiseScoreElement(element);
6140       break;
6141
6142     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6143     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6144     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6145     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6146     case EL_DYNABOMB_INCREASE_NUMBER:
6147     case EL_DYNABOMB_INCREASE_SIZE:
6148     case EL_DYNABOMB_INCREASE_POWER:
6149       explosion_type = EX_TYPE_DYNA;
6150       break;
6151
6152     case EL_DC_LANDMINE:
6153       explosion_type = EX_TYPE_CENTER;
6154       break;
6155
6156     case EL_PENGUIN:
6157     case EL_LAMP:
6158     case EL_LAMP_ACTIVE:
6159     case EL_AMOEBA_TO_DIAMOND:
6160       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6161         explosion_type = EX_TYPE_CENTER;
6162       break;
6163
6164     default:
6165       if (element_info[element].explosion_type == EXPLODES_CROSS)
6166         explosion_type = EX_TYPE_CROSS;
6167       else if (element_info[element].explosion_type == EXPLODES_1X1)
6168         explosion_type = EX_TYPE_CENTER;
6169       break;
6170   }
6171
6172   if (explosion_type == EX_TYPE_DYNA)
6173     DynaExplode(x, y);
6174   else
6175     Explode(x, y, EX_PHASE_START, explosion_type);
6176
6177   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6178 }
6179
6180 static void SplashAcid(int x, int y)
6181 {
6182   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6183       (!IN_LEV_FIELD(x - 1, y - 2) ||
6184        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6185     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6186
6187   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6188       (!IN_LEV_FIELD(x + 1, y - 2) ||
6189        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6190     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6191
6192   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6193 }
6194
6195 static void InitBeltMovement(void)
6196 {
6197   static int belt_base_element[4] =
6198   {
6199     EL_CONVEYOR_BELT_1_LEFT,
6200     EL_CONVEYOR_BELT_2_LEFT,
6201     EL_CONVEYOR_BELT_3_LEFT,
6202     EL_CONVEYOR_BELT_4_LEFT
6203   };
6204   static int belt_base_active_element[4] =
6205   {
6206     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6207     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6208     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6209     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6210   };
6211
6212   int x, y, i, j;
6213
6214   // set frame order for belt animation graphic according to belt direction
6215   for (i = 0; i < NUM_BELTS; i++)
6216   {
6217     int belt_nr = i;
6218
6219     for (j = 0; j < NUM_BELT_PARTS; j++)
6220     {
6221       int element = belt_base_active_element[belt_nr] + j;
6222       int graphic_1 = el2img(element);
6223       int graphic_2 = el2panelimg(element);
6224
6225       if (game.belt_dir[i] == MV_LEFT)
6226       {
6227         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6228         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6229       }
6230       else
6231       {
6232         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6233         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6234       }
6235     }
6236   }
6237
6238   SCAN_PLAYFIELD(x, y)
6239   {
6240     int element = Tile[x][y];
6241
6242     for (i = 0; i < NUM_BELTS; i++)
6243     {
6244       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6245       {
6246         int e_belt_nr = getBeltNrFromBeltElement(element);
6247         int belt_nr = i;
6248
6249         if (e_belt_nr == belt_nr)
6250         {
6251           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6252
6253           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6254         }
6255       }
6256     }
6257   }
6258 }
6259
6260 static void ToggleBeltSwitch(int x, int y)
6261 {
6262   static int belt_base_element[4] =
6263   {
6264     EL_CONVEYOR_BELT_1_LEFT,
6265     EL_CONVEYOR_BELT_2_LEFT,
6266     EL_CONVEYOR_BELT_3_LEFT,
6267     EL_CONVEYOR_BELT_4_LEFT
6268   };
6269   static int belt_base_active_element[4] =
6270   {
6271     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6272     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6273     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6274     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6275   };
6276   static int belt_base_switch_element[4] =
6277   {
6278     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6279     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6280     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6281     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6282   };
6283   static int belt_move_dir[4] =
6284   {
6285     MV_LEFT,
6286     MV_NONE,
6287     MV_RIGHT,
6288     MV_NONE,
6289   };
6290
6291   int element = Tile[x][y];
6292   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6293   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6294   int belt_dir = belt_move_dir[belt_dir_nr];
6295   int xx, yy, i;
6296
6297   if (!IS_BELT_SWITCH(element))
6298     return;
6299
6300   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6301   game.belt_dir[belt_nr] = belt_dir;
6302
6303   if (belt_dir_nr == 3)
6304     belt_dir_nr = 1;
6305
6306   // set frame order for belt animation graphic according to belt direction
6307   for (i = 0; i < NUM_BELT_PARTS; i++)
6308   {
6309     int element = belt_base_active_element[belt_nr] + i;
6310     int graphic_1 = el2img(element);
6311     int graphic_2 = el2panelimg(element);
6312
6313     if (belt_dir == MV_LEFT)
6314     {
6315       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6316       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6317     }
6318     else
6319     {
6320       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6321       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6322     }
6323   }
6324
6325   SCAN_PLAYFIELD(xx, yy)
6326   {
6327     int element = Tile[xx][yy];
6328
6329     if (IS_BELT_SWITCH(element))
6330     {
6331       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6332
6333       if (e_belt_nr == belt_nr)
6334       {
6335         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6336         TEST_DrawLevelField(xx, yy);
6337       }
6338     }
6339     else if (IS_BELT(element) && belt_dir != MV_NONE)
6340     {
6341       int e_belt_nr = getBeltNrFromBeltElement(element);
6342
6343       if (e_belt_nr == belt_nr)
6344       {
6345         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6346
6347         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6348         TEST_DrawLevelField(xx, yy);
6349       }
6350     }
6351     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6352     {
6353       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6354
6355       if (e_belt_nr == belt_nr)
6356       {
6357         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6358
6359         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6360         TEST_DrawLevelField(xx, yy);
6361       }
6362     }
6363   }
6364 }
6365
6366 static void ToggleSwitchgateSwitch(int x, int y)
6367 {
6368   int xx, yy;
6369
6370   game.switchgate_pos = !game.switchgate_pos;
6371
6372   SCAN_PLAYFIELD(xx, yy)
6373   {
6374     int element = Tile[xx][yy];
6375
6376     if (element == EL_SWITCHGATE_SWITCH_UP)
6377     {
6378       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6379       TEST_DrawLevelField(xx, yy);
6380     }
6381     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6382     {
6383       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6384       TEST_DrawLevelField(xx, yy);
6385     }
6386     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6387     {
6388       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6389       TEST_DrawLevelField(xx, yy);
6390     }
6391     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6392     {
6393       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6394       TEST_DrawLevelField(xx, yy);
6395     }
6396     else if (element == EL_SWITCHGATE_OPEN ||
6397              element == EL_SWITCHGATE_OPENING)
6398     {
6399       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6400
6401       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6402     }
6403     else if (element == EL_SWITCHGATE_CLOSED ||
6404              element == EL_SWITCHGATE_CLOSING)
6405     {
6406       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6407
6408       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6409     }
6410   }
6411 }
6412
6413 static int getInvisibleActiveFromInvisibleElement(int element)
6414 {
6415   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6416           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6417           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6418           element);
6419 }
6420
6421 static int getInvisibleFromInvisibleActiveElement(int element)
6422 {
6423   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6424           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6425           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6426           element);
6427 }
6428
6429 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6430 {
6431   int x, y;
6432
6433   SCAN_PLAYFIELD(x, y)
6434   {
6435     int element = Tile[x][y];
6436
6437     if (element == EL_LIGHT_SWITCH &&
6438         game.light_time_left > 0)
6439     {
6440       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6441       TEST_DrawLevelField(x, y);
6442     }
6443     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6444              game.light_time_left == 0)
6445     {
6446       Tile[x][y] = EL_LIGHT_SWITCH;
6447       TEST_DrawLevelField(x, y);
6448     }
6449     else if (element == EL_EMC_DRIPPER &&
6450              game.light_time_left > 0)
6451     {
6452       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6453       TEST_DrawLevelField(x, y);
6454     }
6455     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6456              game.light_time_left == 0)
6457     {
6458       Tile[x][y] = EL_EMC_DRIPPER;
6459       TEST_DrawLevelField(x, y);
6460     }
6461     else if (element == EL_INVISIBLE_STEELWALL ||
6462              element == EL_INVISIBLE_WALL ||
6463              element == EL_INVISIBLE_SAND)
6464     {
6465       if (game.light_time_left > 0)
6466         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6467
6468       TEST_DrawLevelField(x, y);
6469
6470       // uncrumble neighbour fields, if needed
6471       if (element == EL_INVISIBLE_SAND)
6472         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6473     }
6474     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6475              element == EL_INVISIBLE_WALL_ACTIVE ||
6476              element == EL_INVISIBLE_SAND_ACTIVE)
6477     {
6478       if (game.light_time_left == 0)
6479         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6480
6481       TEST_DrawLevelField(x, y);
6482
6483       // re-crumble neighbour fields, if needed
6484       if (element == EL_INVISIBLE_SAND)
6485         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6486     }
6487   }
6488 }
6489
6490 static void RedrawAllInvisibleElementsForLenses(void)
6491 {
6492   int x, y;
6493
6494   SCAN_PLAYFIELD(x, y)
6495   {
6496     int element = Tile[x][y];
6497
6498     if (element == EL_EMC_DRIPPER &&
6499         game.lenses_time_left > 0)
6500     {
6501       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6502       TEST_DrawLevelField(x, y);
6503     }
6504     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6505              game.lenses_time_left == 0)
6506     {
6507       Tile[x][y] = EL_EMC_DRIPPER;
6508       TEST_DrawLevelField(x, y);
6509     }
6510     else if (element == EL_INVISIBLE_STEELWALL ||
6511              element == EL_INVISIBLE_WALL ||
6512              element == EL_INVISIBLE_SAND)
6513     {
6514       if (game.lenses_time_left > 0)
6515         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6516
6517       TEST_DrawLevelField(x, y);
6518
6519       // uncrumble neighbour fields, if needed
6520       if (element == EL_INVISIBLE_SAND)
6521         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6522     }
6523     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6524              element == EL_INVISIBLE_WALL_ACTIVE ||
6525              element == EL_INVISIBLE_SAND_ACTIVE)
6526     {
6527       if (game.lenses_time_left == 0)
6528         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6529
6530       TEST_DrawLevelField(x, y);
6531
6532       // re-crumble neighbour fields, if needed
6533       if (element == EL_INVISIBLE_SAND)
6534         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6535     }
6536   }
6537 }
6538
6539 static void RedrawAllInvisibleElementsForMagnifier(void)
6540 {
6541   int x, y;
6542
6543   SCAN_PLAYFIELD(x, y)
6544   {
6545     int element = Tile[x][y];
6546
6547     if (element == EL_EMC_FAKE_GRASS &&
6548         game.magnify_time_left > 0)
6549     {
6550       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6551       TEST_DrawLevelField(x, y);
6552     }
6553     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6554              game.magnify_time_left == 0)
6555     {
6556       Tile[x][y] = EL_EMC_FAKE_GRASS;
6557       TEST_DrawLevelField(x, y);
6558     }
6559     else if (IS_GATE_GRAY(element) &&
6560              game.magnify_time_left > 0)
6561     {
6562       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6563                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6564                     IS_EM_GATE_GRAY(element) ?
6565                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6566                     IS_EMC_GATE_GRAY(element) ?
6567                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6568                     IS_DC_GATE_GRAY(element) ?
6569                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6570                     element);
6571       TEST_DrawLevelField(x, y);
6572     }
6573     else if (IS_GATE_GRAY_ACTIVE(element) &&
6574              game.magnify_time_left == 0)
6575     {
6576       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6577                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6578                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6579                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6580                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6581                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6582                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6583                     EL_DC_GATE_WHITE_GRAY :
6584                     element);
6585       TEST_DrawLevelField(x, y);
6586     }
6587   }
6588 }
6589
6590 static void ToggleLightSwitch(int x, int y)
6591 {
6592   int element = Tile[x][y];
6593
6594   game.light_time_left =
6595     (element == EL_LIGHT_SWITCH ?
6596      level.time_light * FRAMES_PER_SECOND : 0);
6597
6598   RedrawAllLightSwitchesAndInvisibleElements();
6599 }
6600
6601 static void ActivateTimegateSwitch(int x, int y)
6602 {
6603   int xx, yy;
6604
6605   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6606
6607   SCAN_PLAYFIELD(xx, yy)
6608   {
6609     int element = Tile[xx][yy];
6610
6611     if (element == EL_TIMEGATE_CLOSED ||
6612         element == EL_TIMEGATE_CLOSING)
6613     {
6614       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6615       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6616     }
6617
6618     /*
6619     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6620     {
6621       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6622       TEST_DrawLevelField(xx, yy);
6623     }
6624     */
6625
6626   }
6627
6628   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6629                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6630 }
6631
6632 static void Impact(int x, int y)
6633 {
6634   boolean last_line = (y == lev_fieldy - 1);
6635   boolean object_hit = FALSE;
6636   boolean impact = (last_line || object_hit);
6637   int element = Tile[x][y];
6638   int smashed = EL_STEELWALL;
6639
6640   if (!last_line)       // check if element below was hit
6641   {
6642     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6643       return;
6644
6645     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6646                                          MovDir[x][y + 1] != MV_DOWN ||
6647                                          MovPos[x][y + 1] <= TILEY / 2));
6648
6649     // do not smash moving elements that left the smashed field in time
6650     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6651         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6652       object_hit = FALSE;
6653
6654 #if USE_QUICKSAND_IMPACT_BUGFIX
6655     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6656     {
6657       RemoveMovingField(x, y + 1);
6658       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6659       Tile[x][y + 2] = EL_ROCK;
6660       TEST_DrawLevelField(x, y + 2);
6661
6662       object_hit = TRUE;
6663     }
6664
6665     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6666     {
6667       RemoveMovingField(x, y + 1);
6668       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6669       Tile[x][y + 2] = EL_ROCK;
6670       TEST_DrawLevelField(x, y + 2);
6671
6672       object_hit = TRUE;
6673     }
6674 #endif
6675
6676     if (object_hit)
6677       smashed = MovingOrBlocked2Element(x, y + 1);
6678
6679     impact = (last_line || object_hit);
6680   }
6681
6682   if (!last_line && smashed == EL_ACID) // element falls into acid
6683   {
6684     SplashAcid(x, y + 1);
6685     return;
6686   }
6687
6688   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6689   // only reset graphic animation if graphic really changes after impact
6690   if (impact &&
6691       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6692   {
6693     ResetGfxAnimation(x, y);
6694     TEST_DrawLevelField(x, y);
6695   }
6696
6697   if (impact && CAN_EXPLODE_IMPACT(element))
6698   {
6699     Bang(x, y);
6700     return;
6701   }
6702   else if (impact && element == EL_PEARL &&
6703            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6704   {
6705     ResetGfxAnimation(x, y);
6706
6707     Tile[x][y] = EL_PEARL_BREAKING;
6708     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6709     return;
6710   }
6711   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6712   {
6713     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6714
6715     return;
6716   }
6717
6718   if (impact && element == EL_AMOEBA_DROP)
6719   {
6720     if (object_hit && IS_PLAYER(x, y + 1))
6721       KillPlayerUnlessEnemyProtected(x, y + 1);
6722     else if (object_hit && smashed == EL_PENGUIN)
6723       Bang(x, y + 1);
6724     else
6725     {
6726       Tile[x][y] = EL_AMOEBA_GROWING;
6727       Store[x][y] = EL_AMOEBA_WET;
6728
6729       ResetRandomAnimationValue(x, y);
6730     }
6731     return;
6732   }
6733
6734   if (object_hit)               // check which object was hit
6735   {
6736     if ((CAN_PASS_MAGIC_WALL(element) && 
6737          (smashed == EL_MAGIC_WALL ||
6738           smashed == EL_BD_MAGIC_WALL)) ||
6739         (CAN_PASS_DC_MAGIC_WALL(element) &&
6740          smashed == EL_DC_MAGIC_WALL))
6741     {
6742       int xx, yy;
6743       int activated_magic_wall =
6744         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6745          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6746          EL_DC_MAGIC_WALL_ACTIVE);
6747
6748       // activate magic wall / mill
6749       SCAN_PLAYFIELD(xx, yy)
6750       {
6751         if (Tile[xx][yy] == smashed)
6752           Tile[xx][yy] = activated_magic_wall;
6753       }
6754
6755       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6756       game.magic_wall_active = TRUE;
6757
6758       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6759                             SND_MAGIC_WALL_ACTIVATING :
6760                             smashed == EL_BD_MAGIC_WALL ?
6761                             SND_BD_MAGIC_WALL_ACTIVATING :
6762                             SND_DC_MAGIC_WALL_ACTIVATING));
6763     }
6764
6765     if (IS_PLAYER(x, y + 1))
6766     {
6767       if (CAN_SMASH_PLAYER(element))
6768       {
6769         KillPlayerUnlessEnemyProtected(x, y + 1);
6770         return;
6771       }
6772     }
6773     else if (smashed == EL_PENGUIN)
6774     {
6775       if (CAN_SMASH_PLAYER(element))
6776       {
6777         Bang(x, y + 1);
6778         return;
6779       }
6780     }
6781     else if (element == EL_BD_DIAMOND)
6782     {
6783       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6784       {
6785         Bang(x, y + 1);
6786         return;
6787       }
6788     }
6789     else if (((element == EL_SP_INFOTRON ||
6790                element == EL_SP_ZONK) &&
6791               (smashed == EL_SP_SNIKSNAK ||
6792                smashed == EL_SP_ELECTRON ||
6793                smashed == EL_SP_DISK_ORANGE)) ||
6794              (element == EL_SP_INFOTRON &&
6795               smashed == EL_SP_DISK_YELLOW))
6796     {
6797       Bang(x, y + 1);
6798       return;
6799     }
6800     else if (CAN_SMASH_EVERYTHING(element))
6801     {
6802       if (IS_CLASSIC_ENEMY(smashed) ||
6803           CAN_EXPLODE_SMASHED(smashed))
6804       {
6805         Bang(x, y + 1);
6806         return;
6807       }
6808       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6809       {
6810         if (smashed == EL_LAMP ||
6811             smashed == EL_LAMP_ACTIVE)
6812         {
6813           Bang(x, y + 1);
6814           return;
6815         }
6816         else if (smashed == EL_NUT)
6817         {
6818           Tile[x][y + 1] = EL_NUT_BREAKING;
6819           PlayLevelSound(x, y, SND_NUT_BREAKING);
6820           RaiseScoreElement(EL_NUT);
6821           return;
6822         }
6823         else if (smashed == EL_PEARL)
6824         {
6825           ResetGfxAnimation(x, y);
6826
6827           Tile[x][y + 1] = EL_PEARL_BREAKING;
6828           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6829           return;
6830         }
6831         else if (smashed == EL_DIAMOND)
6832         {
6833           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6834           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6835           return;
6836         }
6837         else if (IS_BELT_SWITCH(smashed))
6838         {
6839           ToggleBeltSwitch(x, y + 1);
6840         }
6841         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6842                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6843                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6844                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6845         {
6846           ToggleSwitchgateSwitch(x, y + 1);
6847         }
6848         else if (smashed == EL_LIGHT_SWITCH ||
6849                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6850         {
6851           ToggleLightSwitch(x, y + 1);
6852         }
6853         else
6854         {
6855           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6856
6857           CheckElementChangeBySide(x, y + 1, smashed, element,
6858                                    CE_SWITCHED, CH_SIDE_TOP);
6859           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6860                                             CH_SIDE_TOP);
6861         }
6862       }
6863       else
6864       {
6865         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6866       }
6867     }
6868   }
6869
6870   // play sound of magic wall / mill
6871   if (!last_line &&
6872       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6873        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6874        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6875   {
6876     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6877       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6878     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6879       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6880     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6881       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6882
6883     return;
6884   }
6885
6886   // play sound of object that hits the ground
6887   if (last_line || object_hit)
6888     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6889 }
6890
6891 static void TurnRoundExt(int x, int y)
6892 {
6893   static struct
6894   {
6895     int dx, dy;
6896   } move_xy[] =
6897   {
6898     {  0,  0 },
6899     { -1,  0 },
6900     { +1,  0 },
6901     {  0,  0 },
6902     {  0, -1 },
6903     {  0,  0 }, { 0, 0 }, { 0, 0 },
6904     {  0, +1 }
6905   };
6906   static struct
6907   {
6908     int left, right, back;
6909   } turn[] =
6910   {
6911     { 0,        0,              0        },
6912     { MV_DOWN,  MV_UP,          MV_RIGHT },
6913     { MV_UP,    MV_DOWN,        MV_LEFT  },
6914     { 0,        0,              0        },
6915     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6916     { 0,        0,              0        },
6917     { 0,        0,              0        },
6918     { 0,        0,              0        },
6919     { MV_RIGHT, MV_LEFT,        MV_UP    }
6920   };
6921
6922   int element = Tile[x][y];
6923   int move_pattern = element_info[element].move_pattern;
6924
6925   int old_move_dir = MovDir[x][y];
6926   int left_dir  = turn[old_move_dir].left;
6927   int right_dir = turn[old_move_dir].right;
6928   int back_dir  = turn[old_move_dir].back;
6929
6930   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6931   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6932   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6933   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6934
6935   int left_x  = x + left_dx,  left_y  = y + left_dy;
6936   int right_x = x + right_dx, right_y = y + right_dy;
6937   int move_x  = x + move_dx,  move_y  = y + move_dy;
6938
6939   int xx, yy;
6940
6941   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6942   {
6943     TestIfBadThingTouchesOtherBadThing(x, y);
6944
6945     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6946       MovDir[x][y] = right_dir;
6947     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6948       MovDir[x][y] = left_dir;
6949
6950     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6951       MovDelay[x][y] = 9;
6952     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6953       MovDelay[x][y] = 1;
6954   }
6955   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6956   {
6957     TestIfBadThingTouchesOtherBadThing(x, y);
6958
6959     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6960       MovDir[x][y] = left_dir;
6961     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6962       MovDir[x][y] = right_dir;
6963
6964     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6965       MovDelay[x][y] = 9;
6966     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6967       MovDelay[x][y] = 1;
6968   }
6969   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6970   {
6971     TestIfBadThingTouchesOtherBadThing(x, y);
6972
6973     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6974       MovDir[x][y] = left_dir;
6975     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6976       MovDir[x][y] = right_dir;
6977
6978     if (MovDir[x][y] != old_move_dir)
6979       MovDelay[x][y] = 9;
6980   }
6981   else if (element == EL_YAMYAM)
6982   {
6983     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6984     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6985
6986     if (can_turn_left && can_turn_right)
6987       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6988     else if (can_turn_left)
6989       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6990     else if (can_turn_right)
6991       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6992     else
6993       MovDir[x][y] = back_dir;
6994
6995     MovDelay[x][y] = 16 + 16 * RND(3);
6996   }
6997   else if (element == EL_DARK_YAMYAM)
6998   {
6999     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7000                                                          left_x, left_y);
7001     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7002                                                          right_x, right_y);
7003
7004     if (can_turn_left && can_turn_right)
7005       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7006     else if (can_turn_left)
7007       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7008     else if (can_turn_right)
7009       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7010     else
7011       MovDir[x][y] = back_dir;
7012
7013     MovDelay[x][y] = 16 + 16 * RND(3);
7014   }
7015   else if (element == EL_PACMAN)
7016   {
7017     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7018     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7019
7020     if (can_turn_left && can_turn_right)
7021       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7022     else if (can_turn_left)
7023       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7024     else if (can_turn_right)
7025       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7026     else
7027       MovDir[x][y] = back_dir;
7028
7029     MovDelay[x][y] = 6 + RND(40);
7030   }
7031   else if (element == EL_PIG)
7032   {
7033     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7034     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7035     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7036     boolean should_turn_left, should_turn_right, should_move_on;
7037     int rnd_value = 24;
7038     int rnd = RND(rnd_value);
7039
7040     should_turn_left = (can_turn_left &&
7041                         (!can_move_on ||
7042                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7043                                                    y + back_dy + left_dy)));
7044     should_turn_right = (can_turn_right &&
7045                          (!can_move_on ||
7046                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7047                                                     y + back_dy + right_dy)));
7048     should_move_on = (can_move_on &&
7049                       (!can_turn_left ||
7050                        !can_turn_right ||
7051                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7052                                                  y + move_dy + left_dy) ||
7053                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7054                                                  y + move_dy + right_dy)));
7055
7056     if (should_turn_left || should_turn_right || should_move_on)
7057     {
7058       if (should_turn_left && should_turn_right && should_move_on)
7059         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7060                         rnd < 2 * rnd_value / 3 ? right_dir :
7061                         old_move_dir);
7062       else if (should_turn_left && should_turn_right)
7063         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7064       else if (should_turn_left && should_move_on)
7065         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7066       else if (should_turn_right && should_move_on)
7067         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7068       else if (should_turn_left)
7069         MovDir[x][y] = left_dir;
7070       else if (should_turn_right)
7071         MovDir[x][y] = right_dir;
7072       else if (should_move_on)
7073         MovDir[x][y] = old_move_dir;
7074     }
7075     else if (can_move_on && rnd > rnd_value / 8)
7076       MovDir[x][y] = old_move_dir;
7077     else if (can_turn_left && can_turn_right)
7078       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7079     else if (can_turn_left && rnd > rnd_value / 8)
7080       MovDir[x][y] = left_dir;
7081     else if (can_turn_right && rnd > rnd_value/8)
7082       MovDir[x][y] = right_dir;
7083     else
7084       MovDir[x][y] = back_dir;
7085
7086     xx = x + move_xy[MovDir[x][y]].dx;
7087     yy = y + move_xy[MovDir[x][y]].dy;
7088
7089     if (!IN_LEV_FIELD(xx, yy) ||
7090         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7091       MovDir[x][y] = old_move_dir;
7092
7093     MovDelay[x][y] = 0;
7094   }
7095   else if (element == EL_DRAGON)
7096   {
7097     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7098     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7099     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7100     int rnd_value = 24;
7101     int rnd = RND(rnd_value);
7102
7103     if (can_move_on && rnd > rnd_value / 8)
7104       MovDir[x][y] = old_move_dir;
7105     else if (can_turn_left && can_turn_right)
7106       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7107     else if (can_turn_left && rnd > rnd_value / 8)
7108       MovDir[x][y] = left_dir;
7109     else if (can_turn_right && rnd > rnd_value / 8)
7110       MovDir[x][y] = right_dir;
7111     else
7112       MovDir[x][y] = back_dir;
7113
7114     xx = x + move_xy[MovDir[x][y]].dx;
7115     yy = y + move_xy[MovDir[x][y]].dy;
7116
7117     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7118       MovDir[x][y] = old_move_dir;
7119
7120     MovDelay[x][y] = 0;
7121   }
7122   else if (element == EL_MOLE)
7123   {
7124     boolean can_move_on =
7125       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7126                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7127                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7128     if (!can_move_on)
7129     {
7130       boolean can_turn_left =
7131         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7132                               IS_AMOEBOID(Tile[left_x][left_y])));
7133
7134       boolean can_turn_right =
7135         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7136                               IS_AMOEBOID(Tile[right_x][right_y])));
7137
7138       if (can_turn_left && can_turn_right)
7139         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7140       else if (can_turn_left)
7141         MovDir[x][y] = left_dir;
7142       else
7143         MovDir[x][y] = right_dir;
7144     }
7145
7146     if (MovDir[x][y] != old_move_dir)
7147       MovDelay[x][y] = 9;
7148   }
7149   else if (element == EL_BALLOON)
7150   {
7151     MovDir[x][y] = game.wind_direction;
7152     MovDelay[x][y] = 0;
7153   }
7154   else if (element == EL_SPRING)
7155   {
7156     if (MovDir[x][y] & MV_HORIZONTAL)
7157     {
7158       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7159           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7160       {
7161         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7162         ResetGfxAnimation(move_x, move_y);
7163         TEST_DrawLevelField(move_x, move_y);
7164
7165         MovDir[x][y] = back_dir;
7166       }
7167       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7168                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7169         MovDir[x][y] = MV_NONE;
7170     }
7171
7172     MovDelay[x][y] = 0;
7173   }
7174   else if (element == EL_ROBOT ||
7175            element == EL_SATELLITE ||
7176            element == EL_PENGUIN ||
7177            element == EL_EMC_ANDROID)
7178   {
7179     int attr_x = -1, attr_y = -1;
7180
7181     if (game.all_players_gone)
7182     {
7183       attr_x = game.exit_x;
7184       attr_y = game.exit_y;
7185     }
7186     else
7187     {
7188       int i;
7189
7190       for (i = 0; i < MAX_PLAYERS; i++)
7191       {
7192         struct PlayerInfo *player = &stored_player[i];
7193         int jx = player->jx, jy = player->jy;
7194
7195         if (!player->active)
7196           continue;
7197
7198         if (attr_x == -1 ||
7199             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7200         {
7201           attr_x = jx;
7202           attr_y = jy;
7203         }
7204       }
7205     }
7206
7207     if (element == EL_ROBOT &&
7208         game.robot_wheel_x >= 0 &&
7209         game.robot_wheel_y >= 0 &&
7210         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7211          game.engine_version < VERSION_IDENT(3,1,0,0)))
7212     {
7213       attr_x = game.robot_wheel_x;
7214       attr_y = game.robot_wheel_y;
7215     }
7216
7217     if (element == EL_PENGUIN)
7218     {
7219       int i;
7220       static int xy[4][2] =
7221       {
7222         { 0, -1 },
7223         { -1, 0 },
7224         { +1, 0 },
7225         { 0, +1 }
7226       };
7227
7228       for (i = 0; i < NUM_DIRECTIONS; i++)
7229       {
7230         int ex = x + xy[i][0];
7231         int ey = y + xy[i][1];
7232
7233         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7234                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7235                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7236                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7237         {
7238           attr_x = ex;
7239           attr_y = ey;
7240           break;
7241         }
7242       }
7243     }
7244
7245     MovDir[x][y] = MV_NONE;
7246     if (attr_x < x)
7247       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7248     else if (attr_x > x)
7249       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7250     if (attr_y < y)
7251       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7252     else if (attr_y > y)
7253       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7254
7255     if (element == EL_ROBOT)
7256     {
7257       int newx, newy;
7258
7259       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7260         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7261       Moving2Blocked(x, y, &newx, &newy);
7262
7263       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7264         MovDelay[x][y] = 8 + 8 * !RND(3);
7265       else
7266         MovDelay[x][y] = 16;
7267     }
7268     else if (element == EL_PENGUIN)
7269     {
7270       int newx, newy;
7271
7272       MovDelay[x][y] = 1;
7273
7274       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7275       {
7276         boolean first_horiz = RND(2);
7277         int new_move_dir = MovDir[x][y];
7278
7279         MovDir[x][y] =
7280           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7281         Moving2Blocked(x, y, &newx, &newy);
7282
7283         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7284           return;
7285
7286         MovDir[x][y] =
7287           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7288         Moving2Blocked(x, y, &newx, &newy);
7289
7290         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7291           return;
7292
7293         MovDir[x][y] = old_move_dir;
7294         return;
7295       }
7296     }
7297     else if (element == EL_SATELLITE)
7298     {
7299       int newx, newy;
7300
7301       MovDelay[x][y] = 1;
7302
7303       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7304       {
7305         boolean first_horiz = RND(2);
7306         int new_move_dir = MovDir[x][y];
7307
7308         MovDir[x][y] =
7309           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7310         Moving2Blocked(x, y, &newx, &newy);
7311
7312         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7313           return;
7314
7315         MovDir[x][y] =
7316           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7317         Moving2Blocked(x, y, &newx, &newy);
7318
7319         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7320           return;
7321
7322         MovDir[x][y] = old_move_dir;
7323         return;
7324       }
7325     }
7326     else if (element == EL_EMC_ANDROID)
7327     {
7328       static int check_pos[16] =
7329       {
7330         -1,             //  0 => (invalid)
7331         7,              //  1 => MV_LEFT
7332         3,              //  2 => MV_RIGHT
7333         -1,             //  3 => (invalid)
7334         1,              //  4 =>            MV_UP
7335         0,              //  5 => MV_LEFT  | MV_UP
7336         2,              //  6 => MV_RIGHT | MV_UP
7337         -1,             //  7 => (invalid)
7338         5,              //  8 =>            MV_DOWN
7339         6,              //  9 => MV_LEFT  | MV_DOWN
7340         4,              // 10 => MV_RIGHT | MV_DOWN
7341         -1,             // 11 => (invalid)
7342         -1,             // 12 => (invalid)
7343         -1,             // 13 => (invalid)
7344         -1,             // 14 => (invalid)
7345         -1,             // 15 => (invalid)
7346       };
7347       static struct
7348       {
7349         int dx, dy;
7350         int dir;
7351       } check_xy[8] =
7352       {
7353         { -1, -1,       MV_LEFT  | MV_UP   },
7354         {  0, -1,                  MV_UP   },
7355         { +1, -1,       MV_RIGHT | MV_UP   },
7356         { +1,  0,       MV_RIGHT           },
7357         { +1, +1,       MV_RIGHT | MV_DOWN },
7358         {  0, +1,                  MV_DOWN },
7359         { -1, +1,       MV_LEFT  | MV_DOWN },
7360         { -1,  0,       MV_LEFT            },
7361       };
7362       int start_pos, check_order;
7363       boolean can_clone = FALSE;
7364       int i;
7365
7366       // check if there is any free field around current position
7367       for (i = 0; i < 8; i++)
7368       {
7369         int newx = x + check_xy[i].dx;
7370         int newy = y + check_xy[i].dy;
7371
7372         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7373         {
7374           can_clone = TRUE;
7375
7376           break;
7377         }
7378       }
7379
7380       if (can_clone)            // randomly find an element to clone
7381       {
7382         can_clone = FALSE;
7383
7384         start_pos = check_pos[RND(8)];
7385         check_order = (RND(2) ? -1 : +1);
7386
7387         for (i = 0; i < 8; i++)
7388         {
7389           int pos_raw = start_pos + i * check_order;
7390           int pos = (pos_raw + 8) % 8;
7391           int newx = x + check_xy[pos].dx;
7392           int newy = y + check_xy[pos].dy;
7393
7394           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7395           {
7396             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7397             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7398
7399             Store[x][y] = Tile[newx][newy];
7400
7401             can_clone = TRUE;
7402
7403             break;
7404           }
7405         }
7406       }
7407
7408       if (can_clone)            // randomly find a direction to move
7409       {
7410         can_clone = FALSE;
7411
7412         start_pos = check_pos[RND(8)];
7413         check_order = (RND(2) ? -1 : +1);
7414
7415         for (i = 0; i < 8; i++)
7416         {
7417           int pos_raw = start_pos + i * check_order;
7418           int pos = (pos_raw + 8) % 8;
7419           int newx = x + check_xy[pos].dx;
7420           int newy = y + check_xy[pos].dy;
7421           int new_move_dir = check_xy[pos].dir;
7422
7423           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7424           {
7425             MovDir[x][y] = new_move_dir;
7426             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7427
7428             can_clone = TRUE;
7429
7430             break;
7431           }
7432         }
7433       }
7434
7435       if (can_clone)            // cloning and moving successful
7436         return;
7437
7438       // cannot clone -- try to move towards player
7439
7440       start_pos = check_pos[MovDir[x][y] & 0x0f];
7441       check_order = (RND(2) ? -1 : +1);
7442
7443       for (i = 0; i < 3; i++)
7444       {
7445         // first check start_pos, then previous/next or (next/previous) pos
7446         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7447         int pos = (pos_raw + 8) % 8;
7448         int newx = x + check_xy[pos].dx;
7449         int newy = y + check_xy[pos].dy;
7450         int new_move_dir = check_xy[pos].dir;
7451
7452         if (IS_PLAYER(newx, newy))
7453           break;
7454
7455         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7456         {
7457           MovDir[x][y] = new_move_dir;
7458           MovDelay[x][y] = level.android_move_time * 8 + 1;
7459
7460           break;
7461         }
7462       }
7463     }
7464   }
7465   else if (move_pattern == MV_TURNING_LEFT ||
7466            move_pattern == MV_TURNING_RIGHT ||
7467            move_pattern == MV_TURNING_LEFT_RIGHT ||
7468            move_pattern == MV_TURNING_RIGHT_LEFT ||
7469            move_pattern == MV_TURNING_RANDOM ||
7470            move_pattern == MV_ALL_DIRECTIONS)
7471   {
7472     boolean can_turn_left =
7473       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7474     boolean can_turn_right =
7475       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7476
7477     if (element_info[element].move_stepsize == 0)       // "not moving"
7478       return;
7479
7480     if (move_pattern == MV_TURNING_LEFT)
7481       MovDir[x][y] = left_dir;
7482     else if (move_pattern == MV_TURNING_RIGHT)
7483       MovDir[x][y] = right_dir;
7484     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7485       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7486     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7487       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7488     else if (move_pattern == MV_TURNING_RANDOM)
7489       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7490                       can_turn_right && !can_turn_left ? right_dir :
7491                       RND(2) ? left_dir : right_dir);
7492     else if (can_turn_left && can_turn_right)
7493       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7494     else if (can_turn_left)
7495       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7496     else if (can_turn_right)
7497       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7498     else
7499       MovDir[x][y] = back_dir;
7500
7501     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7502   }
7503   else if (move_pattern == MV_HORIZONTAL ||
7504            move_pattern == MV_VERTICAL)
7505   {
7506     if (move_pattern & old_move_dir)
7507       MovDir[x][y] = back_dir;
7508     else if (move_pattern == MV_HORIZONTAL)
7509       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7510     else if (move_pattern == MV_VERTICAL)
7511       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7512
7513     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7514   }
7515   else if (move_pattern & MV_ANY_DIRECTION)
7516   {
7517     MovDir[x][y] = move_pattern;
7518     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7519   }
7520   else if (move_pattern & MV_WIND_DIRECTION)
7521   {
7522     MovDir[x][y] = game.wind_direction;
7523     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7524   }
7525   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7526   {
7527     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7528       MovDir[x][y] = left_dir;
7529     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7530       MovDir[x][y] = right_dir;
7531
7532     if (MovDir[x][y] != old_move_dir)
7533       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7534   }
7535   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7536   {
7537     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7538       MovDir[x][y] = right_dir;
7539     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7540       MovDir[x][y] = left_dir;
7541
7542     if (MovDir[x][y] != old_move_dir)
7543       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7544   }
7545   else if (move_pattern == MV_TOWARDS_PLAYER ||
7546            move_pattern == MV_AWAY_FROM_PLAYER)
7547   {
7548     int attr_x = -1, attr_y = -1;
7549     int newx, newy;
7550     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7551
7552     if (game.all_players_gone)
7553     {
7554       attr_x = game.exit_x;
7555       attr_y = game.exit_y;
7556     }
7557     else
7558     {
7559       int i;
7560
7561       for (i = 0; i < MAX_PLAYERS; i++)
7562       {
7563         struct PlayerInfo *player = &stored_player[i];
7564         int jx = player->jx, jy = player->jy;
7565
7566         if (!player->active)
7567           continue;
7568
7569         if (attr_x == -1 ||
7570             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7571         {
7572           attr_x = jx;
7573           attr_y = jy;
7574         }
7575       }
7576     }
7577
7578     MovDir[x][y] = MV_NONE;
7579     if (attr_x < x)
7580       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7581     else if (attr_x > x)
7582       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7583     if (attr_y < y)
7584       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7585     else if (attr_y > y)
7586       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7587
7588     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7589
7590     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7591     {
7592       boolean first_horiz = RND(2);
7593       int new_move_dir = MovDir[x][y];
7594
7595       if (element_info[element].move_stepsize == 0)     // "not moving"
7596       {
7597         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7598         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7599
7600         return;
7601       }
7602
7603       MovDir[x][y] =
7604         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7605       Moving2Blocked(x, y, &newx, &newy);
7606
7607       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7608         return;
7609
7610       MovDir[x][y] =
7611         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7612       Moving2Blocked(x, y, &newx, &newy);
7613
7614       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7615         return;
7616
7617       MovDir[x][y] = old_move_dir;
7618     }
7619   }
7620   else if (move_pattern == MV_WHEN_PUSHED ||
7621            move_pattern == MV_WHEN_DROPPED)
7622   {
7623     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7624       MovDir[x][y] = MV_NONE;
7625
7626     MovDelay[x][y] = 0;
7627   }
7628   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7629   {
7630     static int test_xy[7][2] =
7631     {
7632       { 0, -1 },
7633       { -1, 0 },
7634       { +1, 0 },
7635       { 0, +1 },
7636       { 0, -1 },
7637       { -1, 0 },
7638       { +1, 0 },
7639     };
7640     static int test_dir[7] =
7641     {
7642       MV_UP,
7643       MV_LEFT,
7644       MV_RIGHT,
7645       MV_DOWN,
7646       MV_UP,
7647       MV_LEFT,
7648       MV_RIGHT,
7649     };
7650     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7651     int move_preference = -1000000;     // start with very low preference
7652     int new_move_dir = MV_NONE;
7653     int start_test = RND(4);
7654     int i;
7655
7656     for (i = 0; i < NUM_DIRECTIONS; i++)
7657     {
7658       int move_dir = test_dir[start_test + i];
7659       int move_dir_preference;
7660
7661       xx = x + test_xy[start_test + i][0];
7662       yy = y + test_xy[start_test + i][1];
7663
7664       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7665           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7666       {
7667         new_move_dir = move_dir;
7668
7669         break;
7670       }
7671
7672       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7673         continue;
7674
7675       move_dir_preference = -1 * RunnerVisit[xx][yy];
7676       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7677         move_dir_preference = PlayerVisit[xx][yy];
7678
7679       if (move_dir_preference > move_preference)
7680       {
7681         // prefer field that has not been visited for the longest time
7682         move_preference = move_dir_preference;
7683         new_move_dir = move_dir;
7684       }
7685       else if (move_dir_preference == move_preference &&
7686                move_dir == old_move_dir)
7687       {
7688         // prefer last direction when all directions are preferred equally
7689         move_preference = move_dir_preference;
7690         new_move_dir = move_dir;
7691       }
7692     }
7693
7694     MovDir[x][y] = new_move_dir;
7695     if (old_move_dir != new_move_dir)
7696       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7697   }
7698 }
7699
7700 static void TurnRound(int x, int y)
7701 {
7702   int direction = MovDir[x][y];
7703
7704   TurnRoundExt(x, y);
7705
7706   GfxDir[x][y] = MovDir[x][y];
7707
7708   if (direction != MovDir[x][y])
7709     GfxFrame[x][y] = 0;
7710
7711   if (MovDelay[x][y])
7712     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7713
7714   ResetGfxFrame(x, y);
7715 }
7716
7717 static boolean JustBeingPushed(int x, int y)
7718 {
7719   int i;
7720
7721   for (i = 0; i < MAX_PLAYERS; i++)
7722   {
7723     struct PlayerInfo *player = &stored_player[i];
7724
7725     if (player->active && player->is_pushing && player->MovPos)
7726     {
7727       int next_jx = player->jx + (player->jx - player->last_jx);
7728       int next_jy = player->jy + (player->jy - player->last_jy);
7729
7730       if (x == next_jx && y == next_jy)
7731         return TRUE;
7732     }
7733   }
7734
7735   return FALSE;
7736 }
7737
7738 static void StartMoving(int x, int y)
7739 {
7740   boolean started_moving = FALSE;       // some elements can fall _and_ move
7741   int element = Tile[x][y];
7742
7743   if (Stop[x][y])
7744     return;
7745
7746   if (MovDelay[x][y] == 0)
7747     GfxAction[x][y] = ACTION_DEFAULT;
7748
7749   if (CAN_FALL(element) && y < lev_fieldy - 1)
7750   {
7751     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7752         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7753       if (JustBeingPushed(x, y))
7754         return;
7755
7756     if (element == EL_QUICKSAND_FULL)
7757     {
7758       if (IS_FREE(x, y + 1))
7759       {
7760         InitMovingField(x, y, MV_DOWN);
7761         started_moving = TRUE;
7762
7763         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7764 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7765         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7766           Store[x][y] = EL_ROCK;
7767 #else
7768         Store[x][y] = EL_ROCK;
7769 #endif
7770
7771         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7772       }
7773       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7774       {
7775         if (!MovDelay[x][y])
7776         {
7777           MovDelay[x][y] = TILEY + 1;
7778
7779           ResetGfxAnimation(x, y);
7780           ResetGfxAnimation(x, y + 1);
7781         }
7782
7783         if (MovDelay[x][y])
7784         {
7785           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7786           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7787
7788           MovDelay[x][y]--;
7789           if (MovDelay[x][y])
7790             return;
7791         }
7792
7793         Tile[x][y] = EL_QUICKSAND_EMPTY;
7794         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7795         Store[x][y + 1] = Store[x][y];
7796         Store[x][y] = 0;
7797
7798         PlayLevelSoundAction(x, y, ACTION_FILLING);
7799       }
7800       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7801       {
7802         if (!MovDelay[x][y])
7803         {
7804           MovDelay[x][y] = TILEY + 1;
7805
7806           ResetGfxAnimation(x, y);
7807           ResetGfxAnimation(x, y + 1);
7808         }
7809
7810         if (MovDelay[x][y])
7811         {
7812           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7813           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7814
7815           MovDelay[x][y]--;
7816           if (MovDelay[x][y])
7817             return;
7818         }
7819
7820         Tile[x][y] = EL_QUICKSAND_EMPTY;
7821         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7822         Store[x][y + 1] = Store[x][y];
7823         Store[x][y] = 0;
7824
7825         PlayLevelSoundAction(x, y, ACTION_FILLING);
7826       }
7827     }
7828     else if (element == EL_QUICKSAND_FAST_FULL)
7829     {
7830       if (IS_FREE(x, y + 1))
7831       {
7832         InitMovingField(x, y, MV_DOWN);
7833         started_moving = TRUE;
7834
7835         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7836 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7837         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7838           Store[x][y] = EL_ROCK;
7839 #else
7840         Store[x][y] = EL_ROCK;
7841 #endif
7842
7843         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7844       }
7845       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7846       {
7847         if (!MovDelay[x][y])
7848         {
7849           MovDelay[x][y] = TILEY + 1;
7850
7851           ResetGfxAnimation(x, y);
7852           ResetGfxAnimation(x, y + 1);
7853         }
7854
7855         if (MovDelay[x][y])
7856         {
7857           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7858           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7859
7860           MovDelay[x][y]--;
7861           if (MovDelay[x][y])
7862             return;
7863         }
7864
7865         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7866         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7867         Store[x][y + 1] = Store[x][y];
7868         Store[x][y] = 0;
7869
7870         PlayLevelSoundAction(x, y, ACTION_FILLING);
7871       }
7872       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7873       {
7874         if (!MovDelay[x][y])
7875         {
7876           MovDelay[x][y] = TILEY + 1;
7877
7878           ResetGfxAnimation(x, y);
7879           ResetGfxAnimation(x, y + 1);
7880         }
7881
7882         if (MovDelay[x][y])
7883         {
7884           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7885           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7886
7887           MovDelay[x][y]--;
7888           if (MovDelay[x][y])
7889             return;
7890         }
7891
7892         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7893         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7894         Store[x][y + 1] = Store[x][y];
7895         Store[x][y] = 0;
7896
7897         PlayLevelSoundAction(x, y, ACTION_FILLING);
7898       }
7899     }
7900     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7901              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7902     {
7903       InitMovingField(x, y, MV_DOWN);
7904       started_moving = TRUE;
7905
7906       Tile[x][y] = EL_QUICKSAND_FILLING;
7907       Store[x][y] = element;
7908
7909       PlayLevelSoundAction(x, y, ACTION_FILLING);
7910     }
7911     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7912              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7913     {
7914       InitMovingField(x, y, MV_DOWN);
7915       started_moving = TRUE;
7916
7917       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7918       Store[x][y] = element;
7919
7920       PlayLevelSoundAction(x, y, ACTION_FILLING);
7921     }
7922     else if (element == EL_MAGIC_WALL_FULL)
7923     {
7924       if (IS_FREE(x, y + 1))
7925       {
7926         InitMovingField(x, y, MV_DOWN);
7927         started_moving = TRUE;
7928
7929         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7930         Store[x][y] = EL_CHANGED(Store[x][y]);
7931       }
7932       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7933       {
7934         if (!MovDelay[x][y])
7935           MovDelay[x][y] = TILEY / 4 + 1;
7936
7937         if (MovDelay[x][y])
7938         {
7939           MovDelay[x][y]--;
7940           if (MovDelay[x][y])
7941             return;
7942         }
7943
7944         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7945         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7946         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7947         Store[x][y] = 0;
7948       }
7949     }
7950     else if (element == EL_BD_MAGIC_WALL_FULL)
7951     {
7952       if (IS_FREE(x, y + 1))
7953       {
7954         InitMovingField(x, y, MV_DOWN);
7955         started_moving = TRUE;
7956
7957         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7958         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7959       }
7960       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7961       {
7962         if (!MovDelay[x][y])
7963           MovDelay[x][y] = TILEY / 4 + 1;
7964
7965         if (MovDelay[x][y])
7966         {
7967           MovDelay[x][y]--;
7968           if (MovDelay[x][y])
7969             return;
7970         }
7971
7972         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7973         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7974         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7975         Store[x][y] = 0;
7976       }
7977     }
7978     else if (element == EL_DC_MAGIC_WALL_FULL)
7979     {
7980       if (IS_FREE(x, y + 1))
7981       {
7982         InitMovingField(x, y, MV_DOWN);
7983         started_moving = TRUE;
7984
7985         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7986         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7987       }
7988       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7989       {
7990         if (!MovDelay[x][y])
7991           MovDelay[x][y] = TILEY / 4 + 1;
7992
7993         if (MovDelay[x][y])
7994         {
7995           MovDelay[x][y]--;
7996           if (MovDelay[x][y])
7997             return;
7998         }
7999
8000         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8001         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8002         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8003         Store[x][y] = 0;
8004       }
8005     }
8006     else if ((CAN_PASS_MAGIC_WALL(element) &&
8007               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8008                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8009              (CAN_PASS_DC_MAGIC_WALL(element) &&
8010               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8011
8012     {
8013       InitMovingField(x, y, MV_DOWN);
8014       started_moving = TRUE;
8015
8016       Tile[x][y] =
8017         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8018          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8019          EL_DC_MAGIC_WALL_FILLING);
8020       Store[x][y] = element;
8021     }
8022     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8023     {
8024       SplashAcid(x, y + 1);
8025
8026       InitMovingField(x, y, MV_DOWN);
8027       started_moving = TRUE;
8028
8029       Store[x][y] = EL_ACID;
8030     }
8031     else if (
8032              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8033               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8034              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8035               CAN_FALL(element) && WasJustFalling[x][y] &&
8036               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8037
8038              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8039               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8040               (Tile[x][y + 1] == EL_BLOCKED)))
8041     {
8042       /* this is needed for a special case not covered by calling "Impact()"
8043          from "ContinueMoving()": if an element moves to a tile directly below
8044          another element which was just falling on that tile (which was empty
8045          in the previous frame), the falling element above would just stop
8046          instead of smashing the element below (in previous version, the above
8047          element was just checked for "moving" instead of "falling", resulting
8048          in incorrect smashes caused by horizontal movement of the above
8049          element; also, the case of the player being the element to smash was
8050          simply not covered here... :-/ ) */
8051
8052       CheckCollision[x][y] = 0;
8053       CheckImpact[x][y] = 0;
8054
8055       Impact(x, y);
8056     }
8057     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8058     {
8059       if (MovDir[x][y] == MV_NONE)
8060       {
8061         InitMovingField(x, y, MV_DOWN);
8062         started_moving = TRUE;
8063       }
8064     }
8065     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8066     {
8067       if (WasJustFalling[x][y]) // prevent animation from being restarted
8068         MovDir[x][y] = MV_DOWN;
8069
8070       InitMovingField(x, y, MV_DOWN);
8071       started_moving = TRUE;
8072     }
8073     else if (element == EL_AMOEBA_DROP)
8074     {
8075       Tile[x][y] = EL_AMOEBA_GROWING;
8076       Store[x][y] = EL_AMOEBA_WET;
8077     }
8078     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8079               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8080              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8081              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8082     {
8083       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8084                                 (IS_FREE(x - 1, y + 1) ||
8085                                  Tile[x - 1][y + 1] == EL_ACID));
8086       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8087                                 (IS_FREE(x + 1, y + 1) ||
8088                                  Tile[x + 1][y + 1] == EL_ACID));
8089       boolean can_fall_any  = (can_fall_left || can_fall_right);
8090       boolean can_fall_both = (can_fall_left && can_fall_right);
8091       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8092
8093       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8094       {
8095         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8096           can_fall_right = FALSE;
8097         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8098           can_fall_left = FALSE;
8099         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8100           can_fall_right = FALSE;
8101         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8102           can_fall_left = FALSE;
8103
8104         can_fall_any  = (can_fall_left || can_fall_right);
8105         can_fall_both = FALSE;
8106       }
8107
8108       if (can_fall_both)
8109       {
8110         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8111           can_fall_right = FALSE;       // slip down on left side
8112         else
8113           can_fall_left = !(can_fall_right = RND(2));
8114
8115         can_fall_both = FALSE;
8116       }
8117
8118       if (can_fall_any)
8119       {
8120         // if not determined otherwise, prefer left side for slipping down
8121         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8122         started_moving = TRUE;
8123       }
8124     }
8125     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8126     {
8127       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8128       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8129       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8130       int belt_dir = game.belt_dir[belt_nr];
8131
8132       if ((belt_dir == MV_LEFT  && left_is_free) ||
8133           (belt_dir == MV_RIGHT && right_is_free))
8134       {
8135         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8136
8137         InitMovingField(x, y, belt_dir);
8138         started_moving = TRUE;
8139
8140         Pushed[x][y] = TRUE;
8141         Pushed[nextx][y] = TRUE;
8142
8143         GfxAction[x][y] = ACTION_DEFAULT;
8144       }
8145       else
8146       {
8147         MovDir[x][y] = 0;       // if element was moving, stop it
8148       }
8149     }
8150   }
8151
8152   // not "else if" because of elements that can fall and move (EL_SPRING)
8153   if (CAN_MOVE(element) && !started_moving)
8154   {
8155     int move_pattern = element_info[element].move_pattern;
8156     int newx, newy;
8157
8158     Moving2Blocked(x, y, &newx, &newy);
8159
8160     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8161       return;
8162
8163     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8164         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8165     {
8166       WasJustMoving[x][y] = 0;
8167       CheckCollision[x][y] = 0;
8168
8169       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8170
8171       if (Tile[x][y] != element)        // element has changed
8172         return;
8173     }
8174
8175     if (!MovDelay[x][y])        // start new movement phase
8176     {
8177       // all objects that can change their move direction after each step
8178       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8179
8180       if (element != EL_YAMYAM &&
8181           element != EL_DARK_YAMYAM &&
8182           element != EL_PACMAN &&
8183           !(move_pattern & MV_ANY_DIRECTION) &&
8184           move_pattern != MV_TURNING_LEFT &&
8185           move_pattern != MV_TURNING_RIGHT &&
8186           move_pattern != MV_TURNING_LEFT_RIGHT &&
8187           move_pattern != MV_TURNING_RIGHT_LEFT &&
8188           move_pattern != MV_TURNING_RANDOM)
8189       {
8190         TurnRound(x, y);
8191
8192         if (MovDelay[x][y] && (element == EL_BUG ||
8193                                element == EL_SPACESHIP ||
8194                                element == EL_SP_SNIKSNAK ||
8195                                element == EL_SP_ELECTRON ||
8196                                element == EL_MOLE))
8197           TEST_DrawLevelField(x, y);
8198       }
8199     }
8200
8201     if (MovDelay[x][y])         // wait some time before next movement
8202     {
8203       MovDelay[x][y]--;
8204
8205       if (element == EL_ROBOT ||
8206           element == EL_YAMYAM ||
8207           element == EL_DARK_YAMYAM)
8208       {
8209         DrawLevelElementAnimationIfNeeded(x, y, element);
8210         PlayLevelSoundAction(x, y, ACTION_WAITING);
8211       }
8212       else if (element == EL_SP_ELECTRON)
8213         DrawLevelElementAnimationIfNeeded(x, y, element);
8214       else if (element == EL_DRAGON)
8215       {
8216         int i;
8217         int dir = MovDir[x][y];
8218         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8219         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8220         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8221                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8222                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8223                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8224         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8225
8226         GfxAction[x][y] = ACTION_ATTACKING;
8227
8228         if (IS_PLAYER(x, y))
8229           DrawPlayerField(x, y);
8230         else
8231           TEST_DrawLevelField(x, y);
8232
8233         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8234
8235         for (i = 1; i <= 3; i++)
8236         {
8237           int xx = x + i * dx;
8238           int yy = y + i * dy;
8239           int sx = SCREENX(xx);
8240           int sy = SCREENY(yy);
8241           int flame_graphic = graphic + (i - 1);
8242
8243           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8244             break;
8245
8246           if (MovDelay[x][y])
8247           {
8248             int flamed = MovingOrBlocked2Element(xx, yy);
8249
8250             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8251               Bang(xx, yy);
8252             else
8253               RemoveMovingField(xx, yy);
8254
8255             ChangeDelay[xx][yy] = 0;
8256
8257             Tile[xx][yy] = EL_FLAMES;
8258
8259             if (IN_SCR_FIELD(sx, sy))
8260             {
8261               TEST_DrawLevelFieldCrumbled(xx, yy);
8262               DrawGraphic(sx, sy, flame_graphic, frame);
8263             }
8264           }
8265           else
8266           {
8267             if (Tile[xx][yy] == EL_FLAMES)
8268               Tile[xx][yy] = EL_EMPTY;
8269             TEST_DrawLevelField(xx, yy);
8270           }
8271         }
8272       }
8273
8274       if (MovDelay[x][y])       // element still has to wait some time
8275       {
8276         PlayLevelSoundAction(x, y, ACTION_WAITING);
8277
8278         return;
8279       }
8280     }
8281
8282     // now make next step
8283
8284     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8285
8286     if (DONT_COLLIDE_WITH(element) &&
8287         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8288         !PLAYER_ENEMY_PROTECTED(newx, newy))
8289     {
8290       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8291
8292       return;
8293     }
8294
8295     else if (CAN_MOVE_INTO_ACID(element) &&
8296              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8297              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8298              (MovDir[x][y] == MV_DOWN ||
8299               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8300     {
8301       SplashAcid(newx, newy);
8302       Store[x][y] = EL_ACID;
8303     }
8304     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8305     {
8306       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8307           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8308           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8309           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8310       {
8311         RemoveField(x, y);
8312         TEST_DrawLevelField(x, y);
8313
8314         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8315         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8316           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8317
8318         game.friends_still_needed--;
8319         if (!game.friends_still_needed &&
8320             !game.GameOver &&
8321             game.all_players_gone)
8322           LevelSolved();
8323
8324         return;
8325       }
8326       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8327       {
8328         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8329           TEST_DrawLevelField(newx, newy);
8330         else
8331           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8332       }
8333       else if (!IS_FREE(newx, newy))
8334       {
8335         GfxAction[x][y] = ACTION_WAITING;
8336
8337         if (IS_PLAYER(x, y))
8338           DrawPlayerField(x, y);
8339         else
8340           TEST_DrawLevelField(x, y);
8341
8342         return;
8343       }
8344     }
8345     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8346     {
8347       if (IS_FOOD_PIG(Tile[newx][newy]))
8348       {
8349         if (IS_MOVING(newx, newy))
8350           RemoveMovingField(newx, newy);
8351         else
8352         {
8353           Tile[newx][newy] = EL_EMPTY;
8354           TEST_DrawLevelField(newx, newy);
8355         }
8356
8357         PlayLevelSound(x, y, SND_PIG_DIGGING);
8358       }
8359       else if (!IS_FREE(newx, newy))
8360       {
8361         if (IS_PLAYER(x, y))
8362           DrawPlayerField(x, y);
8363         else
8364           TEST_DrawLevelField(x, y);
8365
8366         return;
8367       }
8368     }
8369     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8370     {
8371       if (Store[x][y] != EL_EMPTY)
8372       {
8373         boolean can_clone = FALSE;
8374         int xx, yy;
8375
8376         // check if element to clone is still there
8377         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8378         {
8379           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8380           {
8381             can_clone = TRUE;
8382
8383             break;
8384           }
8385         }
8386
8387         // cannot clone or target field not free anymore -- do not clone
8388         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8389           Store[x][y] = EL_EMPTY;
8390       }
8391
8392       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8393       {
8394         if (IS_MV_DIAGONAL(MovDir[x][y]))
8395         {
8396           int diagonal_move_dir = MovDir[x][y];
8397           int stored = Store[x][y];
8398           int change_delay = 8;
8399           int graphic;
8400
8401           // android is moving diagonally
8402
8403           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8404
8405           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8406           GfxElement[x][y] = EL_EMC_ANDROID;
8407           GfxAction[x][y] = ACTION_SHRINKING;
8408           GfxDir[x][y] = diagonal_move_dir;
8409           ChangeDelay[x][y] = change_delay;
8410
8411           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8412                                    GfxDir[x][y]);
8413
8414           DrawLevelGraphicAnimation(x, y, graphic);
8415           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8416
8417           if (Tile[newx][newy] == EL_ACID)
8418           {
8419             SplashAcid(newx, newy);
8420
8421             return;
8422           }
8423
8424           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8425
8426           Store[newx][newy] = EL_EMC_ANDROID;
8427           GfxElement[newx][newy] = EL_EMC_ANDROID;
8428           GfxAction[newx][newy] = ACTION_GROWING;
8429           GfxDir[newx][newy] = diagonal_move_dir;
8430           ChangeDelay[newx][newy] = change_delay;
8431
8432           graphic = el_act_dir2img(GfxElement[newx][newy],
8433                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8434
8435           DrawLevelGraphicAnimation(newx, newy, graphic);
8436           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8437
8438           return;
8439         }
8440         else
8441         {
8442           Tile[newx][newy] = EL_EMPTY;
8443           TEST_DrawLevelField(newx, newy);
8444
8445           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8446         }
8447       }
8448       else if (!IS_FREE(newx, newy))
8449       {
8450         return;
8451       }
8452     }
8453     else if (IS_CUSTOM_ELEMENT(element) &&
8454              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8455     {
8456       if (!DigFieldByCE(newx, newy, element))
8457         return;
8458
8459       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8460       {
8461         RunnerVisit[x][y] = FrameCounter;
8462         PlayerVisit[x][y] /= 8;         // expire player visit path
8463       }
8464     }
8465     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8466     {
8467       if (!IS_FREE(newx, newy))
8468       {
8469         if (IS_PLAYER(x, y))
8470           DrawPlayerField(x, y);
8471         else
8472           TEST_DrawLevelField(x, y);
8473
8474         return;
8475       }
8476       else
8477       {
8478         boolean wanna_flame = !RND(10);
8479         int dx = newx - x, dy = newy - y;
8480         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8481         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8482         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8483                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8484         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8485                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8486
8487         if ((wanna_flame ||
8488              IS_CLASSIC_ENEMY(element1) ||
8489              IS_CLASSIC_ENEMY(element2)) &&
8490             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8491             element1 != EL_FLAMES && element2 != EL_FLAMES)
8492         {
8493           ResetGfxAnimation(x, y);
8494           GfxAction[x][y] = ACTION_ATTACKING;
8495
8496           if (IS_PLAYER(x, y))
8497             DrawPlayerField(x, y);
8498           else
8499             TEST_DrawLevelField(x, y);
8500
8501           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8502
8503           MovDelay[x][y] = 50;
8504
8505           Tile[newx][newy] = EL_FLAMES;
8506           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8507             Tile[newx1][newy1] = EL_FLAMES;
8508           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8509             Tile[newx2][newy2] = EL_FLAMES;
8510
8511           return;
8512         }
8513       }
8514     }
8515     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8516              Tile[newx][newy] == EL_DIAMOND)
8517     {
8518       if (IS_MOVING(newx, newy))
8519         RemoveMovingField(newx, newy);
8520       else
8521       {
8522         Tile[newx][newy] = EL_EMPTY;
8523         TEST_DrawLevelField(newx, newy);
8524       }
8525
8526       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8527     }
8528     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8529              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8530     {
8531       if (AmoebaNr[newx][newy])
8532       {
8533         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8534         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8535             Tile[newx][newy] == EL_BD_AMOEBA)
8536           AmoebaCnt[AmoebaNr[newx][newy]]--;
8537       }
8538
8539       if (IS_MOVING(newx, newy))
8540       {
8541         RemoveMovingField(newx, newy);
8542       }
8543       else
8544       {
8545         Tile[newx][newy] = EL_EMPTY;
8546         TEST_DrawLevelField(newx, newy);
8547       }
8548
8549       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8550     }
8551     else if ((element == EL_PACMAN || element == EL_MOLE)
8552              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8553     {
8554       if (AmoebaNr[newx][newy])
8555       {
8556         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8557         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8558             Tile[newx][newy] == EL_BD_AMOEBA)
8559           AmoebaCnt[AmoebaNr[newx][newy]]--;
8560       }
8561
8562       if (element == EL_MOLE)
8563       {
8564         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8565         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8566
8567         ResetGfxAnimation(x, y);
8568         GfxAction[x][y] = ACTION_DIGGING;
8569         TEST_DrawLevelField(x, y);
8570
8571         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8572
8573         return;                         // wait for shrinking amoeba
8574       }
8575       else      // element == EL_PACMAN
8576       {
8577         Tile[newx][newy] = EL_EMPTY;
8578         TEST_DrawLevelField(newx, newy);
8579         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8580       }
8581     }
8582     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8583              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8584               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8585     {
8586       // wait for shrinking amoeba to completely disappear
8587       return;
8588     }
8589     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8590     {
8591       // object was running against a wall
8592
8593       TurnRound(x, y);
8594
8595       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8596         DrawLevelElementAnimation(x, y, element);
8597
8598       if (DONT_TOUCH(element))
8599         TestIfBadThingTouchesPlayer(x, y);
8600
8601       return;
8602     }
8603
8604     InitMovingField(x, y, MovDir[x][y]);
8605
8606     PlayLevelSoundAction(x, y, ACTION_MOVING);
8607   }
8608
8609   if (MovDir[x][y])
8610     ContinueMoving(x, y);
8611 }
8612
8613 void ContinueMoving(int x, int y)
8614 {
8615   int element = Tile[x][y];
8616   struct ElementInfo *ei = &element_info[element];
8617   int direction = MovDir[x][y];
8618   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8619   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8620   int newx = x + dx, newy = y + dy;
8621   int stored = Store[x][y];
8622   int stored_new = Store[newx][newy];
8623   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8624   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8625   boolean last_line = (newy == lev_fieldy - 1);
8626   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8627
8628   if (pushed_by_player)         // special case: moving object pushed by player
8629   {
8630     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8631   }
8632   else if (use_step_delay)      // special case: moving object has step delay
8633   {
8634     if (!MovDelay[x][y])
8635       MovPos[x][y] += getElementMoveStepsize(x, y);
8636
8637     if (MovDelay[x][y])
8638       MovDelay[x][y]--;
8639     else
8640       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8641
8642     if (MovDelay[x][y])
8643     {
8644       TEST_DrawLevelField(x, y);
8645
8646       return;   // element is still waiting
8647     }
8648   }
8649   else                          // normal case: generically moving object
8650   {
8651     MovPos[x][y] += getElementMoveStepsize(x, y);
8652   }
8653
8654   if (ABS(MovPos[x][y]) < TILEX)
8655   {
8656     TEST_DrawLevelField(x, y);
8657
8658     return;     // element is still moving
8659   }
8660
8661   // element reached destination field
8662
8663   Tile[x][y] = EL_EMPTY;
8664   Tile[newx][newy] = element;
8665   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8666
8667   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8668   {
8669     element = Tile[newx][newy] = EL_ACID;
8670   }
8671   else if (element == EL_MOLE)
8672   {
8673     Tile[x][y] = EL_SAND;
8674
8675     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8676   }
8677   else if (element == EL_QUICKSAND_FILLING)
8678   {
8679     element = Tile[newx][newy] = get_next_element(element);
8680     Store[newx][newy] = Store[x][y];
8681   }
8682   else if (element == EL_QUICKSAND_EMPTYING)
8683   {
8684     Tile[x][y] = get_next_element(element);
8685     element = Tile[newx][newy] = Store[x][y];
8686   }
8687   else if (element == EL_QUICKSAND_FAST_FILLING)
8688   {
8689     element = Tile[newx][newy] = get_next_element(element);
8690     Store[newx][newy] = Store[x][y];
8691   }
8692   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8693   {
8694     Tile[x][y] = get_next_element(element);
8695     element = Tile[newx][newy] = Store[x][y];
8696   }
8697   else if (element == EL_MAGIC_WALL_FILLING)
8698   {
8699     element = Tile[newx][newy] = get_next_element(element);
8700     if (!game.magic_wall_active)
8701       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8702     Store[newx][newy] = Store[x][y];
8703   }
8704   else if (element == EL_MAGIC_WALL_EMPTYING)
8705   {
8706     Tile[x][y] = get_next_element(element);
8707     if (!game.magic_wall_active)
8708       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8709     element = Tile[newx][newy] = Store[x][y];
8710
8711     InitField(newx, newy, FALSE);
8712   }
8713   else if (element == EL_BD_MAGIC_WALL_FILLING)
8714   {
8715     element = Tile[newx][newy] = get_next_element(element);
8716     if (!game.magic_wall_active)
8717       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8718     Store[newx][newy] = Store[x][y];
8719   }
8720   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8721   {
8722     Tile[x][y] = get_next_element(element);
8723     if (!game.magic_wall_active)
8724       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8725     element = Tile[newx][newy] = Store[x][y];
8726
8727     InitField(newx, newy, FALSE);
8728   }
8729   else if (element == EL_DC_MAGIC_WALL_FILLING)
8730   {
8731     element = Tile[newx][newy] = get_next_element(element);
8732     if (!game.magic_wall_active)
8733       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8734     Store[newx][newy] = Store[x][y];
8735   }
8736   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8737   {
8738     Tile[x][y] = get_next_element(element);
8739     if (!game.magic_wall_active)
8740       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8741     element = Tile[newx][newy] = Store[x][y];
8742
8743     InitField(newx, newy, FALSE);
8744   }
8745   else if (element == EL_AMOEBA_DROPPING)
8746   {
8747     Tile[x][y] = get_next_element(element);
8748     element = Tile[newx][newy] = Store[x][y];
8749   }
8750   else if (element == EL_SOKOBAN_OBJECT)
8751   {
8752     if (Back[x][y])
8753       Tile[x][y] = Back[x][y];
8754
8755     if (Back[newx][newy])
8756       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8757
8758     Back[x][y] = Back[newx][newy] = 0;
8759   }
8760
8761   Store[x][y] = EL_EMPTY;
8762   MovPos[x][y] = 0;
8763   MovDir[x][y] = 0;
8764   MovDelay[x][y] = 0;
8765
8766   MovDelay[newx][newy] = 0;
8767
8768   if (CAN_CHANGE_OR_HAS_ACTION(element))
8769   {
8770     // copy element change control values to new field
8771     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8772     ChangePage[newx][newy]  = ChangePage[x][y];
8773     ChangeCount[newx][newy] = ChangeCount[x][y];
8774     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8775   }
8776
8777   CustomValue[newx][newy] = CustomValue[x][y];
8778
8779   ChangeDelay[x][y] = 0;
8780   ChangePage[x][y] = -1;
8781   ChangeCount[x][y] = 0;
8782   ChangeEvent[x][y] = -1;
8783
8784   CustomValue[x][y] = 0;
8785
8786   // copy animation control values to new field
8787   GfxFrame[newx][newy]  = GfxFrame[x][y];
8788   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8789   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8790   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8791
8792   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8793
8794   // some elements can leave other elements behind after moving
8795   if (ei->move_leave_element != EL_EMPTY &&
8796       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8797       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8798   {
8799     int move_leave_element = ei->move_leave_element;
8800
8801     // this makes it possible to leave the removed element again
8802     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8803       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8804
8805     Tile[x][y] = move_leave_element;
8806
8807     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8808       MovDir[x][y] = direction;
8809
8810     InitField(x, y, FALSE);
8811
8812     if (GFX_CRUMBLED(Tile[x][y]))
8813       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8814
8815     if (IS_PLAYER_ELEMENT(move_leave_element))
8816       RelocatePlayer(x, y, move_leave_element);
8817   }
8818
8819   // do this after checking for left-behind element
8820   ResetGfxAnimation(x, y);      // reset animation values for old field
8821
8822   if (!CAN_MOVE(element) ||
8823       (CAN_FALL(element) && direction == MV_DOWN &&
8824        (element == EL_SPRING ||
8825         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8826         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8827     GfxDir[x][y] = MovDir[newx][newy] = 0;
8828
8829   TEST_DrawLevelField(x, y);
8830   TEST_DrawLevelField(newx, newy);
8831
8832   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8833
8834   // prevent pushed element from moving on in pushed direction
8835   if (pushed_by_player && CAN_MOVE(element) &&
8836       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8837       !(element_info[element].move_pattern & direction))
8838     TurnRound(newx, newy);
8839
8840   // prevent elements on conveyor belt from moving on in last direction
8841   if (pushed_by_conveyor && CAN_FALL(element) &&
8842       direction & MV_HORIZONTAL)
8843     MovDir[newx][newy] = 0;
8844
8845   if (!pushed_by_player)
8846   {
8847     int nextx = newx + dx, nexty = newy + dy;
8848     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8849
8850     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8851
8852     if (CAN_FALL(element) && direction == MV_DOWN)
8853       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8854
8855     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8856       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8857
8858     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8859       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8860   }
8861
8862   if (DONT_TOUCH(element))      // object may be nasty to player or others
8863   {
8864     TestIfBadThingTouchesPlayer(newx, newy);
8865     TestIfBadThingTouchesFriend(newx, newy);
8866
8867     if (!IS_CUSTOM_ELEMENT(element))
8868       TestIfBadThingTouchesOtherBadThing(newx, newy);
8869   }
8870   else if (element == EL_PENGUIN)
8871     TestIfFriendTouchesBadThing(newx, newy);
8872
8873   if (DONT_GET_HIT_BY(element))
8874   {
8875     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8876   }
8877
8878   // give the player one last chance (one more frame) to move away
8879   if (CAN_FALL(element) && direction == MV_DOWN &&
8880       (last_line || (!IS_FREE(x, newy + 1) &&
8881                      (!IS_PLAYER(x, newy + 1) ||
8882                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8883     Impact(x, newy);
8884
8885   if (pushed_by_player && !game.use_change_when_pushing_bug)
8886   {
8887     int push_side = MV_DIR_OPPOSITE(direction);
8888     struct PlayerInfo *player = PLAYERINFO(x, y);
8889
8890     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8891                                player->index_bit, push_side);
8892     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8893                                         player->index_bit, push_side);
8894   }
8895
8896   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8897     MovDelay[newx][newy] = 1;
8898
8899   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8900
8901   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8902   TestIfElementHitsCustomElement(newx, newy, direction);
8903   TestIfPlayerTouchesCustomElement(newx, newy);
8904   TestIfElementTouchesCustomElement(newx, newy);
8905
8906   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8907       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8908     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8909                              MV_DIR_OPPOSITE(direction));
8910 }
8911
8912 int AmoebaNeighbourNr(int ax, int ay)
8913 {
8914   int i;
8915   int element = Tile[ax][ay];
8916   int group_nr = 0;
8917   static int xy[4][2] =
8918   {
8919     { 0, -1 },
8920     { -1, 0 },
8921     { +1, 0 },
8922     { 0, +1 }
8923   };
8924
8925   for (i = 0; i < NUM_DIRECTIONS; i++)
8926   {
8927     int x = ax + xy[i][0];
8928     int y = ay + xy[i][1];
8929
8930     if (!IN_LEV_FIELD(x, y))
8931       continue;
8932
8933     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8934       group_nr = AmoebaNr[x][y];
8935   }
8936
8937   return group_nr;
8938 }
8939
8940 static void AmoebaMerge(int ax, int ay)
8941 {
8942   int i, x, y, xx, yy;
8943   int new_group_nr = AmoebaNr[ax][ay];
8944   static int xy[4][2] =
8945   {
8946     { 0, -1 },
8947     { -1, 0 },
8948     { +1, 0 },
8949     { 0, +1 }
8950   };
8951
8952   if (new_group_nr == 0)
8953     return;
8954
8955   for (i = 0; i < NUM_DIRECTIONS; i++)
8956   {
8957     x = ax + xy[i][0];
8958     y = ay + xy[i][1];
8959
8960     if (!IN_LEV_FIELD(x, y))
8961       continue;
8962
8963     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8964          Tile[x][y] == EL_BD_AMOEBA ||
8965          Tile[x][y] == EL_AMOEBA_DEAD) &&
8966         AmoebaNr[x][y] != new_group_nr)
8967     {
8968       int old_group_nr = AmoebaNr[x][y];
8969
8970       if (old_group_nr == 0)
8971         return;
8972
8973       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8974       AmoebaCnt[old_group_nr] = 0;
8975       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8976       AmoebaCnt2[old_group_nr] = 0;
8977
8978       SCAN_PLAYFIELD(xx, yy)
8979       {
8980         if (AmoebaNr[xx][yy] == old_group_nr)
8981           AmoebaNr[xx][yy] = new_group_nr;
8982       }
8983     }
8984   }
8985 }
8986
8987 void AmoebaToDiamond(int ax, int ay)
8988 {
8989   int i, x, y;
8990
8991   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8992   {
8993     int group_nr = AmoebaNr[ax][ay];
8994
8995 #ifdef DEBUG
8996     if (group_nr == 0)
8997     {
8998       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8999       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9000
9001       return;
9002     }
9003 #endif
9004
9005     SCAN_PLAYFIELD(x, y)
9006     {
9007       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9008       {
9009         AmoebaNr[x][y] = 0;
9010         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9011       }
9012     }
9013
9014     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9015                             SND_AMOEBA_TURNING_TO_GEM :
9016                             SND_AMOEBA_TURNING_TO_ROCK));
9017     Bang(ax, ay);
9018   }
9019   else
9020   {
9021     static int xy[4][2] =
9022     {
9023       { 0, -1 },
9024       { -1, 0 },
9025       { +1, 0 },
9026       { 0, +1 }
9027     };
9028
9029     for (i = 0; i < NUM_DIRECTIONS; i++)
9030     {
9031       x = ax + xy[i][0];
9032       y = ay + xy[i][1];
9033
9034       if (!IN_LEV_FIELD(x, y))
9035         continue;
9036
9037       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9038       {
9039         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9040                               SND_AMOEBA_TURNING_TO_GEM :
9041                               SND_AMOEBA_TURNING_TO_ROCK));
9042         Bang(x, y);
9043       }
9044     }
9045   }
9046 }
9047
9048 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9049 {
9050   int x, y;
9051   int group_nr = AmoebaNr[ax][ay];
9052   boolean done = FALSE;
9053
9054 #ifdef DEBUG
9055   if (group_nr == 0)
9056   {
9057     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9058     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9059
9060     return;
9061   }
9062 #endif
9063
9064   SCAN_PLAYFIELD(x, y)
9065   {
9066     if (AmoebaNr[x][y] == group_nr &&
9067         (Tile[x][y] == EL_AMOEBA_DEAD ||
9068          Tile[x][y] == EL_BD_AMOEBA ||
9069          Tile[x][y] == EL_AMOEBA_GROWING))
9070     {
9071       AmoebaNr[x][y] = 0;
9072       Tile[x][y] = new_element;
9073       InitField(x, y, FALSE);
9074       TEST_DrawLevelField(x, y);
9075       done = TRUE;
9076     }
9077   }
9078
9079   if (done)
9080     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9081                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9082                             SND_BD_AMOEBA_TURNING_TO_GEM));
9083 }
9084
9085 static void AmoebaGrowing(int x, int y)
9086 {
9087   static unsigned int sound_delay = 0;
9088   static unsigned int sound_delay_value = 0;
9089
9090   if (!MovDelay[x][y])          // start new growing cycle
9091   {
9092     MovDelay[x][y] = 7;
9093
9094     if (DelayReached(&sound_delay, sound_delay_value))
9095     {
9096       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9097       sound_delay_value = 30;
9098     }
9099   }
9100
9101   if (MovDelay[x][y])           // wait some time before growing bigger
9102   {
9103     MovDelay[x][y]--;
9104     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9105     {
9106       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9107                                            6 - MovDelay[x][y]);
9108
9109       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9110     }
9111
9112     if (!MovDelay[x][y])
9113     {
9114       Tile[x][y] = Store[x][y];
9115       Store[x][y] = 0;
9116       TEST_DrawLevelField(x, y);
9117     }
9118   }
9119 }
9120
9121 static void AmoebaShrinking(int x, int y)
9122 {
9123   static unsigned int sound_delay = 0;
9124   static unsigned int sound_delay_value = 0;
9125
9126   if (!MovDelay[x][y])          // start new shrinking cycle
9127   {
9128     MovDelay[x][y] = 7;
9129
9130     if (DelayReached(&sound_delay, sound_delay_value))
9131       sound_delay_value = 30;
9132   }
9133
9134   if (MovDelay[x][y])           // wait some time before shrinking
9135   {
9136     MovDelay[x][y]--;
9137     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9138     {
9139       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9140                                            6 - MovDelay[x][y]);
9141
9142       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9143     }
9144
9145     if (!MovDelay[x][y])
9146     {
9147       Tile[x][y] = EL_EMPTY;
9148       TEST_DrawLevelField(x, y);
9149
9150       // don't let mole enter this field in this cycle;
9151       // (give priority to objects falling to this field from above)
9152       Stop[x][y] = TRUE;
9153     }
9154   }
9155 }
9156
9157 static void AmoebaReproduce(int ax, int ay)
9158 {
9159   int i;
9160   int element = Tile[ax][ay];
9161   int graphic = el2img(element);
9162   int newax = ax, neway = ay;
9163   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9164   static int xy[4][2] =
9165   {
9166     { 0, -1 },
9167     { -1, 0 },
9168     { +1, 0 },
9169     { 0, +1 }
9170   };
9171
9172   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9173   {
9174     Tile[ax][ay] = EL_AMOEBA_DEAD;
9175     TEST_DrawLevelField(ax, ay);
9176     return;
9177   }
9178
9179   if (IS_ANIMATED(graphic))
9180     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9181
9182   if (!MovDelay[ax][ay])        // start making new amoeba field
9183     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9184
9185   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9186   {
9187     MovDelay[ax][ay]--;
9188     if (MovDelay[ax][ay])
9189       return;
9190   }
9191
9192   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9193   {
9194     int start = RND(4);
9195     int x = ax + xy[start][0];
9196     int y = ay + xy[start][1];
9197
9198     if (!IN_LEV_FIELD(x, y))
9199       return;
9200
9201     if (IS_FREE(x, y) ||
9202         CAN_GROW_INTO(Tile[x][y]) ||
9203         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9204         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9205     {
9206       newax = x;
9207       neway = y;
9208     }
9209
9210     if (newax == ax && neway == ay)
9211       return;
9212   }
9213   else                          // normal or "filled" (BD style) amoeba
9214   {
9215     int start = RND(4);
9216     boolean waiting_for_player = FALSE;
9217
9218     for (i = 0; i < NUM_DIRECTIONS; i++)
9219     {
9220       int j = (start + i) % 4;
9221       int x = ax + xy[j][0];
9222       int y = ay + xy[j][1];
9223
9224       if (!IN_LEV_FIELD(x, y))
9225         continue;
9226
9227       if (IS_FREE(x, y) ||
9228           CAN_GROW_INTO(Tile[x][y]) ||
9229           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9230           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9231       {
9232         newax = x;
9233         neway = y;
9234         break;
9235       }
9236       else if (IS_PLAYER(x, y))
9237         waiting_for_player = TRUE;
9238     }
9239
9240     if (newax == ax && neway == ay)             // amoeba cannot grow
9241     {
9242       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9243       {
9244         Tile[ax][ay] = EL_AMOEBA_DEAD;
9245         TEST_DrawLevelField(ax, ay);
9246         AmoebaCnt[AmoebaNr[ax][ay]]--;
9247
9248         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9249         {
9250           if (element == EL_AMOEBA_FULL)
9251             AmoebaToDiamond(ax, ay);
9252           else if (element == EL_BD_AMOEBA)
9253             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9254         }
9255       }
9256       return;
9257     }
9258     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9259     {
9260       // amoeba gets larger by growing in some direction
9261
9262       int new_group_nr = AmoebaNr[ax][ay];
9263
9264 #ifdef DEBUG
9265   if (new_group_nr == 0)
9266   {
9267     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9268           newax, neway);
9269     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9270
9271     return;
9272   }
9273 #endif
9274
9275       AmoebaNr[newax][neway] = new_group_nr;
9276       AmoebaCnt[new_group_nr]++;
9277       AmoebaCnt2[new_group_nr]++;
9278
9279       // if amoeba touches other amoeba(s) after growing, unify them
9280       AmoebaMerge(newax, neway);
9281
9282       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9283       {
9284         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9285         return;
9286       }
9287     }
9288   }
9289
9290   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9291       (neway == lev_fieldy - 1 && newax != ax))
9292   {
9293     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9294     Store[newax][neway] = element;
9295   }
9296   else if (neway == ay || element == EL_EMC_DRIPPER)
9297   {
9298     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9299
9300     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9301   }
9302   else
9303   {
9304     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9305     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9306     Store[ax][ay] = EL_AMOEBA_DROP;
9307     ContinueMoving(ax, ay);
9308     return;
9309   }
9310
9311   TEST_DrawLevelField(newax, neway);
9312 }
9313
9314 static void Life(int ax, int ay)
9315 {
9316   int x1, y1, x2, y2;
9317   int life_time = 40;
9318   int element = Tile[ax][ay];
9319   int graphic = el2img(element);
9320   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9321                          level.biomaze);
9322   boolean changed = FALSE;
9323
9324   if (IS_ANIMATED(graphic))
9325     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9326
9327   if (Stop[ax][ay])
9328     return;
9329
9330   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9331     MovDelay[ax][ay] = life_time;
9332
9333   if (MovDelay[ax][ay])         // wait some time before next cycle
9334   {
9335     MovDelay[ax][ay]--;
9336     if (MovDelay[ax][ay])
9337       return;
9338   }
9339
9340   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9341   {
9342     int xx = ax+x1, yy = ay+y1;
9343     int old_element = Tile[xx][yy];
9344     int num_neighbours = 0;
9345
9346     if (!IN_LEV_FIELD(xx, yy))
9347       continue;
9348
9349     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9350     {
9351       int x = xx+x2, y = yy+y2;
9352
9353       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9354         continue;
9355
9356       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9357       boolean is_neighbour = FALSE;
9358
9359       if (level.use_life_bugs)
9360         is_neighbour =
9361           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9362            (IS_FREE(x, y)                             &&  Stop[x][y]));
9363       else
9364         is_neighbour =
9365           (Last[x][y] == element || is_player_cell);
9366
9367       if (is_neighbour)
9368         num_neighbours++;
9369     }
9370
9371     boolean is_free = FALSE;
9372
9373     if (level.use_life_bugs)
9374       is_free = (IS_FREE(xx, yy));
9375     else
9376       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9377
9378     if (xx == ax && yy == ay)           // field in the middle
9379     {
9380       if (num_neighbours < life_parameter[0] ||
9381           num_neighbours > life_parameter[1])
9382       {
9383         Tile[xx][yy] = EL_EMPTY;
9384         if (Tile[xx][yy] != old_element)
9385           TEST_DrawLevelField(xx, yy);
9386         Stop[xx][yy] = TRUE;
9387         changed = TRUE;
9388       }
9389     }
9390     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9391     {                                   // free border field
9392       if (num_neighbours >= life_parameter[2] &&
9393           num_neighbours <= life_parameter[3])
9394       {
9395         Tile[xx][yy] = element;
9396         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9397         if (Tile[xx][yy] != old_element)
9398           TEST_DrawLevelField(xx, yy);
9399         Stop[xx][yy] = TRUE;
9400         changed = TRUE;
9401       }
9402     }
9403   }
9404
9405   if (changed)
9406     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9407                    SND_GAME_OF_LIFE_GROWING);
9408 }
9409
9410 static void InitRobotWheel(int x, int y)
9411 {
9412   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9413 }
9414
9415 static void RunRobotWheel(int x, int y)
9416 {
9417   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9418 }
9419
9420 static void StopRobotWheel(int x, int y)
9421 {
9422   if (game.robot_wheel_x == x &&
9423       game.robot_wheel_y == y)
9424   {
9425     game.robot_wheel_x = -1;
9426     game.robot_wheel_y = -1;
9427     game.robot_wheel_active = FALSE;
9428   }
9429 }
9430
9431 static void InitTimegateWheel(int x, int y)
9432 {
9433   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9434 }
9435
9436 static void RunTimegateWheel(int x, int y)
9437 {
9438   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9439 }
9440
9441 static void InitMagicBallDelay(int x, int y)
9442 {
9443   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9444 }
9445
9446 static void ActivateMagicBall(int bx, int by)
9447 {
9448   int x, y;
9449
9450   if (level.ball_random)
9451   {
9452     int pos_border = RND(8);    // select one of the eight border elements
9453     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9454     int xx = pos_content % 3;
9455     int yy = pos_content / 3;
9456
9457     x = bx - 1 + xx;
9458     y = by - 1 + yy;
9459
9460     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9461       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9462   }
9463   else
9464   {
9465     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9466     {
9467       int xx = x - bx + 1;
9468       int yy = y - by + 1;
9469
9470       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9471         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9472     }
9473   }
9474
9475   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9476 }
9477
9478 static void CheckExit(int x, int y)
9479 {
9480   if (game.gems_still_needed > 0 ||
9481       game.sokoban_fields_still_needed > 0 ||
9482       game.sokoban_objects_still_needed > 0 ||
9483       game.lights_still_needed > 0)
9484   {
9485     int element = Tile[x][y];
9486     int graphic = el2img(element);
9487
9488     if (IS_ANIMATED(graphic))
9489       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9490
9491     return;
9492   }
9493
9494   // do not re-open exit door closed after last player
9495   if (game.all_players_gone)
9496     return;
9497
9498   Tile[x][y] = EL_EXIT_OPENING;
9499
9500   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9501 }
9502
9503 static void CheckExitEM(int x, int y)
9504 {
9505   if (game.gems_still_needed > 0 ||
9506       game.sokoban_fields_still_needed > 0 ||
9507       game.sokoban_objects_still_needed > 0 ||
9508       game.lights_still_needed > 0)
9509   {
9510     int element = Tile[x][y];
9511     int graphic = el2img(element);
9512
9513     if (IS_ANIMATED(graphic))
9514       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9515
9516     return;
9517   }
9518
9519   // do not re-open exit door closed after last player
9520   if (game.all_players_gone)
9521     return;
9522
9523   Tile[x][y] = EL_EM_EXIT_OPENING;
9524
9525   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9526 }
9527
9528 static void CheckExitSteel(int x, int y)
9529 {
9530   if (game.gems_still_needed > 0 ||
9531       game.sokoban_fields_still_needed > 0 ||
9532       game.sokoban_objects_still_needed > 0 ||
9533       game.lights_still_needed > 0)
9534   {
9535     int element = Tile[x][y];
9536     int graphic = el2img(element);
9537
9538     if (IS_ANIMATED(graphic))
9539       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9540
9541     return;
9542   }
9543
9544   // do not re-open exit door closed after last player
9545   if (game.all_players_gone)
9546     return;
9547
9548   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9549
9550   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9551 }
9552
9553 static void CheckExitSteelEM(int x, int y)
9554 {
9555   if (game.gems_still_needed > 0 ||
9556       game.sokoban_fields_still_needed > 0 ||
9557       game.sokoban_objects_still_needed > 0 ||
9558       game.lights_still_needed > 0)
9559   {
9560     int element = Tile[x][y];
9561     int graphic = el2img(element);
9562
9563     if (IS_ANIMATED(graphic))
9564       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9565
9566     return;
9567   }
9568
9569   // do not re-open exit door closed after last player
9570   if (game.all_players_gone)
9571     return;
9572
9573   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9574
9575   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9576 }
9577
9578 static void CheckExitSP(int x, int y)
9579 {
9580   if (game.gems_still_needed > 0)
9581   {
9582     int element = Tile[x][y];
9583     int graphic = el2img(element);
9584
9585     if (IS_ANIMATED(graphic))
9586       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9587
9588     return;
9589   }
9590
9591   // do not re-open exit door closed after last player
9592   if (game.all_players_gone)
9593     return;
9594
9595   Tile[x][y] = EL_SP_EXIT_OPENING;
9596
9597   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9598 }
9599
9600 static void CloseAllOpenTimegates(void)
9601 {
9602   int x, y;
9603
9604   SCAN_PLAYFIELD(x, y)
9605   {
9606     int element = Tile[x][y];
9607
9608     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9609     {
9610       Tile[x][y] = EL_TIMEGATE_CLOSING;
9611
9612       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9613     }
9614   }
9615 }
9616
9617 static void DrawTwinkleOnField(int x, int y)
9618 {
9619   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9620     return;
9621
9622   if (Tile[x][y] == EL_BD_DIAMOND)
9623     return;
9624
9625   if (MovDelay[x][y] == 0)      // next animation frame
9626     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9627
9628   if (MovDelay[x][y] != 0)      // wait some time before next frame
9629   {
9630     MovDelay[x][y]--;
9631
9632     DrawLevelElementAnimation(x, y, Tile[x][y]);
9633
9634     if (MovDelay[x][y] != 0)
9635     {
9636       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9637                                            10 - MovDelay[x][y]);
9638
9639       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9640     }
9641   }
9642 }
9643
9644 static void MauerWaechst(int x, int y)
9645 {
9646   int delay = 6;
9647
9648   if (!MovDelay[x][y])          // next animation frame
9649     MovDelay[x][y] = 3 * delay;
9650
9651   if (MovDelay[x][y])           // wait some time before next frame
9652   {
9653     MovDelay[x][y]--;
9654
9655     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9656     {
9657       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9658       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9659
9660       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9661     }
9662
9663     if (!MovDelay[x][y])
9664     {
9665       if (MovDir[x][y] == MV_LEFT)
9666       {
9667         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9668           TEST_DrawLevelField(x - 1, y);
9669       }
9670       else if (MovDir[x][y] == MV_RIGHT)
9671       {
9672         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9673           TEST_DrawLevelField(x + 1, y);
9674       }
9675       else if (MovDir[x][y] == MV_UP)
9676       {
9677         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9678           TEST_DrawLevelField(x, y - 1);
9679       }
9680       else
9681       {
9682         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9683           TEST_DrawLevelField(x, y + 1);
9684       }
9685
9686       Tile[x][y] = Store[x][y];
9687       Store[x][y] = 0;
9688       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9689       TEST_DrawLevelField(x, y);
9690     }
9691   }
9692 }
9693
9694 static void MauerAbleger(int ax, int ay)
9695 {
9696   int element = Tile[ax][ay];
9697   int graphic = el2img(element);
9698   boolean oben_frei = FALSE, unten_frei = FALSE;
9699   boolean links_frei = FALSE, rechts_frei = FALSE;
9700   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9701   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9702   boolean new_wall = FALSE;
9703
9704   if (IS_ANIMATED(graphic))
9705     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9706
9707   if (!MovDelay[ax][ay])        // start building new wall
9708     MovDelay[ax][ay] = 6;
9709
9710   if (MovDelay[ax][ay])         // wait some time before building new wall
9711   {
9712     MovDelay[ax][ay]--;
9713     if (MovDelay[ax][ay])
9714       return;
9715   }
9716
9717   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9718     oben_frei = TRUE;
9719   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9720     unten_frei = TRUE;
9721   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9722     links_frei = TRUE;
9723   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9724     rechts_frei = TRUE;
9725
9726   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9727       element == EL_EXPANDABLE_WALL_ANY)
9728   {
9729     if (oben_frei)
9730     {
9731       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9732       Store[ax][ay-1] = element;
9733       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9734       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9735         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9736                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9737       new_wall = TRUE;
9738     }
9739     if (unten_frei)
9740     {
9741       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9742       Store[ax][ay+1] = element;
9743       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9744       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9745         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9746                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9747       new_wall = TRUE;
9748     }
9749   }
9750
9751   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9752       element == EL_EXPANDABLE_WALL_ANY ||
9753       element == EL_EXPANDABLE_WALL ||
9754       element == EL_BD_EXPANDABLE_WALL)
9755   {
9756     if (links_frei)
9757     {
9758       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9759       Store[ax-1][ay] = element;
9760       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9761       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9762         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9763                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9764       new_wall = TRUE;
9765     }
9766
9767     if (rechts_frei)
9768     {
9769       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9770       Store[ax+1][ay] = element;
9771       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9772       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9773         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9774                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9775       new_wall = TRUE;
9776     }
9777   }
9778
9779   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9780     TEST_DrawLevelField(ax, ay);
9781
9782   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9783     oben_massiv = TRUE;
9784   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9785     unten_massiv = TRUE;
9786   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9787     links_massiv = TRUE;
9788   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9789     rechts_massiv = TRUE;
9790
9791   if (((oben_massiv && unten_massiv) ||
9792        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9793        element == EL_EXPANDABLE_WALL) &&
9794       ((links_massiv && rechts_massiv) ||
9795        element == EL_EXPANDABLE_WALL_VERTICAL))
9796     Tile[ax][ay] = EL_WALL;
9797
9798   if (new_wall)
9799     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9800 }
9801
9802 static void MauerAblegerStahl(int ax, int ay)
9803 {
9804   int element = Tile[ax][ay];
9805   int graphic = el2img(element);
9806   boolean oben_frei = FALSE, unten_frei = FALSE;
9807   boolean links_frei = FALSE, rechts_frei = FALSE;
9808   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9809   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9810   boolean new_wall = FALSE;
9811
9812   if (IS_ANIMATED(graphic))
9813     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9814
9815   if (!MovDelay[ax][ay])        // start building new wall
9816     MovDelay[ax][ay] = 6;
9817
9818   if (MovDelay[ax][ay])         // wait some time before building new wall
9819   {
9820     MovDelay[ax][ay]--;
9821     if (MovDelay[ax][ay])
9822       return;
9823   }
9824
9825   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9826     oben_frei = TRUE;
9827   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9828     unten_frei = TRUE;
9829   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9830     links_frei = TRUE;
9831   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9832     rechts_frei = TRUE;
9833
9834   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9835       element == EL_EXPANDABLE_STEELWALL_ANY)
9836   {
9837     if (oben_frei)
9838     {
9839       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9840       Store[ax][ay-1] = element;
9841       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9842       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9843         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9844                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9845       new_wall = TRUE;
9846     }
9847     if (unten_frei)
9848     {
9849       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9850       Store[ax][ay+1] = element;
9851       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9852       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9853         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9854                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9855       new_wall = TRUE;
9856     }
9857   }
9858
9859   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9860       element == EL_EXPANDABLE_STEELWALL_ANY)
9861   {
9862     if (links_frei)
9863     {
9864       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9865       Store[ax-1][ay] = element;
9866       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9867       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9868         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9869                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9870       new_wall = TRUE;
9871     }
9872
9873     if (rechts_frei)
9874     {
9875       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9876       Store[ax+1][ay] = element;
9877       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9878       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9879         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9880                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9881       new_wall = TRUE;
9882     }
9883   }
9884
9885   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9886     oben_massiv = TRUE;
9887   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9888     unten_massiv = TRUE;
9889   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9890     links_massiv = TRUE;
9891   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9892     rechts_massiv = TRUE;
9893
9894   if (((oben_massiv && unten_massiv) ||
9895        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9896       ((links_massiv && rechts_massiv) ||
9897        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9898     Tile[ax][ay] = EL_STEELWALL;
9899
9900   if (new_wall)
9901     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9902 }
9903
9904 static void CheckForDragon(int x, int y)
9905 {
9906   int i, j;
9907   boolean dragon_found = FALSE;
9908   static int xy[4][2] =
9909   {
9910     { 0, -1 },
9911     { -1, 0 },
9912     { +1, 0 },
9913     { 0, +1 }
9914   };
9915
9916   for (i = 0; i < NUM_DIRECTIONS; i++)
9917   {
9918     for (j = 0; j < 4; j++)
9919     {
9920       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9921
9922       if (IN_LEV_FIELD(xx, yy) &&
9923           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9924       {
9925         if (Tile[xx][yy] == EL_DRAGON)
9926           dragon_found = TRUE;
9927       }
9928       else
9929         break;
9930     }
9931   }
9932
9933   if (!dragon_found)
9934   {
9935     for (i = 0; i < NUM_DIRECTIONS; i++)
9936     {
9937       for (j = 0; j < 3; j++)
9938       {
9939         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9940   
9941         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9942         {
9943           Tile[xx][yy] = EL_EMPTY;
9944           TEST_DrawLevelField(xx, yy);
9945         }
9946         else
9947           break;
9948       }
9949     }
9950   }
9951 }
9952
9953 static void InitBuggyBase(int x, int y)
9954 {
9955   int element = Tile[x][y];
9956   int activating_delay = FRAMES_PER_SECOND / 4;
9957
9958   ChangeDelay[x][y] =
9959     (element == EL_SP_BUGGY_BASE ?
9960      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9961      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9962      activating_delay :
9963      element == EL_SP_BUGGY_BASE_ACTIVE ?
9964      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9965 }
9966
9967 static void WarnBuggyBase(int x, int y)
9968 {
9969   int i;
9970   static int xy[4][2] =
9971   {
9972     { 0, -1 },
9973     { -1, 0 },
9974     { +1, 0 },
9975     { 0, +1 }
9976   };
9977
9978   for (i = 0; i < NUM_DIRECTIONS; i++)
9979   {
9980     int xx = x + xy[i][0];
9981     int yy = y + xy[i][1];
9982
9983     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9984     {
9985       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9986
9987       break;
9988     }
9989   }
9990 }
9991
9992 static void InitTrap(int x, int y)
9993 {
9994   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9995 }
9996
9997 static void ActivateTrap(int x, int y)
9998 {
9999   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10000 }
10001
10002 static void ChangeActiveTrap(int x, int y)
10003 {
10004   int graphic = IMG_TRAP_ACTIVE;
10005
10006   // if new animation frame was drawn, correct crumbled sand border
10007   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10008     TEST_DrawLevelFieldCrumbled(x, y);
10009 }
10010
10011 static int getSpecialActionElement(int element, int number, int base_element)
10012 {
10013   return (element != EL_EMPTY ? element :
10014           number != -1 ? base_element + number - 1 :
10015           EL_EMPTY);
10016 }
10017
10018 static int getModifiedActionNumber(int value_old, int operator, int operand,
10019                                    int value_min, int value_max)
10020 {
10021   int value_new = (operator == CA_MODE_SET      ? operand :
10022                    operator == CA_MODE_ADD      ? value_old + operand :
10023                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10024                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10025                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10026                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10027                    value_old);
10028
10029   return (value_new < value_min ? value_min :
10030           value_new > value_max ? value_max :
10031           value_new);
10032 }
10033
10034 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10035 {
10036   struct ElementInfo *ei = &element_info[element];
10037   struct ElementChangeInfo *change = &ei->change_page[page];
10038   int target_element = change->target_element;
10039   int action_type = change->action_type;
10040   int action_mode = change->action_mode;
10041   int action_arg = change->action_arg;
10042   int action_element = change->action_element;
10043   int i;
10044
10045   if (!change->has_action)
10046     return;
10047
10048   // ---------- determine action paramater values -----------------------------
10049
10050   int level_time_value =
10051     (level.time > 0 ? TimeLeft :
10052      TimePlayed);
10053
10054   int action_arg_element_raw =
10055     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10056      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10057      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10058      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10059      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10060      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10061      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10062      EL_EMPTY);
10063   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10064
10065   int action_arg_direction =
10066     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10067      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10068      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10069      change->actual_trigger_side :
10070      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10071      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10072      MV_NONE);
10073
10074   int action_arg_number_min =
10075     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10076      CA_ARG_MIN);
10077
10078   int action_arg_number_max =
10079     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10080      action_type == CA_SET_LEVEL_GEMS ? 999 :
10081      action_type == CA_SET_LEVEL_TIME ? 9999 :
10082      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10083      action_type == CA_SET_CE_VALUE ? 9999 :
10084      action_type == CA_SET_CE_SCORE ? 9999 :
10085      CA_ARG_MAX);
10086
10087   int action_arg_number_reset =
10088     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10089      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10090      action_type == CA_SET_LEVEL_TIME ? level.time :
10091      action_type == CA_SET_LEVEL_SCORE ? 0 :
10092      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10093      action_type == CA_SET_CE_SCORE ? 0 :
10094      0);
10095
10096   int action_arg_number =
10097     (action_arg <= CA_ARG_MAX ? action_arg :
10098      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10099      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10100      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10101      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10102      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10103      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10104      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10105      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10106      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10107      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10108      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10109      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10110      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10111      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10112      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10113      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10114      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10115      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10116      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10117      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10118      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10119      -1);
10120
10121   int action_arg_number_old =
10122     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10123      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10124      action_type == CA_SET_LEVEL_SCORE ? game.score :
10125      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10126      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10127      0);
10128
10129   int action_arg_number_new =
10130     getModifiedActionNumber(action_arg_number_old,
10131                             action_mode, action_arg_number,
10132                             action_arg_number_min, action_arg_number_max);
10133
10134   int trigger_player_bits =
10135     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10136      change->actual_trigger_player_bits : change->trigger_player);
10137
10138   int action_arg_player_bits =
10139     (action_arg >= CA_ARG_PLAYER_1 &&
10140      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10141      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10142      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10143      PLAYER_BITS_ANY);
10144
10145   // ---------- execute action  -----------------------------------------------
10146
10147   switch (action_type)
10148   {
10149     case CA_NO_ACTION:
10150     {
10151       return;
10152     }
10153
10154     // ---------- level actions  ----------------------------------------------
10155
10156     case CA_RESTART_LEVEL:
10157     {
10158       game.restart_level = TRUE;
10159
10160       break;
10161     }
10162
10163     case CA_SHOW_ENVELOPE:
10164     {
10165       int element = getSpecialActionElement(action_arg_element,
10166                                             action_arg_number, EL_ENVELOPE_1);
10167
10168       if (IS_ENVELOPE(element))
10169         local_player->show_envelope = element;
10170
10171       break;
10172     }
10173
10174     case CA_SET_LEVEL_TIME:
10175     {
10176       if (level.time > 0)       // only modify limited time value
10177       {
10178         TimeLeft = action_arg_number_new;
10179
10180         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10181
10182         DisplayGameControlValues();
10183
10184         if (!TimeLeft && setup.time_limit)
10185           for (i = 0; i < MAX_PLAYERS; i++)
10186             KillPlayer(&stored_player[i]);
10187       }
10188
10189       break;
10190     }
10191
10192     case CA_SET_LEVEL_SCORE:
10193     {
10194       game.score = action_arg_number_new;
10195
10196       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10197
10198       DisplayGameControlValues();
10199
10200       break;
10201     }
10202
10203     case CA_SET_LEVEL_GEMS:
10204     {
10205       game.gems_still_needed = action_arg_number_new;
10206
10207       game.snapshot.collected_item = TRUE;
10208
10209       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10210
10211       DisplayGameControlValues();
10212
10213       break;
10214     }
10215
10216     case CA_SET_LEVEL_WIND:
10217     {
10218       game.wind_direction = action_arg_direction;
10219
10220       break;
10221     }
10222
10223     case CA_SET_LEVEL_RANDOM_SEED:
10224     {
10225       // ensure that setting a new random seed while playing is predictable
10226       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10227
10228       break;
10229     }
10230
10231     // ---------- player actions  ---------------------------------------------
10232
10233     case CA_MOVE_PLAYER:
10234     case CA_MOVE_PLAYER_NEW:
10235     {
10236       // automatically move to the next field in specified direction
10237       for (i = 0; i < MAX_PLAYERS; i++)
10238         if (trigger_player_bits & (1 << i))
10239           if (action_type == CA_MOVE_PLAYER ||
10240               stored_player[i].MovPos == 0)
10241             stored_player[i].programmed_action = action_arg_direction;
10242
10243       break;
10244     }
10245
10246     case CA_EXIT_PLAYER:
10247     {
10248       for (i = 0; i < MAX_PLAYERS; i++)
10249         if (action_arg_player_bits & (1 << i))
10250           ExitPlayer(&stored_player[i]);
10251
10252       if (game.players_still_needed == 0)
10253         LevelSolved();
10254
10255       break;
10256     }
10257
10258     case CA_KILL_PLAYER:
10259     {
10260       for (i = 0; i < MAX_PLAYERS; i++)
10261         if (action_arg_player_bits & (1 << i))
10262           KillPlayer(&stored_player[i]);
10263
10264       break;
10265     }
10266
10267     case CA_SET_PLAYER_KEYS:
10268     {
10269       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10270       int element = getSpecialActionElement(action_arg_element,
10271                                             action_arg_number, EL_KEY_1);
10272
10273       if (IS_KEY(element))
10274       {
10275         for (i = 0; i < MAX_PLAYERS; i++)
10276         {
10277           if (trigger_player_bits & (1 << i))
10278           {
10279             stored_player[i].key[KEY_NR(element)] = key_state;
10280
10281             DrawGameDoorValues();
10282           }
10283         }
10284       }
10285
10286       break;
10287     }
10288
10289     case CA_SET_PLAYER_SPEED:
10290     {
10291       for (i = 0; i < MAX_PLAYERS; i++)
10292       {
10293         if (trigger_player_bits & (1 << i))
10294         {
10295           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10296
10297           if (action_arg == CA_ARG_SPEED_FASTER &&
10298               stored_player[i].cannot_move)
10299           {
10300             action_arg_number = STEPSIZE_VERY_SLOW;
10301           }
10302           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10303                    action_arg == CA_ARG_SPEED_FASTER)
10304           {
10305             action_arg_number = 2;
10306             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10307                            CA_MODE_MULTIPLY);
10308           }
10309           else if (action_arg == CA_ARG_NUMBER_RESET)
10310           {
10311             action_arg_number = level.initial_player_stepsize[i];
10312           }
10313
10314           move_stepsize =
10315             getModifiedActionNumber(move_stepsize,
10316                                     action_mode,
10317                                     action_arg_number,
10318                                     action_arg_number_min,
10319                                     action_arg_number_max);
10320
10321           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10322         }
10323       }
10324
10325       break;
10326     }
10327
10328     case CA_SET_PLAYER_SHIELD:
10329     {
10330       for (i = 0; i < MAX_PLAYERS; i++)
10331       {
10332         if (trigger_player_bits & (1 << i))
10333         {
10334           if (action_arg == CA_ARG_SHIELD_OFF)
10335           {
10336             stored_player[i].shield_normal_time_left = 0;
10337             stored_player[i].shield_deadly_time_left = 0;
10338           }
10339           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10340           {
10341             stored_player[i].shield_normal_time_left = 999999;
10342           }
10343           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10344           {
10345             stored_player[i].shield_normal_time_left = 999999;
10346             stored_player[i].shield_deadly_time_left = 999999;
10347           }
10348         }
10349       }
10350
10351       break;
10352     }
10353
10354     case CA_SET_PLAYER_GRAVITY:
10355     {
10356       for (i = 0; i < MAX_PLAYERS; i++)
10357       {
10358         if (trigger_player_bits & (1 << i))
10359         {
10360           stored_player[i].gravity =
10361             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10362              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10363              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10364              stored_player[i].gravity);
10365         }
10366       }
10367
10368       break;
10369     }
10370
10371     case CA_SET_PLAYER_ARTWORK:
10372     {
10373       for (i = 0; i < MAX_PLAYERS; i++)
10374       {
10375         if (trigger_player_bits & (1 << i))
10376         {
10377           int artwork_element = action_arg_element;
10378
10379           if (action_arg == CA_ARG_ELEMENT_RESET)
10380             artwork_element =
10381               (level.use_artwork_element[i] ? level.artwork_element[i] :
10382                stored_player[i].element_nr);
10383
10384           if (stored_player[i].artwork_element != artwork_element)
10385             stored_player[i].Frame = 0;
10386
10387           stored_player[i].artwork_element = artwork_element;
10388
10389           SetPlayerWaiting(&stored_player[i], FALSE);
10390
10391           // set number of special actions for bored and sleeping animation
10392           stored_player[i].num_special_action_bored =
10393             get_num_special_action(artwork_element,
10394                                    ACTION_BORING_1, ACTION_BORING_LAST);
10395           stored_player[i].num_special_action_sleeping =
10396             get_num_special_action(artwork_element,
10397                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10398         }
10399       }
10400
10401       break;
10402     }
10403
10404     case CA_SET_PLAYER_INVENTORY:
10405     {
10406       for (i = 0; i < MAX_PLAYERS; i++)
10407       {
10408         struct PlayerInfo *player = &stored_player[i];
10409         int j, k;
10410
10411         if (trigger_player_bits & (1 << i))
10412         {
10413           int inventory_element = action_arg_element;
10414
10415           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10416               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10417               action_arg == CA_ARG_ELEMENT_ACTION)
10418           {
10419             int element = inventory_element;
10420             int collect_count = element_info[element].collect_count_initial;
10421
10422             if (!IS_CUSTOM_ELEMENT(element))
10423               collect_count = 1;
10424
10425             if (collect_count == 0)
10426               player->inventory_infinite_element = element;
10427             else
10428               for (k = 0; k < collect_count; k++)
10429                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10430                   player->inventory_element[player->inventory_size++] =
10431                     element;
10432           }
10433           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10434                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10435                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10436           {
10437             if (player->inventory_infinite_element != EL_UNDEFINED &&
10438                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10439                                      action_arg_element_raw))
10440               player->inventory_infinite_element = EL_UNDEFINED;
10441
10442             for (k = 0, j = 0; j < player->inventory_size; j++)
10443             {
10444               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10445                                         action_arg_element_raw))
10446                 player->inventory_element[k++] = player->inventory_element[j];
10447             }
10448
10449             player->inventory_size = k;
10450           }
10451           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10452           {
10453             if (player->inventory_size > 0)
10454             {
10455               for (j = 0; j < player->inventory_size - 1; j++)
10456                 player->inventory_element[j] = player->inventory_element[j + 1];
10457
10458               player->inventory_size--;
10459             }
10460           }
10461           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10462           {
10463             if (player->inventory_size > 0)
10464               player->inventory_size--;
10465           }
10466           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10467           {
10468             player->inventory_infinite_element = EL_UNDEFINED;
10469             player->inventory_size = 0;
10470           }
10471           else if (action_arg == CA_ARG_INVENTORY_RESET)
10472           {
10473             player->inventory_infinite_element = EL_UNDEFINED;
10474             player->inventory_size = 0;
10475
10476             if (level.use_initial_inventory[i])
10477             {
10478               for (j = 0; j < level.initial_inventory_size[i]; j++)
10479               {
10480                 int element = level.initial_inventory_content[i][j];
10481                 int collect_count = element_info[element].collect_count_initial;
10482
10483                 if (!IS_CUSTOM_ELEMENT(element))
10484                   collect_count = 1;
10485
10486                 if (collect_count == 0)
10487                   player->inventory_infinite_element = element;
10488                 else
10489                   for (k = 0; k < collect_count; k++)
10490                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10491                       player->inventory_element[player->inventory_size++] =
10492                         element;
10493               }
10494             }
10495           }
10496         }
10497       }
10498
10499       break;
10500     }
10501
10502     // ---------- CE actions  -------------------------------------------------
10503
10504     case CA_SET_CE_VALUE:
10505     {
10506       int last_ce_value = CustomValue[x][y];
10507
10508       CustomValue[x][y] = action_arg_number_new;
10509
10510       if (CustomValue[x][y] != last_ce_value)
10511       {
10512         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10513         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10514
10515         if (CustomValue[x][y] == 0)
10516         {
10517           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10518           ChangeCount[x][y] = 0;        // allow at least one more change
10519
10520           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10521           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10522         }
10523       }
10524
10525       break;
10526     }
10527
10528     case CA_SET_CE_SCORE:
10529     {
10530       int last_ce_score = ei->collect_score;
10531
10532       ei->collect_score = action_arg_number_new;
10533
10534       if (ei->collect_score != last_ce_score)
10535       {
10536         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10537         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10538
10539         if (ei->collect_score == 0)
10540         {
10541           int xx, yy;
10542
10543           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10544           ChangeCount[x][y] = 0;        // allow at least one more change
10545
10546           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10547           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10548
10549           /*
10550             This is a very special case that seems to be a mixture between
10551             CheckElementChange() and CheckTriggeredElementChange(): while
10552             the first one only affects single elements that are triggered
10553             directly, the second one affects multiple elements in the playfield
10554             that are triggered indirectly by another element. This is a third
10555             case: Changing the CE score always affects multiple identical CEs,
10556             so every affected CE must be checked, not only the single CE for
10557             which the CE score was changed in the first place (as every instance
10558             of that CE shares the same CE score, and therefore also can change)!
10559           */
10560           SCAN_PLAYFIELD(xx, yy)
10561           {
10562             if (Tile[xx][yy] == element)
10563               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10564                                  CE_SCORE_GETS_ZERO);
10565           }
10566         }
10567       }
10568
10569       break;
10570     }
10571
10572     case CA_SET_CE_ARTWORK:
10573     {
10574       int artwork_element = action_arg_element;
10575       boolean reset_frame = FALSE;
10576       int xx, yy;
10577
10578       if (action_arg == CA_ARG_ELEMENT_RESET)
10579         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10580                            element);
10581
10582       if (ei->gfx_element != artwork_element)
10583         reset_frame = TRUE;
10584
10585       ei->gfx_element = artwork_element;
10586
10587       SCAN_PLAYFIELD(xx, yy)
10588       {
10589         if (Tile[xx][yy] == element)
10590         {
10591           if (reset_frame)
10592           {
10593             ResetGfxAnimation(xx, yy);
10594             ResetRandomAnimationValue(xx, yy);
10595           }
10596
10597           TEST_DrawLevelField(xx, yy);
10598         }
10599       }
10600
10601       break;
10602     }
10603
10604     // ---------- engine actions  ---------------------------------------------
10605
10606     case CA_SET_ENGINE_SCAN_MODE:
10607     {
10608       InitPlayfieldScanMode(action_arg);
10609
10610       break;
10611     }
10612
10613     default:
10614       break;
10615   }
10616 }
10617
10618 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10619 {
10620   int old_element = Tile[x][y];
10621   int new_element = GetElementFromGroupElement(element);
10622   int previous_move_direction = MovDir[x][y];
10623   int last_ce_value = CustomValue[x][y];
10624   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10625   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10626   boolean add_player_onto_element = (new_element_is_player &&
10627                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10628                                      IS_WALKABLE(old_element));
10629
10630   if (!add_player_onto_element)
10631   {
10632     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10633       RemoveMovingField(x, y);
10634     else
10635       RemoveField(x, y);
10636
10637     Tile[x][y] = new_element;
10638
10639     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10640       MovDir[x][y] = previous_move_direction;
10641
10642     if (element_info[new_element].use_last_ce_value)
10643       CustomValue[x][y] = last_ce_value;
10644
10645     InitField_WithBug1(x, y, FALSE);
10646
10647     new_element = Tile[x][y];   // element may have changed
10648
10649     ResetGfxAnimation(x, y);
10650     ResetRandomAnimationValue(x, y);
10651
10652     TEST_DrawLevelField(x, y);
10653
10654     if (GFX_CRUMBLED(new_element))
10655       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10656   }
10657
10658   // check if element under the player changes from accessible to unaccessible
10659   // (needed for special case of dropping element which then changes)
10660   // (must be checked after creating new element for walkable group elements)
10661   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10662       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10663   {
10664     Bang(x, y);
10665
10666     return;
10667   }
10668
10669   // "ChangeCount" not set yet to allow "entered by player" change one time
10670   if (new_element_is_player)
10671     RelocatePlayer(x, y, new_element);
10672
10673   if (is_change)
10674     ChangeCount[x][y]++;        // count number of changes in the same frame
10675
10676   TestIfBadThingTouchesPlayer(x, y);
10677   TestIfPlayerTouchesCustomElement(x, y);
10678   TestIfElementTouchesCustomElement(x, y);
10679 }
10680
10681 static void CreateField(int x, int y, int element)
10682 {
10683   CreateFieldExt(x, y, element, FALSE);
10684 }
10685
10686 static void CreateElementFromChange(int x, int y, int element)
10687 {
10688   element = GET_VALID_RUNTIME_ELEMENT(element);
10689
10690   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10691   {
10692     int old_element = Tile[x][y];
10693
10694     // prevent changed element from moving in same engine frame
10695     // unless both old and new element can either fall or move
10696     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10697         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10698       Stop[x][y] = TRUE;
10699   }
10700
10701   CreateFieldExt(x, y, element, TRUE);
10702 }
10703
10704 static boolean ChangeElement(int x, int y, int element, int page)
10705 {
10706   struct ElementInfo *ei = &element_info[element];
10707   struct ElementChangeInfo *change = &ei->change_page[page];
10708   int ce_value = CustomValue[x][y];
10709   int ce_score = ei->collect_score;
10710   int target_element;
10711   int old_element = Tile[x][y];
10712
10713   // always use default change event to prevent running into a loop
10714   if (ChangeEvent[x][y] == -1)
10715     ChangeEvent[x][y] = CE_DELAY;
10716
10717   if (ChangeEvent[x][y] == CE_DELAY)
10718   {
10719     // reset actual trigger element, trigger player and action element
10720     change->actual_trigger_element = EL_EMPTY;
10721     change->actual_trigger_player = EL_EMPTY;
10722     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10723     change->actual_trigger_side = CH_SIDE_NONE;
10724     change->actual_trigger_ce_value = 0;
10725     change->actual_trigger_ce_score = 0;
10726   }
10727
10728   // do not change elements more than a specified maximum number of changes
10729   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10730     return FALSE;
10731
10732   ChangeCount[x][y]++;          // count number of changes in the same frame
10733
10734   if (change->explode)
10735   {
10736     Bang(x, y);
10737
10738     return TRUE;
10739   }
10740
10741   if (change->use_target_content)
10742   {
10743     boolean complete_replace = TRUE;
10744     boolean can_replace[3][3];
10745     int xx, yy;
10746
10747     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10748     {
10749       boolean is_empty;
10750       boolean is_walkable;
10751       boolean is_diggable;
10752       boolean is_collectible;
10753       boolean is_removable;
10754       boolean is_destructible;
10755       int ex = x + xx - 1;
10756       int ey = y + yy - 1;
10757       int content_element = change->target_content.e[xx][yy];
10758       int e;
10759
10760       can_replace[xx][yy] = TRUE;
10761
10762       if (ex == x && ey == y)   // do not check changing element itself
10763         continue;
10764
10765       if (content_element == EL_EMPTY_SPACE)
10766       {
10767         can_replace[xx][yy] = FALSE;    // do not replace border with space
10768
10769         continue;
10770       }
10771
10772       if (!IN_LEV_FIELD(ex, ey))
10773       {
10774         can_replace[xx][yy] = FALSE;
10775         complete_replace = FALSE;
10776
10777         continue;
10778       }
10779
10780       e = Tile[ex][ey];
10781
10782       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10783         e = MovingOrBlocked2Element(ex, ey);
10784
10785       is_empty = (IS_FREE(ex, ey) ||
10786                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10787
10788       is_walkable     = (is_empty || IS_WALKABLE(e));
10789       is_diggable     = (is_empty || IS_DIGGABLE(e));
10790       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10791       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10792       is_removable    = (is_diggable || is_collectible);
10793
10794       can_replace[xx][yy] =
10795         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10796           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10797           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10798           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10799           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10800           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10801          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10802
10803       if (!can_replace[xx][yy])
10804         complete_replace = FALSE;
10805     }
10806
10807     if (!change->only_if_complete || complete_replace)
10808     {
10809       boolean something_has_changed = FALSE;
10810
10811       if (change->only_if_complete && change->use_random_replace &&
10812           RND(100) < change->random_percentage)
10813         return FALSE;
10814
10815       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10816       {
10817         int ex = x + xx - 1;
10818         int ey = y + yy - 1;
10819         int content_element;
10820
10821         if (can_replace[xx][yy] && (!change->use_random_replace ||
10822                                     RND(100) < change->random_percentage))
10823         {
10824           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10825             RemoveMovingField(ex, ey);
10826
10827           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10828
10829           content_element = change->target_content.e[xx][yy];
10830           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10831                                               ce_value, ce_score);
10832
10833           CreateElementFromChange(ex, ey, target_element);
10834
10835           something_has_changed = TRUE;
10836
10837           // for symmetry reasons, freeze newly created border elements
10838           if (ex != x || ey != y)
10839             Stop[ex][ey] = TRUE;        // no more moving in this frame
10840         }
10841       }
10842
10843       if (something_has_changed)
10844       {
10845         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10846         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10847       }
10848     }
10849   }
10850   else
10851   {
10852     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10853                                         ce_value, ce_score);
10854
10855     if (element == EL_DIAGONAL_GROWING ||
10856         element == EL_DIAGONAL_SHRINKING)
10857     {
10858       target_element = Store[x][y];
10859
10860       Store[x][y] = EL_EMPTY;
10861     }
10862
10863     // special case: element changes to player (and may be kept if walkable)
10864     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10865       CreateElementFromChange(x, y, EL_EMPTY);
10866
10867     CreateElementFromChange(x, y, target_element);
10868
10869     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10870     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10871   }
10872
10873   // this uses direct change before indirect change
10874   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10875
10876   return TRUE;
10877 }
10878
10879 static void HandleElementChange(int x, int y, int page)
10880 {
10881   int element = MovingOrBlocked2Element(x, y);
10882   struct ElementInfo *ei = &element_info[element];
10883   struct ElementChangeInfo *change = &ei->change_page[page];
10884   boolean handle_action_before_change = FALSE;
10885
10886 #ifdef DEBUG
10887   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10888       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10889   {
10890     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10891           x, y, element, element_info[element].token_name);
10892     Debug("game:playing:HandleElementChange", "This should never happen!");
10893   }
10894 #endif
10895
10896   // this can happen with classic bombs on walkable, changing elements
10897   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10898   {
10899     return;
10900   }
10901
10902   if (ChangeDelay[x][y] == 0)           // initialize element change
10903   {
10904     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10905
10906     if (change->can_change)
10907     {
10908       // !!! not clear why graphic animation should be reset at all here !!!
10909       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10910       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10911
10912       /*
10913         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10914
10915         When using an animation frame delay of 1 (this only happens with
10916         "sp_zonk.moving.left/right" in the classic graphics), the default
10917         (non-moving) animation shows wrong animation frames (while the
10918         moving animation, like "sp_zonk.moving.left/right", is correct,
10919         so this graphical bug never shows up with the classic graphics).
10920         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10921         be drawn instead of the correct frames 0,1,2,3. This is caused by
10922         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10923         an element change: First when the change delay ("ChangeDelay[][]")
10924         counter has reached zero after decrementing, then a second time in
10925         the next frame (after "GfxFrame[][]" was already incremented) when
10926         "ChangeDelay[][]" is reset to the initial delay value again.
10927
10928         This causes frame 0 to be drawn twice, while the last frame won't
10929         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10930
10931         As some animations may already be cleverly designed around this bug
10932         (at least the "Snake Bite" snake tail animation does this), it cannot
10933         simply be fixed here without breaking such existing animations.
10934         Unfortunately, it cannot easily be detected if a graphics set was
10935         designed "before" or "after" the bug was fixed. As a workaround,
10936         a new graphics set option "game.graphics_engine_version" was added
10937         to be able to specify the game's major release version for which the
10938         graphics set was designed, which can then be used to decide if the
10939         bugfix should be used (version 4 and above) or not (version 3 or
10940         below, or if no version was specified at all, as with old sets).
10941
10942         (The wrong/fixed animation frames can be tested with the test level set
10943         "test_gfxframe" and level "000", which contains a specially prepared
10944         custom element at level position (x/y) == (11/9) which uses the zonk
10945         animation mentioned above. Using "game.graphics_engine_version: 4"
10946         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10947         This can also be seen from the debug output for this test element.)
10948       */
10949
10950       // when a custom element is about to change (for example by change delay),
10951       // do not reset graphic animation when the custom element is moving
10952       if (game.graphics_engine_version < 4 &&
10953           !IS_MOVING(x, y))
10954       {
10955         ResetGfxAnimation(x, y);
10956         ResetRandomAnimationValue(x, y);
10957       }
10958
10959       if (change->pre_change_function)
10960         change->pre_change_function(x, y);
10961     }
10962   }
10963
10964   ChangeDelay[x][y]--;
10965
10966   if (ChangeDelay[x][y] != 0)           // continue element change
10967   {
10968     if (change->can_change)
10969     {
10970       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10971
10972       if (IS_ANIMATED(graphic))
10973         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10974
10975       if (change->change_function)
10976         change->change_function(x, y);
10977     }
10978   }
10979   else                                  // finish element change
10980   {
10981     if (ChangePage[x][y] != -1)         // remember page from delayed change
10982     {
10983       page = ChangePage[x][y];
10984       ChangePage[x][y] = -1;
10985
10986       change = &ei->change_page[page];
10987     }
10988
10989     if (IS_MOVING(x, y))                // never change a running system ;-)
10990     {
10991       ChangeDelay[x][y] = 1;            // try change after next move step
10992       ChangePage[x][y] = page;          // remember page to use for change
10993
10994       return;
10995     }
10996
10997     // special case: set new level random seed before changing element
10998     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10999       handle_action_before_change = TRUE;
11000
11001     if (change->has_action && handle_action_before_change)
11002       ExecuteCustomElementAction(x, y, element, page);
11003
11004     if (change->can_change)
11005     {
11006       if (ChangeElement(x, y, element, page))
11007       {
11008         if (change->post_change_function)
11009           change->post_change_function(x, y);
11010       }
11011     }
11012
11013     if (change->has_action && !handle_action_before_change)
11014       ExecuteCustomElementAction(x, y, element, page);
11015   }
11016 }
11017
11018 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11019                                               int trigger_element,
11020                                               int trigger_event,
11021                                               int trigger_player,
11022                                               int trigger_side,
11023                                               int trigger_page)
11024 {
11025   boolean change_done_any = FALSE;
11026   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11027   int i;
11028
11029   if (!(trigger_events[trigger_element][trigger_event]))
11030     return FALSE;
11031
11032   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11033
11034   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11035   {
11036     int element = EL_CUSTOM_START + i;
11037     boolean change_done = FALSE;
11038     int p;
11039
11040     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11041         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11042       continue;
11043
11044     for (p = 0; p < element_info[element].num_change_pages; p++)
11045     {
11046       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11047
11048       if (change->can_change_or_has_action &&
11049           change->has_event[trigger_event] &&
11050           change->trigger_side & trigger_side &&
11051           change->trigger_player & trigger_player &&
11052           change->trigger_page & trigger_page_bits &&
11053           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11054       {
11055         change->actual_trigger_element = trigger_element;
11056         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11057         change->actual_trigger_player_bits = trigger_player;
11058         change->actual_trigger_side = trigger_side;
11059         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11060         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11061
11062         if ((change->can_change && !change_done) || change->has_action)
11063         {
11064           int x, y;
11065
11066           SCAN_PLAYFIELD(x, y)
11067           {
11068             if (Tile[x][y] == element)
11069             {
11070               if (change->can_change && !change_done)
11071               {
11072                 // if element already changed in this frame, not only prevent
11073                 // another element change (checked in ChangeElement()), but
11074                 // also prevent additional element actions for this element
11075
11076                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11077                     !level.use_action_after_change_bug)
11078                   continue;
11079
11080                 ChangeDelay[x][y] = 1;
11081                 ChangeEvent[x][y] = trigger_event;
11082
11083                 HandleElementChange(x, y, p);
11084               }
11085               else if (change->has_action)
11086               {
11087                 // if element already changed in this frame, not only prevent
11088                 // another element change (checked in ChangeElement()), but
11089                 // also prevent additional element actions for this element
11090
11091                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11092                     !level.use_action_after_change_bug)
11093                   continue;
11094
11095                 ExecuteCustomElementAction(x, y, element, p);
11096                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11097               }
11098             }
11099           }
11100
11101           if (change->can_change)
11102           {
11103             change_done = TRUE;
11104             change_done_any = TRUE;
11105           }
11106         }
11107       }
11108     }
11109   }
11110
11111   RECURSION_LOOP_DETECTION_END();
11112
11113   return change_done_any;
11114 }
11115
11116 static boolean CheckElementChangeExt(int x, int y,
11117                                      int element,
11118                                      int trigger_element,
11119                                      int trigger_event,
11120                                      int trigger_player,
11121                                      int trigger_side)
11122 {
11123   boolean change_done = FALSE;
11124   int p;
11125
11126   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11127       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11128     return FALSE;
11129
11130   if (Tile[x][y] == EL_BLOCKED)
11131   {
11132     Blocked2Moving(x, y, &x, &y);
11133     element = Tile[x][y];
11134   }
11135
11136   // check if element has already changed or is about to change after moving
11137   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11138        Tile[x][y] != element) ||
11139
11140       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11141        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11142         ChangePage[x][y] != -1)))
11143     return FALSE;
11144
11145   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11146
11147   for (p = 0; p < element_info[element].num_change_pages; p++)
11148   {
11149     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11150
11151     /* check trigger element for all events where the element that is checked
11152        for changing interacts with a directly adjacent element -- this is
11153        different to element changes that affect other elements to change on the
11154        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11155     boolean check_trigger_element =
11156       (trigger_event == CE_NEXT_TO_X ||
11157        trigger_event == CE_TOUCHING_X ||
11158        trigger_event == CE_HITTING_X ||
11159        trigger_event == CE_HIT_BY_X ||
11160        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11161
11162     if (change->can_change_or_has_action &&
11163         change->has_event[trigger_event] &&
11164         change->trigger_side & trigger_side &&
11165         change->trigger_player & trigger_player &&
11166         (!check_trigger_element ||
11167          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11168     {
11169       change->actual_trigger_element = trigger_element;
11170       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11171       change->actual_trigger_player_bits = trigger_player;
11172       change->actual_trigger_side = trigger_side;
11173       change->actual_trigger_ce_value = CustomValue[x][y];
11174       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11175
11176       // special case: trigger element not at (x,y) position for some events
11177       if (check_trigger_element)
11178       {
11179         static struct
11180         {
11181           int dx, dy;
11182         } move_xy[] =
11183           {
11184             {  0,  0 },
11185             { -1,  0 },
11186             { +1,  0 },
11187             {  0,  0 },
11188             {  0, -1 },
11189             {  0,  0 }, { 0, 0 }, { 0, 0 },
11190             {  0, +1 }
11191           };
11192
11193         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11194         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11195
11196         change->actual_trigger_ce_value = CustomValue[xx][yy];
11197         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11198       }
11199
11200       if (change->can_change && !change_done)
11201       {
11202         ChangeDelay[x][y] = 1;
11203         ChangeEvent[x][y] = trigger_event;
11204
11205         HandleElementChange(x, y, p);
11206
11207         change_done = TRUE;
11208       }
11209       else if (change->has_action)
11210       {
11211         ExecuteCustomElementAction(x, y, element, p);
11212         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11213       }
11214     }
11215   }
11216
11217   RECURSION_LOOP_DETECTION_END();
11218
11219   return change_done;
11220 }
11221
11222 static void PlayPlayerSound(struct PlayerInfo *player)
11223 {
11224   int jx = player->jx, jy = player->jy;
11225   int sound_element = player->artwork_element;
11226   int last_action = player->last_action_waiting;
11227   int action = player->action_waiting;
11228
11229   if (player->is_waiting)
11230   {
11231     if (action != last_action)
11232       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11233     else
11234       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11235   }
11236   else
11237   {
11238     if (action != last_action)
11239       StopSound(element_info[sound_element].sound[last_action]);
11240
11241     if (last_action == ACTION_SLEEPING)
11242       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11243   }
11244 }
11245
11246 static void PlayAllPlayersSound(void)
11247 {
11248   int i;
11249
11250   for (i = 0; i < MAX_PLAYERS; i++)
11251     if (stored_player[i].active)
11252       PlayPlayerSound(&stored_player[i]);
11253 }
11254
11255 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11256 {
11257   boolean last_waiting = player->is_waiting;
11258   int move_dir = player->MovDir;
11259
11260   player->dir_waiting = move_dir;
11261   player->last_action_waiting = player->action_waiting;
11262
11263   if (is_waiting)
11264   {
11265     if (!last_waiting)          // not waiting -> waiting
11266     {
11267       player->is_waiting = TRUE;
11268
11269       player->frame_counter_bored =
11270         FrameCounter +
11271         game.player_boring_delay_fixed +
11272         GetSimpleRandom(game.player_boring_delay_random);
11273       player->frame_counter_sleeping =
11274         FrameCounter +
11275         game.player_sleeping_delay_fixed +
11276         GetSimpleRandom(game.player_sleeping_delay_random);
11277
11278       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11279     }
11280
11281     if (game.player_sleeping_delay_fixed +
11282         game.player_sleeping_delay_random > 0 &&
11283         player->anim_delay_counter == 0 &&
11284         player->post_delay_counter == 0 &&
11285         FrameCounter >= player->frame_counter_sleeping)
11286       player->is_sleeping = TRUE;
11287     else if (game.player_boring_delay_fixed +
11288              game.player_boring_delay_random > 0 &&
11289              FrameCounter >= player->frame_counter_bored)
11290       player->is_bored = TRUE;
11291
11292     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11293                               player->is_bored ? ACTION_BORING :
11294                               ACTION_WAITING);
11295
11296     if (player->is_sleeping && player->use_murphy)
11297     {
11298       // special case for sleeping Murphy when leaning against non-free tile
11299
11300       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11301           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11302            !IS_MOVING(player->jx - 1, player->jy)))
11303         move_dir = MV_LEFT;
11304       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11305                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11306                 !IS_MOVING(player->jx + 1, player->jy)))
11307         move_dir = MV_RIGHT;
11308       else
11309         player->is_sleeping = FALSE;
11310
11311       player->dir_waiting = move_dir;
11312     }
11313
11314     if (player->is_sleeping)
11315     {
11316       if (player->num_special_action_sleeping > 0)
11317       {
11318         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11319         {
11320           int last_special_action = player->special_action_sleeping;
11321           int num_special_action = player->num_special_action_sleeping;
11322           int special_action =
11323             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11324              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11325              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11326              last_special_action + 1 : ACTION_SLEEPING);
11327           int special_graphic =
11328             el_act_dir2img(player->artwork_element, special_action, move_dir);
11329
11330           player->anim_delay_counter =
11331             graphic_info[special_graphic].anim_delay_fixed +
11332             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11333           player->post_delay_counter =
11334             graphic_info[special_graphic].post_delay_fixed +
11335             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11336
11337           player->special_action_sleeping = special_action;
11338         }
11339
11340         if (player->anim_delay_counter > 0)
11341         {
11342           player->action_waiting = player->special_action_sleeping;
11343           player->anim_delay_counter--;
11344         }
11345         else if (player->post_delay_counter > 0)
11346         {
11347           player->post_delay_counter--;
11348         }
11349       }
11350     }
11351     else if (player->is_bored)
11352     {
11353       if (player->num_special_action_bored > 0)
11354       {
11355         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11356         {
11357           int special_action =
11358             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11359           int special_graphic =
11360             el_act_dir2img(player->artwork_element, special_action, move_dir);
11361
11362           player->anim_delay_counter =
11363             graphic_info[special_graphic].anim_delay_fixed +
11364             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11365           player->post_delay_counter =
11366             graphic_info[special_graphic].post_delay_fixed +
11367             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11368
11369           player->special_action_bored = special_action;
11370         }
11371
11372         if (player->anim_delay_counter > 0)
11373         {
11374           player->action_waiting = player->special_action_bored;
11375           player->anim_delay_counter--;
11376         }
11377         else if (player->post_delay_counter > 0)
11378         {
11379           player->post_delay_counter--;
11380         }
11381       }
11382     }
11383   }
11384   else if (last_waiting)        // waiting -> not waiting
11385   {
11386     player->is_waiting = FALSE;
11387     player->is_bored = FALSE;
11388     player->is_sleeping = FALSE;
11389
11390     player->frame_counter_bored = -1;
11391     player->frame_counter_sleeping = -1;
11392
11393     player->anim_delay_counter = 0;
11394     player->post_delay_counter = 0;
11395
11396     player->dir_waiting = player->MovDir;
11397     player->action_waiting = ACTION_DEFAULT;
11398
11399     player->special_action_bored = ACTION_DEFAULT;
11400     player->special_action_sleeping = ACTION_DEFAULT;
11401   }
11402 }
11403
11404 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11405 {
11406   if ((!player->is_moving  && player->was_moving) ||
11407       (player->MovPos == 0 && player->was_moving) ||
11408       (player->is_snapping && !player->was_snapping) ||
11409       (player->is_dropping && !player->was_dropping))
11410   {
11411     if (!CheckSaveEngineSnapshotToList())
11412       return;
11413
11414     player->was_moving = FALSE;
11415     player->was_snapping = TRUE;
11416     player->was_dropping = TRUE;
11417   }
11418   else
11419   {
11420     if (player->is_moving)
11421       player->was_moving = TRUE;
11422
11423     if (!player->is_snapping)
11424       player->was_snapping = FALSE;
11425
11426     if (!player->is_dropping)
11427       player->was_dropping = FALSE;
11428   }
11429
11430   static struct MouseActionInfo mouse_action_last = { 0 };
11431   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11432   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11433
11434   if (new_released)
11435     CheckSaveEngineSnapshotToList();
11436
11437   mouse_action_last = mouse_action;
11438 }
11439
11440 static void CheckSingleStepMode(struct PlayerInfo *player)
11441 {
11442   if (tape.single_step && tape.recording && !tape.pausing)
11443   {
11444     // as it is called "single step mode", just return to pause mode when the
11445     // player stopped moving after one tile (or never starts moving at all)
11446     // (reverse logic needed here in case single step mode used in team mode)
11447     if (player->is_moving ||
11448         player->is_pushing ||
11449         player->is_dropping_pressed ||
11450         player->effective_mouse_action.button)
11451       game.enter_single_step_mode = FALSE;
11452   }
11453
11454   CheckSaveEngineSnapshot(player);
11455 }
11456
11457 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11458 {
11459   int left      = player_action & JOY_LEFT;
11460   int right     = player_action & JOY_RIGHT;
11461   int up        = player_action & JOY_UP;
11462   int down      = player_action & JOY_DOWN;
11463   int button1   = player_action & JOY_BUTTON_1;
11464   int button2   = player_action & JOY_BUTTON_2;
11465   int dx        = (left ? -1 : right ? 1 : 0);
11466   int dy        = (up   ? -1 : down  ? 1 : 0);
11467
11468   if (!player->active || tape.pausing)
11469     return 0;
11470
11471   if (player_action)
11472   {
11473     if (button1)
11474       SnapField(player, dx, dy);
11475     else
11476     {
11477       if (button2)
11478         DropElement(player);
11479
11480       MovePlayer(player, dx, dy);
11481     }
11482
11483     CheckSingleStepMode(player);
11484
11485     SetPlayerWaiting(player, FALSE);
11486
11487     return player_action;
11488   }
11489   else
11490   {
11491     // no actions for this player (no input at player's configured device)
11492
11493     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11494     SnapField(player, 0, 0);
11495     CheckGravityMovementWhenNotMoving(player);
11496
11497     if (player->MovPos == 0)
11498       SetPlayerWaiting(player, TRUE);
11499
11500     if (player->MovPos == 0)    // needed for tape.playing
11501       player->is_moving = FALSE;
11502
11503     player->is_dropping = FALSE;
11504     player->is_dropping_pressed = FALSE;
11505     player->drop_pressed_delay = 0;
11506
11507     CheckSingleStepMode(player);
11508
11509     return 0;
11510   }
11511 }
11512
11513 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11514                                          byte *tape_action)
11515 {
11516   if (!tape.use_mouse_actions)
11517     return;
11518
11519   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11520   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11521   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11522 }
11523
11524 static void SetTapeActionFromMouseAction(byte *tape_action,
11525                                          struct MouseActionInfo *mouse_action)
11526 {
11527   if (!tape.use_mouse_actions)
11528     return;
11529
11530   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11531   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11532   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11533 }
11534
11535 static void CheckLevelSolved(void)
11536 {
11537   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11538   {
11539     if (game_em.level_solved &&
11540         !game_em.game_over)                             // game won
11541     {
11542       LevelSolved();
11543
11544       game_em.game_over = TRUE;
11545
11546       game.all_players_gone = TRUE;
11547     }
11548
11549     if (game_em.game_over)                              // game lost
11550       game.all_players_gone = TRUE;
11551   }
11552   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11553   {
11554     if (game_sp.level_solved &&
11555         !game_sp.game_over)                             // game won
11556     {
11557       LevelSolved();
11558
11559       game_sp.game_over = TRUE;
11560
11561       game.all_players_gone = TRUE;
11562     }
11563
11564     if (game_sp.game_over)                              // game lost
11565       game.all_players_gone = TRUE;
11566   }
11567   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11568   {
11569     if (game_mm.level_solved &&
11570         !game_mm.game_over)                             // game won
11571     {
11572       LevelSolved();
11573
11574       game_mm.game_over = TRUE;
11575
11576       game.all_players_gone = TRUE;
11577     }
11578
11579     if (game_mm.game_over)                              // game lost
11580       game.all_players_gone = TRUE;
11581   }
11582 }
11583
11584 static void CheckLevelTime(void)
11585 {
11586   int i;
11587
11588   if (TimeFrames >= FRAMES_PER_SECOND)
11589   {
11590     TimeFrames = 0;
11591     TapeTime++;
11592
11593     for (i = 0; i < MAX_PLAYERS; i++)
11594     {
11595       struct PlayerInfo *player = &stored_player[i];
11596
11597       if (SHIELD_ON(player))
11598       {
11599         player->shield_normal_time_left--;
11600
11601         if (player->shield_deadly_time_left > 0)
11602           player->shield_deadly_time_left--;
11603       }
11604     }
11605
11606     if (!game.LevelSolved && !level.use_step_counter)
11607     {
11608       TimePlayed++;
11609
11610       if (TimeLeft > 0)
11611       {
11612         TimeLeft--;
11613
11614         if (TimeLeft <= 10 && setup.time_limit)
11615           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11616
11617         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11618            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11619
11620         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11621
11622         if (!TimeLeft && setup.time_limit)
11623         {
11624           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11625             game_em.lev->killed_out_of_time = TRUE;
11626           else
11627             for (i = 0; i < MAX_PLAYERS; i++)
11628               KillPlayer(&stored_player[i]);
11629         }
11630       }
11631       else if (game.no_time_limit && !game.all_players_gone)
11632       {
11633         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11634       }
11635
11636       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11637     }
11638
11639     if (tape.recording || tape.playing)
11640       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11641   }
11642
11643   if (tape.recording || tape.playing)
11644     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11645
11646   UpdateAndDisplayGameControlValues();
11647 }
11648
11649 void AdvanceFrameAndPlayerCounters(int player_nr)
11650 {
11651   int i;
11652
11653   // advance frame counters (global frame counter and time frame counter)
11654   FrameCounter++;
11655   TimeFrames++;
11656
11657   // advance player counters (counters for move delay, move animation etc.)
11658   for (i = 0; i < MAX_PLAYERS; i++)
11659   {
11660     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11661     int move_delay_value = stored_player[i].move_delay_value;
11662     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11663
11664     if (!advance_player_counters)       // not all players may be affected
11665       continue;
11666
11667     if (move_frames == 0)       // less than one move per game frame
11668     {
11669       int stepsize = TILEX / move_delay_value;
11670       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11671       int count = (stored_player[i].is_moving ?
11672                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11673
11674       if (count % delay == 0)
11675         move_frames = 1;
11676     }
11677
11678     stored_player[i].Frame += move_frames;
11679
11680     if (stored_player[i].MovPos != 0)
11681       stored_player[i].StepFrame += move_frames;
11682
11683     if (stored_player[i].move_delay > 0)
11684       stored_player[i].move_delay--;
11685
11686     // due to bugs in previous versions, counter must count up, not down
11687     if (stored_player[i].push_delay != -1)
11688       stored_player[i].push_delay++;
11689
11690     if (stored_player[i].drop_delay > 0)
11691       stored_player[i].drop_delay--;
11692
11693     if (stored_player[i].is_dropping_pressed)
11694       stored_player[i].drop_pressed_delay++;
11695   }
11696 }
11697
11698 void StartGameActions(boolean init_network_game, boolean record_tape,
11699                       int random_seed)
11700 {
11701   unsigned int new_random_seed = InitRND(random_seed);
11702
11703   if (record_tape)
11704     TapeStartRecording(new_random_seed);
11705
11706   if (init_network_game)
11707   {
11708     SendToServer_LevelFile();
11709     SendToServer_StartPlaying();
11710
11711     return;
11712   }
11713
11714   InitGame();
11715 }
11716
11717 static void GameActionsExt(void)
11718 {
11719 #if 0
11720   static unsigned int game_frame_delay = 0;
11721 #endif
11722   unsigned int game_frame_delay_value;
11723   byte *recorded_player_action;
11724   byte summarized_player_action = 0;
11725   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11726   int i;
11727
11728   // detect endless loops, caused by custom element programming
11729   if (recursion_loop_detected && recursion_loop_depth == 0)
11730   {
11731     char *message = getStringCat3("Internal Error! Element ",
11732                                   EL_NAME(recursion_loop_element),
11733                                   " caused endless loop! Quit the game?");
11734
11735     Warn("element '%s' caused endless loop in game engine",
11736          EL_NAME(recursion_loop_element));
11737
11738     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11739
11740     recursion_loop_detected = FALSE;    // if game should be continued
11741
11742     free(message);
11743
11744     return;
11745   }
11746
11747   if (game.restart_level)
11748     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11749
11750   CheckLevelSolved();
11751
11752   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11753     GameWon();
11754
11755   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11756     TapeStop();
11757
11758   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11759     return;
11760
11761   game_frame_delay_value =
11762     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11763
11764   if (tape.playing && tape.warp_forward && !tape.pausing)
11765     game_frame_delay_value = 0;
11766
11767   SetVideoFrameDelay(game_frame_delay_value);
11768
11769   // (de)activate virtual buttons depending on current game status
11770   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11771   {
11772     if (game.all_players_gone)  // if no players there to be controlled anymore
11773       SetOverlayActive(FALSE);
11774     else if (!tape.playing)     // if game continues after tape stopped playing
11775       SetOverlayActive(TRUE);
11776   }
11777
11778 #if 0
11779 #if 0
11780   // ---------- main game synchronization point ----------
11781
11782   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11783
11784   Debug("game:playing:skip", "skip == %d", skip);
11785
11786 #else
11787   // ---------- main game synchronization point ----------
11788
11789   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11790 #endif
11791 #endif
11792
11793   if (network_playing && !network_player_action_received)
11794   {
11795     // try to get network player actions in time
11796
11797     // last chance to get network player actions without main loop delay
11798     HandleNetworking();
11799
11800     // game was quit by network peer
11801     if (game_status != GAME_MODE_PLAYING)
11802       return;
11803
11804     // check if network player actions still missing and game still running
11805     if (!network_player_action_received && !checkGameEnded())
11806       return;           // failed to get network player actions in time
11807
11808     // do not yet reset "network_player_action_received" (for tape.pausing)
11809   }
11810
11811   if (tape.pausing)
11812     return;
11813
11814   // at this point we know that we really continue executing the game
11815
11816   network_player_action_received = FALSE;
11817
11818   // when playing tape, read previously recorded player input from tape data
11819   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11820
11821   local_player->effective_mouse_action = local_player->mouse_action;
11822
11823   if (recorded_player_action != NULL)
11824     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11825                                  recorded_player_action);
11826
11827   // TapePlayAction() may return NULL when toggling to "pause before death"
11828   if (tape.pausing)
11829     return;
11830
11831   if (tape.set_centered_player)
11832   {
11833     game.centered_player_nr_next = tape.centered_player_nr_next;
11834     game.set_centered_player = TRUE;
11835   }
11836
11837   for (i = 0; i < MAX_PLAYERS; i++)
11838   {
11839     summarized_player_action |= stored_player[i].action;
11840
11841     if (!network_playing && (game.team_mode || tape.playing))
11842       stored_player[i].effective_action = stored_player[i].action;
11843   }
11844
11845   if (network_playing && !checkGameEnded())
11846     SendToServer_MovePlayer(summarized_player_action);
11847
11848   // summarize all actions at local players mapped input device position
11849   // (this allows using different input devices in single player mode)
11850   if (!network.enabled && !game.team_mode)
11851     stored_player[map_player_action[local_player->index_nr]].effective_action =
11852       summarized_player_action;
11853
11854   // summarize all actions at centered player in local team mode
11855   if (tape.recording &&
11856       setup.team_mode && !network.enabled &&
11857       setup.input_on_focus &&
11858       game.centered_player_nr != -1)
11859   {
11860     for (i = 0; i < MAX_PLAYERS; i++)
11861       stored_player[map_player_action[i]].effective_action =
11862         (i == game.centered_player_nr ? summarized_player_action : 0);
11863   }
11864
11865   if (recorded_player_action != NULL)
11866     for (i = 0; i < MAX_PLAYERS; i++)
11867       stored_player[i].effective_action = recorded_player_action[i];
11868
11869   for (i = 0; i < MAX_PLAYERS; i++)
11870   {
11871     tape_action[i] = stored_player[i].effective_action;
11872
11873     /* (this may happen in the RND game engine if a player was not present on
11874        the playfield on level start, but appeared later from a custom element */
11875     if (setup.team_mode &&
11876         tape.recording &&
11877         tape_action[i] &&
11878         !tape.player_participates[i])
11879       tape.player_participates[i] = TRUE;
11880   }
11881
11882   SetTapeActionFromMouseAction(tape_action,
11883                                &local_player->effective_mouse_action);
11884
11885   // only record actions from input devices, but not programmed actions
11886   if (tape.recording)
11887     TapeRecordAction(tape_action);
11888
11889   // remember if game was played (especially after tape stopped playing)
11890   if (!tape.playing && summarized_player_action)
11891     game.GamePlayed = TRUE;
11892
11893 #if USE_NEW_PLAYER_ASSIGNMENTS
11894   // !!! also map player actions in single player mode !!!
11895   // if (game.team_mode)
11896   if (1)
11897   {
11898     byte mapped_action[MAX_PLAYERS];
11899
11900 #if DEBUG_PLAYER_ACTIONS
11901     for (i = 0; i < MAX_PLAYERS; i++)
11902       DebugContinued("", "%d, ", stored_player[i].effective_action);
11903 #endif
11904
11905     for (i = 0; i < MAX_PLAYERS; i++)
11906       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11907
11908     for (i = 0; i < MAX_PLAYERS; i++)
11909       stored_player[i].effective_action = mapped_action[i];
11910
11911 #if DEBUG_PLAYER_ACTIONS
11912     DebugContinued("", "=> ");
11913     for (i = 0; i < MAX_PLAYERS; i++)
11914       DebugContinued("", "%d, ", stored_player[i].effective_action);
11915     DebugContinued("game:playing:player", "\n");
11916 #endif
11917   }
11918 #if DEBUG_PLAYER_ACTIONS
11919   else
11920   {
11921     for (i = 0; i < MAX_PLAYERS; i++)
11922       DebugContinued("", "%d, ", stored_player[i].effective_action);
11923     DebugContinued("game:playing:player", "\n");
11924   }
11925 #endif
11926 #endif
11927
11928   for (i = 0; i < MAX_PLAYERS; i++)
11929   {
11930     // allow engine snapshot in case of changed movement attempt
11931     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11932         (stored_player[i].effective_action & KEY_MOTION))
11933       game.snapshot.changed_action = TRUE;
11934
11935     // allow engine snapshot in case of snapping/dropping attempt
11936     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11937         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11938       game.snapshot.changed_action = TRUE;
11939
11940     game.snapshot.last_action[i] = stored_player[i].effective_action;
11941   }
11942
11943   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11944   {
11945     GameActions_EM_Main();
11946   }
11947   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11948   {
11949     GameActions_SP_Main();
11950   }
11951   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11952   {
11953     GameActions_MM_Main();
11954   }
11955   else
11956   {
11957     GameActions_RND_Main();
11958   }
11959
11960   BlitScreenToBitmap(backbuffer);
11961
11962   CheckLevelSolved();
11963   CheckLevelTime();
11964
11965   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11966
11967   if (global.show_frames_per_second)
11968   {
11969     static unsigned int fps_counter = 0;
11970     static int fps_frames = 0;
11971     unsigned int fps_delay_ms = Counter() - fps_counter;
11972
11973     fps_frames++;
11974
11975     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11976     {
11977       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11978
11979       fps_frames = 0;
11980       fps_counter = Counter();
11981
11982       // always draw FPS to screen after FPS value was updated
11983       redraw_mask |= REDRAW_FPS;
11984     }
11985
11986     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11987     if (GetDrawDeactivationMask() == REDRAW_NONE)
11988       redraw_mask |= REDRAW_FPS;
11989   }
11990 }
11991
11992 static void GameActions_CheckSaveEngineSnapshot(void)
11993 {
11994   if (!game.snapshot.save_snapshot)
11995     return;
11996
11997   // clear flag for saving snapshot _before_ saving snapshot
11998   game.snapshot.save_snapshot = FALSE;
11999
12000   SaveEngineSnapshotToList();
12001 }
12002
12003 void GameActions(void)
12004 {
12005   GameActionsExt();
12006
12007   GameActions_CheckSaveEngineSnapshot();
12008 }
12009
12010 void GameActions_EM_Main(void)
12011 {
12012   byte effective_action[MAX_PLAYERS];
12013   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12014   int i;
12015
12016   for (i = 0; i < MAX_PLAYERS; i++)
12017     effective_action[i] = stored_player[i].effective_action;
12018
12019   GameActions_EM(effective_action, warp_mode);
12020 }
12021
12022 void GameActions_SP_Main(void)
12023 {
12024   byte effective_action[MAX_PLAYERS];
12025   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12026   int i;
12027
12028   for (i = 0; i < MAX_PLAYERS; i++)
12029     effective_action[i] = stored_player[i].effective_action;
12030
12031   GameActions_SP(effective_action, warp_mode);
12032
12033   for (i = 0; i < MAX_PLAYERS; i++)
12034   {
12035     if (stored_player[i].force_dropping)
12036       stored_player[i].action |= KEY_BUTTON_DROP;
12037
12038     stored_player[i].force_dropping = FALSE;
12039   }
12040 }
12041
12042 void GameActions_MM_Main(void)
12043 {
12044   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12045
12046   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12047 }
12048
12049 void GameActions_RND_Main(void)
12050 {
12051   GameActions_RND();
12052 }
12053
12054 void GameActions_RND(void)
12055 {
12056   static struct MouseActionInfo mouse_action_last = { 0 };
12057   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12058   int magic_wall_x = 0, magic_wall_y = 0;
12059   int i, x, y, element, graphic, last_gfx_frame;
12060
12061   InitPlayfieldScanModeVars();
12062
12063   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12064   {
12065     SCAN_PLAYFIELD(x, y)
12066     {
12067       ChangeCount[x][y] = 0;
12068       ChangeEvent[x][y] = -1;
12069     }
12070   }
12071
12072   if (game.set_centered_player)
12073   {
12074     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12075
12076     // switching to "all players" only possible if all players fit to screen
12077     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12078     {
12079       game.centered_player_nr_next = game.centered_player_nr;
12080       game.set_centered_player = FALSE;
12081     }
12082
12083     // do not switch focus to non-existing (or non-active) player
12084     if (game.centered_player_nr_next >= 0 &&
12085         !stored_player[game.centered_player_nr_next].active)
12086     {
12087       game.centered_player_nr_next = game.centered_player_nr;
12088       game.set_centered_player = FALSE;
12089     }
12090   }
12091
12092   if (game.set_centered_player &&
12093       ScreenMovPos == 0)        // screen currently aligned at tile position
12094   {
12095     int sx, sy;
12096
12097     if (game.centered_player_nr_next == -1)
12098     {
12099       setScreenCenteredToAllPlayers(&sx, &sy);
12100     }
12101     else
12102     {
12103       sx = stored_player[game.centered_player_nr_next].jx;
12104       sy = stored_player[game.centered_player_nr_next].jy;
12105     }
12106
12107     game.centered_player_nr = game.centered_player_nr_next;
12108     game.set_centered_player = FALSE;
12109
12110     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12111     DrawGameDoorValues();
12112   }
12113
12114   // check single step mode (set flag and clear again if any player is active)
12115   game.enter_single_step_mode =
12116     (tape.single_step && tape.recording && !tape.pausing);
12117
12118   for (i = 0; i < MAX_PLAYERS; i++)
12119   {
12120     int actual_player_action = stored_player[i].effective_action;
12121
12122 #if 1
12123     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12124        - rnd_equinox_tetrachloride 048
12125        - rnd_equinox_tetrachloride_ii 096
12126        - rnd_emanuel_schmieg 002
12127        - doctor_sloan_ww 001, 020
12128     */
12129     if (stored_player[i].MovPos == 0)
12130       CheckGravityMovement(&stored_player[i]);
12131 #endif
12132
12133     // overwrite programmed action with tape action
12134     if (stored_player[i].programmed_action)
12135       actual_player_action = stored_player[i].programmed_action;
12136
12137     PlayerActions(&stored_player[i], actual_player_action);
12138
12139     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12140   }
12141
12142   // single step pause mode may already have been toggled by "ScrollPlayer()"
12143   if (game.enter_single_step_mode && !tape.pausing)
12144     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12145
12146   ScrollScreen(NULL, SCROLL_GO_ON);
12147
12148   /* for backwards compatibility, the following code emulates a fixed bug that
12149      occured when pushing elements (causing elements that just made their last
12150      pushing step to already (if possible) make their first falling step in the
12151      same game frame, which is bad); this code is also needed to use the famous
12152      "spring push bug" which is used in older levels and might be wanted to be
12153      used also in newer levels, but in this case the buggy pushing code is only
12154      affecting the "spring" element and no other elements */
12155
12156   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12157   {
12158     for (i = 0; i < MAX_PLAYERS; i++)
12159     {
12160       struct PlayerInfo *player = &stored_player[i];
12161       int x = player->jx;
12162       int y = player->jy;
12163
12164       if (player->active && player->is_pushing && player->is_moving &&
12165           IS_MOVING(x, y) &&
12166           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12167            Tile[x][y] == EL_SPRING))
12168       {
12169         ContinueMoving(x, y);
12170
12171         // continue moving after pushing (this is actually a bug)
12172         if (!IS_MOVING(x, y))
12173           Stop[x][y] = FALSE;
12174       }
12175     }
12176   }
12177
12178   SCAN_PLAYFIELD(x, y)
12179   {
12180     Last[x][y] = Tile[x][y];
12181
12182     ChangeCount[x][y] = 0;
12183     ChangeEvent[x][y] = -1;
12184
12185     // this must be handled before main playfield loop
12186     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12187     {
12188       MovDelay[x][y]--;
12189       if (MovDelay[x][y] <= 0)
12190         RemoveField(x, y);
12191     }
12192
12193     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12194     {
12195       MovDelay[x][y]--;
12196       if (MovDelay[x][y] <= 0)
12197       {
12198         int element = Store[x][y];
12199         int move_direction = MovDir[x][y];
12200         int player_index_bit = Store2[x][y];
12201
12202         Store[x][y] = 0;
12203         Store2[x][y] = 0;
12204
12205         RemoveField(x, y);
12206         TEST_DrawLevelField(x, y);
12207
12208         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12209
12210         if (IS_ENVELOPE(element))
12211           local_player->show_envelope = element;
12212       }
12213     }
12214
12215 #if DEBUG
12216     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12217     {
12218       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12219             x, y);
12220       Debug("game:playing:GameActions_RND", "This should never happen!");
12221
12222       ChangePage[x][y] = -1;
12223     }
12224 #endif
12225
12226     Stop[x][y] = FALSE;
12227     if (WasJustMoving[x][y] > 0)
12228       WasJustMoving[x][y]--;
12229     if (WasJustFalling[x][y] > 0)
12230       WasJustFalling[x][y]--;
12231     if (CheckCollision[x][y] > 0)
12232       CheckCollision[x][y]--;
12233     if (CheckImpact[x][y] > 0)
12234       CheckImpact[x][y]--;
12235
12236     GfxFrame[x][y]++;
12237
12238     /* reset finished pushing action (not done in ContinueMoving() to allow
12239        continuous pushing animation for elements with zero push delay) */
12240     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12241     {
12242       ResetGfxAnimation(x, y);
12243       TEST_DrawLevelField(x, y);
12244     }
12245
12246 #if DEBUG
12247     if (IS_BLOCKED(x, y))
12248     {
12249       int oldx, oldy;
12250
12251       Blocked2Moving(x, y, &oldx, &oldy);
12252       if (!IS_MOVING(oldx, oldy))
12253       {
12254         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12255         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12256         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12257         Debug("game:playing:GameActions_RND", "This should never happen!");
12258       }
12259     }
12260 #endif
12261   }
12262
12263   if (mouse_action.button)
12264   {
12265     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12266     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12267
12268     x = mouse_action.lx;
12269     y = mouse_action.ly;
12270     element = Tile[x][y];
12271
12272     if (new_button)
12273     {
12274       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12275       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12276                                          ch_button);
12277     }
12278
12279     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12280     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12281                                        ch_button);
12282   }
12283
12284   SCAN_PLAYFIELD(x, y)
12285   {
12286     element = Tile[x][y];
12287     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12288     last_gfx_frame = GfxFrame[x][y];
12289
12290     ResetGfxFrame(x, y);
12291
12292     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12293       DrawLevelGraphicAnimation(x, y, graphic);
12294
12295     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12296         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12297       ResetRandomAnimationValue(x, y);
12298
12299     SetRandomAnimationValue(x, y);
12300
12301     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12302
12303     if (IS_INACTIVE(element))
12304     {
12305       if (IS_ANIMATED(graphic))
12306         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12307
12308       continue;
12309     }
12310
12311     // this may take place after moving, so 'element' may have changed
12312     if (IS_CHANGING(x, y) &&
12313         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12314     {
12315       int page = element_info[element].event_page_nr[CE_DELAY];
12316
12317       HandleElementChange(x, y, page);
12318
12319       element = Tile[x][y];
12320       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12321     }
12322
12323     CheckNextToConditions(x, y);
12324
12325     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12326     {
12327       StartMoving(x, y);
12328
12329       element = Tile[x][y];
12330       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12331
12332       if (IS_ANIMATED(graphic) &&
12333           !IS_MOVING(x, y) &&
12334           !Stop[x][y])
12335         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12336
12337       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12338         TEST_DrawTwinkleOnField(x, y);
12339     }
12340     else if (element == EL_ACID)
12341     {
12342       if (!Stop[x][y])
12343         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12344     }
12345     else if ((element == EL_EXIT_OPEN ||
12346               element == EL_EM_EXIT_OPEN ||
12347               element == EL_SP_EXIT_OPEN ||
12348               element == EL_STEEL_EXIT_OPEN ||
12349               element == EL_EM_STEEL_EXIT_OPEN ||
12350               element == EL_SP_TERMINAL ||
12351               element == EL_SP_TERMINAL_ACTIVE ||
12352               element == EL_EXTRA_TIME ||
12353               element == EL_SHIELD_NORMAL ||
12354               element == EL_SHIELD_DEADLY) &&
12355              IS_ANIMATED(graphic))
12356       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12357     else if (IS_MOVING(x, y))
12358       ContinueMoving(x, y);
12359     else if (IS_ACTIVE_BOMB(element))
12360       CheckDynamite(x, y);
12361     else if (element == EL_AMOEBA_GROWING)
12362       AmoebaGrowing(x, y);
12363     else if (element == EL_AMOEBA_SHRINKING)
12364       AmoebaShrinking(x, y);
12365
12366 #if !USE_NEW_AMOEBA_CODE
12367     else if (IS_AMOEBALIVE(element))
12368       AmoebaReproduce(x, y);
12369 #endif
12370
12371     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12372       Life(x, y);
12373     else if (element == EL_EXIT_CLOSED)
12374       CheckExit(x, y);
12375     else if (element == EL_EM_EXIT_CLOSED)
12376       CheckExitEM(x, y);
12377     else if (element == EL_STEEL_EXIT_CLOSED)
12378       CheckExitSteel(x, y);
12379     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12380       CheckExitSteelEM(x, y);
12381     else if (element == EL_SP_EXIT_CLOSED)
12382       CheckExitSP(x, y);
12383     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12384              element == EL_EXPANDABLE_STEELWALL_GROWING)
12385       MauerWaechst(x, y);
12386     else if (element == EL_EXPANDABLE_WALL ||
12387              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12388              element == EL_EXPANDABLE_WALL_VERTICAL ||
12389              element == EL_EXPANDABLE_WALL_ANY ||
12390              element == EL_BD_EXPANDABLE_WALL)
12391       MauerAbleger(x, y);
12392     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12393              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12394              element == EL_EXPANDABLE_STEELWALL_ANY)
12395       MauerAblegerStahl(x, y);
12396     else if (element == EL_FLAMES)
12397       CheckForDragon(x, y);
12398     else if (element == EL_EXPLOSION)
12399       ; // drawing of correct explosion animation is handled separately
12400     else if (element == EL_ELEMENT_SNAPPING ||
12401              element == EL_DIAGONAL_SHRINKING ||
12402              element == EL_DIAGONAL_GROWING)
12403     {
12404       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12405
12406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12407     }
12408     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12409       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12410
12411     if (IS_BELT_ACTIVE(element))
12412       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12413
12414     if (game.magic_wall_active)
12415     {
12416       int jx = local_player->jx, jy = local_player->jy;
12417
12418       // play the element sound at the position nearest to the player
12419       if ((element == EL_MAGIC_WALL_FULL ||
12420            element == EL_MAGIC_WALL_ACTIVE ||
12421            element == EL_MAGIC_WALL_EMPTYING ||
12422            element == EL_BD_MAGIC_WALL_FULL ||
12423            element == EL_BD_MAGIC_WALL_ACTIVE ||
12424            element == EL_BD_MAGIC_WALL_EMPTYING ||
12425            element == EL_DC_MAGIC_WALL_FULL ||
12426            element == EL_DC_MAGIC_WALL_ACTIVE ||
12427            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12428           ABS(x - jx) + ABS(y - jy) <
12429           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12430       {
12431         magic_wall_x = x;
12432         magic_wall_y = y;
12433       }
12434     }
12435   }
12436
12437 #if USE_NEW_AMOEBA_CODE
12438   // new experimental amoeba growth stuff
12439   if (!(FrameCounter % 8))
12440   {
12441     static unsigned int random = 1684108901;
12442
12443     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12444     {
12445       x = RND(lev_fieldx);
12446       y = RND(lev_fieldy);
12447       element = Tile[x][y];
12448
12449       if (!IS_PLAYER(x,y) &&
12450           (element == EL_EMPTY ||
12451            CAN_GROW_INTO(element) ||
12452            element == EL_QUICKSAND_EMPTY ||
12453            element == EL_QUICKSAND_FAST_EMPTY ||
12454            element == EL_ACID_SPLASH_LEFT ||
12455            element == EL_ACID_SPLASH_RIGHT))
12456       {
12457         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12458             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12459             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12460             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12461           Tile[x][y] = EL_AMOEBA_DROP;
12462       }
12463
12464       random = random * 129 + 1;
12465     }
12466   }
12467 #endif
12468
12469   game.explosions_delayed = FALSE;
12470
12471   SCAN_PLAYFIELD(x, y)
12472   {
12473     element = Tile[x][y];
12474
12475     if (ExplodeField[x][y])
12476       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12477     else if (element == EL_EXPLOSION)
12478       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12479
12480     ExplodeField[x][y] = EX_TYPE_NONE;
12481   }
12482
12483   game.explosions_delayed = TRUE;
12484
12485   if (game.magic_wall_active)
12486   {
12487     if (!(game.magic_wall_time_left % 4))
12488     {
12489       int element = Tile[magic_wall_x][magic_wall_y];
12490
12491       if (element == EL_BD_MAGIC_WALL_FULL ||
12492           element == EL_BD_MAGIC_WALL_ACTIVE ||
12493           element == EL_BD_MAGIC_WALL_EMPTYING)
12494         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12495       else if (element == EL_DC_MAGIC_WALL_FULL ||
12496                element == EL_DC_MAGIC_WALL_ACTIVE ||
12497                element == EL_DC_MAGIC_WALL_EMPTYING)
12498         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12499       else
12500         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12501     }
12502
12503     if (game.magic_wall_time_left > 0)
12504     {
12505       game.magic_wall_time_left--;
12506
12507       if (!game.magic_wall_time_left)
12508       {
12509         SCAN_PLAYFIELD(x, y)
12510         {
12511           element = Tile[x][y];
12512
12513           if (element == EL_MAGIC_WALL_ACTIVE ||
12514               element == EL_MAGIC_WALL_FULL)
12515           {
12516             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12517             TEST_DrawLevelField(x, y);
12518           }
12519           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12520                    element == EL_BD_MAGIC_WALL_FULL)
12521           {
12522             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12523             TEST_DrawLevelField(x, y);
12524           }
12525           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12526                    element == EL_DC_MAGIC_WALL_FULL)
12527           {
12528             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12529             TEST_DrawLevelField(x, y);
12530           }
12531         }
12532
12533         game.magic_wall_active = FALSE;
12534       }
12535     }
12536   }
12537
12538   if (game.light_time_left > 0)
12539   {
12540     game.light_time_left--;
12541
12542     if (game.light_time_left == 0)
12543       RedrawAllLightSwitchesAndInvisibleElements();
12544   }
12545
12546   if (game.timegate_time_left > 0)
12547   {
12548     game.timegate_time_left--;
12549
12550     if (game.timegate_time_left == 0)
12551       CloseAllOpenTimegates();
12552   }
12553
12554   if (game.lenses_time_left > 0)
12555   {
12556     game.lenses_time_left--;
12557
12558     if (game.lenses_time_left == 0)
12559       RedrawAllInvisibleElementsForLenses();
12560   }
12561
12562   if (game.magnify_time_left > 0)
12563   {
12564     game.magnify_time_left--;
12565
12566     if (game.magnify_time_left == 0)
12567       RedrawAllInvisibleElementsForMagnifier();
12568   }
12569
12570   for (i = 0; i < MAX_PLAYERS; i++)
12571   {
12572     struct PlayerInfo *player = &stored_player[i];
12573
12574     if (SHIELD_ON(player))
12575     {
12576       if (player->shield_deadly_time_left)
12577         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12578       else if (player->shield_normal_time_left)
12579         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12580     }
12581   }
12582
12583 #if USE_DELAYED_GFX_REDRAW
12584   SCAN_PLAYFIELD(x, y)
12585   {
12586     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12587     {
12588       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12589          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12590
12591       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12592         DrawLevelField(x, y);
12593
12594       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12595         DrawLevelFieldCrumbled(x, y);
12596
12597       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12598         DrawLevelFieldCrumbledNeighbours(x, y);
12599
12600       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12601         DrawTwinkleOnField(x, y);
12602     }
12603
12604     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12605   }
12606 #endif
12607
12608   DrawAllPlayers();
12609   PlayAllPlayersSound();
12610
12611   for (i = 0; i < MAX_PLAYERS; i++)
12612   {
12613     struct PlayerInfo *player = &stored_player[i];
12614
12615     if (player->show_envelope != 0 && (!player->active ||
12616                                        player->MovPos == 0))
12617     {
12618       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12619
12620       player->show_envelope = 0;
12621     }
12622   }
12623
12624   // use random number generator in every frame to make it less predictable
12625   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12626     RND(1);
12627
12628   mouse_action_last = mouse_action;
12629 }
12630
12631 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12632 {
12633   int min_x = x, min_y = y, max_x = x, max_y = y;
12634   int scr_fieldx = getScreenFieldSizeX();
12635   int scr_fieldy = getScreenFieldSizeY();
12636   int i;
12637
12638   for (i = 0; i < MAX_PLAYERS; i++)
12639   {
12640     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12641
12642     if (!stored_player[i].active || &stored_player[i] == player)
12643       continue;
12644
12645     min_x = MIN(min_x, jx);
12646     min_y = MIN(min_y, jy);
12647     max_x = MAX(max_x, jx);
12648     max_y = MAX(max_y, jy);
12649   }
12650
12651   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12652 }
12653
12654 static boolean AllPlayersInVisibleScreen(void)
12655 {
12656   int i;
12657
12658   for (i = 0; i < MAX_PLAYERS; i++)
12659   {
12660     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12661
12662     if (!stored_player[i].active)
12663       continue;
12664
12665     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12666       return FALSE;
12667   }
12668
12669   return TRUE;
12670 }
12671
12672 void ScrollLevel(int dx, int dy)
12673 {
12674   int scroll_offset = 2 * TILEX_VAR;
12675   int x, y;
12676
12677   BlitBitmap(drawto_field, drawto_field,
12678              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12679              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12680              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12681              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12682              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12683              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12684
12685   if (dx != 0)
12686   {
12687     x = (dx == 1 ? BX1 : BX2);
12688     for (y = BY1; y <= BY2; y++)
12689       DrawScreenField(x, y);
12690   }
12691
12692   if (dy != 0)
12693   {
12694     y = (dy == 1 ? BY1 : BY2);
12695     for (x = BX1; x <= BX2; x++)
12696       DrawScreenField(x, y);
12697   }
12698
12699   redraw_mask |= REDRAW_FIELD;
12700 }
12701
12702 static boolean canFallDown(struct PlayerInfo *player)
12703 {
12704   int jx = player->jx, jy = player->jy;
12705
12706   return (IN_LEV_FIELD(jx, jy + 1) &&
12707           (IS_FREE(jx, jy + 1) ||
12708            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12709           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12710           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12711 }
12712
12713 static boolean canPassField(int x, int y, int move_dir)
12714 {
12715   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12716   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12717   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12718   int nextx = x + dx;
12719   int nexty = y + dy;
12720   int element = Tile[x][y];
12721
12722   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12723           !CAN_MOVE(element) &&
12724           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12725           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12726           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12727 }
12728
12729 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12730 {
12731   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12732   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12733   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12734   int newx = x + dx;
12735   int newy = y + dy;
12736
12737   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12738           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12739           (IS_DIGGABLE(Tile[newx][newy]) ||
12740            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12741            canPassField(newx, newy, move_dir)));
12742 }
12743
12744 static void CheckGravityMovement(struct PlayerInfo *player)
12745 {
12746   if (player->gravity && !player->programmed_action)
12747   {
12748     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12749     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12750     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12751     int jx = player->jx, jy = player->jy;
12752     boolean player_is_moving_to_valid_field =
12753       (!player_is_snapping &&
12754        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12755         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12756     boolean player_can_fall_down = canFallDown(player);
12757
12758     if (player_can_fall_down &&
12759         !player_is_moving_to_valid_field)
12760       player->programmed_action = MV_DOWN;
12761   }
12762 }
12763
12764 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12765 {
12766   return CheckGravityMovement(player);
12767
12768   if (player->gravity && !player->programmed_action)
12769   {
12770     int jx = player->jx, jy = player->jy;
12771     boolean field_under_player_is_free =
12772       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12773     boolean player_is_standing_on_valid_field =
12774       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12775        (IS_WALKABLE(Tile[jx][jy]) &&
12776         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12777
12778     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12779       player->programmed_action = MV_DOWN;
12780   }
12781 }
12782
12783 /*
12784   MovePlayerOneStep()
12785   -----------------------------------------------------------------------------
12786   dx, dy:               direction (non-diagonal) to try to move the player to
12787   real_dx, real_dy:     direction as read from input device (can be diagonal)
12788 */
12789
12790 boolean MovePlayerOneStep(struct PlayerInfo *player,
12791                           int dx, int dy, int real_dx, int real_dy)
12792 {
12793   int jx = player->jx, jy = player->jy;
12794   int new_jx = jx + dx, new_jy = jy + dy;
12795   int can_move;
12796   boolean player_can_move = !player->cannot_move;
12797
12798   if (!player->active || (!dx && !dy))
12799     return MP_NO_ACTION;
12800
12801   player->MovDir = (dx < 0 ? MV_LEFT :
12802                     dx > 0 ? MV_RIGHT :
12803                     dy < 0 ? MV_UP :
12804                     dy > 0 ? MV_DOWN :  MV_NONE);
12805
12806   if (!IN_LEV_FIELD(new_jx, new_jy))
12807     return MP_NO_ACTION;
12808
12809   if (!player_can_move)
12810   {
12811     if (player->MovPos == 0)
12812     {
12813       player->is_moving = FALSE;
12814       player->is_digging = FALSE;
12815       player->is_collecting = FALSE;
12816       player->is_snapping = FALSE;
12817       player->is_pushing = FALSE;
12818     }
12819   }
12820
12821   if (!network.enabled && game.centered_player_nr == -1 &&
12822       !AllPlayersInSight(player, new_jx, new_jy))
12823     return MP_NO_ACTION;
12824
12825   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12826   if (can_move != MP_MOVING)
12827     return can_move;
12828
12829   // check if DigField() has caused relocation of the player
12830   if (player->jx != jx || player->jy != jy)
12831     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12832
12833   StorePlayer[jx][jy] = 0;
12834   player->last_jx = jx;
12835   player->last_jy = jy;
12836   player->jx = new_jx;
12837   player->jy = new_jy;
12838   StorePlayer[new_jx][new_jy] = player->element_nr;
12839
12840   if (player->move_delay_value_next != -1)
12841   {
12842     player->move_delay_value = player->move_delay_value_next;
12843     player->move_delay_value_next = -1;
12844   }
12845
12846   player->MovPos =
12847     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12848
12849   player->step_counter++;
12850
12851   PlayerVisit[jx][jy] = FrameCounter;
12852
12853   player->is_moving = TRUE;
12854
12855 #if 1
12856   // should better be called in MovePlayer(), but this breaks some tapes
12857   ScrollPlayer(player, SCROLL_INIT);
12858 #endif
12859
12860   return MP_MOVING;
12861 }
12862
12863 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12864 {
12865   int jx = player->jx, jy = player->jy;
12866   int old_jx = jx, old_jy = jy;
12867   int moved = MP_NO_ACTION;
12868
12869   if (!player->active)
12870     return FALSE;
12871
12872   if (!dx && !dy)
12873   {
12874     if (player->MovPos == 0)
12875     {
12876       player->is_moving = FALSE;
12877       player->is_digging = FALSE;
12878       player->is_collecting = FALSE;
12879       player->is_snapping = FALSE;
12880       player->is_pushing = FALSE;
12881     }
12882
12883     return FALSE;
12884   }
12885
12886   if (player->move_delay > 0)
12887     return FALSE;
12888
12889   player->move_delay = -1;              // set to "uninitialized" value
12890
12891   // store if player is automatically moved to next field
12892   player->is_auto_moving = (player->programmed_action != MV_NONE);
12893
12894   // remove the last programmed player action
12895   player->programmed_action = 0;
12896
12897   if (player->MovPos)
12898   {
12899     // should only happen if pre-1.2 tape recordings are played
12900     // this is only for backward compatibility
12901
12902     int original_move_delay_value = player->move_delay_value;
12903
12904 #if DEBUG
12905     Debug("game:playing:MovePlayer",
12906           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12907           tape.counter);
12908 #endif
12909
12910     // scroll remaining steps with finest movement resolution
12911     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12912
12913     while (player->MovPos)
12914     {
12915       ScrollPlayer(player, SCROLL_GO_ON);
12916       ScrollScreen(NULL, SCROLL_GO_ON);
12917
12918       AdvanceFrameAndPlayerCounters(player->index_nr);
12919
12920       DrawAllPlayers();
12921       BackToFront_WithFrameDelay(0);
12922     }
12923
12924     player->move_delay_value = original_move_delay_value;
12925   }
12926
12927   player->is_active = FALSE;
12928
12929   if (player->last_move_dir & MV_HORIZONTAL)
12930   {
12931     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12932       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12933   }
12934   else
12935   {
12936     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12937       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12938   }
12939
12940   if (!moved && !player->is_active)
12941   {
12942     player->is_moving = FALSE;
12943     player->is_digging = FALSE;
12944     player->is_collecting = FALSE;
12945     player->is_snapping = FALSE;
12946     player->is_pushing = FALSE;
12947   }
12948
12949   jx = player->jx;
12950   jy = player->jy;
12951
12952   if (moved & MP_MOVING && !ScreenMovPos &&
12953       (player->index_nr == game.centered_player_nr ||
12954        game.centered_player_nr == -1))
12955   {
12956     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12957
12958     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12959     {
12960       // actual player has left the screen -- scroll in that direction
12961       if (jx != old_jx)         // player has moved horizontally
12962         scroll_x += (jx - old_jx);
12963       else                      // player has moved vertically
12964         scroll_y += (jy - old_jy);
12965     }
12966     else
12967     {
12968       int offset_raw = game.scroll_delay_value;
12969
12970       if (jx != old_jx)         // player has moved horizontally
12971       {
12972         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12973         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12974         int new_scroll_x = jx - MIDPOSX + offset_x;
12975
12976         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12977             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12978           scroll_x = new_scroll_x;
12979
12980         // don't scroll over playfield boundaries
12981         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12982
12983         // don't scroll more than one field at a time
12984         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12985
12986         // don't scroll against the player's moving direction
12987         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12988             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12989           scroll_x = old_scroll_x;
12990       }
12991       else                      // player has moved vertically
12992       {
12993         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12994         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12995         int new_scroll_y = jy - MIDPOSY + offset_y;
12996
12997         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12998             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12999           scroll_y = new_scroll_y;
13000
13001         // don't scroll over playfield boundaries
13002         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13003
13004         // don't scroll more than one field at a time
13005         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13006
13007         // don't scroll against the player's moving direction
13008         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13009             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13010           scroll_y = old_scroll_y;
13011       }
13012     }
13013
13014     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13015     {
13016       if (!network.enabled && game.centered_player_nr == -1 &&
13017           !AllPlayersInVisibleScreen())
13018       {
13019         scroll_x = old_scroll_x;
13020         scroll_y = old_scroll_y;
13021       }
13022       else
13023       {
13024         ScrollScreen(player, SCROLL_INIT);
13025         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13026       }
13027     }
13028   }
13029
13030   player->StepFrame = 0;
13031
13032   if (moved & MP_MOVING)
13033   {
13034     if (old_jx != jx && old_jy == jy)
13035       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13036     else if (old_jx == jx && old_jy != jy)
13037       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13038
13039     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13040
13041     player->last_move_dir = player->MovDir;
13042     player->is_moving = TRUE;
13043     player->is_snapping = FALSE;
13044     player->is_switching = FALSE;
13045     player->is_dropping = FALSE;
13046     player->is_dropping_pressed = FALSE;
13047     player->drop_pressed_delay = 0;
13048
13049 #if 0
13050     // should better be called here than above, but this breaks some tapes
13051     ScrollPlayer(player, SCROLL_INIT);
13052 #endif
13053   }
13054   else
13055   {
13056     CheckGravityMovementWhenNotMoving(player);
13057
13058     player->is_moving = FALSE;
13059
13060     /* at this point, the player is allowed to move, but cannot move right now
13061        (e.g. because of something blocking the way) -- ensure that the player
13062        is also allowed to move in the next frame (in old versions before 3.1.1,
13063        the player was forced to wait again for eight frames before next try) */
13064
13065     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13066       player->move_delay = 0;   // allow direct movement in the next frame
13067   }
13068
13069   if (player->move_delay == -1)         // not yet initialized by DigField()
13070     player->move_delay = player->move_delay_value;
13071
13072   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13073   {
13074     TestIfPlayerTouchesBadThing(jx, jy);
13075     TestIfPlayerTouchesCustomElement(jx, jy);
13076   }
13077
13078   if (!player->active)
13079     RemovePlayer(player);
13080
13081   return moved;
13082 }
13083
13084 void ScrollPlayer(struct PlayerInfo *player, int mode)
13085 {
13086   int jx = player->jx, jy = player->jy;
13087   int last_jx = player->last_jx, last_jy = player->last_jy;
13088   int move_stepsize = TILEX / player->move_delay_value;
13089
13090   if (!player->active)
13091     return;
13092
13093   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13094     return;
13095
13096   if (mode == SCROLL_INIT)
13097   {
13098     player->actual_frame_counter = FrameCounter;
13099     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13100
13101     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13102         Tile[last_jx][last_jy] == EL_EMPTY)
13103     {
13104       int last_field_block_delay = 0;   // start with no blocking at all
13105       int block_delay_adjustment = player->block_delay_adjustment;
13106
13107       // if player blocks last field, add delay for exactly one move
13108       if (player->block_last_field)
13109       {
13110         last_field_block_delay += player->move_delay_value;
13111
13112         // when blocking enabled, prevent moving up despite gravity
13113         if (player->gravity && player->MovDir == MV_UP)
13114           block_delay_adjustment = -1;
13115       }
13116
13117       // add block delay adjustment (also possible when not blocking)
13118       last_field_block_delay += block_delay_adjustment;
13119
13120       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13121       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13122     }
13123
13124     if (player->MovPos != 0)    // player has not yet reached destination
13125       return;
13126   }
13127   else if (!FrameReached(&player->actual_frame_counter, 1))
13128     return;
13129
13130   if (player->MovPos != 0)
13131   {
13132     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13133     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13134
13135     // before DrawPlayer() to draw correct player graphic for this case
13136     if (player->MovPos == 0)
13137       CheckGravityMovement(player);
13138   }
13139
13140   if (player->MovPos == 0)      // player reached destination field
13141   {
13142     if (player->move_delay_reset_counter > 0)
13143     {
13144       player->move_delay_reset_counter--;
13145
13146       if (player->move_delay_reset_counter == 0)
13147       {
13148         // continue with normal speed after quickly moving through gate
13149         HALVE_PLAYER_SPEED(player);
13150
13151         // be able to make the next move without delay
13152         player->move_delay = 0;
13153       }
13154     }
13155
13156     player->last_jx = jx;
13157     player->last_jy = jy;
13158
13159     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13160         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13161         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13162         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13163         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13164         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13165         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13166         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13167     {
13168       ExitPlayer(player);
13169
13170       if (game.players_still_needed == 0 &&
13171           (game.friends_still_needed == 0 ||
13172            IS_SP_ELEMENT(Tile[jx][jy])))
13173         LevelSolved();
13174     }
13175
13176     // this breaks one level: "machine", level 000
13177     {
13178       int move_direction = player->MovDir;
13179       int enter_side = MV_DIR_OPPOSITE(move_direction);
13180       int leave_side = move_direction;
13181       int old_jx = last_jx;
13182       int old_jy = last_jy;
13183       int old_element = Tile[old_jx][old_jy];
13184       int new_element = Tile[jx][jy];
13185
13186       if (IS_CUSTOM_ELEMENT(old_element))
13187         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13188                                    CE_LEFT_BY_PLAYER,
13189                                    player->index_bit, leave_side);
13190
13191       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13192                                           CE_PLAYER_LEAVES_X,
13193                                           player->index_bit, leave_side);
13194
13195       if (IS_CUSTOM_ELEMENT(new_element))
13196         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13197                                    player->index_bit, enter_side);
13198
13199       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13200                                           CE_PLAYER_ENTERS_X,
13201                                           player->index_bit, enter_side);
13202
13203       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13204                                         CE_MOVE_OF_X, move_direction);
13205     }
13206
13207     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13208     {
13209       TestIfPlayerTouchesBadThing(jx, jy);
13210       TestIfPlayerTouchesCustomElement(jx, jy);
13211
13212       /* needed because pushed element has not yet reached its destination,
13213          so it would trigger a change event at its previous field location */
13214       if (!player->is_pushing)
13215         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13216
13217       if (level.finish_dig_collect &&
13218           (player->is_digging || player->is_collecting))
13219       {
13220         int last_element = player->last_removed_element;
13221         int move_direction = player->MovDir;
13222         int enter_side = MV_DIR_OPPOSITE(move_direction);
13223         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13224                             CE_PLAYER_COLLECTS_X);
13225
13226         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13227                                             player->index_bit, enter_side);
13228
13229         player->last_removed_element = EL_UNDEFINED;
13230       }
13231
13232       if (!player->active)
13233         RemovePlayer(player);
13234     }
13235
13236     if (level.use_step_counter)
13237     {
13238       int i;
13239
13240       TimePlayed++;
13241
13242       if (TimeLeft > 0)
13243       {
13244         TimeLeft--;
13245
13246         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13247           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13248
13249         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13250
13251         DisplayGameControlValues();
13252
13253         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13254           for (i = 0; i < MAX_PLAYERS; i++)
13255             KillPlayer(&stored_player[i]);
13256       }
13257       else if (game.no_time_limit && !game.all_players_gone)
13258       {
13259         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13260
13261         DisplayGameControlValues();
13262       }
13263     }
13264
13265     if (tape.single_step && tape.recording && !tape.pausing &&
13266         !player->programmed_action)
13267       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13268
13269     if (!player->programmed_action)
13270       CheckSaveEngineSnapshot(player);
13271   }
13272 }
13273
13274 void ScrollScreen(struct PlayerInfo *player, int mode)
13275 {
13276   static unsigned int screen_frame_counter = 0;
13277
13278   if (mode == SCROLL_INIT)
13279   {
13280     // set scrolling step size according to actual player's moving speed
13281     ScrollStepSize = TILEX / player->move_delay_value;
13282
13283     screen_frame_counter = FrameCounter;
13284     ScreenMovDir = player->MovDir;
13285     ScreenMovPos = player->MovPos;
13286     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13287     return;
13288   }
13289   else if (!FrameReached(&screen_frame_counter, 1))
13290     return;
13291
13292   if (ScreenMovPos)
13293   {
13294     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13295     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13296     redraw_mask |= REDRAW_FIELD;
13297   }
13298   else
13299     ScreenMovDir = MV_NONE;
13300 }
13301
13302 void CheckNextToConditions(int x, int y)
13303 {
13304   int element = Tile[x][y];
13305
13306   if (IS_PLAYER(x, y))
13307     TestIfPlayerNextToCustomElement(x, y);
13308
13309   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13310       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13311     TestIfElementNextToCustomElement(x, y);
13312 }
13313
13314 void TestIfPlayerNextToCustomElement(int x, int y)
13315 {
13316   static int xy[4][2] =
13317   {
13318     { 0, -1 },
13319     { -1, 0 },
13320     { +1, 0 },
13321     { 0, +1 }
13322   };
13323   static int trigger_sides[4][2] =
13324   {
13325     // center side       border side
13326     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13327     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13328     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13329     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13330   };
13331   int i;
13332
13333   if (!IS_PLAYER(x, y))
13334     return;
13335
13336   struct PlayerInfo *player = PLAYERINFO(x, y);
13337
13338   if (player->is_moving)
13339     return;
13340
13341   for (i = 0; i < NUM_DIRECTIONS; i++)
13342   {
13343     int xx = x + xy[i][0];
13344     int yy = y + xy[i][1];
13345     int border_side = trigger_sides[i][1];
13346     int border_element;
13347
13348     if (!IN_LEV_FIELD(xx, yy))
13349       continue;
13350
13351     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13352       continue;         // center and border element not connected
13353
13354     border_element = Tile[xx][yy];
13355
13356     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13357                                player->index_bit, border_side);
13358     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13359                                         CE_PLAYER_NEXT_TO_X,
13360                                         player->index_bit, border_side);
13361
13362     /* use player element that is initially defined in the level playfield,
13363        not the player element that corresponds to the runtime player number
13364        (example: a level that contains EL_PLAYER_3 as the only player would
13365        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13366
13367     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13368                              CE_NEXT_TO_X, border_side);
13369   }
13370 }
13371
13372 void TestIfPlayerTouchesCustomElement(int x, int y)
13373 {
13374   static int xy[4][2] =
13375   {
13376     { 0, -1 },
13377     { -1, 0 },
13378     { +1, 0 },
13379     { 0, +1 }
13380   };
13381   static int trigger_sides[4][2] =
13382   {
13383     // center side       border side
13384     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13385     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13386     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13387     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13388   };
13389   static int touch_dir[4] =
13390   {
13391     MV_LEFT | MV_RIGHT,
13392     MV_UP   | MV_DOWN,
13393     MV_UP   | MV_DOWN,
13394     MV_LEFT | MV_RIGHT
13395   };
13396   int center_element = Tile[x][y];      // should always be non-moving!
13397   int i;
13398
13399   for (i = 0; i < NUM_DIRECTIONS; i++)
13400   {
13401     int xx = x + xy[i][0];
13402     int yy = y + xy[i][1];
13403     int center_side = trigger_sides[i][0];
13404     int border_side = trigger_sides[i][1];
13405     int border_element;
13406
13407     if (!IN_LEV_FIELD(xx, yy))
13408       continue;
13409
13410     if (IS_PLAYER(x, y))                // player found at center element
13411     {
13412       struct PlayerInfo *player = PLAYERINFO(x, y);
13413
13414       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13415         border_element = Tile[xx][yy];          // may be moving!
13416       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13417         border_element = Tile[xx][yy];
13418       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13419         border_element = MovingOrBlocked2Element(xx, yy);
13420       else
13421         continue;               // center and border element do not touch
13422
13423       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13424                                  player->index_bit, border_side);
13425       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13426                                           CE_PLAYER_TOUCHES_X,
13427                                           player->index_bit, border_side);
13428
13429       {
13430         /* use player element that is initially defined in the level playfield,
13431            not the player element that corresponds to the runtime player number
13432            (example: a level that contains EL_PLAYER_3 as the only player would
13433            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13434         int player_element = PLAYERINFO(x, y)->initial_element;
13435
13436         CheckElementChangeBySide(xx, yy, border_element, player_element,
13437                                  CE_TOUCHING_X, border_side);
13438       }
13439     }
13440     else if (IS_PLAYER(xx, yy))         // player found at border element
13441     {
13442       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13443
13444       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13445       {
13446         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13447           continue;             // center and border element do not touch
13448       }
13449
13450       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13451                                  player->index_bit, center_side);
13452       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13453                                           CE_PLAYER_TOUCHES_X,
13454                                           player->index_bit, center_side);
13455
13456       {
13457         /* use player element that is initially defined in the level playfield,
13458            not the player element that corresponds to the runtime player number
13459            (example: a level that contains EL_PLAYER_3 as the only player would
13460            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13461         int player_element = PLAYERINFO(xx, yy)->initial_element;
13462
13463         CheckElementChangeBySide(x, y, center_element, player_element,
13464                                  CE_TOUCHING_X, center_side);
13465       }
13466
13467       break;
13468     }
13469   }
13470 }
13471
13472 void TestIfElementNextToCustomElement(int x, int y)
13473 {
13474   static int xy[4][2] =
13475   {
13476     { 0, -1 },
13477     { -1, 0 },
13478     { +1, 0 },
13479     { 0, +1 }
13480   };
13481   static int trigger_sides[4][2] =
13482   {
13483     // center side      border side
13484     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13485     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13486     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13487     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13488   };
13489   int center_element = Tile[x][y];      // should always be non-moving!
13490   int i;
13491
13492   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13493     return;
13494
13495   for (i = 0; i < NUM_DIRECTIONS; i++)
13496   {
13497     int xx = x + xy[i][0];
13498     int yy = y + xy[i][1];
13499     int border_side = trigger_sides[i][1];
13500     int border_element;
13501
13502     if (!IN_LEV_FIELD(xx, yy))
13503       continue;
13504
13505     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13506       continue;                 // center and border element not connected
13507
13508     border_element = Tile[xx][yy];
13509
13510     // check for change of center element (but change it only once)
13511     if (CheckElementChangeBySide(x, y, center_element, border_element,
13512                                  CE_NEXT_TO_X, border_side))
13513       break;
13514   }
13515 }
13516
13517 void TestIfElementTouchesCustomElement(int x, int y)
13518 {
13519   static int xy[4][2] =
13520   {
13521     { 0, -1 },
13522     { -1, 0 },
13523     { +1, 0 },
13524     { 0, +1 }
13525   };
13526   static int trigger_sides[4][2] =
13527   {
13528     // center side      border side
13529     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13530     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13531     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13532     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13533   };
13534   static int touch_dir[4] =
13535   {
13536     MV_LEFT | MV_RIGHT,
13537     MV_UP   | MV_DOWN,
13538     MV_UP   | MV_DOWN,
13539     MV_LEFT | MV_RIGHT
13540   };
13541   boolean change_center_element = FALSE;
13542   int center_element = Tile[x][y];      // should always be non-moving!
13543   int border_element_old[NUM_DIRECTIONS];
13544   int i;
13545
13546   for (i = 0; i < NUM_DIRECTIONS; i++)
13547   {
13548     int xx = x + xy[i][0];
13549     int yy = y + xy[i][1];
13550     int border_element;
13551
13552     border_element_old[i] = -1;
13553
13554     if (!IN_LEV_FIELD(xx, yy))
13555       continue;
13556
13557     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13558       border_element = Tile[xx][yy];    // may be moving!
13559     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13560       border_element = Tile[xx][yy];
13561     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13562       border_element = MovingOrBlocked2Element(xx, yy);
13563     else
13564       continue;                 // center and border element do not touch
13565
13566     border_element_old[i] = border_element;
13567   }
13568
13569   for (i = 0; i < NUM_DIRECTIONS; i++)
13570   {
13571     int xx = x + xy[i][0];
13572     int yy = y + xy[i][1];
13573     int center_side = trigger_sides[i][0];
13574     int border_element = border_element_old[i];
13575
13576     if (border_element == -1)
13577       continue;
13578
13579     // check for change of border element
13580     CheckElementChangeBySide(xx, yy, border_element, center_element,
13581                              CE_TOUCHING_X, center_side);
13582
13583     // (center element cannot be player, so we dont have to check this here)
13584   }
13585
13586   for (i = 0; i < NUM_DIRECTIONS; i++)
13587   {
13588     int xx = x + xy[i][0];
13589     int yy = y + xy[i][1];
13590     int border_side = trigger_sides[i][1];
13591     int border_element = border_element_old[i];
13592
13593     if (border_element == -1)
13594       continue;
13595
13596     // check for change of center element (but change it only once)
13597     if (!change_center_element)
13598       change_center_element =
13599         CheckElementChangeBySide(x, y, center_element, border_element,
13600                                  CE_TOUCHING_X, border_side);
13601
13602     if (IS_PLAYER(xx, yy))
13603     {
13604       /* use player element that is initially defined in the level playfield,
13605          not the player element that corresponds to the runtime player number
13606          (example: a level that contains EL_PLAYER_3 as the only player would
13607          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13608       int player_element = PLAYERINFO(xx, yy)->initial_element;
13609
13610       CheckElementChangeBySide(x, y, center_element, player_element,
13611                                CE_TOUCHING_X, border_side);
13612     }
13613   }
13614 }
13615
13616 void TestIfElementHitsCustomElement(int x, int y, int direction)
13617 {
13618   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13619   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13620   int hitx = x + dx, hity = y + dy;
13621   int hitting_element = Tile[x][y];
13622   int touched_element;
13623
13624   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13625     return;
13626
13627   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13628                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13629
13630   if (IN_LEV_FIELD(hitx, hity))
13631   {
13632     int opposite_direction = MV_DIR_OPPOSITE(direction);
13633     int hitting_side = direction;
13634     int touched_side = opposite_direction;
13635     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13636                           MovDir[hitx][hity] != direction ||
13637                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13638
13639     object_hit = TRUE;
13640
13641     if (object_hit)
13642     {
13643       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13644                                CE_HITTING_X, touched_side);
13645
13646       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13647                                CE_HIT_BY_X, hitting_side);
13648
13649       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13650                                CE_HIT_BY_SOMETHING, opposite_direction);
13651
13652       if (IS_PLAYER(hitx, hity))
13653       {
13654         /* use player element that is initially defined in the level playfield,
13655            not the player element that corresponds to the runtime player number
13656            (example: a level that contains EL_PLAYER_3 as the only player would
13657            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13658         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13659
13660         CheckElementChangeBySide(x, y, hitting_element, player_element,
13661                                  CE_HITTING_X, touched_side);
13662       }
13663     }
13664   }
13665
13666   // "hitting something" is also true when hitting the playfield border
13667   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13668                            CE_HITTING_SOMETHING, direction);
13669 }
13670
13671 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13672 {
13673   int i, kill_x = -1, kill_y = -1;
13674
13675   int bad_element = -1;
13676   static int test_xy[4][2] =
13677   {
13678     { 0, -1 },
13679     { -1, 0 },
13680     { +1, 0 },
13681     { 0, +1 }
13682   };
13683   static int test_dir[4] =
13684   {
13685     MV_UP,
13686     MV_LEFT,
13687     MV_RIGHT,
13688     MV_DOWN
13689   };
13690
13691   for (i = 0; i < NUM_DIRECTIONS; i++)
13692   {
13693     int test_x, test_y, test_move_dir, test_element;
13694
13695     test_x = good_x + test_xy[i][0];
13696     test_y = good_y + test_xy[i][1];
13697
13698     if (!IN_LEV_FIELD(test_x, test_y))
13699       continue;
13700
13701     test_move_dir =
13702       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13703
13704     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13705
13706     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13707        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13708     */
13709     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13710         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13711     {
13712       kill_x = test_x;
13713       kill_y = test_y;
13714       bad_element = test_element;
13715
13716       break;
13717     }
13718   }
13719
13720   if (kill_x != -1 || kill_y != -1)
13721   {
13722     if (IS_PLAYER(good_x, good_y))
13723     {
13724       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13725
13726       if (player->shield_deadly_time_left > 0 &&
13727           !IS_INDESTRUCTIBLE(bad_element))
13728         Bang(kill_x, kill_y);
13729       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13730         KillPlayer(player);
13731     }
13732     else
13733       Bang(good_x, good_y);
13734   }
13735 }
13736
13737 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13738 {
13739   int i, kill_x = -1, kill_y = -1;
13740   int bad_element = Tile[bad_x][bad_y];
13741   static int test_xy[4][2] =
13742   {
13743     { 0, -1 },
13744     { -1, 0 },
13745     { +1, 0 },
13746     { 0, +1 }
13747   };
13748   static int touch_dir[4] =
13749   {
13750     MV_LEFT | MV_RIGHT,
13751     MV_UP   | MV_DOWN,
13752     MV_UP   | MV_DOWN,
13753     MV_LEFT | MV_RIGHT
13754   };
13755   static int test_dir[4] =
13756   {
13757     MV_UP,
13758     MV_LEFT,
13759     MV_RIGHT,
13760     MV_DOWN
13761   };
13762
13763   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13764     return;
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 = bad_x + test_xy[i][0];
13771     test_y = bad_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 = Tile[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(bad_element) &&  bad_move_dir == test_dir[i]) ||
13785         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13786     {
13787       // good thing is player or penguin that does not move away
13788       if (IS_PLAYER(test_x, test_y))
13789       {
13790         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13791
13792         if (bad_element == EL_ROBOT && player->is_moving)
13793           continue;     // robot does not kill player if he is moving
13794
13795         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13796         {
13797           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13798             continue;           // center and border element do not touch
13799         }
13800
13801         kill_x = test_x;
13802         kill_y = test_y;
13803
13804         break;
13805       }
13806       else if (test_element == EL_PENGUIN)
13807       {
13808         kill_x = test_x;
13809         kill_y = test_y;
13810
13811         break;
13812       }
13813     }
13814   }
13815
13816   if (kill_x != -1 || kill_y != -1)
13817   {
13818     if (IS_PLAYER(kill_x, kill_y))
13819     {
13820       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13821
13822       if (player->shield_deadly_time_left > 0 &&
13823           !IS_INDESTRUCTIBLE(bad_element))
13824         Bang(bad_x, bad_y);
13825       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13826         KillPlayer(player);
13827     }
13828     else
13829       Bang(kill_x, kill_y);
13830   }
13831 }
13832
13833 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13834 {
13835   int bad_element = Tile[bad_x][bad_y];
13836   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13837   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13838   int test_x = bad_x + dx, test_y = bad_y + dy;
13839   int test_move_dir, test_element;
13840   int kill_x = -1, kill_y = -1;
13841
13842   if (!IN_LEV_FIELD(test_x, test_y))
13843     return;
13844
13845   test_move_dir =
13846     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13847
13848   test_element = Tile[test_x][test_y];
13849
13850   if (test_move_dir != bad_move_dir)
13851   {
13852     // good thing can be player or penguin that does not move away
13853     if (IS_PLAYER(test_x, test_y))
13854     {
13855       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13856
13857       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13858          player as being hit when he is moving towards the bad thing, because
13859          the "get hit by" condition would be lost after the player stops) */
13860       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13861         return;         // player moves away from bad thing
13862
13863       kill_x = test_x;
13864       kill_y = test_y;
13865     }
13866     else if (test_element == EL_PENGUIN)
13867     {
13868       kill_x = test_x;
13869       kill_y = test_y;
13870     }
13871   }
13872
13873   if (kill_x != -1 || kill_y != -1)
13874   {
13875     if (IS_PLAYER(kill_x, kill_y))
13876     {
13877       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13878
13879       if (player->shield_deadly_time_left > 0 &&
13880           !IS_INDESTRUCTIBLE(bad_element))
13881         Bang(bad_x, bad_y);
13882       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13883         KillPlayer(player);
13884     }
13885     else
13886       Bang(kill_x, kill_y);
13887   }
13888 }
13889
13890 void TestIfPlayerTouchesBadThing(int x, int y)
13891 {
13892   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13893 }
13894
13895 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13896 {
13897   TestIfGoodThingHitsBadThing(x, y, move_dir);
13898 }
13899
13900 void TestIfBadThingTouchesPlayer(int x, int y)
13901 {
13902   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13903 }
13904
13905 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13906 {
13907   TestIfBadThingHitsGoodThing(x, y, move_dir);
13908 }
13909
13910 void TestIfFriendTouchesBadThing(int x, int y)
13911 {
13912   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13913 }
13914
13915 void TestIfBadThingTouchesFriend(int x, int y)
13916 {
13917   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13918 }
13919
13920 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13921 {
13922   int i, kill_x = bad_x, kill_y = bad_y;
13923   static int xy[4][2] =
13924   {
13925     { 0, -1 },
13926     { -1, 0 },
13927     { +1, 0 },
13928     { 0, +1 }
13929   };
13930
13931   for (i = 0; i < NUM_DIRECTIONS; i++)
13932   {
13933     int x, y, element;
13934
13935     x = bad_x + xy[i][0];
13936     y = bad_y + xy[i][1];
13937     if (!IN_LEV_FIELD(x, y))
13938       continue;
13939
13940     element = Tile[x][y];
13941     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13942         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13943     {
13944       kill_x = x;
13945       kill_y = y;
13946       break;
13947     }
13948   }
13949
13950   if (kill_x != bad_x || kill_y != bad_y)
13951     Bang(bad_x, bad_y);
13952 }
13953
13954 void KillPlayer(struct PlayerInfo *player)
13955 {
13956   int jx = player->jx, jy = player->jy;
13957
13958   if (!player->active)
13959     return;
13960
13961 #if 0
13962   Debug("game:playing:KillPlayer",
13963         "0: killed == %d, active == %d, reanimated == %d",
13964         player->killed, player->active, player->reanimated);
13965 #endif
13966
13967   /* the following code was introduced to prevent an infinite loop when calling
13968      -> Bang()
13969      -> CheckTriggeredElementChangeExt()
13970      -> ExecuteCustomElementAction()
13971      -> KillPlayer()
13972      -> (infinitely repeating the above sequence of function calls)
13973      which occurs when killing the player while having a CE with the setting
13974      "kill player X when explosion of <player X>"; the solution using a new
13975      field "player->killed" was chosen for backwards compatibility, although
13976      clever use of the fields "player->active" etc. would probably also work */
13977 #if 1
13978   if (player->killed)
13979     return;
13980 #endif
13981
13982   player->killed = TRUE;
13983
13984   // remove accessible field at the player's position
13985   Tile[jx][jy] = EL_EMPTY;
13986
13987   // deactivate shield (else Bang()/Explode() would not work right)
13988   player->shield_normal_time_left = 0;
13989   player->shield_deadly_time_left = 0;
13990
13991 #if 0
13992   Debug("game:playing:KillPlayer",
13993         "1: killed == %d, active == %d, reanimated == %d",
13994         player->killed, player->active, player->reanimated);
13995 #endif
13996
13997   Bang(jx, jy);
13998
13999 #if 0
14000   Debug("game:playing:KillPlayer",
14001         "2: killed == %d, active == %d, reanimated == %d",
14002         player->killed, player->active, player->reanimated);
14003 #endif
14004
14005   if (player->reanimated)       // killed player may have been reanimated
14006     player->killed = player->reanimated = FALSE;
14007   else
14008     BuryPlayer(player);
14009 }
14010
14011 static void KillPlayerUnlessEnemyProtected(int x, int y)
14012 {
14013   if (!PLAYER_ENEMY_PROTECTED(x, y))
14014     KillPlayer(PLAYERINFO(x, y));
14015 }
14016
14017 static void KillPlayerUnlessExplosionProtected(int x, int y)
14018 {
14019   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14020     KillPlayer(PLAYERINFO(x, y));
14021 }
14022
14023 void BuryPlayer(struct PlayerInfo *player)
14024 {
14025   int jx = player->jx, jy = player->jy;
14026
14027   if (!player->active)
14028     return;
14029
14030   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14031   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14032
14033   RemovePlayer(player);
14034
14035   player->buried = TRUE;
14036
14037   if (game.all_players_gone)
14038     game.GameOver = TRUE;
14039 }
14040
14041 void RemovePlayer(struct PlayerInfo *player)
14042 {
14043   int jx = player->jx, jy = player->jy;
14044   int i, found = FALSE;
14045
14046   player->present = FALSE;
14047   player->active = FALSE;
14048
14049   // required for some CE actions (even if the player is not active anymore)
14050   player->MovPos = 0;
14051
14052   if (!ExplodeField[jx][jy])
14053     StorePlayer[jx][jy] = 0;
14054
14055   if (player->is_moving)
14056     TEST_DrawLevelField(player->last_jx, player->last_jy);
14057
14058   for (i = 0; i < MAX_PLAYERS; i++)
14059     if (stored_player[i].active)
14060       found = TRUE;
14061
14062   if (!found)
14063   {
14064     game.all_players_gone = TRUE;
14065     game.GameOver = TRUE;
14066   }
14067
14068   game.exit_x = game.robot_wheel_x = jx;
14069   game.exit_y = game.robot_wheel_y = jy;
14070 }
14071
14072 void ExitPlayer(struct PlayerInfo *player)
14073 {
14074   DrawPlayer(player);   // needed here only to cleanup last field
14075   RemovePlayer(player);
14076
14077   if (game.players_still_needed > 0)
14078     game.players_still_needed--;
14079 }
14080
14081 static void SetFieldForSnapping(int x, int y, int element, int direction,
14082                                 int player_index_bit)
14083 {
14084   struct ElementInfo *ei = &element_info[element];
14085   int direction_bit = MV_DIR_TO_BIT(direction);
14086   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14087   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14088                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14089
14090   Tile[x][y] = EL_ELEMENT_SNAPPING;
14091   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14092   MovDir[x][y] = direction;
14093   Store[x][y] = element;
14094   Store2[x][y] = player_index_bit;
14095
14096   ResetGfxAnimation(x, y);
14097
14098   GfxElement[x][y] = element;
14099   GfxAction[x][y] = action;
14100   GfxDir[x][y] = direction;
14101   GfxFrame[x][y] = -1;
14102 }
14103
14104 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14105                                    int player_index_bit)
14106 {
14107   TestIfElementTouchesCustomElement(x, y);      // for empty space
14108
14109   if (level.finish_dig_collect)
14110   {
14111     int dig_side = MV_DIR_OPPOSITE(direction);
14112     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14113                         CE_PLAYER_COLLECTS_X);
14114
14115     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14116                                         player_index_bit, dig_side);
14117     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14118                                         player_index_bit, dig_side);
14119   }
14120 }
14121
14122 /*
14123   =============================================================================
14124   checkDiagonalPushing()
14125   -----------------------------------------------------------------------------
14126   check if diagonal input device direction results in pushing of object
14127   (by checking if the alternative direction is walkable, diggable, ...)
14128   =============================================================================
14129 */
14130
14131 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14132                                     int x, int y, int real_dx, int real_dy)
14133 {
14134   int jx, jy, dx, dy, xx, yy;
14135
14136   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14137     return TRUE;
14138
14139   // diagonal direction: check alternative direction
14140   jx = player->jx;
14141   jy = player->jy;
14142   dx = x - jx;
14143   dy = y - jy;
14144   xx = jx + (dx == 0 ? real_dx : 0);
14145   yy = jy + (dy == 0 ? real_dy : 0);
14146
14147   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14148 }
14149
14150 /*
14151   =============================================================================
14152   DigField()
14153   -----------------------------------------------------------------------------
14154   x, y:                 field next to player (non-diagonal) to try to dig to
14155   real_dx, real_dy:     direction as read from input device (can be diagonal)
14156   =============================================================================
14157 */
14158
14159 static int DigField(struct PlayerInfo *player,
14160                     int oldx, int oldy, int x, int y,
14161                     int real_dx, int real_dy, int mode)
14162 {
14163   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14164   boolean player_was_pushing = player->is_pushing;
14165   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14166   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14167   int jx = oldx, jy = oldy;
14168   int dx = x - jx, dy = y - jy;
14169   int nextx = x + dx, nexty = y + dy;
14170   int move_direction = (dx == -1 ? MV_LEFT  :
14171                         dx == +1 ? MV_RIGHT :
14172                         dy == -1 ? MV_UP    :
14173                         dy == +1 ? MV_DOWN  : MV_NONE);
14174   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14175   int dig_side = MV_DIR_OPPOSITE(move_direction);
14176   int old_element = Tile[jx][jy];
14177   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14178   int collect_count;
14179
14180   if (is_player)                // function can also be called by EL_PENGUIN
14181   {
14182     if (player->MovPos == 0)
14183     {
14184       player->is_digging = FALSE;
14185       player->is_collecting = FALSE;
14186     }
14187
14188     if (player->MovPos == 0)    // last pushing move finished
14189       player->is_pushing = FALSE;
14190
14191     if (mode == DF_NO_PUSH)     // player just stopped pushing
14192     {
14193       player->is_switching = FALSE;
14194       player->push_delay = -1;
14195
14196       return MP_NO_ACTION;
14197     }
14198   }
14199
14200   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14201     old_element = Back[jx][jy];
14202
14203   // in case of element dropped at player position, check background
14204   else if (Back[jx][jy] != EL_EMPTY &&
14205            game.engine_version >= VERSION_IDENT(2,2,0,0))
14206     old_element = Back[jx][jy];
14207
14208   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14209     return MP_NO_ACTION;        // field has no opening in this direction
14210
14211   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14212     return MP_NO_ACTION;        // field has no opening in this direction
14213
14214   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14215   {
14216     SplashAcid(x, y);
14217
14218     Tile[jx][jy] = player->artwork_element;
14219     InitMovingField(jx, jy, MV_DOWN);
14220     Store[jx][jy] = EL_ACID;
14221     ContinueMoving(jx, jy);
14222     BuryPlayer(player);
14223
14224     return MP_DONT_RUN_INTO;
14225   }
14226
14227   if (player_can_move && DONT_RUN_INTO(element))
14228   {
14229     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14230
14231     return MP_DONT_RUN_INTO;
14232   }
14233
14234   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14235     return MP_NO_ACTION;
14236
14237   collect_count = element_info[element].collect_count_initial;
14238
14239   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14240     return MP_NO_ACTION;
14241
14242   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14243     player_can_move = player_can_move_or_snap;
14244
14245   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14246       game.engine_version >= VERSION_IDENT(2,2,0,0))
14247   {
14248     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14249                                player->index_bit, dig_side);
14250     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14251                                         player->index_bit, dig_side);
14252
14253     if (element == EL_DC_LANDMINE)
14254       Bang(x, y);
14255
14256     if (Tile[x][y] != element)          // field changed by snapping
14257       return MP_ACTION;
14258
14259     return MP_NO_ACTION;
14260   }
14261
14262   if (player->gravity && is_player && !player->is_auto_moving &&
14263       canFallDown(player) && move_direction != MV_DOWN &&
14264       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14265     return MP_NO_ACTION;        // player cannot walk here due to gravity
14266
14267   if (player_can_move &&
14268       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14269   {
14270     int sound_element = SND_ELEMENT(element);
14271     int sound_action = ACTION_WALKING;
14272
14273     if (IS_RND_GATE(element))
14274     {
14275       if (!player->key[RND_GATE_NR(element)])
14276         return MP_NO_ACTION;
14277     }
14278     else if (IS_RND_GATE_GRAY(element))
14279     {
14280       if (!player->key[RND_GATE_GRAY_NR(element)])
14281         return MP_NO_ACTION;
14282     }
14283     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14284     {
14285       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14286         return MP_NO_ACTION;
14287     }
14288     else if (element == EL_EXIT_OPEN ||
14289              element == EL_EM_EXIT_OPEN ||
14290              element == EL_EM_EXIT_OPENING ||
14291              element == EL_STEEL_EXIT_OPEN ||
14292              element == EL_EM_STEEL_EXIT_OPEN ||
14293              element == EL_EM_STEEL_EXIT_OPENING ||
14294              element == EL_SP_EXIT_OPEN ||
14295              element == EL_SP_EXIT_OPENING)
14296     {
14297       sound_action = ACTION_PASSING;    // player is passing exit
14298     }
14299     else if (element == EL_EMPTY)
14300     {
14301       sound_action = ACTION_MOVING;             // nothing to walk on
14302     }
14303
14304     // play sound from background or player, whatever is available
14305     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14306       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14307     else
14308       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14309   }
14310   else if (player_can_move &&
14311            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14312   {
14313     if (!ACCESS_FROM(element, opposite_direction))
14314       return MP_NO_ACTION;      // field not accessible from this direction
14315
14316     if (CAN_MOVE(element))      // only fixed elements can be passed!
14317       return MP_NO_ACTION;
14318
14319     if (IS_EM_GATE(element))
14320     {
14321       if (!player->key[EM_GATE_NR(element)])
14322         return MP_NO_ACTION;
14323     }
14324     else if (IS_EM_GATE_GRAY(element))
14325     {
14326       if (!player->key[EM_GATE_GRAY_NR(element)])
14327         return MP_NO_ACTION;
14328     }
14329     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14330     {
14331       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14332         return MP_NO_ACTION;
14333     }
14334     else if (IS_EMC_GATE(element))
14335     {
14336       if (!player->key[EMC_GATE_NR(element)])
14337         return MP_NO_ACTION;
14338     }
14339     else if (IS_EMC_GATE_GRAY(element))
14340     {
14341       if (!player->key[EMC_GATE_GRAY_NR(element)])
14342         return MP_NO_ACTION;
14343     }
14344     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14345     {
14346       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14347         return MP_NO_ACTION;
14348     }
14349     else if (element == EL_DC_GATE_WHITE ||
14350              element == EL_DC_GATE_WHITE_GRAY ||
14351              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14352     {
14353       if (player->num_white_keys == 0)
14354         return MP_NO_ACTION;
14355
14356       player->num_white_keys--;
14357     }
14358     else if (IS_SP_PORT(element))
14359     {
14360       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14361           element == EL_SP_GRAVITY_PORT_RIGHT ||
14362           element == EL_SP_GRAVITY_PORT_UP ||
14363           element == EL_SP_GRAVITY_PORT_DOWN)
14364         player->gravity = !player->gravity;
14365       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14366                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14367                element == EL_SP_GRAVITY_ON_PORT_UP ||
14368                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14369         player->gravity = TRUE;
14370       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14371                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14372                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14373                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14374         player->gravity = FALSE;
14375     }
14376
14377     // automatically move to the next field with double speed
14378     player->programmed_action = move_direction;
14379
14380     if (player->move_delay_reset_counter == 0)
14381     {
14382       player->move_delay_reset_counter = 2;     // two double speed steps
14383
14384       DOUBLE_PLAYER_SPEED(player);
14385     }
14386
14387     PlayLevelSoundAction(x, y, ACTION_PASSING);
14388   }
14389   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14390   {
14391     RemoveField(x, y);
14392
14393     if (mode != DF_SNAP)
14394     {
14395       GfxElement[x][y] = GFX_ELEMENT(element);
14396       player->is_digging = TRUE;
14397     }
14398
14399     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14400
14401     // use old behaviour for old levels (digging)
14402     if (!level.finish_dig_collect)
14403     {
14404       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14405                                           player->index_bit, dig_side);
14406
14407       // if digging triggered player relocation, finish digging tile
14408       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14409         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14410     }
14411
14412     if (mode == DF_SNAP)
14413     {
14414       if (level.block_snap_field)
14415         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14416       else
14417         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14418
14419       // use old behaviour for old levels (snapping)
14420       if (!level.finish_dig_collect)
14421         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14422                                             player->index_bit, dig_side);
14423     }
14424   }
14425   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14426   {
14427     RemoveField(x, y);
14428
14429     if (is_player && mode != DF_SNAP)
14430     {
14431       GfxElement[x][y] = element;
14432       player->is_collecting = TRUE;
14433     }
14434
14435     if (element == EL_SPEED_PILL)
14436     {
14437       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14438     }
14439     else if (element == EL_EXTRA_TIME && level.time > 0)
14440     {
14441       TimeLeft += level.extra_time;
14442
14443       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14444
14445       DisplayGameControlValues();
14446     }
14447     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14448     {
14449       player->shield_normal_time_left += level.shield_normal_time;
14450       if (element == EL_SHIELD_DEADLY)
14451         player->shield_deadly_time_left += level.shield_deadly_time;
14452     }
14453     else if (element == EL_DYNAMITE ||
14454              element == EL_EM_DYNAMITE ||
14455              element == EL_SP_DISK_RED)
14456     {
14457       if (player->inventory_size < MAX_INVENTORY_SIZE)
14458         player->inventory_element[player->inventory_size++] = element;
14459
14460       DrawGameDoorValues();
14461     }
14462     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14463     {
14464       player->dynabomb_count++;
14465       player->dynabombs_left++;
14466     }
14467     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14468     {
14469       player->dynabomb_size++;
14470     }
14471     else if (element == EL_DYNABOMB_INCREASE_POWER)
14472     {
14473       player->dynabomb_xl = TRUE;
14474     }
14475     else if (IS_KEY(element))
14476     {
14477       player->key[KEY_NR(element)] = TRUE;
14478
14479       DrawGameDoorValues();
14480     }
14481     else if (element == EL_DC_KEY_WHITE)
14482     {
14483       player->num_white_keys++;
14484
14485       // display white keys?
14486       // DrawGameDoorValues();
14487     }
14488     else if (IS_ENVELOPE(element))
14489     {
14490       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14491
14492       if (!wait_for_snapping)
14493         player->show_envelope = element;
14494     }
14495     else if (element == EL_EMC_LENSES)
14496     {
14497       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14498
14499       RedrawAllInvisibleElementsForLenses();
14500     }
14501     else if (element == EL_EMC_MAGNIFIER)
14502     {
14503       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14504
14505       RedrawAllInvisibleElementsForMagnifier();
14506     }
14507     else if (IS_DROPPABLE(element) ||
14508              IS_THROWABLE(element))     // can be collected and dropped
14509     {
14510       int i;
14511
14512       if (collect_count == 0)
14513         player->inventory_infinite_element = element;
14514       else
14515         for (i = 0; i < collect_count; i++)
14516           if (player->inventory_size < MAX_INVENTORY_SIZE)
14517             player->inventory_element[player->inventory_size++] = element;
14518
14519       DrawGameDoorValues();
14520     }
14521     else if (collect_count > 0)
14522     {
14523       game.gems_still_needed -= collect_count;
14524       if (game.gems_still_needed < 0)
14525         game.gems_still_needed = 0;
14526
14527       game.snapshot.collected_item = TRUE;
14528
14529       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14530
14531       DisplayGameControlValues();
14532     }
14533
14534     RaiseScoreElement(element);
14535     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14536
14537     // use old behaviour for old levels (collecting)
14538     if (!level.finish_dig_collect && is_player)
14539     {
14540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14541                                           player->index_bit, dig_side);
14542
14543       // if collecting triggered player relocation, finish collecting tile
14544       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14545         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14546     }
14547
14548     if (mode == DF_SNAP)
14549     {
14550       if (level.block_snap_field)
14551         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14552       else
14553         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14554
14555       // use old behaviour for old levels (snapping)
14556       if (!level.finish_dig_collect)
14557         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14558                                             player->index_bit, dig_side);
14559     }
14560   }
14561   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14562   {
14563     if (mode == DF_SNAP && element != EL_BD_ROCK)
14564       return MP_NO_ACTION;
14565
14566     if (CAN_FALL(element) && dy)
14567       return MP_NO_ACTION;
14568
14569     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14570         !(element == EL_SPRING && level.use_spring_bug))
14571       return MP_NO_ACTION;
14572
14573     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14574         ((move_direction & MV_VERTICAL &&
14575           ((element_info[element].move_pattern & MV_LEFT &&
14576             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14577            (element_info[element].move_pattern & MV_RIGHT &&
14578             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14579          (move_direction & MV_HORIZONTAL &&
14580           ((element_info[element].move_pattern & MV_UP &&
14581             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14582            (element_info[element].move_pattern & MV_DOWN &&
14583             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14584       return MP_NO_ACTION;
14585
14586     // do not push elements already moving away faster than player
14587     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14588         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14589       return MP_NO_ACTION;
14590
14591     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14592     {
14593       if (player->push_delay_value == -1 || !player_was_pushing)
14594         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14595     }
14596     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14597     {
14598       if (player->push_delay_value == -1)
14599         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14600     }
14601     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14602     {
14603       if (!player->is_pushing)
14604         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14605     }
14606
14607     player->is_pushing = TRUE;
14608     player->is_active = TRUE;
14609
14610     if (!(IN_LEV_FIELD(nextx, nexty) &&
14611           (IS_FREE(nextx, nexty) ||
14612            (IS_SB_ELEMENT(element) &&
14613             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14614            (IS_CUSTOM_ELEMENT(element) &&
14615             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14616       return MP_NO_ACTION;
14617
14618     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14619       return MP_NO_ACTION;
14620
14621     if (player->push_delay == -1)       // new pushing; restart delay
14622       player->push_delay = 0;
14623
14624     if (player->push_delay < player->push_delay_value &&
14625         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14626         element != EL_SPRING && element != EL_BALLOON)
14627     {
14628       // make sure that there is no move delay before next try to push
14629       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14630         player->move_delay = 0;
14631
14632       return MP_NO_ACTION;
14633     }
14634
14635     if (IS_CUSTOM_ELEMENT(element) &&
14636         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14637     {
14638       if (!DigFieldByCE(nextx, nexty, element))
14639         return MP_NO_ACTION;
14640     }
14641
14642     if (IS_SB_ELEMENT(element))
14643     {
14644       boolean sokoban_task_solved = FALSE;
14645
14646       if (element == EL_SOKOBAN_FIELD_FULL)
14647       {
14648         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14649
14650         IncrementSokobanFieldsNeeded();
14651         IncrementSokobanObjectsNeeded();
14652       }
14653
14654       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14655       {
14656         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14657
14658         DecrementSokobanFieldsNeeded();
14659         DecrementSokobanObjectsNeeded();
14660
14661         // sokoban object was pushed from empty field to sokoban field
14662         if (Back[x][y] == EL_EMPTY)
14663           sokoban_task_solved = TRUE;
14664       }
14665
14666       Tile[x][y] = EL_SOKOBAN_OBJECT;
14667
14668       if (Back[x][y] == Back[nextx][nexty])
14669         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14670       else if (Back[x][y] != 0)
14671         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14672                                     ACTION_EMPTYING);
14673       else
14674         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14675                                     ACTION_FILLING);
14676
14677       if (sokoban_task_solved &&
14678           game.sokoban_fields_still_needed == 0 &&
14679           game.sokoban_objects_still_needed == 0 &&
14680           level.auto_exit_sokoban)
14681       {
14682         game.players_still_needed = 0;
14683
14684         LevelSolved();
14685
14686         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14687       }
14688     }
14689     else
14690       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14691
14692     InitMovingField(x, y, move_direction);
14693     GfxAction[x][y] = ACTION_PUSHING;
14694
14695     if (mode == DF_SNAP)
14696       ContinueMoving(x, y);
14697     else
14698       MovPos[x][y] = (dx != 0 ? dx : dy);
14699
14700     Pushed[x][y] = TRUE;
14701     Pushed[nextx][nexty] = TRUE;
14702
14703     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14704       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14705     else
14706       player->push_delay_value = -1;    // get new value later
14707
14708     // check for element change _after_ element has been pushed
14709     if (game.use_change_when_pushing_bug)
14710     {
14711       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14712                                  player->index_bit, dig_side);
14713       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14714                                           player->index_bit, dig_side);
14715     }
14716   }
14717   else if (IS_SWITCHABLE(element))
14718   {
14719     if (PLAYER_SWITCHING(player, x, y))
14720     {
14721       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14722                                           player->index_bit, dig_side);
14723
14724       return MP_ACTION;
14725     }
14726
14727     player->is_switching = TRUE;
14728     player->switch_x = x;
14729     player->switch_y = y;
14730
14731     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14732
14733     if (element == EL_ROBOT_WHEEL)
14734     {
14735       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14736
14737       game.robot_wheel_x = x;
14738       game.robot_wheel_y = y;
14739       game.robot_wheel_active = TRUE;
14740
14741       TEST_DrawLevelField(x, y);
14742     }
14743     else if (element == EL_SP_TERMINAL)
14744     {
14745       int xx, yy;
14746
14747       SCAN_PLAYFIELD(xx, yy)
14748       {
14749         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14750         {
14751           Bang(xx, yy);
14752         }
14753         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14754         {
14755           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14756
14757           ResetGfxAnimation(xx, yy);
14758           TEST_DrawLevelField(xx, yy);
14759         }
14760       }
14761     }
14762     else if (IS_BELT_SWITCH(element))
14763     {
14764       ToggleBeltSwitch(x, y);
14765     }
14766     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14767              element == EL_SWITCHGATE_SWITCH_DOWN ||
14768              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14769              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14770     {
14771       ToggleSwitchgateSwitch(x, y);
14772     }
14773     else if (element == EL_LIGHT_SWITCH ||
14774              element == EL_LIGHT_SWITCH_ACTIVE)
14775     {
14776       ToggleLightSwitch(x, y);
14777     }
14778     else if (element == EL_TIMEGATE_SWITCH ||
14779              element == EL_DC_TIMEGATE_SWITCH)
14780     {
14781       ActivateTimegateSwitch(x, y);
14782     }
14783     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14784              element == EL_BALLOON_SWITCH_RIGHT ||
14785              element == EL_BALLOON_SWITCH_UP    ||
14786              element == EL_BALLOON_SWITCH_DOWN  ||
14787              element == EL_BALLOON_SWITCH_NONE  ||
14788              element == EL_BALLOON_SWITCH_ANY)
14789     {
14790       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14791                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14792                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14793                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14794                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14795                              move_direction);
14796     }
14797     else if (element == EL_LAMP)
14798     {
14799       Tile[x][y] = EL_LAMP_ACTIVE;
14800       game.lights_still_needed--;
14801
14802       ResetGfxAnimation(x, y);
14803       TEST_DrawLevelField(x, y);
14804     }
14805     else if (element == EL_TIME_ORB_FULL)
14806     {
14807       Tile[x][y] = EL_TIME_ORB_EMPTY;
14808
14809       if (level.time > 0 || level.use_time_orb_bug)
14810       {
14811         TimeLeft += level.time_orb_time;
14812         game.no_time_limit = FALSE;
14813
14814         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14815
14816         DisplayGameControlValues();
14817       }
14818
14819       ResetGfxAnimation(x, y);
14820       TEST_DrawLevelField(x, y);
14821     }
14822     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14823              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14824     {
14825       int xx, yy;
14826
14827       game.ball_active = !game.ball_active;
14828
14829       SCAN_PLAYFIELD(xx, yy)
14830       {
14831         int e = Tile[xx][yy];
14832
14833         if (game.ball_active)
14834         {
14835           if (e == EL_EMC_MAGIC_BALL)
14836             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14837           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14838             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14839         }
14840         else
14841         {
14842           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14843             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14844           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14845             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14846         }
14847       }
14848     }
14849
14850     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14851                                         player->index_bit, dig_side);
14852
14853     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14854                                         player->index_bit, dig_side);
14855
14856     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14857                                         player->index_bit, dig_side);
14858
14859     return MP_ACTION;
14860   }
14861   else
14862   {
14863     if (!PLAYER_SWITCHING(player, x, y))
14864     {
14865       player->is_switching = TRUE;
14866       player->switch_x = x;
14867       player->switch_y = y;
14868
14869       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14870                                  player->index_bit, dig_side);
14871       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14872                                           player->index_bit, dig_side);
14873
14874       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14875                                  player->index_bit, dig_side);
14876       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14877                                           player->index_bit, dig_side);
14878     }
14879
14880     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14881                                player->index_bit, dig_side);
14882     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14883                                         player->index_bit, dig_side);
14884
14885     return MP_NO_ACTION;
14886   }
14887
14888   player->push_delay = -1;
14889
14890   if (is_player)                // function can also be called by EL_PENGUIN
14891   {
14892     if (Tile[x][y] != element)          // really digged/collected something
14893     {
14894       player->is_collecting = !player->is_digging;
14895       player->is_active = TRUE;
14896
14897       player->last_removed_element = element;
14898     }
14899   }
14900
14901   return MP_MOVING;
14902 }
14903
14904 static boolean DigFieldByCE(int x, int y, int digging_element)
14905 {
14906   int element = Tile[x][y];
14907
14908   if (!IS_FREE(x, y))
14909   {
14910     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14911                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14912                   ACTION_BREAKING);
14913
14914     // no element can dig solid indestructible elements
14915     if (IS_INDESTRUCTIBLE(element) &&
14916         !IS_DIGGABLE(element) &&
14917         !IS_COLLECTIBLE(element))
14918       return FALSE;
14919
14920     if (AmoebaNr[x][y] &&
14921         (element == EL_AMOEBA_FULL ||
14922          element == EL_BD_AMOEBA ||
14923          element == EL_AMOEBA_GROWING))
14924     {
14925       AmoebaCnt[AmoebaNr[x][y]]--;
14926       AmoebaCnt2[AmoebaNr[x][y]]--;
14927     }
14928
14929     if (IS_MOVING(x, y))
14930       RemoveMovingField(x, y);
14931     else
14932     {
14933       RemoveField(x, y);
14934       TEST_DrawLevelField(x, y);
14935     }
14936
14937     // if digged element was about to explode, prevent the explosion
14938     ExplodeField[x][y] = EX_TYPE_NONE;
14939
14940     PlayLevelSoundAction(x, y, action);
14941   }
14942
14943   Store[x][y] = EL_EMPTY;
14944
14945   // this makes it possible to leave the removed element again
14946   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14947     Store[x][y] = element;
14948
14949   return TRUE;
14950 }
14951
14952 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14953 {
14954   int jx = player->jx, jy = player->jy;
14955   int x = jx + dx, y = jy + dy;
14956   int snap_direction = (dx == -1 ? MV_LEFT  :
14957                         dx == +1 ? MV_RIGHT :
14958                         dy == -1 ? MV_UP    :
14959                         dy == +1 ? MV_DOWN  : MV_NONE);
14960   boolean can_continue_snapping = (level.continuous_snapping &&
14961                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14962
14963   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14964     return FALSE;
14965
14966   if (!player->active || !IN_LEV_FIELD(x, y))
14967     return FALSE;
14968
14969   if (dx && dy)
14970     return FALSE;
14971
14972   if (!dx && !dy)
14973   {
14974     if (player->MovPos == 0)
14975       player->is_pushing = FALSE;
14976
14977     player->is_snapping = FALSE;
14978
14979     if (player->MovPos == 0)
14980     {
14981       player->is_moving = FALSE;
14982       player->is_digging = FALSE;
14983       player->is_collecting = FALSE;
14984     }
14985
14986     return FALSE;
14987   }
14988
14989   // prevent snapping with already pressed snap key when not allowed
14990   if (player->is_snapping && !can_continue_snapping)
14991     return FALSE;
14992
14993   player->MovDir = snap_direction;
14994
14995   if (player->MovPos == 0)
14996   {
14997     player->is_moving = FALSE;
14998     player->is_digging = FALSE;
14999     player->is_collecting = FALSE;
15000   }
15001
15002   player->is_dropping = FALSE;
15003   player->is_dropping_pressed = FALSE;
15004   player->drop_pressed_delay = 0;
15005
15006   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15007     return FALSE;
15008
15009   player->is_snapping = TRUE;
15010   player->is_active = TRUE;
15011
15012   if (player->MovPos == 0)
15013   {
15014     player->is_moving = FALSE;
15015     player->is_digging = FALSE;
15016     player->is_collecting = FALSE;
15017   }
15018
15019   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15020     TEST_DrawLevelField(player->last_jx, player->last_jy);
15021
15022   TEST_DrawLevelField(x, y);
15023
15024   return TRUE;
15025 }
15026
15027 static boolean DropElement(struct PlayerInfo *player)
15028 {
15029   int old_element, new_element;
15030   int dropx = player->jx, dropy = player->jy;
15031   int drop_direction = player->MovDir;
15032   int drop_side = drop_direction;
15033   int drop_element = get_next_dropped_element(player);
15034
15035   /* do not drop an element on top of another element; when holding drop key
15036      pressed without moving, dropped element must move away before the next
15037      element can be dropped (this is especially important if the next element
15038      is dynamite, which can be placed on background for historical reasons) */
15039   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15040     return MP_ACTION;
15041
15042   if (IS_THROWABLE(drop_element))
15043   {
15044     dropx += GET_DX_FROM_DIR(drop_direction);
15045     dropy += GET_DY_FROM_DIR(drop_direction);
15046
15047     if (!IN_LEV_FIELD(dropx, dropy))
15048       return FALSE;
15049   }
15050
15051   old_element = Tile[dropx][dropy];     // old element at dropping position
15052   new_element = drop_element;           // default: no change when dropping
15053
15054   // check if player is active, not moving and ready to drop
15055   if (!player->active || player->MovPos || player->drop_delay > 0)
15056     return FALSE;
15057
15058   // check if player has anything that can be dropped
15059   if (new_element == EL_UNDEFINED)
15060     return FALSE;
15061
15062   // only set if player has anything that can be dropped
15063   player->is_dropping_pressed = TRUE;
15064
15065   // check if drop key was pressed long enough for EM style dynamite
15066   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15067     return FALSE;
15068
15069   // check if anything can be dropped at the current position
15070   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15071     return FALSE;
15072
15073   // collected custom elements can only be dropped on empty fields
15074   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15075     return FALSE;
15076
15077   if (old_element != EL_EMPTY)
15078     Back[dropx][dropy] = old_element;   // store old element on this field
15079
15080   ResetGfxAnimation(dropx, dropy);
15081   ResetRandomAnimationValue(dropx, dropy);
15082
15083   if (player->inventory_size > 0 ||
15084       player->inventory_infinite_element != EL_UNDEFINED)
15085   {
15086     if (player->inventory_size > 0)
15087     {
15088       player->inventory_size--;
15089
15090       DrawGameDoorValues();
15091
15092       if (new_element == EL_DYNAMITE)
15093         new_element = EL_DYNAMITE_ACTIVE;
15094       else if (new_element == EL_EM_DYNAMITE)
15095         new_element = EL_EM_DYNAMITE_ACTIVE;
15096       else if (new_element == EL_SP_DISK_RED)
15097         new_element = EL_SP_DISK_RED_ACTIVE;
15098     }
15099
15100     Tile[dropx][dropy] = new_element;
15101
15102     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15103       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15104                           el2img(Tile[dropx][dropy]), 0);
15105
15106     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15107
15108     // needed if previous element just changed to "empty" in the last frame
15109     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15110
15111     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15112                                player->index_bit, drop_side);
15113     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15114                                         CE_PLAYER_DROPS_X,
15115                                         player->index_bit, drop_side);
15116
15117     TestIfElementTouchesCustomElement(dropx, dropy);
15118   }
15119   else          // player is dropping a dyna bomb
15120   {
15121     player->dynabombs_left--;
15122
15123     Tile[dropx][dropy] = new_element;
15124
15125     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15126       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15127                           el2img(Tile[dropx][dropy]), 0);
15128
15129     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15130   }
15131
15132   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15133     InitField_WithBug1(dropx, dropy, FALSE);
15134
15135   new_element = Tile[dropx][dropy];     // element might have changed
15136
15137   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15138       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15139   {
15140     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15141       MovDir[dropx][dropy] = drop_direction;
15142
15143     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15144
15145     // do not cause impact style collision by dropping elements that can fall
15146     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15147   }
15148
15149   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15150   player->is_dropping = TRUE;
15151
15152   player->drop_pressed_delay = 0;
15153   player->is_dropping_pressed = FALSE;
15154
15155   player->drop_x = dropx;
15156   player->drop_y = dropy;
15157
15158   return TRUE;
15159 }
15160
15161 // ----------------------------------------------------------------------------
15162 // game sound playing functions
15163 // ----------------------------------------------------------------------------
15164
15165 static int *loop_sound_frame = NULL;
15166 static int *loop_sound_volume = NULL;
15167
15168 void InitPlayLevelSound(void)
15169 {
15170   int num_sounds = getSoundListSize();
15171
15172   checked_free(loop_sound_frame);
15173   checked_free(loop_sound_volume);
15174
15175   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15176   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15177 }
15178
15179 static void PlayLevelSound(int x, int y, int nr)
15180 {
15181   int sx = SCREENX(x), sy = SCREENY(y);
15182   int volume, stereo_position;
15183   int max_distance = 8;
15184   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15185
15186   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15187       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15188     return;
15189
15190   if (!IN_LEV_FIELD(x, y) ||
15191       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15192       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15193     return;
15194
15195   volume = SOUND_MAX_VOLUME;
15196
15197   if (!IN_SCR_FIELD(sx, sy))
15198   {
15199     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15200     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15201
15202     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15203   }
15204
15205   stereo_position = (SOUND_MAX_LEFT +
15206                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15207                      (SCR_FIELDX + 2 * max_distance));
15208
15209   if (IS_LOOP_SOUND(nr))
15210   {
15211     /* This assures that quieter loop sounds do not overwrite louder ones,
15212        while restarting sound volume comparison with each new game frame. */
15213
15214     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15215       return;
15216
15217     loop_sound_volume[nr] = volume;
15218     loop_sound_frame[nr] = FrameCounter;
15219   }
15220
15221   PlaySoundExt(nr, volume, stereo_position, type);
15222 }
15223
15224 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15225 {
15226   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15227                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15228                  y < LEVELY(BY1) ? LEVELY(BY1) :
15229                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15230                  sound_action);
15231 }
15232
15233 static void PlayLevelSoundAction(int x, int y, int action)
15234 {
15235   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15236 }
15237
15238 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15239 {
15240   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15241
15242   if (sound_effect != SND_UNDEFINED)
15243     PlayLevelSound(x, y, sound_effect);
15244 }
15245
15246 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15247                                               int action)
15248 {
15249   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15250
15251   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15252     PlayLevelSound(x, y, sound_effect);
15253 }
15254
15255 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15256 {
15257   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15258
15259   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15260     PlayLevelSound(x, y, sound_effect);
15261 }
15262
15263 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15264 {
15265   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15266
15267   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15268     StopSound(sound_effect);
15269 }
15270
15271 static int getLevelMusicNr(void)
15272 {
15273   if (levelset.music[level_nr] != MUS_UNDEFINED)
15274     return levelset.music[level_nr];            // from config file
15275   else
15276     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15277 }
15278
15279 static void FadeLevelSounds(void)
15280 {
15281   FadeSounds();
15282 }
15283
15284 static void FadeLevelMusic(void)
15285 {
15286   int music_nr = getLevelMusicNr();
15287   char *curr_music = getCurrentlyPlayingMusicFilename();
15288   char *next_music = getMusicInfoEntryFilename(music_nr);
15289
15290   if (!strEqual(curr_music, next_music))
15291     FadeMusic();
15292 }
15293
15294 void FadeLevelSoundsAndMusic(void)
15295 {
15296   FadeLevelSounds();
15297   FadeLevelMusic();
15298 }
15299
15300 static void PlayLevelMusic(void)
15301 {
15302   int music_nr = getLevelMusicNr();
15303   char *curr_music = getCurrentlyPlayingMusicFilename();
15304   char *next_music = getMusicInfoEntryFilename(music_nr);
15305
15306   if (!strEqual(curr_music, next_music))
15307     PlayMusicLoop(music_nr);
15308 }
15309
15310 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15311 {
15312   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15313   int offset = 0;
15314   int x = xx - offset;
15315   int y = yy - offset;
15316
15317   switch (sample)
15318   {
15319     case SOUND_blank:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15321       break;
15322
15323     case SOUND_roll:
15324       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15325       break;
15326
15327     case SOUND_stone:
15328       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15329       break;
15330
15331     case SOUND_nut:
15332       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15333       break;
15334
15335     case SOUND_crack:
15336       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15337       break;
15338
15339     case SOUND_bug:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15341       break;
15342
15343     case SOUND_tank:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15345       break;
15346
15347     case SOUND_android_clone:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15349       break;
15350
15351     case SOUND_android_move:
15352       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15353       break;
15354
15355     case SOUND_spring:
15356       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15357       break;
15358
15359     case SOUND_slurp:
15360       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15361       break;
15362
15363     case SOUND_eater:
15364       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15365       break;
15366
15367     case SOUND_eater_eat:
15368       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15369       break;
15370
15371     case SOUND_alien:
15372       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15373       break;
15374
15375     case SOUND_collect:
15376       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15377       break;
15378
15379     case SOUND_diamond:
15380       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15381       break;
15382
15383     case SOUND_squash:
15384       // !!! CHECK THIS !!!
15385 #if 1
15386       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15387 #else
15388       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15389 #endif
15390       break;
15391
15392     case SOUND_wonderfall:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15394       break;
15395
15396     case SOUND_drip:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15398       break;
15399
15400     case SOUND_push:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15402       break;
15403
15404     case SOUND_dirt:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15406       break;
15407
15408     case SOUND_acid:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15410       break;
15411
15412     case SOUND_ball:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15414       break;
15415
15416     case SOUND_slide:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15418       break;
15419
15420     case SOUND_wonder:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15422       break;
15423
15424     case SOUND_door:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15426       break;
15427
15428     case SOUND_exit_open:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15430       break;
15431
15432     case SOUND_exit_leave:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15434       break;
15435
15436     case SOUND_dynamite:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15438       break;
15439
15440     case SOUND_tick:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15442       break;
15443
15444     case SOUND_press:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15446       break;
15447
15448     case SOUND_wheel:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15450       break;
15451
15452     case SOUND_boom:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15454       break;
15455
15456     case SOUND_die:
15457       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15458       break;
15459
15460     case SOUND_time:
15461       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15462       break;
15463
15464     default:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15466       break;
15467   }
15468 }
15469
15470 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15471 {
15472   int element = map_element_SP_to_RND(element_sp);
15473   int action = map_action_SP_to_RND(action_sp);
15474   int offset = (setup.sp_show_border_elements ? 0 : 1);
15475   int x = xx - offset;
15476   int y = yy - offset;
15477
15478   PlayLevelSoundElementAction(x, y, element, action);
15479 }
15480
15481 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15482 {
15483   int element = map_element_MM_to_RND(element_mm);
15484   int action = map_action_MM_to_RND(action_mm);
15485   int offset = 0;
15486   int x = xx - offset;
15487   int y = yy - offset;
15488
15489   if (!IS_MM_ELEMENT(element))
15490     element = EL_MM_DEFAULT;
15491
15492   PlayLevelSoundElementAction(x, y, element, action);
15493 }
15494
15495 void PlaySound_MM(int sound_mm)
15496 {
15497   int sound = map_sound_MM_to_RND(sound_mm);
15498
15499   if (sound == SND_UNDEFINED)
15500     return;
15501
15502   PlaySound(sound);
15503 }
15504
15505 void PlaySoundLoop_MM(int sound_mm)
15506 {
15507   int sound = map_sound_MM_to_RND(sound_mm);
15508
15509   if (sound == SND_UNDEFINED)
15510     return;
15511
15512   PlaySoundLoop(sound);
15513 }
15514
15515 void StopSound_MM(int sound_mm)
15516 {
15517   int sound = map_sound_MM_to_RND(sound_mm);
15518
15519   if (sound == SND_UNDEFINED)
15520     return;
15521
15522   StopSound(sound);
15523 }
15524
15525 void RaiseScore(int value)
15526 {
15527   game.score += value;
15528
15529   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15530
15531   DisplayGameControlValues();
15532 }
15533
15534 void RaiseScoreElement(int element)
15535 {
15536   switch (element)
15537   {
15538     case EL_EMERALD:
15539     case EL_BD_DIAMOND:
15540     case EL_EMERALD_YELLOW:
15541     case EL_EMERALD_RED:
15542     case EL_EMERALD_PURPLE:
15543     case EL_SP_INFOTRON:
15544       RaiseScore(level.score[SC_EMERALD]);
15545       break;
15546     case EL_DIAMOND:
15547       RaiseScore(level.score[SC_DIAMOND]);
15548       break;
15549     case EL_CRYSTAL:
15550       RaiseScore(level.score[SC_CRYSTAL]);
15551       break;
15552     case EL_PEARL:
15553       RaiseScore(level.score[SC_PEARL]);
15554       break;
15555     case EL_BUG:
15556     case EL_BD_BUTTERFLY:
15557     case EL_SP_ELECTRON:
15558       RaiseScore(level.score[SC_BUG]);
15559       break;
15560     case EL_SPACESHIP:
15561     case EL_BD_FIREFLY:
15562     case EL_SP_SNIKSNAK:
15563       RaiseScore(level.score[SC_SPACESHIP]);
15564       break;
15565     case EL_YAMYAM:
15566     case EL_DARK_YAMYAM:
15567       RaiseScore(level.score[SC_YAMYAM]);
15568       break;
15569     case EL_ROBOT:
15570       RaiseScore(level.score[SC_ROBOT]);
15571       break;
15572     case EL_PACMAN:
15573       RaiseScore(level.score[SC_PACMAN]);
15574       break;
15575     case EL_NUT:
15576       RaiseScore(level.score[SC_NUT]);
15577       break;
15578     case EL_DYNAMITE:
15579     case EL_EM_DYNAMITE:
15580     case EL_SP_DISK_RED:
15581     case EL_DYNABOMB_INCREASE_NUMBER:
15582     case EL_DYNABOMB_INCREASE_SIZE:
15583     case EL_DYNABOMB_INCREASE_POWER:
15584       RaiseScore(level.score[SC_DYNAMITE]);
15585       break;
15586     case EL_SHIELD_NORMAL:
15587     case EL_SHIELD_DEADLY:
15588       RaiseScore(level.score[SC_SHIELD]);
15589       break;
15590     case EL_EXTRA_TIME:
15591       RaiseScore(level.extra_time_score);
15592       break;
15593     case EL_KEY_1:
15594     case EL_KEY_2:
15595     case EL_KEY_3:
15596     case EL_KEY_4:
15597     case EL_EM_KEY_1:
15598     case EL_EM_KEY_2:
15599     case EL_EM_KEY_3:
15600     case EL_EM_KEY_4:
15601     case EL_EMC_KEY_5:
15602     case EL_EMC_KEY_6:
15603     case EL_EMC_KEY_7:
15604     case EL_EMC_KEY_8:
15605     case EL_DC_KEY_WHITE:
15606       RaiseScore(level.score[SC_KEY]);
15607       break;
15608     default:
15609       RaiseScore(element_info[element].collect_score);
15610       break;
15611   }
15612 }
15613
15614 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15615 {
15616   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15617   {
15618     if (!quick_quit)
15619     {
15620       // prevent short reactivation of overlay buttons while closing door
15621       SetOverlayActive(FALSE);
15622
15623       // door may still be open due to skipped or envelope style request
15624       CloseDoor(DOOR_CLOSE_1);
15625     }
15626
15627     if (network.enabled)
15628       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15629     else
15630     {
15631       if (quick_quit)
15632         FadeSkipNextFadeIn();
15633
15634       SetGameStatus(GAME_MODE_MAIN);
15635
15636       DrawMainMenu();
15637     }
15638   }
15639   else          // continue playing the game
15640   {
15641     if (tape.playing && tape.deactivate_display)
15642       TapeDeactivateDisplayOff(TRUE);
15643
15644     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15645
15646     if (tape.playing && tape.deactivate_display)
15647       TapeDeactivateDisplayOn();
15648   }
15649 }
15650
15651 void RequestQuitGame(boolean escape_key_pressed)
15652 {
15653   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15654   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15655                         level_editor_test_game);
15656   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15657                           quick_quit);
15658
15659   RequestQuitGameExt(skip_request, quick_quit,
15660                      "Do you really want to quit the game?");
15661 }
15662
15663 void RequestRestartGame(char *message)
15664 {
15665   game.restart_game_message = NULL;
15666
15667   boolean has_started_game = hasStartedNetworkGame();
15668   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15669
15670   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15671   {
15672     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15673   }
15674   else
15675   {
15676     // needed in case of envelope request to close game panel
15677     CloseDoor(DOOR_CLOSE_1);
15678
15679     SetGameStatus(GAME_MODE_MAIN);
15680
15681     DrawMainMenu();
15682   }
15683 }
15684
15685 void CheckGameOver(void)
15686 {
15687   static boolean last_game_over = FALSE;
15688   static int game_over_delay = 0;
15689   int game_over_delay_value = 50;
15690   boolean game_over = checkGameFailed();
15691
15692   // do not handle game over if request dialog is already active
15693   if (game.request_active)
15694     return;
15695
15696   // do not ask to play again if game was never actually played
15697   if (!game.GamePlayed)
15698     return;
15699
15700   if (!game_over)
15701   {
15702     last_game_over = FALSE;
15703     game_over_delay = game_over_delay_value;
15704
15705     return;
15706   }
15707
15708   if (game_over_delay > 0)
15709   {
15710     game_over_delay--;
15711
15712     return;
15713   }
15714
15715   if (last_game_over != game_over)
15716     game.restart_game_message = (hasStartedNetworkGame() ?
15717                                  "Game over! Play it again?" :
15718                                  "Game over!");
15719
15720   last_game_over = game_over;
15721 }
15722
15723 boolean checkGameSolved(void)
15724 {
15725   // set for all game engines if level was solved
15726   return game.LevelSolved_GameEnd;
15727 }
15728
15729 boolean checkGameFailed(void)
15730 {
15731   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15732     return (game_em.game_over && !game_em.level_solved);
15733   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15734     return (game_sp.game_over && !game_sp.level_solved);
15735   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15736     return (game_mm.game_over && !game_mm.level_solved);
15737   else                          // GAME_ENGINE_TYPE_RND
15738     return (game.GameOver && !game.LevelSolved);
15739 }
15740
15741 boolean checkGameEnded(void)
15742 {
15743   return (checkGameSolved() || checkGameFailed());
15744 }
15745
15746
15747 // ----------------------------------------------------------------------------
15748 // random generator functions
15749 // ----------------------------------------------------------------------------
15750
15751 unsigned int InitEngineRandom_RND(int seed)
15752 {
15753   game.num_random_calls = 0;
15754
15755   return InitEngineRandom(seed);
15756 }
15757
15758 unsigned int RND(int max)
15759 {
15760   if (max > 0)
15761   {
15762     game.num_random_calls++;
15763
15764     return GetEngineRandom(max);
15765   }
15766
15767   return 0;
15768 }
15769
15770
15771 // ----------------------------------------------------------------------------
15772 // game engine snapshot handling functions
15773 // ----------------------------------------------------------------------------
15774
15775 struct EngineSnapshotInfo
15776 {
15777   // runtime values for custom element collect score
15778   int collect_score[NUM_CUSTOM_ELEMENTS];
15779
15780   // runtime values for group element choice position
15781   int choice_pos[NUM_GROUP_ELEMENTS];
15782
15783   // runtime values for belt position animations
15784   int belt_graphic[4][NUM_BELT_PARTS];
15785   int belt_anim_mode[4][NUM_BELT_PARTS];
15786 };
15787
15788 static struct EngineSnapshotInfo engine_snapshot_rnd;
15789 static char *snapshot_level_identifier = NULL;
15790 static int snapshot_level_nr = -1;
15791
15792 static void SaveEngineSnapshotValues_RND(void)
15793 {
15794   static int belt_base_active_element[4] =
15795   {
15796     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15797     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15798     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15799     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15800   };
15801   int i, j;
15802
15803   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15804   {
15805     int element = EL_CUSTOM_START + i;
15806
15807     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15808   }
15809
15810   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15811   {
15812     int element = EL_GROUP_START + i;
15813
15814     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15815   }
15816
15817   for (i = 0; i < 4; i++)
15818   {
15819     for (j = 0; j < NUM_BELT_PARTS; j++)
15820     {
15821       int element = belt_base_active_element[i] + j;
15822       int graphic = el2img(element);
15823       int anim_mode = graphic_info[graphic].anim_mode;
15824
15825       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15826       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15827     }
15828   }
15829 }
15830
15831 static void LoadEngineSnapshotValues_RND(void)
15832 {
15833   unsigned int num_random_calls = game.num_random_calls;
15834   int i, j;
15835
15836   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15837   {
15838     int element = EL_CUSTOM_START + i;
15839
15840     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15841   }
15842
15843   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15844   {
15845     int element = EL_GROUP_START + i;
15846
15847     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15848   }
15849
15850   for (i = 0; i < 4; i++)
15851   {
15852     for (j = 0; j < NUM_BELT_PARTS; j++)
15853     {
15854       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15855       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15856
15857       graphic_info[graphic].anim_mode = anim_mode;
15858     }
15859   }
15860
15861   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15862   {
15863     InitRND(tape.random_seed);
15864     for (i = 0; i < num_random_calls; i++)
15865       RND(1);
15866   }
15867
15868   if (game.num_random_calls != num_random_calls)
15869   {
15870     Error("number of random calls out of sync");
15871     Error("number of random calls should be %d", num_random_calls);
15872     Error("number of random calls is %d", game.num_random_calls);
15873
15874     Fail("this should not happen -- please debug");
15875   }
15876 }
15877
15878 void FreeEngineSnapshotSingle(void)
15879 {
15880   FreeSnapshotSingle();
15881
15882   setString(&snapshot_level_identifier, NULL);
15883   snapshot_level_nr = -1;
15884 }
15885
15886 void FreeEngineSnapshotList(void)
15887 {
15888   FreeSnapshotList();
15889 }
15890
15891 static ListNode *SaveEngineSnapshotBuffers(void)
15892 {
15893   ListNode *buffers = NULL;
15894
15895   // copy some special values to a structure better suited for the snapshot
15896
15897   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15898     SaveEngineSnapshotValues_RND();
15899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15900     SaveEngineSnapshotValues_EM();
15901   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15902     SaveEngineSnapshotValues_SP(&buffers);
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15904     SaveEngineSnapshotValues_MM(&buffers);
15905
15906   // save values stored in special snapshot structure
15907
15908   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15909     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15910   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15911     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15912   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15913     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15914   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15915     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15916
15917   // save further RND engine values
15918
15919   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15921   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15922
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15928
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15932
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15934
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15937
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15956
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15959
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15963
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15966
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15972
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15975
15976 #if 0
15977   ListNode *node = engine_snapshot_list_rnd;
15978   int num_bytes = 0;
15979
15980   while (node != NULL)
15981   {
15982     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15983
15984     node = node->next;
15985   }
15986
15987   Debug("game:playing:SaveEngineSnapshotBuffers",
15988         "size of engine snapshot: %d bytes", num_bytes);
15989 #endif
15990
15991   return buffers;
15992 }
15993
15994 void SaveEngineSnapshotSingle(void)
15995 {
15996   ListNode *buffers = SaveEngineSnapshotBuffers();
15997
15998   // finally save all snapshot buffers to single snapshot
15999   SaveSnapshotSingle(buffers);
16000
16001   // save level identification information
16002   setString(&snapshot_level_identifier, leveldir_current->identifier);
16003   snapshot_level_nr = level_nr;
16004 }
16005
16006 boolean CheckSaveEngineSnapshotToList(void)
16007 {
16008   boolean save_snapshot =
16009     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16010      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16011       game.snapshot.changed_action) ||
16012      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16013       game.snapshot.collected_item));
16014
16015   game.snapshot.changed_action = FALSE;
16016   game.snapshot.collected_item = FALSE;
16017   game.snapshot.save_snapshot = save_snapshot;
16018
16019   return save_snapshot;
16020 }
16021
16022 void SaveEngineSnapshotToList(void)
16023 {
16024   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16025       tape.quick_resume)
16026     return;
16027
16028   ListNode *buffers = SaveEngineSnapshotBuffers();
16029
16030   // finally save all snapshot buffers to snapshot list
16031   SaveSnapshotToList(buffers);
16032 }
16033
16034 void SaveEngineSnapshotToListInitial(void)
16035 {
16036   FreeEngineSnapshotList();
16037
16038   SaveEngineSnapshotToList();
16039 }
16040
16041 static void LoadEngineSnapshotValues(void)
16042 {
16043   // restore special values from snapshot structure
16044
16045   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16046     LoadEngineSnapshotValues_RND();
16047   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16048     LoadEngineSnapshotValues_EM();
16049   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16050     LoadEngineSnapshotValues_SP();
16051   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16052     LoadEngineSnapshotValues_MM();
16053 }
16054
16055 void LoadEngineSnapshotSingle(void)
16056 {
16057   LoadSnapshotSingle();
16058
16059   LoadEngineSnapshotValues();
16060 }
16061
16062 static void LoadEngineSnapshot_Undo(int steps)
16063 {
16064   LoadSnapshotFromList_Older(steps);
16065
16066   LoadEngineSnapshotValues();
16067 }
16068
16069 static void LoadEngineSnapshot_Redo(int steps)
16070 {
16071   LoadSnapshotFromList_Newer(steps);
16072
16073   LoadEngineSnapshotValues();
16074 }
16075
16076 boolean CheckEngineSnapshotSingle(void)
16077 {
16078   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16079           snapshot_level_nr == level_nr);
16080 }
16081
16082 boolean CheckEngineSnapshotList(void)
16083 {
16084   return CheckSnapshotList();
16085 }
16086
16087
16088 // ---------- new game button stuff -------------------------------------------
16089
16090 static struct
16091 {
16092   int graphic;
16093   struct XY *pos;
16094   int gadget_id;
16095   boolean *setup_value;
16096   boolean allowed_on_tape;
16097   boolean is_touch_button;
16098   char *infotext;
16099 } gamebutton_info[NUM_GAME_BUTTONS] =
16100 {
16101   {
16102     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16103     GAME_CTRL_ID_STOP,                          NULL,
16104     TRUE, FALSE,                                "stop game"
16105   },
16106   {
16107     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16108     GAME_CTRL_ID_PAUSE,                         NULL,
16109     TRUE, FALSE,                                "pause game"
16110   },
16111   {
16112     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16113     GAME_CTRL_ID_PLAY,                          NULL,
16114     TRUE, FALSE,                                "play game"
16115   },
16116   {
16117     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16118     GAME_CTRL_ID_UNDO,                          NULL,
16119     TRUE, FALSE,                                "undo step"
16120   },
16121   {
16122     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16123     GAME_CTRL_ID_REDO,                          NULL,
16124     TRUE, FALSE,                                "redo step"
16125   },
16126   {
16127     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16128     GAME_CTRL_ID_SAVE,                          NULL,
16129     TRUE, FALSE,                                "save game"
16130   },
16131   {
16132     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16133     GAME_CTRL_ID_PAUSE2,                        NULL,
16134     TRUE, FALSE,                                "pause game"
16135   },
16136   {
16137     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16138     GAME_CTRL_ID_LOAD,                          NULL,
16139     TRUE, FALSE,                                "load game"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16143     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16144     FALSE, FALSE,                               "stop game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16148     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16149     FALSE, FALSE,                               "pause game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16153     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16154     FALSE, FALSE,                               "play game"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16158     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16159     FALSE, TRUE,                                "stop game"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16163     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16164     FALSE, TRUE,                                "pause game"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16168     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16169     TRUE, FALSE,                                "background music on/off"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16173     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16174     TRUE, FALSE,                                "sound loops on/off"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16178     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16179     TRUE, FALSE,                                "normal sounds on/off"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16183     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16184     FALSE, FALSE,                               "background music on/off"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16188     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16189     FALSE, FALSE,                               "sound loops on/off"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16193     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16194     FALSE, FALSE,                               "normal sounds on/off"
16195   }
16196 };
16197
16198 void CreateGameButtons(void)
16199 {
16200   int i;
16201
16202   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16203   {
16204     int graphic = gamebutton_info[i].graphic;
16205     struct GraphicInfo *gfx = &graphic_info[graphic];
16206     struct XY *pos = gamebutton_info[i].pos;
16207     struct GadgetInfo *gi;
16208     int button_type;
16209     boolean checked;
16210     unsigned int event_mask;
16211     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16212     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16213     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16214     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16215     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16216     int gd_x   = gfx->src_x;
16217     int gd_y   = gfx->src_y;
16218     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16219     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16220     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16221     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16222     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16223     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16224     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16225     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16226     int id = i;
16227
16228     if (gfx->bitmap == NULL)
16229     {
16230       game_gadget[id] = NULL;
16231
16232       continue;
16233     }
16234
16235     if (id == GAME_CTRL_ID_STOP ||
16236         id == GAME_CTRL_ID_PANEL_STOP ||
16237         id == GAME_CTRL_ID_TOUCH_STOP ||
16238         id == GAME_CTRL_ID_PLAY ||
16239         id == GAME_CTRL_ID_PANEL_PLAY ||
16240         id == GAME_CTRL_ID_SAVE ||
16241         id == GAME_CTRL_ID_LOAD)
16242     {
16243       button_type = GD_TYPE_NORMAL_BUTTON;
16244       checked = FALSE;
16245       event_mask = GD_EVENT_RELEASED;
16246     }
16247     else if (id == GAME_CTRL_ID_UNDO ||
16248              id == GAME_CTRL_ID_REDO)
16249     {
16250       button_type = GD_TYPE_NORMAL_BUTTON;
16251       checked = FALSE;
16252       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16253     }
16254     else
16255     {
16256       button_type = GD_TYPE_CHECK_BUTTON;
16257       checked = (gamebutton_info[i].setup_value != NULL ?
16258                  *gamebutton_info[i].setup_value : FALSE);
16259       event_mask = GD_EVENT_PRESSED;
16260     }
16261
16262     gi = CreateGadget(GDI_CUSTOM_ID, id,
16263                       GDI_IMAGE_ID, graphic,
16264                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16265                       GDI_X, base_x + x,
16266                       GDI_Y, base_y + y,
16267                       GDI_WIDTH, gfx->width,
16268                       GDI_HEIGHT, gfx->height,
16269                       GDI_TYPE, button_type,
16270                       GDI_STATE, GD_BUTTON_UNPRESSED,
16271                       GDI_CHECKED, checked,
16272                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16273                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16274                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16275                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16276                       GDI_DIRECT_DRAW, FALSE,
16277                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16278                       GDI_EVENT_MASK, event_mask,
16279                       GDI_CALLBACK_ACTION, HandleGameButtons,
16280                       GDI_END);
16281
16282     if (gi == NULL)
16283       Fail("cannot create gadget");
16284
16285     game_gadget[id] = gi;
16286   }
16287 }
16288
16289 void FreeGameButtons(void)
16290 {
16291   int i;
16292
16293   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16294     FreeGadget(game_gadget[i]);
16295 }
16296
16297 static void UnmapGameButtonsAtSamePosition(int id)
16298 {
16299   int i;
16300
16301   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16302     if (i != id &&
16303         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16304         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16305       UnmapGadget(game_gadget[i]);
16306 }
16307
16308 static void UnmapGameButtonsAtSamePosition_All(void)
16309 {
16310   if (setup.show_load_save_buttons)
16311   {
16312     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16313     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16314     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16315   }
16316   else if (setup.show_undo_redo_buttons)
16317   {
16318     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16319     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16320     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16321   }
16322   else
16323   {
16324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16325     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16326     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16327
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16329     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16330     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16331   }
16332 }
16333
16334 void MapLoadSaveButtons(void)
16335 {
16336   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16337   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16338
16339   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16340   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16341 }
16342
16343 void MapUndoRedoButtons(void)
16344 {
16345   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16346   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16347
16348   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16349   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16350 }
16351
16352 void ModifyPauseButtons(void)
16353 {
16354   static int ids[] =
16355   {
16356     GAME_CTRL_ID_PAUSE,
16357     GAME_CTRL_ID_PAUSE2,
16358     GAME_CTRL_ID_PANEL_PAUSE,
16359     GAME_CTRL_ID_TOUCH_PAUSE,
16360     -1
16361   };
16362   int i;
16363
16364   for (i = 0; ids[i] > -1; i++)
16365     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16366 }
16367
16368 static void MapGameButtonsExt(boolean on_tape)
16369 {
16370   int i;
16371
16372   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16373     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16374       MapGadget(game_gadget[i]);
16375
16376   UnmapGameButtonsAtSamePosition_All();
16377
16378   RedrawGameButtons();
16379 }
16380
16381 static void UnmapGameButtonsExt(boolean on_tape)
16382 {
16383   int i;
16384
16385   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16386     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16387       UnmapGadget(game_gadget[i]);
16388 }
16389
16390 static void RedrawGameButtonsExt(boolean on_tape)
16391 {
16392   int i;
16393
16394   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16395     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16396       RedrawGadget(game_gadget[i]);
16397 }
16398
16399 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16400 {
16401   if (gi == NULL)
16402     return;
16403
16404   gi->checked = state;
16405 }
16406
16407 static void RedrawSoundButtonGadget(int id)
16408 {
16409   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16410              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16411              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16412              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16413              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16414              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16415              id);
16416
16417   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16418   RedrawGadget(game_gadget[id2]);
16419 }
16420
16421 void MapGameButtons(void)
16422 {
16423   MapGameButtonsExt(FALSE);
16424 }
16425
16426 void UnmapGameButtons(void)
16427 {
16428   UnmapGameButtonsExt(FALSE);
16429 }
16430
16431 void RedrawGameButtons(void)
16432 {
16433   RedrawGameButtonsExt(FALSE);
16434 }
16435
16436 void MapGameButtonsOnTape(void)
16437 {
16438   MapGameButtonsExt(TRUE);
16439 }
16440
16441 void UnmapGameButtonsOnTape(void)
16442 {
16443   UnmapGameButtonsExt(TRUE);
16444 }
16445
16446 void RedrawGameButtonsOnTape(void)
16447 {
16448   RedrawGameButtonsExt(TRUE);
16449 }
16450
16451 static void GameUndoRedoExt(void)
16452 {
16453   ClearPlayerAction();
16454
16455   tape.pausing = TRUE;
16456
16457   RedrawPlayfield();
16458   UpdateAndDisplayGameControlValues();
16459
16460   DrawCompleteVideoDisplay();
16461   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16462   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16463   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16464
16465   ModifyPauseButtons();
16466
16467   BackToFront();
16468 }
16469
16470 static void GameUndo(int steps)
16471 {
16472   if (!CheckEngineSnapshotList())
16473     return;
16474
16475   int tape_property_bits = tape.property_bits;
16476
16477   LoadEngineSnapshot_Undo(steps);
16478
16479   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16480
16481   GameUndoRedoExt();
16482 }
16483
16484 static void GameRedo(int steps)
16485 {
16486   if (!CheckEngineSnapshotList())
16487     return;
16488
16489   int tape_property_bits = tape.property_bits;
16490
16491   LoadEngineSnapshot_Redo(steps);
16492
16493   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16494
16495   GameUndoRedoExt();
16496 }
16497
16498 static void HandleGameButtonsExt(int id, int button)
16499 {
16500   static boolean game_undo_executed = FALSE;
16501   int steps = BUTTON_STEPSIZE(button);
16502   boolean handle_game_buttons =
16503     (game_status == GAME_MODE_PLAYING ||
16504      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16505
16506   if (!handle_game_buttons)
16507     return;
16508
16509   switch (id)
16510   {
16511     case GAME_CTRL_ID_STOP:
16512     case GAME_CTRL_ID_PANEL_STOP:
16513     case GAME_CTRL_ID_TOUCH_STOP:
16514       if (game_status == GAME_MODE_MAIN)
16515         break;
16516
16517       if (tape.playing)
16518         TapeStop();
16519       else
16520         RequestQuitGame(FALSE);
16521
16522       break;
16523
16524     case GAME_CTRL_ID_PAUSE:
16525     case GAME_CTRL_ID_PAUSE2:
16526     case GAME_CTRL_ID_PANEL_PAUSE:
16527     case GAME_CTRL_ID_TOUCH_PAUSE:
16528       if (network.enabled && game_status == GAME_MODE_PLAYING)
16529       {
16530         if (tape.pausing)
16531           SendToServer_ContinuePlaying();
16532         else
16533           SendToServer_PausePlaying();
16534       }
16535       else
16536         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16537
16538       game_undo_executed = FALSE;
16539
16540       break;
16541
16542     case GAME_CTRL_ID_PLAY:
16543     case GAME_CTRL_ID_PANEL_PLAY:
16544       if (game_status == GAME_MODE_MAIN)
16545       {
16546         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16547       }
16548       else if (tape.pausing)
16549       {
16550         if (network.enabled)
16551           SendToServer_ContinuePlaying();
16552         else
16553           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16554       }
16555       break;
16556
16557     case GAME_CTRL_ID_UNDO:
16558       // Important: When using "save snapshot when collecting an item" mode,
16559       // load last (current) snapshot for first "undo" after pressing "pause"
16560       // (else the last-but-one snapshot would be loaded, because the snapshot
16561       // pointer already points to the last snapshot when pressing "pause",
16562       // which is fine for "every step/move" mode, but not for "every collect")
16563       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16564           !game_undo_executed)
16565         steps--;
16566
16567       game_undo_executed = TRUE;
16568
16569       GameUndo(steps);
16570       break;
16571
16572     case GAME_CTRL_ID_REDO:
16573       GameRedo(steps);
16574       break;
16575
16576     case GAME_CTRL_ID_SAVE:
16577       TapeQuickSave();
16578       break;
16579
16580     case GAME_CTRL_ID_LOAD:
16581       TapeQuickLoad();
16582       break;
16583
16584     case SOUND_CTRL_ID_MUSIC:
16585     case SOUND_CTRL_ID_PANEL_MUSIC:
16586       if (setup.sound_music)
16587       { 
16588         setup.sound_music = FALSE;
16589
16590         FadeMusic();
16591       }
16592       else if (audio.music_available)
16593       { 
16594         setup.sound = setup.sound_music = TRUE;
16595
16596         SetAudioMode(setup.sound);
16597
16598         if (game_status == GAME_MODE_PLAYING)
16599           PlayLevelMusic();
16600       }
16601
16602       RedrawSoundButtonGadget(id);
16603
16604       break;
16605
16606     case SOUND_CTRL_ID_LOOPS:
16607     case SOUND_CTRL_ID_PANEL_LOOPS:
16608       if (setup.sound_loops)
16609         setup.sound_loops = FALSE;
16610       else if (audio.loops_available)
16611       {
16612         setup.sound = setup.sound_loops = TRUE;
16613
16614         SetAudioMode(setup.sound);
16615       }
16616
16617       RedrawSoundButtonGadget(id);
16618
16619       break;
16620
16621     case SOUND_CTRL_ID_SIMPLE:
16622     case SOUND_CTRL_ID_PANEL_SIMPLE:
16623       if (setup.sound_simple)
16624         setup.sound_simple = FALSE;
16625       else if (audio.sound_available)
16626       {
16627         setup.sound = setup.sound_simple = TRUE;
16628
16629         SetAudioMode(setup.sound);
16630       }
16631
16632       RedrawSoundButtonGadget(id);
16633
16634       break;
16635
16636     default:
16637       break;
16638   }
16639 }
16640
16641 static void HandleGameButtons(struct GadgetInfo *gi)
16642 {
16643   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16644 }
16645
16646 void HandleSoundButtonKeys(Key key)
16647 {
16648   if (key == setup.shortcut.sound_simple)
16649     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16650   else if (key == setup.shortcut.sound_loops)
16651     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16652   else if (key == setup.shortcut.sound_music)
16653     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16654 }