added support for more than five digits for score on game panel
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_INVENTORY_COUNT ||
2685           nr == GAME_PANEL_SCORE ||
2686           nr == GAME_PANEL_HIGHSCORE ||
2687           nr == GAME_PANEL_TIME)
2688       {
2689         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2690
2691         if (use_dynamic_size)           // use dynamic number of digits
2692         {
2693           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2694                               nr == GAME_PANEL_INVENTORY_COUNT ||
2695                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2696           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2697                           nr == GAME_PANEL_INVENTORY_COUNT ||
2698                           nr == GAME_PANEL_TIME ? 1 : 2);
2699           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2700                        nr == GAME_PANEL_INVENTORY_COUNT ||
2701                        nr == GAME_PANEL_TIME ? 3 : 5);
2702           int size2 = size1 + size_add;
2703           int font1 = pos->font;
2704           int font2 = pos->font_alt;
2705
2706           size = (value < value_change ? size1 : size2);
2707           font = (value < value_change ? font1 : font2);
2708         }
2709       }
2710
2711       // correct text size if "digits" is zero or less
2712       if (size <= 0)
2713         size = strlen(int2str(value, size));
2714
2715       // dynamically correct text alignment
2716       pos->width = size * getFontWidth(font);
2717
2718       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2719                   int2str(value, size), font, mask_mode);
2720     }
2721     else if (type == TYPE_ELEMENT)
2722     {
2723       int element, graphic;
2724       Bitmap *src_bitmap;
2725       int src_x, src_y;
2726       int width, height;
2727       int dst_x = PANEL_XPOS(pos);
2728       int dst_y = PANEL_YPOS(pos);
2729
2730       if (value != EL_UNDEFINED && value != EL_EMPTY)
2731       {
2732         element = value;
2733         graphic = el2panelimg(value);
2734
2735 #if 0
2736         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2737               element, EL_NAME(element), size);
2738 #endif
2739
2740         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2741           size = TILESIZE;
2742
2743         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2744                               &src_x, &src_y);
2745
2746         width  = graphic_info[graphic].width  * size / TILESIZE;
2747         height = graphic_info[graphic].height * size / TILESIZE;
2748
2749         if (draw_masked)
2750           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2751                            dst_x, dst_y);
2752         else
2753           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2754                      dst_x, dst_y);
2755       }
2756     }
2757     else if (type == TYPE_GRAPHIC)
2758     {
2759       int graphic        = gpc->graphic;
2760       int graphic_active = gpc->graphic_active;
2761       Bitmap *src_bitmap;
2762       int src_x, src_y;
2763       int width, height;
2764       int dst_x = PANEL_XPOS(pos);
2765       int dst_y = PANEL_YPOS(pos);
2766       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2767                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2768
2769       if (graphic != IMG_UNDEFINED && !skip)
2770       {
2771         if (pos->style == STYLE_REVERSE)
2772           value = 100 - value;
2773
2774         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2775
2776         if (pos->direction & MV_HORIZONTAL)
2777         {
2778           width  = graphic_info[graphic_active].width * value / 100;
2779           height = graphic_info[graphic_active].height;
2780
2781           if (pos->direction == MV_LEFT)
2782           {
2783             src_x += graphic_info[graphic_active].width - width;
2784             dst_x += graphic_info[graphic_active].width - width;
2785           }
2786         }
2787         else
2788         {
2789           width  = graphic_info[graphic_active].width;
2790           height = graphic_info[graphic_active].height * value / 100;
2791
2792           if (pos->direction == MV_UP)
2793           {
2794             src_y += graphic_info[graphic_active].height - height;
2795             dst_y += graphic_info[graphic_active].height - height;
2796           }
2797         }
2798
2799         if (draw_masked)
2800           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2801                            dst_x, dst_y);
2802         else
2803           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2804                      dst_x, dst_y);
2805
2806         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2807
2808         if (pos->direction & MV_HORIZONTAL)
2809         {
2810           if (pos->direction == MV_RIGHT)
2811           {
2812             src_x += width;
2813             dst_x += width;
2814           }
2815           else
2816           {
2817             dst_x = PANEL_XPOS(pos);
2818           }
2819
2820           width = graphic_info[graphic].width - width;
2821         }
2822         else
2823         {
2824           if (pos->direction == MV_DOWN)
2825           {
2826             src_y += height;
2827             dst_y += height;
2828           }
2829           else
2830           {
2831             dst_y = PANEL_YPOS(pos);
2832           }
2833
2834           height = graphic_info[graphic].height - height;
2835         }
2836
2837         if (draw_masked)
2838           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2839                            dst_x, dst_y);
2840         else
2841           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2842                      dst_x, dst_y);
2843       }
2844     }
2845     else if (type == TYPE_STRING)
2846     {
2847       boolean active = (value != 0);
2848       char *state_normal = "off";
2849       char *state_active = "on";
2850       char *state = (active ? state_active : state_normal);
2851       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2852                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2853                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2854                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2855
2856       if (nr == GAME_PANEL_GRAVITY_STATE)
2857       {
2858         int font1 = pos->font;          // (used for normal state)
2859         int font2 = pos->font_alt;      // (used for active state)
2860
2861         font = (active ? font2 : font1);
2862       }
2863
2864       if (s != NULL)
2865       {
2866         char *s_cut;
2867
2868         if (size <= 0)
2869         {
2870           // don't truncate output if "chars" is zero or less
2871           size = strlen(s);
2872
2873           // dynamically correct text alignment
2874           pos->width = size * getFontWidth(font);
2875         }
2876
2877         s_cut = getStringCopyN(s, size);
2878
2879         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2880                     s_cut, font, mask_mode);
2881
2882         free(s_cut);
2883       }
2884     }
2885
2886     redraw_mask |= REDRAW_DOOR_1;
2887   }
2888
2889   SetGameStatus(GAME_MODE_PLAYING);
2890 }
2891
2892 void UpdateAndDisplayGameControlValues(void)
2893 {
2894   if (tape.deactivate_display)
2895     return;
2896
2897   UpdateGameControlValues();
2898   DisplayGameControlValues();
2899 }
2900
2901 void UpdateGameDoorValues(void)
2902 {
2903   UpdateGameControlValues();
2904 }
2905
2906 void DrawGameDoorValues(void)
2907 {
2908   DisplayGameControlValues();
2909 }
2910
2911
2912 // ============================================================================
2913 // InitGameEngine()
2914 // ----------------------------------------------------------------------------
2915 // initialize game engine due to level / tape version number
2916 // ============================================================================
2917
2918 static void InitGameEngine(void)
2919 {
2920   int i, j, k, l, x, y;
2921
2922   // set game engine from tape file when re-playing, else from level file
2923   game.engine_version = (tape.playing ? tape.engine_version :
2924                          level.game_version);
2925
2926   // set single or multi-player game mode (needed for re-playing tapes)
2927   game.team_mode = setup.team_mode;
2928
2929   if (tape.playing)
2930   {
2931     int num_players = 0;
2932
2933     for (i = 0; i < MAX_PLAYERS; i++)
2934       if (tape.player_participates[i])
2935         num_players++;
2936
2937     // multi-player tapes contain input data for more than one player
2938     game.team_mode = (num_players > 1);
2939   }
2940
2941 #if 0
2942   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2943         level.game_version);
2944   Debug("game:init:level", "          tape.file_version   == %06d",
2945         tape.file_version);
2946   Debug("game:init:level", "          tape.game_version   == %06d",
2947         tape.game_version);
2948   Debug("game:init:level", "          tape.engine_version == %06d",
2949         tape.engine_version);
2950   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2951         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2952 #endif
2953
2954   // --------------------------------------------------------------------------
2955   // set flags for bugs and changes according to active game engine version
2956   // --------------------------------------------------------------------------
2957
2958   /*
2959     Summary of bugfix:
2960     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2961
2962     Bug was introduced in version:
2963     2.0.1
2964
2965     Bug was fixed in version:
2966     4.2.0.0
2967
2968     Description:
2969     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2970     but the property "can fall" was missing, which caused some levels to be
2971     unsolvable. This was fixed in version 4.2.0.0.
2972
2973     Affected levels/tapes:
2974     An example for a tape that was fixed by this bugfix is tape 029 from the
2975     level set "rnd_sam_bateman".
2976     The wrong behaviour will still be used for all levels or tapes that were
2977     created/recorded with it. An example for this is tape 023 from the level
2978     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2979   */
2980
2981   boolean use_amoeba_dropping_cannot_fall_bug =
2982     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2983       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2984      (tape.playing &&
2985       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2986       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2987
2988   /*
2989     Summary of bugfix/change:
2990     Fixed move speed of elements entering or leaving magic wall.
2991
2992     Fixed/changed in version:
2993     2.0.1
2994
2995     Description:
2996     Before 2.0.1, move speed of elements entering or leaving magic wall was
2997     twice as fast as it is now.
2998     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2999
3000     Affected levels/tapes:
3001     The first condition is generally needed for all levels/tapes before version
3002     2.0.1, which might use the old behaviour before it was changed; known tapes
3003     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3004     The second condition is an exception from the above case and is needed for
3005     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3006     above, but before it was known that this change would break tapes like the
3007     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3008     although the engine version while recording maybe was before 2.0.1. There
3009     are a lot of tapes that are affected by this exception, like tape 006 from
3010     the level set "rnd_conor_mancone".
3011   */
3012
3013   boolean use_old_move_stepsize_for_magic_wall =
3014     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3015      !(tape.playing &&
3016        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3017        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3018
3019   /*
3020     Summary of bugfix/change:
3021     Fixed handling for custom elements that change when pushed by the player.
3022
3023     Fixed/changed in version:
3024     3.1.0
3025
3026     Description:
3027     Before 3.1.0, custom elements that "change when pushing" changed directly
3028     after the player started pushing them (until then handled in "DigField()").
3029     Since 3.1.0, these custom elements are not changed until the "pushing"
3030     move of the element is finished (now handled in "ContinueMoving()").
3031
3032     Affected levels/tapes:
3033     The first condition is generally needed for all levels/tapes before version
3034     3.1.0, which might use the old behaviour before it was changed; known tapes
3035     that are affected are some tapes from the level set "Walpurgis Gardens" by
3036     Jamie Cullen.
3037     The second condition is an exception from the above case and is needed for
3038     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3039     above (including some development versions of 3.1.0), but before it was
3040     known that this change would break tapes like the above and was fixed in
3041     3.1.1, so that the changed behaviour was active although the engine version
3042     while recording maybe was before 3.1.0. There is at least one tape that is
3043     affected by this exception, which is the tape for the one-level set "Bug
3044     Machine" by Juergen Bonhagen.
3045   */
3046
3047   game.use_change_when_pushing_bug =
3048     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3049      !(tape.playing &&
3050        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3051        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for blocking the field the player leaves when moving.
3056
3057     Fixed/changed in version:
3058     3.1.1
3059
3060     Description:
3061     Before 3.1.1, when "block last field when moving" was enabled, the field
3062     the player is leaving when moving was blocked for the time of the move,
3063     and was directly unblocked afterwards. This resulted in the last field
3064     being blocked for exactly one less than the number of frames of one player
3065     move. Additionally, even when blocking was disabled, the last field was
3066     blocked for exactly one frame.
3067     Since 3.1.1, due to changes in player movement handling, the last field
3068     is not blocked at all when blocking is disabled. When blocking is enabled,
3069     the last field is blocked for exactly the number of frames of one player
3070     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3071     last field is blocked for exactly one more than the number of frames of
3072     one player move.
3073
3074     Affected levels/tapes:
3075     (!!! yet to be determined -- probably many !!!)
3076   */
3077
3078   game.use_block_last_field_bug =
3079     (game.engine_version < VERSION_IDENT(3,1,1,0));
3080
3081   /* various special flags and settings for native Emerald Mine game engine */
3082
3083   game_em.use_single_button =
3084     (game.engine_version > VERSION_IDENT(4,0,0,2));
3085
3086   game_em.use_snap_key_bug =
3087     (game.engine_version < VERSION_IDENT(4,0,1,0));
3088
3089   game_em.use_random_bug =
3090     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3091
3092   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3093
3094   game_em.use_old_explosions            = use_old_em_engine;
3095   game_em.use_old_android               = use_old_em_engine;
3096   game_em.use_old_push_elements         = use_old_em_engine;
3097   game_em.use_old_push_into_acid        = use_old_em_engine;
3098
3099   game_em.use_wrap_around               = !use_old_em_engine;
3100
3101   // --------------------------------------------------------------------------
3102
3103   // set maximal allowed number of custom element changes per game frame
3104   game.max_num_changes_per_frame = 1;
3105
3106   // default scan direction: scan playfield from top/left to bottom/right
3107   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3108
3109   // dynamically adjust element properties according to game engine version
3110   InitElementPropertiesEngine(game.engine_version);
3111
3112   // ---------- initialize special element properties -------------------------
3113
3114   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3115   if (use_amoeba_dropping_cannot_fall_bug)
3116     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3117
3118   // ---------- initialize player's initial move delay ------------------------
3119
3120   // dynamically adjust player properties according to level information
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     game.initial_move_delay_value[i] =
3123       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3124
3125   // dynamically adjust player properties according to game engine version
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     game.initial_move_delay[i] =
3128       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3129        game.initial_move_delay_value[i] : 0);
3130
3131   // ---------- initialize player's initial push delay ------------------------
3132
3133   // dynamically adjust player properties according to game engine version
3134   game.initial_push_delay_value =
3135     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3136
3137   // ---------- initialize changing elements ----------------------------------
3138
3139   // initialize changing elements information
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141   {
3142     struct ElementInfo *ei = &element_info[i];
3143
3144     // this pointer might have been changed in the level editor
3145     ei->change = &ei->change_page[0];
3146
3147     if (!IS_CUSTOM_ELEMENT(i))
3148     {
3149       ei->change->target_element = EL_EMPTY_SPACE;
3150       ei->change->delay_fixed = 0;
3151       ei->change->delay_random = 0;
3152       ei->change->delay_frames = 1;
3153     }
3154
3155     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3156     {
3157       ei->has_change_event[j] = FALSE;
3158
3159       ei->event_page_nr[j] = 0;
3160       ei->event_page[j] = &ei->change_page[0];
3161     }
3162   }
3163
3164   // add changing elements from pre-defined list
3165   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3166   {
3167     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3168     struct ElementInfo *ei = &element_info[ch_delay->element];
3169
3170     ei->change->target_element       = ch_delay->target_element;
3171     ei->change->delay_fixed          = ch_delay->change_delay;
3172
3173     ei->change->pre_change_function  = ch_delay->pre_change_function;
3174     ei->change->change_function      = ch_delay->change_function;
3175     ei->change->post_change_function = ch_delay->post_change_function;
3176
3177     ei->change->can_change = TRUE;
3178     ei->change->can_change_or_has_action = TRUE;
3179
3180     ei->has_change_event[CE_DELAY] = TRUE;
3181
3182     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3183     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3184   }
3185
3186   // ---------- initialize internal run-time variables ------------------------
3187
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       ei->change_page[j].can_change_or_has_action =
3195         (ei->change_page[j].can_change |
3196          ei->change_page[j].has_action);
3197     }
3198   }
3199
3200   // add change events from custom element configuration
3201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3202   {
3203     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3204
3205     for (j = 0; j < ei->num_change_pages; j++)
3206     {
3207       if (!ei->change_page[j].can_change_or_has_action)
3208         continue;
3209
3210       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3211       {
3212         // only add event page for the first page found with this event
3213         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3214         {
3215           ei->has_change_event[k] = TRUE;
3216
3217           ei->event_page_nr[k] = j;
3218           ei->event_page[k] = &ei->change_page[j];
3219         }
3220       }
3221     }
3222   }
3223
3224   // ---------- initialize reference elements in change conditions ------------
3225
3226   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3227   {
3228     int element = EL_CUSTOM_START + i;
3229     struct ElementInfo *ei = &element_info[element];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       int trigger_element = ei->change_page[j].initial_trigger_element;
3234
3235       if (trigger_element >= EL_PREV_CE_8 &&
3236           trigger_element <= EL_NEXT_CE_8)
3237         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3238
3239       ei->change_page[j].trigger_element = trigger_element;
3240     }
3241   }
3242
3243   // ---------- initialize run-time trigger player and element ----------------
3244
3245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246   {
3247     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248
3249     for (j = 0; j < ei->num_change_pages; j++)
3250     {
3251       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3252       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3253       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3254       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3255       ei->change_page[j].actual_trigger_ce_value = 0;
3256       ei->change_page[j].actual_trigger_ce_score = 0;
3257     }
3258   }
3259
3260   // ---------- initialize trigger events -------------------------------------
3261
3262   // initialize trigger events information
3263   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3264     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3265       trigger_events[i][j] = FALSE;
3266
3267   // add trigger events from element change event properties
3268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       if (!ei->change_page[j].can_change_or_has_action)
3275         continue;
3276
3277       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3278       {
3279         int trigger_element = ei->change_page[j].trigger_element;
3280
3281         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3282         {
3283           if (ei->change_page[j].has_event[k])
3284           {
3285             if (IS_GROUP_ELEMENT(trigger_element))
3286             {
3287               struct ElementGroupInfo *group =
3288                 element_info[trigger_element].group;
3289
3290               for (l = 0; l < group->num_elements_resolved; l++)
3291                 trigger_events[group->element_resolved[l]][k] = TRUE;
3292             }
3293             else if (trigger_element == EL_ANY_ELEMENT)
3294               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3295                 trigger_events[l][k] = TRUE;
3296             else
3297               trigger_events[trigger_element][k] = TRUE;
3298           }
3299         }
3300       }
3301     }
3302   }
3303
3304   // ---------- initialize push delay -----------------------------------------
3305
3306   // initialize push delay values to default
3307   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3308   {
3309     if (!IS_CUSTOM_ELEMENT(i))
3310     {
3311       // set default push delay values (corrected since version 3.0.7-1)
3312       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3313       {
3314         element_info[i].push_delay_fixed = 2;
3315         element_info[i].push_delay_random = 8;
3316       }
3317       else
3318       {
3319         element_info[i].push_delay_fixed = 8;
3320         element_info[i].push_delay_random = 8;
3321       }
3322     }
3323   }
3324
3325   // set push delay value for certain elements from pre-defined list
3326   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3327   {
3328     int e = push_delay_list[i].element;
3329
3330     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3331     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3332   }
3333
3334   // set push delay value for Supaplex elements for newer engine versions
3335   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3336   {
3337     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338     {
3339       if (IS_SP_ELEMENT(i))
3340       {
3341         // set SP push delay to just enough to push under a falling zonk
3342         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3343
3344         element_info[i].push_delay_fixed  = delay;
3345         element_info[i].push_delay_random = 0;
3346       }
3347     }
3348   }
3349
3350   // ---------- initialize move stepsize --------------------------------------
3351
3352   // initialize move stepsize values to default
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     if (!IS_CUSTOM_ELEMENT(i))
3355       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3356
3357   // set move stepsize value for certain elements from pre-defined list
3358   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = move_stepsize_list[i].element;
3361
3362     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3363
3364     // set move stepsize value for certain elements for older engine versions
3365     if (use_old_move_stepsize_for_magic_wall)
3366     {
3367       if (e == EL_MAGIC_WALL_FILLING ||
3368           e == EL_MAGIC_WALL_EMPTYING ||
3369           e == EL_BD_MAGIC_WALL_FILLING ||
3370           e == EL_BD_MAGIC_WALL_EMPTYING)
3371         element_info[e].move_stepsize *= 2;
3372     }
3373   }
3374
3375   // ---------- initialize collect score --------------------------------------
3376
3377   // initialize collect score values for custom elements from initial value
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379     if (IS_CUSTOM_ELEMENT(i))
3380       element_info[i].collect_score = element_info[i].collect_score_initial;
3381
3382   // ---------- initialize collect count --------------------------------------
3383
3384   // initialize collect count values for non-custom elements
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].collect_count_initial = 0;
3388
3389   // add collect count values for all elements from pre-defined list
3390   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3391     element_info[collect_count_list[i].element].collect_count_initial =
3392       collect_count_list[i].count;
3393
3394   // ---------- initialize access direction -----------------------------------
3395
3396   // initialize access direction values to default (access from every side)
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3400
3401   // set access direction value for certain elements from pre-defined list
3402   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3403     element_info[access_direction_list[i].element].access_direction =
3404       access_direction_list[i].direction;
3405
3406   // ---------- initialize explosion content ----------------------------------
3407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3408   {
3409     if (IS_CUSTOM_ELEMENT(i))
3410       continue;
3411
3412     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3413     {
3414       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3415
3416       element_info[i].content.e[x][y] =
3417         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3418          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3419          i == EL_PLAYER_3 ? EL_EMERALD :
3420          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3421          i == EL_MOLE ? EL_EMERALD_RED :
3422          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3423          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3424          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3425          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3426          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3427          i == EL_WALL_EMERALD ? EL_EMERALD :
3428          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3429          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3430          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3431          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3432          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3433          i == EL_WALL_PEARL ? EL_PEARL :
3434          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3435          EL_EMPTY);
3436     }
3437   }
3438
3439   // ---------- initialize recursion detection --------------------------------
3440   recursion_loop_depth = 0;
3441   recursion_loop_detected = FALSE;
3442   recursion_loop_element = EL_UNDEFINED;
3443
3444   // ---------- initialize graphics engine ------------------------------------
3445   game.scroll_delay_value =
3446     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3447      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3448      !setup.forced_scroll_delay           ? 0 :
3449      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3450   game.scroll_delay_value =
3451     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3452
3453   // ---------- initialize game engine snapshots ------------------------------
3454   for (i = 0; i < MAX_PLAYERS; i++)
3455     game.snapshot.last_action[i] = 0;
3456   game.snapshot.changed_action = FALSE;
3457   game.snapshot.collected_item = FALSE;
3458   game.snapshot.mode =
3459     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3460      SNAPSHOT_MODE_EVERY_STEP :
3461      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3462      SNAPSHOT_MODE_EVERY_MOVE :
3463      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3464      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3465   game.snapshot.save_snapshot = FALSE;
3466
3467   // ---------- initialize level time for Supaplex engine ---------------------
3468   // Supaplex levels with time limit currently unsupported -- should be added
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3470     level.time = 0;
3471
3472   // ---------- initialize flags for handling game actions --------------------
3473
3474   // set flags for game actions to default values
3475   game.use_key_actions = TRUE;
3476   game.use_mouse_actions = FALSE;
3477
3478   // when using Mirror Magic game engine, handle mouse events only
3479   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3480   {
3481     game.use_key_actions = FALSE;
3482     game.use_mouse_actions = TRUE;
3483   }
3484
3485   // check for custom elements with mouse click events
3486   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3487   {
3488     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3489     {
3490       int element = EL_CUSTOM_START + i;
3491
3492       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3493           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3494           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3495           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3496         game.use_mouse_actions = TRUE;
3497     }
3498   }
3499 }
3500
3501 static int get_num_special_action(int element, int action_first,
3502                                   int action_last)
3503 {
3504   int num_special_action = 0;
3505   int i, j;
3506
3507   for (i = action_first; i <= action_last; i++)
3508   {
3509     boolean found = FALSE;
3510
3511     for (j = 0; j < NUM_DIRECTIONS; j++)
3512       if (el_act_dir2img(element, i, j) !=
3513           el_act_dir2img(element, ACTION_DEFAULT, j))
3514         found = TRUE;
3515
3516     if (found)
3517       num_special_action++;
3518     else
3519       break;
3520   }
3521
3522   return num_special_action;
3523 }
3524
3525
3526 // ============================================================================
3527 // InitGame()
3528 // ----------------------------------------------------------------------------
3529 // initialize and start new game
3530 // ============================================================================
3531
3532 #if DEBUG_INIT_PLAYER
3533 static void DebugPrintPlayerStatus(char *message)
3534 {
3535   int i;
3536
3537   if (!options.debug)
3538     return;
3539
3540   Debug("game:init:player", "%s:", message);
3541
3542   for (i = 0; i < MAX_PLAYERS; i++)
3543   {
3544     struct PlayerInfo *player = &stored_player[i];
3545
3546     Debug("game:init:player",
3547           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3548           i + 1,
3549           player->present,
3550           player->connected,
3551           player->connected_locally,
3552           player->connected_network,
3553           player->active,
3554           (local_player == player ? " (local player)" : ""));
3555   }
3556 }
3557 #endif
3558
3559 void InitGame(void)
3560 {
3561   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3562   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3563   int fade_mask = REDRAW_FIELD;
3564
3565   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3566   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3567   int initial_move_dir = MV_DOWN;
3568   int i, j, x, y;
3569
3570   // required here to update video display before fading (FIX THIS)
3571   DrawMaskedBorder(REDRAW_DOOR_2);
3572
3573   if (!game.restart_level)
3574     CloseDoor(DOOR_CLOSE_1);
3575
3576   SetGameStatus(GAME_MODE_PLAYING);
3577
3578   if (level_editor_test_game)
3579     FadeSkipNextFadeOut();
3580   else
3581     FadeSetEnterScreen();
3582
3583   if (CheckFadeAll())
3584     fade_mask = REDRAW_ALL;
3585
3586   FadeLevelSoundsAndMusic();
3587
3588   ExpireSoundLoops(TRUE);
3589
3590   FadeOut(fade_mask);
3591
3592   if (level_editor_test_game)
3593     FadeSkipNextFadeIn();
3594
3595   // needed if different viewport properties defined for playing
3596   ChangeViewportPropertiesIfNeeded();
3597
3598   ClearField();
3599
3600   DrawCompleteVideoDisplay();
3601
3602   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3603
3604   InitGameEngine();
3605   InitGameControlValues();
3606
3607   if (tape.recording)
3608   {
3609     // initialize tape actions from game when recording tape
3610     tape.use_key_actions   = game.use_key_actions;
3611     tape.use_mouse_actions = game.use_mouse_actions;
3612
3613     // initialize visible playfield size when recording tape (for team mode)
3614     tape.scr_fieldx = SCR_FIELDX;
3615     tape.scr_fieldy = SCR_FIELDY;
3616   }
3617
3618   // don't play tapes over network
3619   network_playing = (network.enabled && !tape.playing);
3620
3621   for (i = 0; i < MAX_PLAYERS; i++)
3622   {
3623     struct PlayerInfo *player = &stored_player[i];
3624
3625     player->index_nr = i;
3626     player->index_bit = (1 << i);
3627     player->element_nr = EL_PLAYER_1 + i;
3628
3629     player->present = FALSE;
3630     player->active = FALSE;
3631     player->mapped = FALSE;
3632
3633     player->killed = FALSE;
3634     player->reanimated = FALSE;
3635     player->buried = FALSE;
3636
3637     player->action = 0;
3638     player->effective_action = 0;
3639     player->programmed_action = 0;
3640     player->snap_action = 0;
3641
3642     player->mouse_action.lx = 0;
3643     player->mouse_action.ly = 0;
3644     player->mouse_action.button = 0;
3645     player->mouse_action.button_hint = 0;
3646
3647     player->effective_mouse_action.lx = 0;
3648     player->effective_mouse_action.ly = 0;
3649     player->effective_mouse_action.button = 0;
3650     player->effective_mouse_action.button_hint = 0;
3651
3652     for (j = 0; j < MAX_NUM_KEYS; j++)
3653       player->key[j] = FALSE;
3654
3655     player->num_white_keys = 0;
3656
3657     player->dynabomb_count = 0;
3658     player->dynabomb_size = 1;
3659     player->dynabombs_left = 0;
3660     player->dynabomb_xl = FALSE;
3661
3662     player->MovDir = initial_move_dir;
3663     player->MovPos = 0;
3664     player->GfxPos = 0;
3665     player->GfxDir = initial_move_dir;
3666     player->GfxAction = ACTION_DEFAULT;
3667     player->Frame = 0;
3668     player->StepFrame = 0;
3669
3670     player->initial_element = player->element_nr;
3671     player->artwork_element =
3672       (level.use_artwork_element[i] ? level.artwork_element[i] :
3673        player->element_nr);
3674     player->use_murphy = FALSE;
3675
3676     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3677     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3678
3679     player->gravity = level.initial_player_gravity[i];
3680
3681     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3682
3683     player->actual_frame_counter = 0;
3684
3685     player->step_counter = 0;
3686
3687     player->last_move_dir = initial_move_dir;
3688
3689     player->is_active = FALSE;
3690
3691     player->is_waiting = FALSE;
3692     player->is_moving = FALSE;
3693     player->is_auto_moving = FALSE;
3694     player->is_digging = FALSE;
3695     player->is_snapping = FALSE;
3696     player->is_collecting = FALSE;
3697     player->is_pushing = FALSE;
3698     player->is_switching = FALSE;
3699     player->is_dropping = FALSE;
3700     player->is_dropping_pressed = FALSE;
3701
3702     player->is_bored = FALSE;
3703     player->is_sleeping = FALSE;
3704
3705     player->was_waiting = TRUE;
3706     player->was_moving = FALSE;
3707     player->was_snapping = FALSE;
3708     player->was_dropping = FALSE;
3709
3710     player->force_dropping = FALSE;
3711
3712     player->frame_counter_bored = -1;
3713     player->frame_counter_sleeping = -1;
3714
3715     player->anim_delay_counter = 0;
3716     player->post_delay_counter = 0;
3717
3718     player->dir_waiting = initial_move_dir;
3719     player->action_waiting = ACTION_DEFAULT;
3720     player->last_action_waiting = ACTION_DEFAULT;
3721     player->special_action_bored = ACTION_DEFAULT;
3722     player->special_action_sleeping = ACTION_DEFAULT;
3723
3724     player->switch_x = -1;
3725     player->switch_y = -1;
3726
3727     player->drop_x = -1;
3728     player->drop_y = -1;
3729
3730     player->show_envelope = 0;
3731
3732     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3733
3734     player->push_delay       = -1;      // initialized when pushing starts
3735     player->push_delay_value = game.initial_push_delay_value;
3736
3737     player->drop_delay = 0;
3738     player->drop_pressed_delay = 0;
3739
3740     player->last_jx = -1;
3741     player->last_jy = -1;
3742     player->jx = -1;
3743     player->jy = -1;
3744
3745     player->shield_normal_time_left = 0;
3746     player->shield_deadly_time_left = 0;
3747
3748     player->last_removed_element = EL_UNDEFINED;
3749
3750     player->inventory_infinite_element = EL_UNDEFINED;
3751     player->inventory_size = 0;
3752
3753     if (level.use_initial_inventory[i])
3754     {
3755       for (j = 0; j < level.initial_inventory_size[i]; j++)
3756       {
3757         int element = level.initial_inventory_content[i][j];
3758         int collect_count = element_info[element].collect_count_initial;
3759         int k;
3760
3761         if (!IS_CUSTOM_ELEMENT(element))
3762           collect_count = 1;
3763
3764         if (collect_count == 0)
3765           player->inventory_infinite_element = element;
3766         else
3767           for (k = 0; k < collect_count; k++)
3768             if (player->inventory_size < MAX_INVENTORY_SIZE)
3769               player->inventory_element[player->inventory_size++] = element;
3770       }
3771     }
3772
3773     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3774     SnapField(player, 0, 0);
3775
3776     map_player_action[i] = i;
3777   }
3778
3779   network_player_action_received = FALSE;
3780
3781   // initial null action
3782   if (network_playing)
3783     SendToServer_MovePlayer(MV_NONE);
3784
3785   FrameCounter = 0;
3786   TimeFrames = 0;
3787   TimePlayed = 0;
3788   TimeLeft = level.time;
3789   TapeTime = 0;
3790
3791   ScreenMovDir = MV_NONE;
3792   ScreenMovPos = 0;
3793   ScreenGfxPos = 0;
3794
3795   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3796
3797   game.robot_wheel_x = -1;
3798   game.robot_wheel_y = -1;
3799
3800   game.exit_x = -1;
3801   game.exit_y = -1;
3802
3803   game.all_players_gone = FALSE;
3804
3805   game.LevelSolved = FALSE;
3806   game.GameOver = FALSE;
3807
3808   game.GamePlayed = !tape.playing;
3809
3810   game.LevelSolved_GameWon = FALSE;
3811   game.LevelSolved_GameEnd = FALSE;
3812   game.LevelSolved_SaveTape = FALSE;
3813   game.LevelSolved_SaveScore = FALSE;
3814
3815   game.LevelSolved_CountingTime = 0;
3816   game.LevelSolved_CountingScore = 0;
3817   game.LevelSolved_CountingHealth = 0;
3818
3819   game.panel.active = TRUE;
3820
3821   game.no_time_limit = (level.time == 0);
3822
3823   game.yamyam_content_nr = 0;
3824   game.robot_wheel_active = FALSE;
3825   game.magic_wall_active = FALSE;
3826   game.magic_wall_time_left = 0;
3827   game.light_time_left = 0;
3828   game.timegate_time_left = 0;
3829   game.switchgate_pos = 0;
3830   game.wind_direction = level.wind_direction_initial;
3831
3832   game.time_final = 0;
3833   game.score_time_final = 0;
3834
3835   game.score = 0;
3836   game.score_final = 0;
3837
3838   game.health = MAX_HEALTH;
3839   game.health_final = MAX_HEALTH;
3840
3841   game.gems_still_needed = level.gems_needed;
3842   game.sokoban_fields_still_needed = 0;
3843   game.sokoban_objects_still_needed = 0;
3844   game.lights_still_needed = 0;
3845   game.players_still_needed = 0;
3846   game.friends_still_needed = 0;
3847
3848   game.lenses_time_left = 0;
3849   game.magnify_time_left = 0;
3850
3851   game.ball_active = level.ball_active_initial;
3852   game.ball_content_nr = 0;
3853
3854   game.explosions_delayed = TRUE;
3855
3856   game.envelope_active = FALSE;
3857
3858   // special case: set custom artwork setting to initial value
3859   game.use_masked_elements = game.use_masked_elements_initial;
3860
3861   for (i = 0; i < NUM_BELTS; i++)
3862   {
3863     game.belt_dir[i] = MV_NONE;
3864     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3865   }
3866
3867   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3868     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3869
3870 #if DEBUG_INIT_PLAYER
3871   DebugPrintPlayerStatus("Player status at level initialization");
3872 #endif
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     Tile[x][y] = Last[x][y] = level.field[x][y];
3877     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3878     ChangeDelay[x][y] = 0;
3879     ChangePage[x][y] = -1;
3880     CustomValue[x][y] = 0;              // initialized in InitField()
3881     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3882     AmoebaNr[x][y] = 0;
3883     WasJustMoving[x][y] = 0;
3884     WasJustFalling[x][y] = 0;
3885     CheckCollision[x][y] = 0;
3886     CheckImpact[x][y] = 0;
3887     Stop[x][y] = FALSE;
3888     Pushed[x][y] = FALSE;
3889
3890     ChangeCount[x][y] = 0;
3891     ChangeEvent[x][y] = -1;
3892
3893     ExplodePhase[x][y] = 0;
3894     ExplodeDelay[x][y] = 0;
3895     ExplodeField[x][y] = EX_TYPE_NONE;
3896
3897     RunnerVisit[x][y] = 0;
3898     PlayerVisit[x][y] = 0;
3899
3900     GfxFrame[x][y] = 0;
3901     GfxRandom[x][y] = INIT_GFX_RANDOM();
3902     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3903     GfxElement[x][y] = EL_UNDEFINED;
3904     GfxElementEmpty[x][y] = EL_EMPTY;
3905     GfxAction[x][y] = ACTION_DEFAULT;
3906     GfxDir[x][y] = MV_NONE;
3907     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3908   }
3909
3910   SCAN_PLAYFIELD(x, y)
3911   {
3912     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3913       emulate_bd = FALSE;
3914     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3915       emulate_sp = FALSE;
3916
3917     InitField(x, y, TRUE);
3918
3919     ResetGfxAnimation(x, y);
3920   }
3921
3922   InitBeltMovement();
3923
3924   for (i = 0; i < MAX_PLAYERS; i++)
3925   {
3926     struct PlayerInfo *player = &stored_player[i];
3927
3928     // set number of special actions for bored and sleeping animation
3929     player->num_special_action_bored =
3930       get_num_special_action(player->artwork_element,
3931                              ACTION_BORING_1, ACTION_BORING_LAST);
3932     player->num_special_action_sleeping =
3933       get_num_special_action(player->artwork_element,
3934                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3935   }
3936
3937   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3938                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3939
3940   // initialize type of slippery elements
3941   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3942   {
3943     if (!IS_CUSTOM_ELEMENT(i))
3944     {
3945       // default: elements slip down either to the left or right randomly
3946       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3947
3948       // SP style elements prefer to slip down on the left side
3949       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3950         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3951
3952       // BD style elements prefer to slip down on the left side
3953       if (game.emulation == EMU_BOULDERDASH)
3954         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3955     }
3956   }
3957
3958   // initialize explosion and ignition delay
3959   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3960   {
3961     if (!IS_CUSTOM_ELEMENT(i))
3962     {
3963       int num_phase = 8;
3964       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3965                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3966                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3967       int last_phase = (num_phase + 1) * delay;
3968       int half_phase = (num_phase / 2) * delay;
3969
3970       element_info[i].explosion_delay = last_phase - 1;
3971       element_info[i].ignition_delay = half_phase;
3972
3973       if (i == EL_BLACK_ORB)
3974         element_info[i].ignition_delay = 1;
3975     }
3976   }
3977
3978   // correct non-moving belts to start moving left
3979   for (i = 0; i < NUM_BELTS; i++)
3980     if (game.belt_dir[i] == MV_NONE)
3981       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3982
3983 #if USE_NEW_PLAYER_ASSIGNMENTS
3984   // use preferred player also in local single-player mode
3985   if (!network.enabled && !game.team_mode)
3986   {
3987     int new_index_nr = setup.network_player_nr;
3988
3989     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3990     {
3991       for (i = 0; i < MAX_PLAYERS; i++)
3992         stored_player[i].connected_locally = FALSE;
3993
3994       stored_player[new_index_nr].connected_locally = TRUE;
3995     }
3996   }
3997
3998   for (i = 0; i < MAX_PLAYERS; i++)
3999   {
4000     stored_player[i].connected = FALSE;
4001
4002     // in network game mode, the local player might not be the first player
4003     if (stored_player[i].connected_locally)
4004       local_player = &stored_player[i];
4005   }
4006
4007   if (!network.enabled)
4008     local_player->connected = TRUE;
4009
4010   if (tape.playing)
4011   {
4012     for (i = 0; i < MAX_PLAYERS; i++)
4013       stored_player[i].connected = tape.player_participates[i];
4014   }
4015   else if (network.enabled)
4016   {
4017     // add team mode players connected over the network (needed for correct
4018     // assignment of player figures from level to locally playing players)
4019
4020     for (i = 0; i < MAX_PLAYERS; i++)
4021       if (stored_player[i].connected_network)
4022         stored_player[i].connected = TRUE;
4023   }
4024   else if (game.team_mode)
4025   {
4026     // try to guess locally connected team mode players (needed for correct
4027     // assignment of player figures from level to locally playing players)
4028
4029     for (i = 0; i < MAX_PLAYERS; i++)
4030       if (setup.input[i].use_joystick ||
4031           setup.input[i].key.left != KSYM_UNDEFINED)
4032         stored_player[i].connected = TRUE;
4033   }
4034
4035 #if DEBUG_INIT_PLAYER
4036   DebugPrintPlayerStatus("Player status after level initialization");
4037 #endif
4038
4039 #if DEBUG_INIT_PLAYER
4040   Debug("game:init:player", "Reassigning players ...");
4041 #endif
4042
4043   // check if any connected player was not found in playfield
4044   for (i = 0; i < MAX_PLAYERS; i++)
4045   {
4046     struct PlayerInfo *player = &stored_player[i];
4047
4048     if (player->connected && !player->present)
4049     {
4050       struct PlayerInfo *field_player = NULL;
4051
4052 #if DEBUG_INIT_PLAYER
4053       Debug("game:init:player",
4054             "- looking for field player for player %d ...", i + 1);
4055 #endif
4056
4057       // assign first free player found that is present in the playfield
4058
4059       // first try: look for unmapped playfield player that is not connected
4060       for (j = 0; j < MAX_PLAYERS; j++)
4061         if (field_player == NULL &&
4062             stored_player[j].present &&
4063             !stored_player[j].mapped &&
4064             !stored_player[j].connected)
4065           field_player = &stored_player[j];
4066
4067       // second try: look for *any* unmapped playfield player
4068       for (j = 0; j < MAX_PLAYERS; j++)
4069         if (field_player == NULL &&
4070             stored_player[j].present &&
4071             !stored_player[j].mapped)
4072           field_player = &stored_player[j];
4073
4074       if (field_player != NULL)
4075       {
4076         int jx = field_player->jx, jy = field_player->jy;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- found player %d",
4080               field_player->index_nr + 1);
4081 #endif
4082
4083         player->present = FALSE;
4084         player->active = FALSE;
4085
4086         field_player->present = TRUE;
4087         field_player->active = TRUE;
4088
4089         /*
4090         player->initial_element = field_player->initial_element;
4091         player->artwork_element = field_player->artwork_element;
4092
4093         player->block_last_field       = field_player->block_last_field;
4094         player->block_delay_adjustment = field_player->block_delay_adjustment;
4095         */
4096
4097         StorePlayer[jx][jy] = field_player->element_nr;
4098
4099         field_player->jx = field_player->last_jx = jx;
4100         field_player->jy = field_player->last_jy = jy;
4101
4102         if (local_player == player)
4103           local_player = field_player;
4104
4105         map_player_action[field_player->index_nr] = i;
4106
4107         field_player->mapped = TRUE;
4108
4109 #if DEBUG_INIT_PLAYER
4110         Debug("game:init:player", "- map_player_action[%d] == %d",
4111               field_player->index_nr + 1, i + 1);
4112 #endif
4113       }
4114     }
4115
4116     if (player->connected && player->present)
4117       player->mapped = TRUE;
4118   }
4119
4120 #if DEBUG_INIT_PLAYER
4121   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4122 #endif
4123
4124 #else
4125
4126   // check if any connected player was not found in playfield
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128   {
4129     struct PlayerInfo *player = &stored_player[i];
4130
4131     if (player->connected && !player->present)
4132     {
4133       for (j = 0; j < MAX_PLAYERS; j++)
4134       {
4135         struct PlayerInfo *field_player = &stored_player[j];
4136         int jx = field_player->jx, jy = field_player->jy;
4137
4138         // assign first free player found that is present in the playfield
4139         if (field_player->present && !field_player->connected)
4140         {
4141           player->present = TRUE;
4142           player->active = TRUE;
4143
4144           field_player->present = FALSE;
4145           field_player->active = FALSE;
4146
4147           player->initial_element = field_player->initial_element;
4148           player->artwork_element = field_player->artwork_element;
4149
4150           player->block_last_field       = field_player->block_last_field;
4151           player->block_delay_adjustment = field_player->block_delay_adjustment;
4152
4153           StorePlayer[jx][jy] = player->element_nr;
4154
4155           player->jx = player->last_jx = jx;
4156           player->jy = player->last_jy = jy;
4157
4158           break;
4159         }
4160       }
4161     }
4162   }
4163 #endif
4164
4165 #if 0
4166   Debug("game:init:player", "local_player->present == %d",
4167         local_player->present);
4168 #endif
4169
4170   // set focus to local player for network games, else to all players
4171   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4172   game.centered_player_nr_next = game.centered_player_nr;
4173   game.set_centered_player = FALSE;
4174   game.set_centered_player_wrap = FALSE;
4175
4176   if (network_playing && tape.recording)
4177   {
4178     // store client dependent player focus when recording network games
4179     tape.centered_player_nr_next = game.centered_player_nr_next;
4180     tape.set_centered_player = TRUE;
4181   }
4182
4183   if (tape.playing)
4184   {
4185     // when playing a tape, eliminate all players who do not participate
4186
4187 #if USE_NEW_PLAYER_ASSIGNMENTS
4188
4189     if (!game.team_mode)
4190     {
4191       for (i = 0; i < MAX_PLAYERS; i++)
4192       {
4193         if (stored_player[i].active &&
4194             !tape.player_participates[map_player_action[i]])
4195         {
4196           struct PlayerInfo *player = &stored_player[i];
4197           int jx = player->jx, jy = player->jy;
4198
4199 #if DEBUG_INIT_PLAYER
4200           Debug("game:init:player", "Removing player %d at (%d, %d)",
4201                 i + 1, jx, jy);
4202 #endif
4203
4204           player->active = FALSE;
4205           StorePlayer[jx][jy] = 0;
4206           Tile[jx][jy] = EL_EMPTY;
4207         }
4208       }
4209     }
4210
4211 #else
4212
4213     for (i = 0; i < MAX_PLAYERS; i++)
4214     {
4215       if (stored_player[i].active &&
4216           !tape.player_participates[i])
4217       {
4218         struct PlayerInfo *player = &stored_player[i];
4219         int jx = player->jx, jy = player->jy;
4220
4221         player->active = FALSE;
4222         StorePlayer[jx][jy] = 0;
4223         Tile[jx][jy] = EL_EMPTY;
4224       }
4225     }
4226 #endif
4227   }
4228   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4229   {
4230     // when in single player mode, eliminate all but the local player
4231
4232     for (i = 0; i < MAX_PLAYERS; i++)
4233     {
4234       struct PlayerInfo *player = &stored_player[i];
4235
4236       if (player->active && player != local_player)
4237       {
4238         int jx = player->jx, jy = player->jy;
4239
4240         player->active = FALSE;
4241         player->present = FALSE;
4242
4243         StorePlayer[jx][jy] = 0;
4244         Tile[jx][jy] = EL_EMPTY;
4245       }
4246     }
4247   }
4248
4249   for (i = 0; i < MAX_PLAYERS; i++)
4250     if (stored_player[i].active)
4251       game.players_still_needed++;
4252
4253   if (level.solved_by_one_player)
4254     game.players_still_needed = 1;
4255
4256   // when recording the game, store which players take part in the game
4257   if (tape.recording)
4258   {
4259 #if USE_NEW_PLAYER_ASSIGNMENTS
4260     for (i = 0; i < MAX_PLAYERS; i++)
4261       if (stored_player[i].connected)
4262         tape.player_participates[i] = TRUE;
4263 #else
4264     for (i = 0; i < MAX_PLAYERS; i++)
4265       if (stored_player[i].active)
4266         tape.player_participates[i] = TRUE;
4267 #endif
4268   }
4269
4270 #if DEBUG_INIT_PLAYER
4271   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4272 #endif
4273
4274   if (BorderElement == EL_EMPTY)
4275   {
4276     SBX_Left = 0;
4277     SBX_Right = lev_fieldx - SCR_FIELDX;
4278     SBY_Upper = 0;
4279     SBY_Lower = lev_fieldy - SCR_FIELDY;
4280   }
4281   else
4282   {
4283     SBX_Left = -1;
4284     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4285     SBY_Upper = -1;
4286     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4287   }
4288
4289   if (full_lev_fieldx <= SCR_FIELDX)
4290     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4291   if (full_lev_fieldy <= SCR_FIELDY)
4292     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4293
4294   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4295     SBX_Left--;
4296   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4297     SBY_Upper--;
4298
4299   // if local player not found, look for custom element that might create
4300   // the player (make some assumptions about the right custom element)
4301   if (!local_player->present)
4302   {
4303     int start_x = 0, start_y = 0;
4304     int found_rating = 0;
4305     int found_element = EL_UNDEFINED;
4306     int player_nr = local_player->index_nr;
4307
4308     SCAN_PLAYFIELD(x, y)
4309     {
4310       int element = Tile[x][y];
4311       int content;
4312       int xx, yy;
4313       boolean is_player;
4314
4315       if (level.use_start_element[player_nr] &&
4316           level.start_element[player_nr] == element &&
4317           found_rating < 4)
4318       {
4319         start_x = x;
4320         start_y = y;
4321
4322         found_rating = 4;
4323         found_element = element;
4324       }
4325
4326       if (!IS_CUSTOM_ELEMENT(element))
4327         continue;
4328
4329       if (CAN_CHANGE(element))
4330       {
4331         for (i = 0; i < element_info[element].num_change_pages; i++)
4332         {
4333           // check for player created from custom element as single target
4334           content = element_info[element].change_page[i].target_element;
4335           is_player = IS_PLAYER_ELEMENT(content);
4336
4337           if (is_player && (found_rating < 3 ||
4338                             (found_rating == 3 && element < found_element)))
4339           {
4340             start_x = x;
4341             start_y = y;
4342
4343             found_rating = 3;
4344             found_element = element;
4345           }
4346         }
4347       }
4348
4349       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4350       {
4351         // check for player created from custom element as explosion content
4352         content = element_info[element].content.e[xx][yy];
4353         is_player = IS_PLAYER_ELEMENT(content);
4354
4355         if (is_player && (found_rating < 2 ||
4356                           (found_rating == 2 && element < found_element)))
4357         {
4358           start_x = x + xx - 1;
4359           start_y = y + yy - 1;
4360
4361           found_rating = 2;
4362           found_element = element;
4363         }
4364
4365         if (!CAN_CHANGE(element))
4366           continue;
4367
4368         for (i = 0; i < element_info[element].num_change_pages; i++)
4369         {
4370           // check for player created from custom element as extended target
4371           content =
4372             element_info[element].change_page[i].target_content.e[xx][yy];
4373
4374           is_player = IS_PLAYER_ELEMENT(content);
4375
4376           if (is_player && (found_rating < 1 ||
4377                             (found_rating == 1 && element < found_element)))
4378           {
4379             start_x = x + xx - 1;
4380             start_y = y + yy - 1;
4381
4382             found_rating = 1;
4383             found_element = element;
4384           }
4385         }
4386       }
4387     }
4388
4389     scroll_x = SCROLL_POSITION_X(start_x);
4390     scroll_y = SCROLL_POSITION_Y(start_y);
4391   }
4392   else
4393   {
4394     scroll_x = SCROLL_POSITION_X(local_player->jx);
4395     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4396   }
4397
4398   // !!! FIX THIS (START) !!!
4399   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4400   {
4401     InitGameEngine_EM();
4402   }
4403   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4404   {
4405     InitGameEngine_SP();
4406   }
4407   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4408   {
4409     InitGameEngine_MM();
4410   }
4411   else
4412   {
4413     DrawLevel(REDRAW_FIELD);
4414     DrawAllPlayers();
4415
4416     // after drawing the level, correct some elements
4417     if (game.timegate_time_left == 0)
4418       CloseAllOpenTimegates();
4419   }
4420
4421   // blit playfield from scroll buffer to normal back buffer for fading in
4422   BlitScreenToBitmap(backbuffer);
4423   // !!! FIX THIS (END) !!!
4424
4425   DrawMaskedBorder(fade_mask);
4426
4427   FadeIn(fade_mask);
4428
4429 #if 1
4430   // full screen redraw is required at this point in the following cases:
4431   // - special editor door undrawn when game was started from level editor
4432   // - drawing area (playfield) was changed and has to be removed completely
4433   redraw_mask = REDRAW_ALL;
4434   BackToFront();
4435 #endif
4436
4437   if (!game.restart_level)
4438   {
4439     // copy default game door content to main double buffer
4440
4441     // !!! CHECK AGAIN !!!
4442     SetPanelBackground();
4443     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4444     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4445   }
4446
4447   SetPanelBackground();
4448   SetDrawBackgroundMask(REDRAW_DOOR_1);
4449
4450   UpdateAndDisplayGameControlValues();
4451
4452   if (!game.restart_level)
4453   {
4454     UnmapGameButtons();
4455     UnmapTapeButtons();
4456
4457     FreeGameButtons();
4458     CreateGameButtons();
4459
4460     MapGameButtons();
4461     MapTapeButtons();
4462
4463     // copy actual game door content to door double buffer for OpenDoor()
4464     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4465
4466     OpenDoor(DOOR_OPEN_ALL);
4467
4468     KeyboardAutoRepeatOffUnlessAutoplay();
4469
4470 #if DEBUG_INIT_PLAYER
4471     DebugPrintPlayerStatus("Player status (final)");
4472 #endif
4473   }
4474
4475   UnmapAllGadgets();
4476
4477   MapGameButtons();
4478   MapTapeButtons();
4479
4480   if (!game.restart_level && !tape.playing)
4481   {
4482     LevelStats_incPlayed(level_nr);
4483
4484     SaveLevelSetup_SeriesInfo();
4485   }
4486
4487   game.restart_level = FALSE;
4488   game.restart_game_message = NULL;
4489
4490   game.request_active = FALSE;
4491   game.request_active_or_moving = FALSE;
4492
4493   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4494     InitGameActions_MM();
4495
4496   SaveEngineSnapshotToListInitial();
4497
4498   if (!game.restart_level)
4499   {
4500     PlaySound(SND_GAME_STARTING);
4501
4502     if (setup.sound_music)
4503       PlayLevelMusic();
4504   }
4505
4506   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4507 }
4508
4509 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4510                         int actual_player_x, int actual_player_y)
4511 {
4512   // this is used for non-R'n'D game engines to update certain engine values
4513
4514   // needed to determine if sounds are played within the visible screen area
4515   scroll_x = actual_scroll_x;
4516   scroll_y = actual_scroll_y;
4517
4518   // needed to get player position for "follow finger" playing input method
4519   local_player->jx = actual_player_x;
4520   local_player->jy = actual_player_y;
4521 }
4522
4523 void InitMovDir(int x, int y)
4524 {
4525   int i, element = Tile[x][y];
4526   static int xy[4][2] =
4527   {
4528     {  0, +1 },
4529     { +1,  0 },
4530     {  0, -1 },
4531     { -1,  0 }
4532   };
4533   static int direction[3][4] =
4534   {
4535     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4536     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4537     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4538   };
4539
4540   switch (element)
4541   {
4542     case EL_BUG_RIGHT:
4543     case EL_BUG_UP:
4544     case EL_BUG_LEFT:
4545     case EL_BUG_DOWN:
4546       Tile[x][y] = EL_BUG;
4547       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4548       break;
4549
4550     case EL_SPACESHIP_RIGHT:
4551     case EL_SPACESHIP_UP:
4552     case EL_SPACESHIP_LEFT:
4553     case EL_SPACESHIP_DOWN:
4554       Tile[x][y] = EL_SPACESHIP;
4555       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4556       break;
4557
4558     case EL_BD_BUTTERFLY_RIGHT:
4559     case EL_BD_BUTTERFLY_UP:
4560     case EL_BD_BUTTERFLY_LEFT:
4561     case EL_BD_BUTTERFLY_DOWN:
4562       Tile[x][y] = EL_BD_BUTTERFLY;
4563       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4564       break;
4565
4566     case EL_BD_FIREFLY_RIGHT:
4567     case EL_BD_FIREFLY_UP:
4568     case EL_BD_FIREFLY_LEFT:
4569     case EL_BD_FIREFLY_DOWN:
4570       Tile[x][y] = EL_BD_FIREFLY;
4571       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4572       break;
4573
4574     case EL_PACMAN_RIGHT:
4575     case EL_PACMAN_UP:
4576     case EL_PACMAN_LEFT:
4577     case EL_PACMAN_DOWN:
4578       Tile[x][y] = EL_PACMAN;
4579       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4580       break;
4581
4582     case EL_YAMYAM_LEFT:
4583     case EL_YAMYAM_RIGHT:
4584     case EL_YAMYAM_UP:
4585     case EL_YAMYAM_DOWN:
4586       Tile[x][y] = EL_YAMYAM;
4587       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4588       break;
4589
4590     case EL_SP_SNIKSNAK:
4591       MovDir[x][y] = MV_UP;
4592       break;
4593
4594     case EL_SP_ELECTRON:
4595       MovDir[x][y] = MV_LEFT;
4596       break;
4597
4598     case EL_MOLE_LEFT:
4599     case EL_MOLE_RIGHT:
4600     case EL_MOLE_UP:
4601     case EL_MOLE_DOWN:
4602       Tile[x][y] = EL_MOLE;
4603       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4604       break;
4605
4606     case EL_SPRING_LEFT:
4607     case EL_SPRING_RIGHT:
4608       Tile[x][y] = EL_SPRING;
4609       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4610       break;
4611
4612     default:
4613       if (IS_CUSTOM_ELEMENT(element))
4614       {
4615         struct ElementInfo *ei = &element_info[element];
4616         int move_direction_initial = ei->move_direction_initial;
4617         int move_pattern = ei->move_pattern;
4618
4619         if (move_direction_initial == MV_START_PREVIOUS)
4620         {
4621           if (MovDir[x][y] != MV_NONE)
4622             return;
4623
4624           move_direction_initial = MV_START_AUTOMATIC;
4625         }
4626
4627         if (move_direction_initial == MV_START_RANDOM)
4628           MovDir[x][y] = 1 << RND(4);
4629         else if (move_direction_initial & MV_ANY_DIRECTION)
4630           MovDir[x][y] = move_direction_initial;
4631         else if (move_pattern == MV_ALL_DIRECTIONS ||
4632                  move_pattern == MV_TURNING_LEFT ||
4633                  move_pattern == MV_TURNING_RIGHT ||
4634                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4635                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4636                  move_pattern == MV_TURNING_RANDOM)
4637           MovDir[x][y] = 1 << RND(4);
4638         else if (move_pattern == MV_HORIZONTAL)
4639           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4640         else if (move_pattern == MV_VERTICAL)
4641           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4642         else if (move_pattern & MV_ANY_DIRECTION)
4643           MovDir[x][y] = element_info[element].move_pattern;
4644         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4645                  move_pattern == MV_ALONG_RIGHT_SIDE)
4646         {
4647           // use random direction as default start direction
4648           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4649             MovDir[x][y] = 1 << RND(4);
4650
4651           for (i = 0; i < NUM_DIRECTIONS; i++)
4652           {
4653             int x1 = x + xy[i][0];
4654             int y1 = y + xy[i][1];
4655
4656             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4657             {
4658               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4659                 MovDir[x][y] = direction[0][i];
4660               else
4661                 MovDir[x][y] = direction[1][i];
4662
4663               break;
4664             }
4665           }
4666         }                
4667       }
4668       else
4669       {
4670         MovDir[x][y] = 1 << RND(4);
4671
4672         if (element != EL_BUG &&
4673             element != EL_SPACESHIP &&
4674             element != EL_BD_BUTTERFLY &&
4675             element != EL_BD_FIREFLY)
4676           break;
4677
4678         for (i = 0; i < NUM_DIRECTIONS; i++)
4679         {
4680           int x1 = x + xy[i][0];
4681           int y1 = y + xy[i][1];
4682
4683           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4684           {
4685             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4686             {
4687               MovDir[x][y] = direction[0][i];
4688               break;
4689             }
4690             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4691                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4692             {
4693               MovDir[x][y] = direction[1][i];
4694               break;
4695             }
4696           }
4697         }
4698       }
4699       break;
4700   }
4701
4702   GfxDir[x][y] = MovDir[x][y];
4703 }
4704
4705 void InitAmoebaNr(int x, int y)
4706 {
4707   int i;
4708   int group_nr = AmoebaNeighbourNr(x, y);
4709
4710   if (group_nr == 0)
4711   {
4712     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4713     {
4714       if (AmoebaCnt[i] == 0)
4715       {
4716         group_nr = i;
4717         break;
4718       }
4719     }
4720   }
4721
4722   AmoebaNr[x][y] = group_nr;
4723   AmoebaCnt[group_nr]++;
4724   AmoebaCnt2[group_nr]++;
4725 }
4726
4727 static void LevelSolved_SetFinalGameValues(void)
4728 {
4729   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4730   game.score_time_final = (level.use_step_counter ? TimePlayed :
4731                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4732
4733   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4734                       game_em.lev->score :
4735                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4736                       game_mm.score :
4737                       game.score);
4738
4739   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                        MM_HEALTH(game_mm.laser_overload_value) :
4741                        game.health);
4742
4743   game.LevelSolved_CountingTime = game.time_final;
4744   game.LevelSolved_CountingScore = game.score_final;
4745   game.LevelSolved_CountingHealth = game.health_final;
4746 }
4747
4748 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4749 {
4750   game.LevelSolved_CountingTime = time;
4751   game.LevelSolved_CountingScore = score;
4752   game.LevelSolved_CountingHealth = health;
4753
4754   game_panel_controls[GAME_PANEL_TIME].value = time;
4755   game_panel_controls[GAME_PANEL_SCORE].value = score;
4756   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4757
4758   DisplayGameControlValues();
4759 }
4760
4761 static void LevelSolved(void)
4762 {
4763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4764       game.players_still_needed > 0)
4765     return;
4766
4767   game.LevelSolved = TRUE;
4768   game.GameOver = TRUE;
4769
4770   // needed here to display correct panel values while player walks into exit
4771   LevelSolved_SetFinalGameValues();
4772 }
4773
4774 void GameWon(void)
4775 {
4776   static int time_count_steps;
4777   static int time, time_final;
4778   static float score, score_final; // needed for time score < 10 for 10 seconds
4779   static int health, health_final;
4780   static int game_over_delay_1 = 0;
4781   static int game_over_delay_2 = 0;
4782   static int game_over_delay_3 = 0;
4783   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4784   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4785
4786   if (!game.LevelSolved_GameWon)
4787   {
4788     int i;
4789
4790     // do not start end game actions before the player stops moving (to exit)
4791     if (local_player->active && local_player->MovPos)
4792       return;
4793
4794     // calculate final game values after player finished walking into exit
4795     LevelSolved_SetFinalGameValues();
4796
4797     game.LevelSolved_GameWon = TRUE;
4798     game.LevelSolved_SaveTape = tape.recording;
4799     game.LevelSolved_SaveScore = !tape.playing;
4800
4801     if (!tape.playing)
4802     {
4803       LevelStats_incSolved(level_nr);
4804
4805       SaveLevelSetup_SeriesInfo();
4806     }
4807
4808     if (tape.auto_play)         // tape might already be stopped here
4809       tape.auto_play_level_solved = TRUE;
4810
4811     TapeStop();
4812
4813     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4814     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4815     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4816
4817     time = time_final = game.time_final;
4818     score = score_final = game.score_final;
4819     health = health_final = game.health_final;
4820
4821     // update game panel values before (delayed) counting of score (if any)
4822     LevelSolved_DisplayFinalGameValues(time, score, health);
4823
4824     // if level has time score defined, calculate new final game values
4825     if (time_score > 0)
4826     {
4827       int time_final_max = 999;
4828       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4829       int time_frames = 0;
4830       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4831       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4832
4833       if (TimeLeft > 0)
4834       {
4835         time_final = 0;
4836         time_frames = time_frames_left;
4837       }
4838       else if (game.no_time_limit && TimePlayed < time_final_max)
4839       {
4840         time_final = time_final_max;
4841         time_frames = time_frames_final_max - time_frames_played;
4842       }
4843
4844       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4845
4846       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4847
4848       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4849       {
4850         health_final = 0;
4851         score_final += health * time_score;
4852       }
4853
4854       game.score_final = score_final;
4855       game.health_final = health_final;
4856     }
4857
4858     // if not counting score after game, immediately update game panel values
4859     if (level_editor_test_game || !setup.count_score_after_game)
4860     {
4861       time = time_final;
4862       score = score_final;
4863
4864       LevelSolved_DisplayFinalGameValues(time, score, health);
4865     }
4866
4867     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4868     {
4869       // check if last player has left the level
4870       if (game.exit_x >= 0 &&
4871           game.exit_y >= 0)
4872       {
4873         int x = game.exit_x;
4874         int y = game.exit_y;
4875         int element = Tile[x][y];
4876
4877         // close exit door after last player
4878         if ((game.all_players_gone &&
4879              (element == EL_EXIT_OPEN ||
4880               element == EL_SP_EXIT_OPEN ||
4881               element == EL_STEEL_EXIT_OPEN)) ||
4882             element == EL_EM_EXIT_OPEN ||
4883             element == EL_EM_STEEL_EXIT_OPEN)
4884         {
4885
4886           Tile[x][y] =
4887             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4888              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4889              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4890              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4891              EL_EM_STEEL_EXIT_CLOSING);
4892
4893           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4894         }
4895
4896         // player disappears
4897         DrawLevelField(x, y);
4898       }
4899
4900       for (i = 0; i < MAX_PLAYERS; i++)
4901       {
4902         struct PlayerInfo *player = &stored_player[i];
4903
4904         if (player->present)
4905         {
4906           RemovePlayer(player);
4907
4908           // player disappears
4909           DrawLevelField(player->jx, player->jy);
4910         }
4911       }
4912     }
4913
4914     PlaySound(SND_GAME_WINNING);
4915   }
4916
4917   if (setup.count_score_after_game)
4918   {
4919     if (time != time_final)
4920     {
4921       if (game_over_delay_1 > 0)
4922       {
4923         game_over_delay_1--;
4924
4925         return;
4926       }
4927
4928       int time_to_go = ABS(time_final - time);
4929       int time_count_dir = (time < time_final ? +1 : -1);
4930
4931       if (time_to_go < time_count_steps)
4932         time_count_steps = 1;
4933
4934       time  += time_count_steps * time_count_dir;
4935       score += time_count_steps * time_score;
4936
4937       // set final score to correct rounding differences after counting score
4938       if (time == time_final)
4939         score = score_final;
4940
4941       LevelSolved_DisplayFinalGameValues(time, score, health);
4942
4943       if (time == time_final)
4944         StopSound(SND_GAME_LEVELTIME_BONUS);
4945       else if (setup.sound_loops)
4946         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4947       else
4948         PlaySound(SND_GAME_LEVELTIME_BONUS);
4949
4950       return;
4951     }
4952
4953     if (health != health_final)
4954     {
4955       if (game_over_delay_2 > 0)
4956       {
4957         game_over_delay_2--;
4958
4959         return;
4960       }
4961
4962       int health_count_dir = (health < health_final ? +1 : -1);
4963
4964       health += health_count_dir;
4965       score  += time_score;
4966
4967       LevelSolved_DisplayFinalGameValues(time, score, health);
4968
4969       if (health == health_final)
4970         StopSound(SND_GAME_LEVELTIME_BONUS);
4971       else if (setup.sound_loops)
4972         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4973       else
4974         PlaySound(SND_GAME_LEVELTIME_BONUS);
4975
4976       return;
4977     }
4978   }
4979
4980   game.panel.active = FALSE;
4981
4982   if (game_over_delay_3 > 0)
4983   {
4984     game_over_delay_3--;
4985
4986     return;
4987   }
4988
4989   GameEnd();
4990 }
4991
4992 void GameEnd(void)
4993 {
4994   // used instead of "level_nr" (needed for network games)
4995   int last_level_nr = levelset.level_nr;
4996   boolean tape_saved = FALSE;
4997
4998   game.LevelSolved_GameEnd = TRUE;
4999
5000   if (game.LevelSolved_SaveTape)
5001   {
5002     // make sure that request dialog to save tape does not open door again
5003     if (!global.use_envelope_request)
5004       CloseDoor(DOOR_CLOSE_1);
5005
5006     // ask to save tape
5007     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5008
5009     // set unique basename for score tape (also saved in high score table)
5010     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5011   }
5012
5013   // if no tape is to be saved, close both doors simultaneously
5014   CloseDoor(DOOR_CLOSE_ALL);
5015
5016   if (level_editor_test_game)
5017   {
5018     SetGameStatus(GAME_MODE_MAIN);
5019
5020     DrawMainMenu();
5021
5022     return;
5023   }
5024
5025   if (!game.LevelSolved_SaveScore)
5026   {
5027     SetGameStatus(GAME_MODE_MAIN);
5028
5029     DrawMainMenu();
5030
5031     return;
5032   }
5033
5034   if (level_nr == leveldir_current->handicap_level)
5035   {
5036     leveldir_current->handicap_level++;
5037
5038     SaveLevelSetup_SeriesInfo();
5039   }
5040
5041   // save score and score tape before potentially erasing tape below
5042   NewHighScore(last_level_nr, tape_saved);
5043
5044   if (setup.increment_levels &&
5045       level_nr < leveldir_current->last_level &&
5046       !network_playing)
5047   {
5048     level_nr++;         // advance to next level
5049     TapeErase();        // start with empty tape
5050
5051     if (setup.auto_play_next_level)
5052     {
5053       LoadLevel(level_nr);
5054
5055       SaveLevelSetup_SeriesInfo();
5056     }
5057   }
5058
5059   if (scores.last_added >= 0 && setup.show_scores_after_game)
5060   {
5061     SetGameStatus(GAME_MODE_SCORES);
5062
5063     DrawHallOfFame(last_level_nr);
5064   }
5065   else if (setup.auto_play_next_level && setup.increment_levels &&
5066            last_level_nr < leveldir_current->last_level &&
5067            !network_playing)
5068   {
5069     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5070   }
5071   else
5072   {
5073     SetGameStatus(GAME_MODE_MAIN);
5074
5075     DrawMainMenu();
5076   }
5077 }
5078
5079 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5080                          boolean one_score_entry_per_name)
5081 {
5082   int i;
5083
5084   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5085     return -1;
5086
5087   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5088   {
5089     struct ScoreEntry *entry = &list->entry[i];
5090     boolean score_is_better = (new_entry->score >  entry->score);
5091     boolean score_is_equal  = (new_entry->score == entry->score);
5092     boolean time_is_better  = (new_entry->time  <  entry->time);
5093     boolean time_is_equal   = (new_entry->time  == entry->time);
5094     boolean better_by_score = (score_is_better ||
5095                                (score_is_equal && time_is_better));
5096     boolean better_by_time  = (time_is_better ||
5097                                (time_is_equal && score_is_better));
5098     boolean is_better = (level.rate_time_over_score ? better_by_time :
5099                          better_by_score);
5100     boolean entry_is_empty = (entry->score == 0 &&
5101                               entry->time == 0);
5102
5103     // prevent adding server score entries if also existing in local score file
5104     // (special case: historic score entries have an empty tape basename entry)
5105     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5106         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5107     {
5108       // special case: use server score instead of local score value if higher
5109       // (historic scores might have been truncated to 16-bit values locally)
5110       if (score_is_better)
5111         entry->score = new_entry->score;
5112
5113       return -1;
5114     }
5115
5116     if (is_better || entry_is_empty)
5117     {
5118       // player has made it to the hall of fame
5119
5120       if (i < MAX_SCORE_ENTRIES - 1)
5121       {
5122         int m = MAX_SCORE_ENTRIES - 1;
5123         int l;
5124
5125         if (one_score_entry_per_name)
5126         {
5127           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5128             if (strEqual(list->entry[l].name, new_entry->name))
5129               m = l;
5130
5131           if (m == i)   // player's new highscore overwrites his old one
5132             goto put_into_list;
5133         }
5134
5135         for (l = m; l > i; l--)
5136           list->entry[l] = list->entry[l - 1];
5137       }
5138
5139       put_into_list:
5140
5141       *entry = *new_entry;
5142
5143       return i;
5144     }
5145     else if (one_score_entry_per_name &&
5146              strEqual(entry->name, new_entry->name))
5147     {
5148       // player already in high score list with better score or time
5149
5150       return -1;
5151     }
5152   }
5153
5154   return -1;
5155 }
5156
5157 void NewHighScore(int level_nr, boolean tape_saved)
5158 {
5159   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5160   boolean one_per_name = FALSE;
5161
5162   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5163   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5164
5165   new_entry.score = game.score_final;
5166   new_entry.time = game.score_time_final;
5167
5168   LoadScore(level_nr);
5169
5170   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5171
5172   if (scores.last_added < 0)
5173     return;
5174
5175   SaveScore(level_nr);
5176
5177   // store last added local score entry (before merging server scores)
5178   scores.last_added_local = scores.last_added;
5179
5180   if (!game.LevelSolved_SaveTape)
5181     return;
5182
5183   SaveScoreTape(level_nr);
5184
5185   if (setup.ask_for_using_api_server)
5186   {
5187     setup.use_api_server =
5188       Request("Upload your score and tape to the high score server?", REQ_ASK);
5189
5190     if (!setup.use_api_server)
5191       Request("Not using high score server! Use setup menu to enable again!",
5192               REQ_CONFIRM);
5193
5194     runtime.use_api_server = setup.use_api_server;
5195
5196     // after asking for using API server once, do not ask again
5197     setup.ask_for_using_api_server = FALSE;
5198
5199     SaveSetup_ServerSetup();
5200   }
5201
5202   SaveServerScore(level_nr, tape_saved);
5203 }
5204
5205 void MergeServerScore(void)
5206 {
5207   struct ScoreEntry last_added_entry;
5208   boolean one_per_name = FALSE;
5209   int i;
5210
5211   if (scores.last_added >= 0)
5212     last_added_entry = scores.entry[scores.last_added];
5213
5214   for (i = 0; i < server_scores.num_entries; i++)
5215   {
5216     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5217
5218     if (pos >= 0 && pos <= scores.last_added)
5219       scores.last_added++;
5220   }
5221
5222   if (scores.last_added >= MAX_SCORE_ENTRIES)
5223   {
5224     scores.last_added = MAX_SCORE_ENTRIES - 1;
5225     scores.force_last_added = TRUE;
5226
5227     scores.entry[scores.last_added] = last_added_entry;
5228   }
5229 }
5230
5231 static int getElementMoveStepsizeExt(int x, int y, int direction)
5232 {
5233   int element = Tile[x][y];
5234   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5235   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5236   int horiz_move = (dx != 0);
5237   int sign = (horiz_move ? dx : dy);
5238   int step = sign * element_info[element].move_stepsize;
5239
5240   // special values for move stepsize for spring and things on conveyor belt
5241   if (horiz_move)
5242   {
5243     if (CAN_FALL(element) &&
5244         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5245       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5246     else if (element == EL_SPRING)
5247       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5248   }
5249
5250   return step;
5251 }
5252
5253 static int getElementMoveStepsize(int x, int y)
5254 {
5255   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5256 }
5257
5258 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5259 {
5260   if (player->GfxAction != action || player->GfxDir != dir)
5261   {
5262     player->GfxAction = action;
5263     player->GfxDir = dir;
5264     player->Frame = 0;
5265     player->StepFrame = 0;
5266   }
5267 }
5268
5269 static void ResetGfxFrame(int x, int y)
5270 {
5271   // profiling showed that "autotest" spends 10~20% of its time in this function
5272   if (DrawingDeactivatedField())
5273     return;
5274
5275   int element = Tile[x][y];
5276   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5277
5278   if (graphic_info[graphic].anim_global_sync)
5279     GfxFrame[x][y] = FrameCounter;
5280   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5281     GfxFrame[x][y] = CustomValue[x][y];
5282   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5283     GfxFrame[x][y] = element_info[element].collect_score;
5284   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5285     GfxFrame[x][y] = ChangeDelay[x][y];
5286 }
5287
5288 static void ResetGfxAnimation(int x, int y)
5289 {
5290   GfxAction[x][y] = ACTION_DEFAULT;
5291   GfxDir[x][y] = MovDir[x][y];
5292   GfxFrame[x][y] = 0;
5293
5294   ResetGfxFrame(x, y);
5295 }
5296
5297 static void ResetRandomAnimationValue(int x, int y)
5298 {
5299   GfxRandom[x][y] = INIT_GFX_RANDOM();
5300 }
5301
5302 static void InitMovingField(int x, int y, int direction)
5303 {
5304   int element = Tile[x][y];
5305   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5306   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5307   int newx = x + dx;
5308   int newy = y + dy;
5309   boolean is_moving_before, is_moving_after;
5310
5311   // check if element was/is moving or being moved before/after mode change
5312   is_moving_before = (WasJustMoving[x][y] != 0);
5313   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5314
5315   // reset animation only for moving elements which change direction of moving
5316   // or which just started or stopped moving
5317   // (else CEs with property "can move" / "not moving" are reset each frame)
5318   if (is_moving_before != is_moving_after ||
5319       direction != MovDir[x][y])
5320     ResetGfxAnimation(x, y);
5321
5322   MovDir[x][y] = direction;
5323   GfxDir[x][y] = direction;
5324
5325   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5326                      direction == MV_DOWN && CAN_FALL(element) ?
5327                      ACTION_FALLING : ACTION_MOVING);
5328
5329   // this is needed for CEs with property "can move" / "not moving"
5330
5331   if (is_moving_after)
5332   {
5333     if (Tile[newx][newy] == EL_EMPTY)
5334       Tile[newx][newy] = EL_BLOCKED;
5335
5336     MovDir[newx][newy] = MovDir[x][y];
5337
5338     CustomValue[newx][newy] = CustomValue[x][y];
5339
5340     GfxFrame[newx][newy] = GfxFrame[x][y];
5341     GfxRandom[newx][newy] = GfxRandom[x][y];
5342     GfxAction[newx][newy] = GfxAction[x][y];
5343     GfxDir[newx][newy] = GfxDir[x][y];
5344   }
5345 }
5346
5347 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5348 {
5349   int direction = MovDir[x][y];
5350   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5351   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5352
5353   *goes_to_x = newx;
5354   *goes_to_y = newy;
5355 }
5356
5357 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5358 {
5359   int oldx = x, oldy = y;
5360   int direction = MovDir[x][y];
5361
5362   if (direction == MV_LEFT)
5363     oldx++;
5364   else if (direction == MV_RIGHT)
5365     oldx--;
5366   else if (direction == MV_UP)
5367     oldy++;
5368   else if (direction == MV_DOWN)
5369     oldy--;
5370
5371   *comes_from_x = oldx;
5372   *comes_from_y = oldy;
5373 }
5374
5375 static int MovingOrBlocked2Element(int x, int y)
5376 {
5377   int element = Tile[x][y];
5378
5379   if (element == EL_BLOCKED)
5380   {
5381     int oldx, oldy;
5382
5383     Blocked2Moving(x, y, &oldx, &oldy);
5384     return Tile[oldx][oldy];
5385   }
5386   else
5387     return element;
5388 }
5389
5390 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5391 {
5392   // like MovingOrBlocked2Element(), but if element is moving
5393   // and (x,y) is the field the moving element is just leaving,
5394   // return EL_BLOCKED instead of the element value
5395   int element = Tile[x][y];
5396
5397   if (IS_MOVING(x, y))
5398   {
5399     if (element == EL_BLOCKED)
5400     {
5401       int oldx, oldy;
5402
5403       Blocked2Moving(x, y, &oldx, &oldy);
5404       return Tile[oldx][oldy];
5405     }
5406     else
5407       return EL_BLOCKED;
5408   }
5409   else
5410     return element;
5411 }
5412
5413 static void RemoveField(int x, int y)
5414 {
5415   Tile[x][y] = EL_EMPTY;
5416
5417   MovPos[x][y] = 0;
5418   MovDir[x][y] = 0;
5419   MovDelay[x][y] = 0;
5420
5421   CustomValue[x][y] = 0;
5422
5423   AmoebaNr[x][y] = 0;
5424   ChangeDelay[x][y] = 0;
5425   ChangePage[x][y] = -1;
5426   Pushed[x][y] = FALSE;
5427
5428   GfxElement[x][y] = EL_UNDEFINED;
5429   GfxAction[x][y] = ACTION_DEFAULT;
5430   GfxDir[x][y] = MV_NONE;
5431 }
5432
5433 static void RemoveMovingField(int x, int y)
5434 {
5435   int oldx = x, oldy = y, newx = x, newy = y;
5436   int element = Tile[x][y];
5437   int next_element = EL_UNDEFINED;
5438
5439   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5440     return;
5441
5442   if (IS_MOVING(x, y))
5443   {
5444     Moving2Blocked(x, y, &newx, &newy);
5445
5446     if (Tile[newx][newy] != EL_BLOCKED)
5447     {
5448       // element is moving, but target field is not free (blocked), but
5449       // already occupied by something different (example: acid pool);
5450       // in this case, only remove the moving field, but not the target
5451
5452       RemoveField(oldx, oldy);
5453
5454       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5455
5456       TEST_DrawLevelField(oldx, oldy);
5457
5458       return;
5459     }
5460   }
5461   else if (element == EL_BLOCKED)
5462   {
5463     Blocked2Moving(x, y, &oldx, &oldy);
5464     if (!IS_MOVING(oldx, oldy))
5465       return;
5466   }
5467
5468   if (element == EL_BLOCKED &&
5469       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5470        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5471        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5472        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5473        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5474        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5475     next_element = get_next_element(Tile[oldx][oldy]);
5476
5477   RemoveField(oldx, oldy);
5478   RemoveField(newx, newy);
5479
5480   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5481
5482   if (next_element != EL_UNDEFINED)
5483     Tile[oldx][oldy] = next_element;
5484
5485   TEST_DrawLevelField(oldx, oldy);
5486   TEST_DrawLevelField(newx, newy);
5487 }
5488
5489 void DrawDynamite(int x, int y)
5490 {
5491   int sx = SCREENX(x), sy = SCREENY(y);
5492   int graphic = el2img(Tile[x][y]);
5493   int frame;
5494
5495   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5496     return;
5497
5498   if (IS_WALKABLE_INSIDE(Back[x][y]))
5499     return;
5500
5501   if (Back[x][y])
5502     DrawLevelElement(x, y, Back[x][y]);
5503   else if (Store[x][y])
5504     DrawLevelElement(x, y, Store[x][y]);
5505   else if (game.use_masked_elements)
5506     DrawLevelElement(x, y, EL_EMPTY);
5507
5508   frame = getGraphicAnimationFrameXY(graphic, x, y);
5509
5510   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5511     DrawGraphicThruMask(sx, sy, graphic, frame);
5512   else
5513     DrawGraphic(sx, sy, graphic, frame);
5514 }
5515
5516 static void CheckDynamite(int x, int y)
5517 {
5518   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5519   {
5520     MovDelay[x][y]--;
5521
5522     if (MovDelay[x][y] != 0)
5523     {
5524       DrawDynamite(x, y);
5525       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5526
5527       return;
5528     }
5529   }
5530
5531   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5532
5533   Bang(x, y);
5534 }
5535
5536 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5537 {
5538   boolean num_checked_players = 0;
5539   int i;
5540
5541   for (i = 0; i < MAX_PLAYERS; i++)
5542   {
5543     if (stored_player[i].active)
5544     {
5545       int sx = stored_player[i].jx;
5546       int sy = stored_player[i].jy;
5547
5548       if (num_checked_players == 0)
5549       {
5550         *sx1 = *sx2 = sx;
5551         *sy1 = *sy2 = sy;
5552       }
5553       else
5554       {
5555         *sx1 = MIN(*sx1, sx);
5556         *sy1 = MIN(*sy1, sy);
5557         *sx2 = MAX(*sx2, sx);
5558         *sy2 = MAX(*sy2, sy);
5559       }
5560
5561       num_checked_players++;
5562     }
5563   }
5564 }
5565
5566 static boolean checkIfAllPlayersFitToScreen_RND(void)
5567 {
5568   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5569
5570   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5571
5572   return (sx2 - sx1 < SCR_FIELDX &&
5573           sy2 - sy1 < SCR_FIELDY);
5574 }
5575
5576 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5577 {
5578   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5579
5580   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5581
5582   *sx = (sx1 + sx2) / 2;
5583   *sy = (sy1 + sy2) / 2;
5584 }
5585
5586 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5587                                boolean center_screen, boolean quick_relocation)
5588 {
5589   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5590   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5591   boolean no_delay = (tape.warp_forward);
5592   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5593   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5594   int new_scroll_x, new_scroll_y;
5595
5596   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5597   {
5598     // case 1: quick relocation inside visible screen (without scrolling)
5599
5600     RedrawPlayfield();
5601
5602     return;
5603   }
5604
5605   if (!level.shifted_relocation || center_screen)
5606   {
5607     // relocation _with_ centering of screen
5608
5609     new_scroll_x = SCROLL_POSITION_X(x);
5610     new_scroll_y = SCROLL_POSITION_Y(y);
5611   }
5612   else
5613   {
5614     // relocation _without_ centering of screen
5615
5616     int center_scroll_x = SCROLL_POSITION_X(old_x);
5617     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5618     int offset_x = x + (scroll_x - center_scroll_x);
5619     int offset_y = y + (scroll_y - center_scroll_y);
5620
5621     // for new screen position, apply previous offset to center position
5622     new_scroll_x = SCROLL_POSITION_X(offset_x);
5623     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5624   }
5625
5626   if (quick_relocation)
5627   {
5628     // case 2: quick relocation (redraw without visible scrolling)
5629
5630     scroll_x = new_scroll_x;
5631     scroll_y = new_scroll_y;
5632
5633     RedrawPlayfield();
5634
5635     return;
5636   }
5637
5638   // case 3: visible relocation (with scrolling to new position)
5639
5640   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5641
5642   SetVideoFrameDelay(wait_delay_value);
5643
5644   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5645   {
5646     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5647     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5648
5649     if (dx == 0 && dy == 0)             // no scrolling needed at all
5650       break;
5651
5652     scroll_x -= dx;
5653     scroll_y -= dy;
5654
5655     // set values for horizontal/vertical screen scrolling (half tile size)
5656     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5657     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5658     int pos_x = dx * TILEX / 2;
5659     int pos_y = dy * TILEY / 2;
5660     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5661     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5662
5663     ScrollLevel(dx, dy);
5664     DrawAllPlayers();
5665
5666     // scroll in two steps of half tile size to make things smoother
5667     BlitScreenToBitmapExt_RND(window, fx, fy);
5668
5669     // scroll second step to align at full tile size
5670     BlitScreenToBitmap(window);
5671   }
5672
5673   DrawAllPlayers();
5674   BackToFront();
5675
5676   SetVideoFrameDelay(frame_delay_value_old);
5677 }
5678
5679 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5680 {
5681   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5682   int player_nr = GET_PLAYER_NR(el_player);
5683   struct PlayerInfo *player = &stored_player[player_nr];
5684   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5685   boolean no_delay = (tape.warp_forward);
5686   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5687   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5688   int old_jx = player->jx;
5689   int old_jy = player->jy;
5690   int old_element = Tile[old_jx][old_jy];
5691   int element = Tile[jx][jy];
5692   boolean player_relocated = (old_jx != jx || old_jy != jy);
5693
5694   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5695   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5696   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5697   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5698   int leave_side_horiz = move_dir_horiz;
5699   int leave_side_vert  = move_dir_vert;
5700   int enter_side = enter_side_horiz | enter_side_vert;
5701   int leave_side = leave_side_horiz | leave_side_vert;
5702
5703   if (player->buried)           // do not reanimate dead player
5704     return;
5705
5706   if (!player_relocated)        // no need to relocate the player
5707     return;
5708
5709   if (IS_PLAYER(jx, jy))        // player already placed at new position
5710   {
5711     RemoveField(jx, jy);        // temporarily remove newly placed player
5712     DrawLevelField(jx, jy);
5713   }
5714
5715   if (player->present)
5716   {
5717     while (player->MovPos)
5718     {
5719       ScrollPlayer(player, SCROLL_GO_ON);
5720       ScrollScreen(NULL, SCROLL_GO_ON);
5721
5722       AdvanceFrameAndPlayerCounters(player->index_nr);
5723
5724       DrawPlayer(player);
5725
5726       BackToFront_WithFrameDelay(wait_delay_value);
5727     }
5728
5729     DrawPlayer(player);         // needed here only to cleanup last field
5730     DrawLevelField(player->jx, player->jy);     // remove player graphic
5731
5732     player->is_moving = FALSE;
5733   }
5734
5735   if (IS_CUSTOM_ELEMENT(old_element))
5736     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5737                                CE_LEFT_BY_PLAYER,
5738                                player->index_bit, leave_side);
5739
5740   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5741                                       CE_PLAYER_LEAVES_X,
5742                                       player->index_bit, leave_side);
5743
5744   Tile[jx][jy] = el_player;
5745   InitPlayerField(jx, jy, el_player, TRUE);
5746
5747   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5748      possible that the relocation target field did not contain a player element,
5749      but a walkable element, to which the new player was relocated -- in this
5750      case, restore that (already initialized!) element on the player field */
5751   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5752   {
5753     Tile[jx][jy] = element;     // restore previously existing element
5754   }
5755
5756   // only visually relocate centered player
5757   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5758                      FALSE, level.instant_relocation);
5759
5760   TestIfPlayerTouchesBadThing(jx, jy);
5761   TestIfPlayerTouchesCustomElement(jx, jy);
5762
5763   if (IS_CUSTOM_ELEMENT(element))
5764     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5765                                player->index_bit, enter_side);
5766
5767   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5768                                       player->index_bit, enter_side);
5769
5770   if (player->is_switching)
5771   {
5772     /* ensure that relocation while still switching an element does not cause
5773        a new element to be treated as also switched directly after relocation
5774        (this is important for teleporter switches that teleport the player to
5775        a place where another teleporter switch is in the same direction, which
5776        would then incorrectly be treated as immediately switched before the
5777        direction key that caused the switch was released) */
5778
5779     player->switch_x += jx - old_jx;
5780     player->switch_y += jy - old_jy;
5781   }
5782 }
5783
5784 static void Explode(int ex, int ey, int phase, int mode)
5785 {
5786   int x, y;
5787   int last_phase;
5788   int border_element;
5789
5790   // !!! eliminate this variable !!!
5791   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5792
5793   if (game.explosions_delayed)
5794   {
5795     ExplodeField[ex][ey] = mode;
5796     return;
5797   }
5798
5799   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5800   {
5801     int center_element = Tile[ex][ey];
5802     int artwork_element, explosion_element;     // set these values later
5803
5804     // remove things displayed in background while burning dynamite
5805     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5806       Back[ex][ey] = 0;
5807
5808     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5809     {
5810       // put moving element to center field (and let it explode there)
5811       center_element = MovingOrBlocked2Element(ex, ey);
5812       RemoveMovingField(ex, ey);
5813       Tile[ex][ey] = center_element;
5814     }
5815
5816     // now "center_element" is finally determined -- set related values now
5817     artwork_element = center_element;           // for custom player artwork
5818     explosion_element = center_element;         // for custom player artwork
5819
5820     if (IS_PLAYER(ex, ey))
5821     {
5822       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5823
5824       artwork_element = stored_player[player_nr].artwork_element;
5825
5826       if (level.use_explosion_element[player_nr])
5827       {
5828         explosion_element = level.explosion_element[player_nr];
5829         artwork_element = explosion_element;
5830       }
5831     }
5832
5833     if (mode == EX_TYPE_NORMAL ||
5834         mode == EX_TYPE_CENTER ||
5835         mode == EX_TYPE_CROSS)
5836       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5837
5838     last_phase = element_info[explosion_element].explosion_delay + 1;
5839
5840     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5841     {
5842       int xx = x - ex + 1;
5843       int yy = y - ey + 1;
5844       int element;
5845
5846       if (!IN_LEV_FIELD(x, y) ||
5847           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5848           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5849         continue;
5850
5851       element = Tile[x][y];
5852
5853       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5854       {
5855         element = MovingOrBlocked2Element(x, y);
5856
5857         if (!IS_EXPLOSION_PROOF(element))
5858           RemoveMovingField(x, y);
5859       }
5860
5861       // indestructible elements can only explode in center (but not flames)
5862       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5863                                            mode == EX_TYPE_BORDER)) ||
5864           element == EL_FLAMES)
5865         continue;
5866
5867       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5868          behaviour, for example when touching a yamyam that explodes to rocks
5869          with active deadly shield, a rock is created under the player !!! */
5870       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5871 #if 0
5872       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5873           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5874            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5875 #else
5876       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5877 #endif
5878       {
5879         if (IS_ACTIVE_BOMB(element))
5880         {
5881           // re-activate things under the bomb like gate or penguin
5882           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5883           Back[x][y] = 0;
5884         }
5885
5886         continue;
5887       }
5888
5889       // save walkable background elements while explosion on same tile
5890       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5891           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5892         Back[x][y] = element;
5893
5894       // ignite explodable elements reached by other explosion
5895       if (element == EL_EXPLOSION)
5896         element = Store2[x][y];
5897
5898       if (AmoebaNr[x][y] &&
5899           (element == EL_AMOEBA_FULL ||
5900            element == EL_BD_AMOEBA ||
5901            element == EL_AMOEBA_GROWING))
5902       {
5903         AmoebaCnt[AmoebaNr[x][y]]--;
5904         AmoebaCnt2[AmoebaNr[x][y]]--;
5905       }
5906
5907       RemoveField(x, y);
5908
5909       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5910       {
5911         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5912
5913         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5914
5915         if (PLAYERINFO(ex, ey)->use_murphy)
5916           Store[x][y] = EL_EMPTY;
5917       }
5918
5919       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5920       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5921       else if (IS_PLAYER_ELEMENT(center_element))
5922         Store[x][y] = EL_EMPTY;
5923       else if (center_element == EL_YAMYAM)
5924         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5925       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5926         Store[x][y] = element_info[center_element].content.e[xx][yy];
5927 #if 1
5928       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5929       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5930       // otherwise) -- FIX THIS !!!
5931       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5932         Store[x][y] = element_info[element].content.e[1][1];
5933 #else
5934       else if (!CAN_EXPLODE(element))
5935         Store[x][y] = element_info[element].content.e[1][1];
5936 #endif
5937       else
5938         Store[x][y] = EL_EMPTY;
5939
5940       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5941           center_element == EL_AMOEBA_TO_DIAMOND)
5942         Store2[x][y] = element;
5943
5944       Tile[x][y] = EL_EXPLOSION;
5945       GfxElement[x][y] = artwork_element;
5946
5947       ExplodePhase[x][y] = 1;
5948       ExplodeDelay[x][y] = last_phase;
5949
5950       Stop[x][y] = TRUE;
5951     }
5952
5953     if (center_element == EL_YAMYAM)
5954       game.yamyam_content_nr =
5955         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5956
5957     return;
5958   }
5959
5960   if (Stop[ex][ey])
5961     return;
5962
5963   x = ex;
5964   y = ey;
5965
5966   if (phase == 1)
5967     GfxFrame[x][y] = 0;         // restart explosion animation
5968
5969   last_phase = ExplodeDelay[x][y];
5970
5971   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5972
5973   // this can happen if the player leaves an explosion just in time
5974   if (GfxElement[x][y] == EL_UNDEFINED)
5975     GfxElement[x][y] = EL_EMPTY;
5976
5977   border_element = Store2[x][y];
5978   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5979     border_element = StorePlayer[x][y];
5980
5981   if (phase == element_info[border_element].ignition_delay ||
5982       phase == last_phase)
5983   {
5984     boolean border_explosion = FALSE;
5985
5986     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5987         !PLAYER_EXPLOSION_PROTECTED(x, y))
5988     {
5989       KillPlayerUnlessExplosionProtected(x, y);
5990       border_explosion = TRUE;
5991     }
5992     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5993     {
5994       Tile[x][y] = Store2[x][y];
5995       Store2[x][y] = 0;
5996       Bang(x, y);
5997       border_explosion = TRUE;
5998     }
5999     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6000     {
6001       AmoebaToDiamond(x, y);
6002       Store2[x][y] = 0;
6003       border_explosion = TRUE;
6004     }
6005
6006     // if an element just explodes due to another explosion (chain-reaction),
6007     // do not immediately end the new explosion when it was the last frame of
6008     // the explosion (as it would be done in the following "if"-statement!)
6009     if (border_explosion && phase == last_phase)
6010       return;
6011   }
6012
6013   // this can happen if the player was just killed by an explosion
6014   if (GfxElement[x][y] == EL_UNDEFINED)
6015     GfxElement[x][y] = EL_EMPTY;
6016
6017   if (phase == last_phase)
6018   {
6019     int element;
6020
6021     element = Tile[x][y] = Store[x][y];
6022     Store[x][y] = Store2[x][y] = 0;
6023     GfxElement[x][y] = EL_UNDEFINED;
6024
6025     // player can escape from explosions and might therefore be still alive
6026     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6027         element <= EL_PLAYER_IS_EXPLODING_4)
6028     {
6029       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6030       int explosion_element = EL_PLAYER_1 + player_nr;
6031       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6032       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6033
6034       if (level.use_explosion_element[player_nr])
6035         explosion_element = level.explosion_element[player_nr];
6036
6037       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6038                     element_info[explosion_element].content.e[xx][yy]);
6039     }
6040
6041     // restore probably existing indestructible background element
6042     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6043       element = Tile[x][y] = Back[x][y];
6044     Back[x][y] = 0;
6045
6046     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6047     GfxDir[x][y] = MV_NONE;
6048     ChangeDelay[x][y] = 0;
6049     ChangePage[x][y] = -1;
6050
6051     CustomValue[x][y] = 0;
6052
6053     InitField_WithBug2(x, y, FALSE);
6054
6055     TEST_DrawLevelField(x, y);
6056
6057     TestIfElementTouchesCustomElement(x, y);
6058
6059     if (GFX_CRUMBLED(element))
6060       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6061
6062     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6063       StorePlayer[x][y] = 0;
6064
6065     if (IS_PLAYER_ELEMENT(element))
6066       RelocatePlayer(x, y, element);
6067   }
6068   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6069   {
6070     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6071     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6072
6073     if (phase == delay)
6074       TEST_DrawLevelFieldCrumbled(x, y);
6075
6076     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6077     {
6078       DrawLevelElement(x, y, Back[x][y]);
6079       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6080     }
6081     else if (IS_WALKABLE_UNDER(Back[x][y]))
6082     {
6083       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6084       DrawLevelElementThruMask(x, y, Back[x][y]);
6085     }
6086     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6087       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6088   }
6089 }
6090
6091 static void DynaExplode(int ex, int ey)
6092 {
6093   int i, j;
6094   int dynabomb_element = Tile[ex][ey];
6095   int dynabomb_size = 1;
6096   boolean dynabomb_xl = FALSE;
6097   struct PlayerInfo *player;
6098   static int xy[4][2] =
6099   {
6100     { 0, -1 },
6101     { -1, 0 },
6102     { +1, 0 },
6103     { 0, +1 }
6104   };
6105
6106   if (IS_ACTIVE_BOMB(dynabomb_element))
6107   {
6108     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6109     dynabomb_size = player->dynabomb_size;
6110     dynabomb_xl = player->dynabomb_xl;
6111     player->dynabombs_left++;
6112   }
6113
6114   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6115
6116   for (i = 0; i < NUM_DIRECTIONS; i++)
6117   {
6118     for (j = 1; j <= dynabomb_size; j++)
6119     {
6120       int x = ex + j * xy[i][0];
6121       int y = ey + j * xy[i][1];
6122       int element;
6123
6124       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6125         break;
6126
6127       element = Tile[x][y];
6128
6129       // do not restart explosions of fields with active bombs
6130       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6131         continue;
6132
6133       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6134
6135       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6136           !IS_DIGGABLE(element) && !dynabomb_xl)
6137         break;
6138     }
6139   }
6140 }
6141
6142 void Bang(int x, int y)
6143 {
6144   int element = MovingOrBlocked2Element(x, y);
6145   int explosion_type = EX_TYPE_NORMAL;
6146
6147   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6148   {
6149     struct PlayerInfo *player = PLAYERINFO(x, y);
6150
6151     element = Tile[x][y] = player->initial_element;
6152
6153     if (level.use_explosion_element[player->index_nr])
6154     {
6155       int explosion_element = level.explosion_element[player->index_nr];
6156
6157       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6158         explosion_type = EX_TYPE_CROSS;
6159       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6160         explosion_type = EX_TYPE_CENTER;
6161     }
6162   }
6163
6164   switch (element)
6165   {
6166     case EL_BUG:
6167     case EL_SPACESHIP:
6168     case EL_BD_BUTTERFLY:
6169     case EL_BD_FIREFLY:
6170     case EL_YAMYAM:
6171     case EL_DARK_YAMYAM:
6172     case EL_ROBOT:
6173     case EL_PACMAN:
6174     case EL_MOLE:
6175       RaiseScoreElement(element);
6176       break;
6177
6178     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6179     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6180     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6181     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6182     case EL_DYNABOMB_INCREASE_NUMBER:
6183     case EL_DYNABOMB_INCREASE_SIZE:
6184     case EL_DYNABOMB_INCREASE_POWER:
6185       explosion_type = EX_TYPE_DYNA;
6186       break;
6187
6188     case EL_DC_LANDMINE:
6189       explosion_type = EX_TYPE_CENTER;
6190       break;
6191
6192     case EL_PENGUIN:
6193     case EL_LAMP:
6194     case EL_LAMP_ACTIVE:
6195     case EL_AMOEBA_TO_DIAMOND:
6196       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6197         explosion_type = EX_TYPE_CENTER;
6198       break;
6199
6200     default:
6201       if (element_info[element].explosion_type == EXPLODES_CROSS)
6202         explosion_type = EX_TYPE_CROSS;
6203       else if (element_info[element].explosion_type == EXPLODES_1X1)
6204         explosion_type = EX_TYPE_CENTER;
6205       break;
6206   }
6207
6208   if (explosion_type == EX_TYPE_DYNA)
6209     DynaExplode(x, y);
6210   else
6211     Explode(x, y, EX_PHASE_START, explosion_type);
6212
6213   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6214 }
6215
6216 static void SplashAcid(int x, int y)
6217 {
6218   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6219       (!IN_LEV_FIELD(x - 1, y - 2) ||
6220        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6221     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6222
6223   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6224       (!IN_LEV_FIELD(x + 1, y - 2) ||
6225        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6226     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6227
6228   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6229 }
6230
6231 static void InitBeltMovement(void)
6232 {
6233   static int belt_base_element[4] =
6234   {
6235     EL_CONVEYOR_BELT_1_LEFT,
6236     EL_CONVEYOR_BELT_2_LEFT,
6237     EL_CONVEYOR_BELT_3_LEFT,
6238     EL_CONVEYOR_BELT_4_LEFT
6239   };
6240   static int belt_base_active_element[4] =
6241   {
6242     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6243     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6244     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6245     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6246   };
6247
6248   int x, y, i, j;
6249
6250   // set frame order for belt animation graphic according to belt direction
6251   for (i = 0; i < NUM_BELTS; i++)
6252   {
6253     int belt_nr = i;
6254
6255     for (j = 0; j < NUM_BELT_PARTS; j++)
6256     {
6257       int element = belt_base_active_element[belt_nr] + j;
6258       int graphic_1 = el2img(element);
6259       int graphic_2 = el2panelimg(element);
6260
6261       if (game.belt_dir[i] == MV_LEFT)
6262       {
6263         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6264         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6265       }
6266       else
6267       {
6268         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6269         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6270       }
6271     }
6272   }
6273
6274   SCAN_PLAYFIELD(x, y)
6275   {
6276     int element = Tile[x][y];
6277
6278     for (i = 0; i < NUM_BELTS; i++)
6279     {
6280       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6281       {
6282         int e_belt_nr = getBeltNrFromBeltElement(element);
6283         int belt_nr = i;
6284
6285         if (e_belt_nr == belt_nr)
6286         {
6287           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6288
6289           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6290         }
6291       }
6292     }
6293   }
6294 }
6295
6296 static void ToggleBeltSwitch(int x, int y)
6297 {
6298   static int belt_base_element[4] =
6299   {
6300     EL_CONVEYOR_BELT_1_LEFT,
6301     EL_CONVEYOR_BELT_2_LEFT,
6302     EL_CONVEYOR_BELT_3_LEFT,
6303     EL_CONVEYOR_BELT_4_LEFT
6304   };
6305   static int belt_base_active_element[4] =
6306   {
6307     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6308     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6309     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6310     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6311   };
6312   static int belt_base_switch_element[4] =
6313   {
6314     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6315     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6316     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6317     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6318   };
6319   static int belt_move_dir[4] =
6320   {
6321     MV_LEFT,
6322     MV_NONE,
6323     MV_RIGHT,
6324     MV_NONE,
6325   };
6326
6327   int element = Tile[x][y];
6328   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6329   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6330   int belt_dir = belt_move_dir[belt_dir_nr];
6331   int xx, yy, i;
6332
6333   if (!IS_BELT_SWITCH(element))
6334     return;
6335
6336   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6337   game.belt_dir[belt_nr] = belt_dir;
6338
6339   if (belt_dir_nr == 3)
6340     belt_dir_nr = 1;
6341
6342   // set frame order for belt animation graphic according to belt direction
6343   for (i = 0; i < NUM_BELT_PARTS; i++)
6344   {
6345     int element = belt_base_active_element[belt_nr] + i;
6346     int graphic_1 = el2img(element);
6347     int graphic_2 = el2panelimg(element);
6348
6349     if (belt_dir == MV_LEFT)
6350     {
6351       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6352       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6353     }
6354     else
6355     {
6356       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6357       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6358     }
6359   }
6360
6361   SCAN_PLAYFIELD(xx, yy)
6362   {
6363     int element = Tile[xx][yy];
6364
6365     if (IS_BELT_SWITCH(element))
6366     {
6367       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6368
6369       if (e_belt_nr == belt_nr)
6370       {
6371         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6372         TEST_DrawLevelField(xx, yy);
6373       }
6374     }
6375     else if (IS_BELT(element) && belt_dir != MV_NONE)
6376     {
6377       int e_belt_nr = getBeltNrFromBeltElement(element);
6378
6379       if (e_belt_nr == belt_nr)
6380       {
6381         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6382
6383         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6384         TEST_DrawLevelField(xx, yy);
6385       }
6386     }
6387     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6388     {
6389       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6390
6391       if (e_belt_nr == belt_nr)
6392       {
6393         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6394
6395         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6396         TEST_DrawLevelField(xx, yy);
6397       }
6398     }
6399   }
6400 }
6401
6402 static void ToggleSwitchgateSwitch(int x, int y)
6403 {
6404   int xx, yy;
6405
6406   game.switchgate_pos = !game.switchgate_pos;
6407
6408   SCAN_PLAYFIELD(xx, yy)
6409   {
6410     int element = Tile[xx][yy];
6411
6412     if (element == EL_SWITCHGATE_SWITCH_UP)
6413     {
6414       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6415       TEST_DrawLevelField(xx, yy);
6416     }
6417     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6418     {
6419       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6420       TEST_DrawLevelField(xx, yy);
6421     }
6422     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6423     {
6424       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6425       TEST_DrawLevelField(xx, yy);
6426     }
6427     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6428     {
6429       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6430       TEST_DrawLevelField(xx, yy);
6431     }
6432     else if (element == EL_SWITCHGATE_OPEN ||
6433              element == EL_SWITCHGATE_OPENING)
6434     {
6435       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6436
6437       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6438     }
6439     else if (element == EL_SWITCHGATE_CLOSED ||
6440              element == EL_SWITCHGATE_CLOSING)
6441     {
6442       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6443
6444       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6445     }
6446   }
6447 }
6448
6449 static int getInvisibleActiveFromInvisibleElement(int element)
6450 {
6451   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6452           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6453           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6454           element);
6455 }
6456
6457 static int getInvisibleFromInvisibleActiveElement(int element)
6458 {
6459   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6460           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6461           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6462           element);
6463 }
6464
6465 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6466 {
6467   int x, y;
6468
6469   SCAN_PLAYFIELD(x, y)
6470   {
6471     int element = Tile[x][y];
6472
6473     if (element == EL_LIGHT_SWITCH &&
6474         game.light_time_left > 0)
6475     {
6476       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6477       TEST_DrawLevelField(x, y);
6478     }
6479     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6480              game.light_time_left == 0)
6481     {
6482       Tile[x][y] = EL_LIGHT_SWITCH;
6483       TEST_DrawLevelField(x, y);
6484     }
6485     else if (element == EL_EMC_DRIPPER &&
6486              game.light_time_left > 0)
6487     {
6488       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6489       TEST_DrawLevelField(x, y);
6490     }
6491     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6492              game.light_time_left == 0)
6493     {
6494       Tile[x][y] = EL_EMC_DRIPPER;
6495       TEST_DrawLevelField(x, y);
6496     }
6497     else if (element == EL_INVISIBLE_STEELWALL ||
6498              element == EL_INVISIBLE_WALL ||
6499              element == EL_INVISIBLE_SAND)
6500     {
6501       if (game.light_time_left > 0)
6502         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6503
6504       TEST_DrawLevelField(x, y);
6505
6506       // uncrumble neighbour fields, if needed
6507       if (element == EL_INVISIBLE_SAND)
6508         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6509     }
6510     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6511              element == EL_INVISIBLE_WALL_ACTIVE ||
6512              element == EL_INVISIBLE_SAND_ACTIVE)
6513     {
6514       if (game.light_time_left == 0)
6515         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6516
6517       TEST_DrawLevelField(x, y);
6518
6519       // re-crumble neighbour fields, if needed
6520       if (element == EL_INVISIBLE_SAND)
6521         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6522     }
6523   }
6524 }
6525
6526 static void RedrawAllInvisibleElementsForLenses(void)
6527 {
6528   int x, y;
6529
6530   SCAN_PLAYFIELD(x, y)
6531   {
6532     int element = Tile[x][y];
6533
6534     if (element == EL_EMC_DRIPPER &&
6535         game.lenses_time_left > 0)
6536     {
6537       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6538       TEST_DrawLevelField(x, y);
6539     }
6540     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6541              game.lenses_time_left == 0)
6542     {
6543       Tile[x][y] = EL_EMC_DRIPPER;
6544       TEST_DrawLevelField(x, y);
6545     }
6546     else if (element == EL_INVISIBLE_STEELWALL ||
6547              element == EL_INVISIBLE_WALL ||
6548              element == EL_INVISIBLE_SAND)
6549     {
6550       if (game.lenses_time_left > 0)
6551         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6552
6553       TEST_DrawLevelField(x, y);
6554
6555       // uncrumble neighbour fields, if needed
6556       if (element == EL_INVISIBLE_SAND)
6557         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6558     }
6559     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6560              element == EL_INVISIBLE_WALL_ACTIVE ||
6561              element == EL_INVISIBLE_SAND_ACTIVE)
6562     {
6563       if (game.lenses_time_left == 0)
6564         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6565
6566       TEST_DrawLevelField(x, y);
6567
6568       // re-crumble neighbour fields, if needed
6569       if (element == EL_INVISIBLE_SAND)
6570         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6571     }
6572   }
6573 }
6574
6575 static void RedrawAllInvisibleElementsForMagnifier(void)
6576 {
6577   int x, y;
6578
6579   SCAN_PLAYFIELD(x, y)
6580   {
6581     int element = Tile[x][y];
6582
6583     if (element == EL_EMC_FAKE_GRASS &&
6584         game.magnify_time_left > 0)
6585     {
6586       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6587       TEST_DrawLevelField(x, y);
6588     }
6589     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6590              game.magnify_time_left == 0)
6591     {
6592       Tile[x][y] = EL_EMC_FAKE_GRASS;
6593       TEST_DrawLevelField(x, y);
6594     }
6595     else if (IS_GATE_GRAY(element) &&
6596              game.magnify_time_left > 0)
6597     {
6598       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6599                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6600                     IS_EM_GATE_GRAY(element) ?
6601                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6602                     IS_EMC_GATE_GRAY(element) ?
6603                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6604                     IS_DC_GATE_GRAY(element) ?
6605                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6606                     element);
6607       TEST_DrawLevelField(x, y);
6608     }
6609     else if (IS_GATE_GRAY_ACTIVE(element) &&
6610              game.magnify_time_left == 0)
6611     {
6612       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6613                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6614                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6615                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6616                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6617                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6618                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6619                     EL_DC_GATE_WHITE_GRAY :
6620                     element);
6621       TEST_DrawLevelField(x, y);
6622     }
6623   }
6624 }
6625
6626 static void ToggleLightSwitch(int x, int y)
6627 {
6628   int element = Tile[x][y];
6629
6630   game.light_time_left =
6631     (element == EL_LIGHT_SWITCH ?
6632      level.time_light * FRAMES_PER_SECOND : 0);
6633
6634   RedrawAllLightSwitchesAndInvisibleElements();
6635 }
6636
6637 static void ActivateTimegateSwitch(int x, int y)
6638 {
6639   int xx, yy;
6640
6641   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6642
6643   SCAN_PLAYFIELD(xx, yy)
6644   {
6645     int element = Tile[xx][yy];
6646
6647     if (element == EL_TIMEGATE_CLOSED ||
6648         element == EL_TIMEGATE_CLOSING)
6649     {
6650       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6651       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6652     }
6653
6654     /*
6655     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6656     {
6657       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6658       TEST_DrawLevelField(xx, yy);
6659     }
6660     */
6661
6662   }
6663
6664   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6665                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6666 }
6667
6668 static void Impact(int x, int y)
6669 {
6670   boolean last_line = (y == lev_fieldy - 1);
6671   boolean object_hit = FALSE;
6672   boolean impact = (last_line || object_hit);
6673   int element = Tile[x][y];
6674   int smashed = EL_STEELWALL;
6675
6676   if (!last_line)       // check if element below was hit
6677   {
6678     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6679       return;
6680
6681     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6682                                          MovDir[x][y + 1] != MV_DOWN ||
6683                                          MovPos[x][y + 1] <= TILEY / 2));
6684
6685     // do not smash moving elements that left the smashed field in time
6686     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6687         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6688       object_hit = FALSE;
6689
6690 #if USE_QUICKSAND_IMPACT_BUGFIX
6691     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6692     {
6693       RemoveMovingField(x, y + 1);
6694       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6695       Tile[x][y + 2] = EL_ROCK;
6696       TEST_DrawLevelField(x, y + 2);
6697
6698       object_hit = TRUE;
6699     }
6700
6701     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6702     {
6703       RemoveMovingField(x, y + 1);
6704       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6705       Tile[x][y + 2] = EL_ROCK;
6706       TEST_DrawLevelField(x, y + 2);
6707
6708       object_hit = TRUE;
6709     }
6710 #endif
6711
6712     if (object_hit)
6713       smashed = MovingOrBlocked2Element(x, y + 1);
6714
6715     impact = (last_line || object_hit);
6716   }
6717
6718   if (!last_line && smashed == EL_ACID) // element falls into acid
6719   {
6720     SplashAcid(x, y + 1);
6721     return;
6722   }
6723
6724   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6725   // only reset graphic animation if graphic really changes after impact
6726   if (impact &&
6727       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6728   {
6729     ResetGfxAnimation(x, y);
6730     TEST_DrawLevelField(x, y);
6731   }
6732
6733   if (impact && CAN_EXPLODE_IMPACT(element))
6734   {
6735     Bang(x, y);
6736     return;
6737   }
6738   else if (impact && element == EL_PEARL &&
6739            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6740   {
6741     ResetGfxAnimation(x, y);
6742
6743     Tile[x][y] = EL_PEARL_BREAKING;
6744     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6745     return;
6746   }
6747   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6748   {
6749     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6750
6751     return;
6752   }
6753
6754   if (impact && element == EL_AMOEBA_DROP)
6755   {
6756     if (object_hit && IS_PLAYER(x, y + 1))
6757       KillPlayerUnlessEnemyProtected(x, y + 1);
6758     else if (object_hit && smashed == EL_PENGUIN)
6759       Bang(x, y + 1);
6760     else
6761     {
6762       Tile[x][y] = EL_AMOEBA_GROWING;
6763       Store[x][y] = EL_AMOEBA_WET;
6764
6765       ResetRandomAnimationValue(x, y);
6766     }
6767     return;
6768   }
6769
6770   if (object_hit)               // check which object was hit
6771   {
6772     if ((CAN_PASS_MAGIC_WALL(element) && 
6773          (smashed == EL_MAGIC_WALL ||
6774           smashed == EL_BD_MAGIC_WALL)) ||
6775         (CAN_PASS_DC_MAGIC_WALL(element) &&
6776          smashed == EL_DC_MAGIC_WALL))
6777     {
6778       int xx, yy;
6779       int activated_magic_wall =
6780         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6781          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6782          EL_DC_MAGIC_WALL_ACTIVE);
6783
6784       // activate magic wall / mill
6785       SCAN_PLAYFIELD(xx, yy)
6786       {
6787         if (Tile[xx][yy] == smashed)
6788           Tile[xx][yy] = activated_magic_wall;
6789       }
6790
6791       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6792       game.magic_wall_active = TRUE;
6793
6794       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6795                             SND_MAGIC_WALL_ACTIVATING :
6796                             smashed == EL_BD_MAGIC_WALL ?
6797                             SND_BD_MAGIC_WALL_ACTIVATING :
6798                             SND_DC_MAGIC_WALL_ACTIVATING));
6799     }
6800
6801     if (IS_PLAYER(x, y + 1))
6802     {
6803       if (CAN_SMASH_PLAYER(element))
6804       {
6805         KillPlayerUnlessEnemyProtected(x, y + 1);
6806         return;
6807       }
6808     }
6809     else if (smashed == EL_PENGUIN)
6810     {
6811       if (CAN_SMASH_PLAYER(element))
6812       {
6813         Bang(x, y + 1);
6814         return;
6815       }
6816     }
6817     else if (element == EL_BD_DIAMOND)
6818     {
6819       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6820       {
6821         Bang(x, y + 1);
6822         return;
6823       }
6824     }
6825     else if (((element == EL_SP_INFOTRON ||
6826                element == EL_SP_ZONK) &&
6827               (smashed == EL_SP_SNIKSNAK ||
6828                smashed == EL_SP_ELECTRON ||
6829                smashed == EL_SP_DISK_ORANGE)) ||
6830              (element == EL_SP_INFOTRON &&
6831               smashed == EL_SP_DISK_YELLOW))
6832     {
6833       Bang(x, y + 1);
6834       return;
6835     }
6836     else if (CAN_SMASH_EVERYTHING(element))
6837     {
6838       if (IS_CLASSIC_ENEMY(smashed) ||
6839           CAN_EXPLODE_SMASHED(smashed))
6840       {
6841         Bang(x, y + 1);
6842         return;
6843       }
6844       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6845       {
6846         if (smashed == EL_LAMP ||
6847             smashed == EL_LAMP_ACTIVE)
6848         {
6849           Bang(x, y + 1);
6850           return;
6851         }
6852         else if (smashed == EL_NUT)
6853         {
6854           Tile[x][y + 1] = EL_NUT_BREAKING;
6855           PlayLevelSound(x, y, SND_NUT_BREAKING);
6856           RaiseScoreElement(EL_NUT);
6857           return;
6858         }
6859         else if (smashed == EL_PEARL)
6860         {
6861           ResetGfxAnimation(x, y);
6862
6863           Tile[x][y + 1] = EL_PEARL_BREAKING;
6864           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6865           return;
6866         }
6867         else if (smashed == EL_DIAMOND)
6868         {
6869           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6870           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6871           return;
6872         }
6873         else if (IS_BELT_SWITCH(smashed))
6874         {
6875           ToggleBeltSwitch(x, y + 1);
6876         }
6877         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6878                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6879                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6880                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6881         {
6882           ToggleSwitchgateSwitch(x, y + 1);
6883         }
6884         else if (smashed == EL_LIGHT_SWITCH ||
6885                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6886         {
6887           ToggleLightSwitch(x, y + 1);
6888         }
6889         else
6890         {
6891           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6892
6893           CheckElementChangeBySide(x, y + 1, smashed, element,
6894                                    CE_SWITCHED, CH_SIDE_TOP);
6895           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6896                                             CH_SIDE_TOP);
6897         }
6898       }
6899       else
6900       {
6901         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6902       }
6903     }
6904   }
6905
6906   // play sound of magic wall / mill
6907   if (!last_line &&
6908       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6911   {
6912     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6918
6919     return;
6920   }
6921
6922   // play sound of object that hits the ground
6923   if (last_line || object_hit)
6924     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6925 }
6926
6927 static void TurnRoundExt(int x, int y)
6928 {
6929   static struct
6930   {
6931     int dx, dy;
6932   } move_xy[] =
6933   {
6934     {  0,  0 },
6935     { -1,  0 },
6936     { +1,  0 },
6937     {  0,  0 },
6938     {  0, -1 },
6939     {  0,  0 }, { 0, 0 }, { 0, 0 },
6940     {  0, +1 }
6941   };
6942   static struct
6943   {
6944     int left, right, back;
6945   } turn[] =
6946   {
6947     { 0,        0,              0        },
6948     { MV_DOWN,  MV_UP,          MV_RIGHT },
6949     { MV_UP,    MV_DOWN,        MV_LEFT  },
6950     { 0,        0,              0        },
6951     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6952     { 0,        0,              0        },
6953     { 0,        0,              0        },
6954     { 0,        0,              0        },
6955     { MV_RIGHT, MV_LEFT,        MV_UP    }
6956   };
6957
6958   int element = Tile[x][y];
6959   int move_pattern = element_info[element].move_pattern;
6960
6961   int old_move_dir = MovDir[x][y];
6962   int left_dir  = turn[old_move_dir].left;
6963   int right_dir = turn[old_move_dir].right;
6964   int back_dir  = turn[old_move_dir].back;
6965
6966   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6967   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6968   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6969   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6970
6971   int left_x  = x + left_dx,  left_y  = y + left_dy;
6972   int right_x = x + right_dx, right_y = y + right_dy;
6973   int move_x  = x + move_dx,  move_y  = y + move_dy;
6974
6975   int xx, yy;
6976
6977   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6978   {
6979     TestIfBadThingTouchesOtherBadThing(x, y);
6980
6981     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982       MovDir[x][y] = right_dir;
6983     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984       MovDir[x][y] = left_dir;
6985
6986     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6987       MovDelay[x][y] = 9;
6988     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6989       MovDelay[x][y] = 1;
6990   }
6991   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6992   {
6993     TestIfBadThingTouchesOtherBadThing(x, y);
6994
6995     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996       MovDir[x][y] = left_dir;
6997     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998       MovDir[x][y] = right_dir;
6999
7000     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7001       MovDelay[x][y] = 9;
7002     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7003       MovDelay[x][y] = 1;
7004   }
7005   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010       MovDir[x][y] = left_dir;
7011     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012       MovDir[x][y] = right_dir;
7013
7014     if (MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016   }
7017   else if (element == EL_YAMYAM)
7018   {
7019     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7021
7022     if (can_turn_left && can_turn_right)
7023       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024     else if (can_turn_left)
7025       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026     else if (can_turn_right)
7027       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7028     else
7029       MovDir[x][y] = back_dir;
7030
7031     MovDelay[x][y] = 16 + 16 * RND(3);
7032   }
7033   else if (element == EL_DARK_YAMYAM)
7034   {
7035     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7036                                                          left_x, left_y);
7037     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7038                                                          right_x, right_y);
7039
7040     if (can_turn_left && can_turn_right)
7041       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042     else if (can_turn_left)
7043       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044     else if (can_turn_right)
7045       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7046     else
7047       MovDir[x][y] = back_dir;
7048
7049     MovDelay[x][y] = 16 + 16 * RND(3);
7050   }
7051   else if (element == EL_PACMAN)
7052   {
7053     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7055
7056     if (can_turn_left && can_turn_right)
7057       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058     else if (can_turn_left)
7059       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060     else if (can_turn_right)
7061       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7062     else
7063       MovDir[x][y] = back_dir;
7064
7065     MovDelay[x][y] = 6 + RND(40);
7066   }
7067   else if (element == EL_PIG)
7068   {
7069     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072     boolean should_turn_left, should_turn_right, should_move_on;
7073     int rnd_value = 24;
7074     int rnd = RND(rnd_value);
7075
7076     should_turn_left = (can_turn_left &&
7077                         (!can_move_on ||
7078                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079                                                    y + back_dy + left_dy)));
7080     should_turn_right = (can_turn_right &&
7081                          (!can_move_on ||
7082                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083                                                     y + back_dy + right_dy)));
7084     should_move_on = (can_move_on &&
7085                       (!can_turn_left ||
7086                        !can_turn_right ||
7087                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088                                                  y + move_dy + left_dy) ||
7089                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090                                                  y + move_dy + right_dy)));
7091
7092     if (should_turn_left || should_turn_right || should_move_on)
7093     {
7094       if (should_turn_left && should_turn_right && should_move_on)
7095         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7096                         rnd < 2 * rnd_value / 3 ? right_dir :
7097                         old_move_dir);
7098       else if (should_turn_left && should_turn_right)
7099         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100       else if (should_turn_left && should_move_on)
7101         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102       else if (should_turn_right && should_move_on)
7103         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104       else if (should_turn_left)
7105         MovDir[x][y] = left_dir;
7106       else if (should_turn_right)
7107         MovDir[x][y] = right_dir;
7108       else if (should_move_on)
7109         MovDir[x][y] = old_move_dir;
7110     }
7111     else if (can_move_on && rnd > rnd_value / 8)
7112       MovDir[x][y] = old_move_dir;
7113     else if (can_turn_left && can_turn_right)
7114       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115     else if (can_turn_left && rnd > rnd_value / 8)
7116       MovDir[x][y] = left_dir;
7117     else if (can_turn_right && rnd > rnd_value/8)
7118       MovDir[x][y] = right_dir;
7119     else
7120       MovDir[x][y] = back_dir;
7121
7122     xx = x + move_xy[MovDir[x][y]].dx;
7123     yy = y + move_xy[MovDir[x][y]].dy;
7124
7125     if (!IN_LEV_FIELD(xx, yy) ||
7126         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7127       MovDir[x][y] = old_move_dir;
7128
7129     MovDelay[x][y] = 0;
7130   }
7131   else if (element == EL_DRAGON)
7132   {
7133     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7136     int rnd_value = 24;
7137     int rnd = RND(rnd_value);
7138
7139     if (can_move_on && rnd > rnd_value / 8)
7140       MovDir[x][y] = old_move_dir;
7141     else if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143     else if (can_turn_left && rnd > rnd_value / 8)
7144       MovDir[x][y] = left_dir;
7145     else if (can_turn_right && rnd > rnd_value / 8)
7146       MovDir[x][y] = right_dir;
7147     else
7148       MovDir[x][y] = back_dir;
7149
7150     xx = x + move_xy[MovDir[x][y]].dx;
7151     yy = y + move_xy[MovDir[x][y]].dy;
7152
7153     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154       MovDir[x][y] = old_move_dir;
7155
7156     MovDelay[x][y] = 0;
7157   }
7158   else if (element == EL_MOLE)
7159   {
7160     boolean can_move_on =
7161       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7163                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7164     if (!can_move_on)
7165     {
7166       boolean can_turn_left =
7167         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168                               IS_AMOEBOID(Tile[left_x][left_y])));
7169
7170       boolean can_turn_right =
7171         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172                               IS_AMOEBOID(Tile[right_x][right_y])));
7173
7174       if (can_turn_left && can_turn_right)
7175         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176       else if (can_turn_left)
7177         MovDir[x][y] = left_dir;
7178       else
7179         MovDir[x][y] = right_dir;
7180     }
7181
7182     if (MovDir[x][y] != old_move_dir)
7183       MovDelay[x][y] = 9;
7184   }
7185   else if (element == EL_BALLOON)
7186   {
7187     MovDir[x][y] = game.wind_direction;
7188     MovDelay[x][y] = 0;
7189   }
7190   else if (element == EL_SPRING)
7191   {
7192     if (MovDir[x][y] & MV_HORIZONTAL)
7193     {
7194       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7195           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7196       {
7197         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7198         ResetGfxAnimation(move_x, move_y);
7199         TEST_DrawLevelField(move_x, move_y);
7200
7201         MovDir[x][y] = back_dir;
7202       }
7203       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7204                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7205         MovDir[x][y] = MV_NONE;
7206     }
7207
7208     MovDelay[x][y] = 0;
7209   }
7210   else if (element == EL_ROBOT ||
7211            element == EL_SATELLITE ||
7212            element == EL_PENGUIN ||
7213            element == EL_EMC_ANDROID)
7214   {
7215     int attr_x = -1, attr_y = -1;
7216
7217     if (game.all_players_gone)
7218     {
7219       attr_x = game.exit_x;
7220       attr_y = game.exit_y;
7221     }
7222     else
7223     {
7224       int i;
7225
7226       for (i = 0; i < MAX_PLAYERS; i++)
7227       {
7228         struct PlayerInfo *player = &stored_player[i];
7229         int jx = player->jx, jy = player->jy;
7230
7231         if (!player->active)
7232           continue;
7233
7234         if (attr_x == -1 ||
7235             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7236         {
7237           attr_x = jx;
7238           attr_y = jy;
7239         }
7240       }
7241     }
7242
7243     if (element == EL_ROBOT &&
7244         game.robot_wheel_x >= 0 &&
7245         game.robot_wheel_y >= 0 &&
7246         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7247          game.engine_version < VERSION_IDENT(3,1,0,0)))
7248     {
7249       attr_x = game.robot_wheel_x;
7250       attr_y = game.robot_wheel_y;
7251     }
7252
7253     if (element == EL_PENGUIN)
7254     {
7255       int i;
7256       static int xy[4][2] =
7257       {
7258         { 0, -1 },
7259         { -1, 0 },
7260         { +1, 0 },
7261         { 0, +1 }
7262       };
7263
7264       for (i = 0; i < NUM_DIRECTIONS; i++)
7265       {
7266         int ex = x + xy[i][0];
7267         int ey = y + xy[i][1];
7268
7269         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7270                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7271                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7272                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7273         {
7274           attr_x = ex;
7275           attr_y = ey;
7276           break;
7277         }
7278       }
7279     }
7280
7281     MovDir[x][y] = MV_NONE;
7282     if (attr_x < x)
7283       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7284     else if (attr_x > x)
7285       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7286     if (attr_y < y)
7287       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7288     else if (attr_y > y)
7289       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7290
7291     if (element == EL_ROBOT)
7292     {
7293       int newx, newy;
7294
7295       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7296         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7297       Moving2Blocked(x, y, &newx, &newy);
7298
7299       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7300         MovDelay[x][y] = 8 + 8 * !RND(3);
7301       else
7302         MovDelay[x][y] = 16;
7303     }
7304     else if (element == EL_PENGUIN)
7305     {
7306       int newx, newy;
7307
7308       MovDelay[x][y] = 1;
7309
7310       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7311       {
7312         boolean first_horiz = RND(2);
7313         int new_move_dir = MovDir[x][y];
7314
7315         MovDir[x][y] =
7316           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7317         Moving2Blocked(x, y, &newx, &newy);
7318
7319         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7320           return;
7321
7322         MovDir[x][y] =
7323           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7324         Moving2Blocked(x, y, &newx, &newy);
7325
7326         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7327           return;
7328
7329         MovDir[x][y] = old_move_dir;
7330         return;
7331       }
7332     }
7333     else if (element == EL_SATELLITE)
7334     {
7335       int newx, newy;
7336
7337       MovDelay[x][y] = 1;
7338
7339       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7340       {
7341         boolean first_horiz = RND(2);
7342         int new_move_dir = MovDir[x][y];
7343
7344         MovDir[x][y] =
7345           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7346         Moving2Blocked(x, y, &newx, &newy);
7347
7348         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7349           return;
7350
7351         MovDir[x][y] =
7352           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7353         Moving2Blocked(x, y, &newx, &newy);
7354
7355         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7356           return;
7357
7358         MovDir[x][y] = old_move_dir;
7359         return;
7360       }
7361     }
7362     else if (element == EL_EMC_ANDROID)
7363     {
7364       static int check_pos[16] =
7365       {
7366         -1,             //  0 => (invalid)
7367         7,              //  1 => MV_LEFT
7368         3,              //  2 => MV_RIGHT
7369         -1,             //  3 => (invalid)
7370         1,              //  4 =>            MV_UP
7371         0,              //  5 => MV_LEFT  | MV_UP
7372         2,              //  6 => MV_RIGHT | MV_UP
7373         -1,             //  7 => (invalid)
7374         5,              //  8 =>            MV_DOWN
7375         6,              //  9 => MV_LEFT  | MV_DOWN
7376         4,              // 10 => MV_RIGHT | MV_DOWN
7377         -1,             // 11 => (invalid)
7378         -1,             // 12 => (invalid)
7379         -1,             // 13 => (invalid)
7380         -1,             // 14 => (invalid)
7381         -1,             // 15 => (invalid)
7382       };
7383       static struct
7384       {
7385         int dx, dy;
7386         int dir;
7387       } check_xy[8] =
7388       {
7389         { -1, -1,       MV_LEFT  | MV_UP   },
7390         {  0, -1,                  MV_UP   },
7391         { +1, -1,       MV_RIGHT | MV_UP   },
7392         { +1,  0,       MV_RIGHT           },
7393         { +1, +1,       MV_RIGHT | MV_DOWN },
7394         {  0, +1,                  MV_DOWN },
7395         { -1, +1,       MV_LEFT  | MV_DOWN },
7396         { -1,  0,       MV_LEFT            },
7397       };
7398       int start_pos, check_order;
7399       boolean can_clone = FALSE;
7400       int i;
7401
7402       // check if there is any free field around current position
7403       for (i = 0; i < 8; i++)
7404       {
7405         int newx = x + check_xy[i].dx;
7406         int newy = y + check_xy[i].dy;
7407
7408         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7409         {
7410           can_clone = TRUE;
7411
7412           break;
7413         }
7414       }
7415
7416       if (can_clone)            // randomly find an element to clone
7417       {
7418         can_clone = FALSE;
7419
7420         start_pos = check_pos[RND(8)];
7421         check_order = (RND(2) ? -1 : +1);
7422
7423         for (i = 0; i < 8; i++)
7424         {
7425           int pos_raw = start_pos + i * check_order;
7426           int pos = (pos_raw + 8) % 8;
7427           int newx = x + check_xy[pos].dx;
7428           int newy = y + check_xy[pos].dy;
7429
7430           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7431           {
7432             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7433             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7434
7435             Store[x][y] = Tile[newx][newy];
7436
7437             can_clone = TRUE;
7438
7439             break;
7440           }
7441         }
7442       }
7443
7444       if (can_clone)            // randomly find a direction to move
7445       {
7446         can_clone = FALSE;
7447
7448         start_pos = check_pos[RND(8)];
7449         check_order = (RND(2) ? -1 : +1);
7450
7451         for (i = 0; i < 8; i++)
7452         {
7453           int pos_raw = start_pos + i * check_order;
7454           int pos = (pos_raw + 8) % 8;
7455           int newx = x + check_xy[pos].dx;
7456           int newy = y + check_xy[pos].dy;
7457           int new_move_dir = check_xy[pos].dir;
7458
7459           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7460           {
7461             MovDir[x][y] = new_move_dir;
7462             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7463
7464             can_clone = TRUE;
7465
7466             break;
7467           }
7468         }
7469       }
7470
7471       if (can_clone)            // cloning and moving successful
7472         return;
7473
7474       // cannot clone -- try to move towards player
7475
7476       start_pos = check_pos[MovDir[x][y] & 0x0f];
7477       check_order = (RND(2) ? -1 : +1);
7478
7479       for (i = 0; i < 3; i++)
7480       {
7481         // first check start_pos, then previous/next or (next/previous) pos
7482         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7483         int pos = (pos_raw + 8) % 8;
7484         int newx = x + check_xy[pos].dx;
7485         int newy = y + check_xy[pos].dy;
7486         int new_move_dir = check_xy[pos].dir;
7487
7488         if (IS_PLAYER(newx, newy))
7489           break;
7490
7491         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7492         {
7493           MovDir[x][y] = new_move_dir;
7494           MovDelay[x][y] = level.android_move_time * 8 + 1;
7495
7496           break;
7497         }
7498       }
7499     }
7500   }
7501   else if (move_pattern == MV_TURNING_LEFT ||
7502            move_pattern == MV_TURNING_RIGHT ||
7503            move_pattern == MV_TURNING_LEFT_RIGHT ||
7504            move_pattern == MV_TURNING_RIGHT_LEFT ||
7505            move_pattern == MV_TURNING_RANDOM ||
7506            move_pattern == MV_ALL_DIRECTIONS)
7507   {
7508     boolean can_turn_left =
7509       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7510     boolean can_turn_right =
7511       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7512
7513     if (element_info[element].move_stepsize == 0)       // "not moving"
7514       return;
7515
7516     if (move_pattern == MV_TURNING_LEFT)
7517       MovDir[x][y] = left_dir;
7518     else if (move_pattern == MV_TURNING_RIGHT)
7519       MovDir[x][y] = right_dir;
7520     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7521       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7522     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7523       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7524     else if (move_pattern == MV_TURNING_RANDOM)
7525       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7526                       can_turn_right && !can_turn_left ? right_dir :
7527                       RND(2) ? left_dir : right_dir);
7528     else if (can_turn_left && can_turn_right)
7529       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7530     else if (can_turn_left)
7531       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7532     else if (can_turn_right)
7533       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7534     else
7535       MovDir[x][y] = back_dir;
7536
7537     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7538   }
7539   else if (move_pattern == MV_HORIZONTAL ||
7540            move_pattern == MV_VERTICAL)
7541   {
7542     if (move_pattern & old_move_dir)
7543       MovDir[x][y] = back_dir;
7544     else if (move_pattern == MV_HORIZONTAL)
7545       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7546     else if (move_pattern == MV_VERTICAL)
7547       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7548
7549     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7550   }
7551   else if (move_pattern & MV_ANY_DIRECTION)
7552   {
7553     MovDir[x][y] = move_pattern;
7554     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7555   }
7556   else if (move_pattern & MV_WIND_DIRECTION)
7557   {
7558     MovDir[x][y] = game.wind_direction;
7559     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7562   {
7563     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7564       MovDir[x][y] = left_dir;
7565     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7566       MovDir[x][y] = right_dir;
7567
7568     if (MovDir[x][y] != old_move_dir)
7569       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7570   }
7571   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7572   {
7573     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7574       MovDir[x][y] = right_dir;
7575     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7576       MovDir[x][y] = left_dir;
7577
7578     if (MovDir[x][y] != old_move_dir)
7579       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7580   }
7581   else if (move_pattern == MV_TOWARDS_PLAYER ||
7582            move_pattern == MV_AWAY_FROM_PLAYER)
7583   {
7584     int attr_x = -1, attr_y = -1;
7585     int newx, newy;
7586     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7587
7588     if (game.all_players_gone)
7589     {
7590       attr_x = game.exit_x;
7591       attr_y = game.exit_y;
7592     }
7593     else
7594     {
7595       int i;
7596
7597       for (i = 0; i < MAX_PLAYERS; i++)
7598       {
7599         struct PlayerInfo *player = &stored_player[i];
7600         int jx = player->jx, jy = player->jy;
7601
7602         if (!player->active)
7603           continue;
7604
7605         if (attr_x == -1 ||
7606             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7607         {
7608           attr_x = jx;
7609           attr_y = jy;
7610         }
7611       }
7612     }
7613
7614     MovDir[x][y] = MV_NONE;
7615     if (attr_x < x)
7616       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7617     else if (attr_x > x)
7618       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7619     if (attr_y < y)
7620       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7621     else if (attr_y > y)
7622       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7623
7624     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7625
7626     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7627     {
7628       boolean first_horiz = RND(2);
7629       int new_move_dir = MovDir[x][y];
7630
7631       if (element_info[element].move_stepsize == 0)     // "not moving"
7632       {
7633         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7634         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7635
7636         return;
7637       }
7638
7639       MovDir[x][y] =
7640         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7641       Moving2Blocked(x, y, &newx, &newy);
7642
7643       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7644         return;
7645
7646       MovDir[x][y] =
7647         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7648       Moving2Blocked(x, y, &newx, &newy);
7649
7650       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7651         return;
7652
7653       MovDir[x][y] = old_move_dir;
7654     }
7655   }
7656   else if (move_pattern == MV_WHEN_PUSHED ||
7657            move_pattern == MV_WHEN_DROPPED)
7658   {
7659     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7660       MovDir[x][y] = MV_NONE;
7661
7662     MovDelay[x][y] = 0;
7663   }
7664   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7665   {
7666     static int test_xy[7][2] =
7667     {
7668       { 0, -1 },
7669       { -1, 0 },
7670       { +1, 0 },
7671       { 0, +1 },
7672       { 0, -1 },
7673       { -1, 0 },
7674       { +1, 0 },
7675     };
7676     static int test_dir[7] =
7677     {
7678       MV_UP,
7679       MV_LEFT,
7680       MV_RIGHT,
7681       MV_DOWN,
7682       MV_UP,
7683       MV_LEFT,
7684       MV_RIGHT,
7685     };
7686     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7687     int move_preference = -1000000;     // start with very low preference
7688     int new_move_dir = MV_NONE;
7689     int start_test = RND(4);
7690     int i;
7691
7692     for (i = 0; i < NUM_DIRECTIONS; i++)
7693     {
7694       int move_dir = test_dir[start_test + i];
7695       int move_dir_preference;
7696
7697       xx = x + test_xy[start_test + i][0];
7698       yy = y + test_xy[start_test + i][1];
7699
7700       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7701           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7702       {
7703         new_move_dir = move_dir;
7704
7705         break;
7706       }
7707
7708       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7709         continue;
7710
7711       move_dir_preference = -1 * RunnerVisit[xx][yy];
7712       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7713         move_dir_preference = PlayerVisit[xx][yy];
7714
7715       if (move_dir_preference > move_preference)
7716       {
7717         // prefer field that has not been visited for the longest time
7718         move_preference = move_dir_preference;
7719         new_move_dir = move_dir;
7720       }
7721       else if (move_dir_preference == move_preference &&
7722                move_dir == old_move_dir)
7723       {
7724         // prefer last direction when all directions are preferred equally
7725         move_preference = move_dir_preference;
7726         new_move_dir = move_dir;
7727       }
7728     }
7729
7730     MovDir[x][y] = new_move_dir;
7731     if (old_move_dir != new_move_dir)
7732       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7733   }
7734 }
7735
7736 static void TurnRound(int x, int y)
7737 {
7738   int direction = MovDir[x][y];
7739
7740   TurnRoundExt(x, y);
7741
7742   GfxDir[x][y] = MovDir[x][y];
7743
7744   if (direction != MovDir[x][y])
7745     GfxFrame[x][y] = 0;
7746
7747   if (MovDelay[x][y])
7748     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7749
7750   ResetGfxFrame(x, y);
7751 }
7752
7753 static boolean JustBeingPushed(int x, int y)
7754 {
7755   int i;
7756
7757   for (i = 0; i < MAX_PLAYERS; i++)
7758   {
7759     struct PlayerInfo *player = &stored_player[i];
7760
7761     if (player->active && player->is_pushing && player->MovPos)
7762     {
7763       int next_jx = player->jx + (player->jx - player->last_jx);
7764       int next_jy = player->jy + (player->jy - player->last_jy);
7765
7766       if (x == next_jx && y == next_jy)
7767         return TRUE;
7768     }
7769   }
7770
7771   return FALSE;
7772 }
7773
7774 static void StartMoving(int x, int y)
7775 {
7776   boolean started_moving = FALSE;       // some elements can fall _and_ move
7777   int element = Tile[x][y];
7778
7779   if (Stop[x][y])
7780     return;
7781
7782   if (MovDelay[x][y] == 0)
7783     GfxAction[x][y] = ACTION_DEFAULT;
7784
7785   if (CAN_FALL(element) && y < lev_fieldy - 1)
7786   {
7787     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7788         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7789       if (JustBeingPushed(x, y))
7790         return;
7791
7792     if (element == EL_QUICKSAND_FULL)
7793     {
7794       if (IS_FREE(x, y + 1))
7795       {
7796         InitMovingField(x, y, MV_DOWN);
7797         started_moving = TRUE;
7798
7799         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7800 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7801         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7802           Store[x][y] = EL_ROCK;
7803 #else
7804         Store[x][y] = EL_ROCK;
7805 #endif
7806
7807         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7808       }
7809       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7810       {
7811         if (!MovDelay[x][y])
7812         {
7813           MovDelay[x][y] = TILEY + 1;
7814
7815           ResetGfxAnimation(x, y);
7816           ResetGfxAnimation(x, y + 1);
7817         }
7818
7819         if (MovDelay[x][y])
7820         {
7821           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7822           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7823
7824           MovDelay[x][y]--;
7825           if (MovDelay[x][y])
7826             return;
7827         }
7828
7829         Tile[x][y] = EL_QUICKSAND_EMPTY;
7830         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7831         Store[x][y + 1] = Store[x][y];
7832         Store[x][y] = 0;
7833
7834         PlayLevelSoundAction(x, y, ACTION_FILLING);
7835       }
7836       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7837       {
7838         if (!MovDelay[x][y])
7839         {
7840           MovDelay[x][y] = TILEY + 1;
7841
7842           ResetGfxAnimation(x, y);
7843           ResetGfxAnimation(x, y + 1);
7844         }
7845
7846         if (MovDelay[x][y])
7847         {
7848           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7849           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7850
7851           MovDelay[x][y]--;
7852           if (MovDelay[x][y])
7853             return;
7854         }
7855
7856         Tile[x][y] = EL_QUICKSAND_EMPTY;
7857         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7858         Store[x][y + 1] = Store[x][y];
7859         Store[x][y] = 0;
7860
7861         PlayLevelSoundAction(x, y, ACTION_FILLING);
7862       }
7863     }
7864     else if (element == EL_QUICKSAND_FAST_FULL)
7865     {
7866       if (IS_FREE(x, y + 1))
7867       {
7868         InitMovingField(x, y, MV_DOWN);
7869         started_moving = TRUE;
7870
7871         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7872 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7873         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7874           Store[x][y] = EL_ROCK;
7875 #else
7876         Store[x][y] = EL_ROCK;
7877 #endif
7878
7879         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7880       }
7881       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7882       {
7883         if (!MovDelay[x][y])
7884         {
7885           MovDelay[x][y] = TILEY + 1;
7886
7887           ResetGfxAnimation(x, y);
7888           ResetGfxAnimation(x, y + 1);
7889         }
7890
7891         if (MovDelay[x][y])
7892         {
7893           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7894           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7895
7896           MovDelay[x][y]--;
7897           if (MovDelay[x][y])
7898             return;
7899         }
7900
7901         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7902         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7903         Store[x][y + 1] = Store[x][y];
7904         Store[x][y] = 0;
7905
7906         PlayLevelSoundAction(x, y, ACTION_FILLING);
7907       }
7908       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7909       {
7910         if (!MovDelay[x][y])
7911         {
7912           MovDelay[x][y] = TILEY + 1;
7913
7914           ResetGfxAnimation(x, y);
7915           ResetGfxAnimation(x, y + 1);
7916         }
7917
7918         if (MovDelay[x][y])
7919         {
7920           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7921           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7922
7923           MovDelay[x][y]--;
7924           if (MovDelay[x][y])
7925             return;
7926         }
7927
7928         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7929         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7930         Store[x][y + 1] = Store[x][y];
7931         Store[x][y] = 0;
7932
7933         PlayLevelSoundAction(x, y, ACTION_FILLING);
7934       }
7935     }
7936     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7937              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7938     {
7939       InitMovingField(x, y, MV_DOWN);
7940       started_moving = TRUE;
7941
7942       Tile[x][y] = EL_QUICKSAND_FILLING;
7943       Store[x][y] = element;
7944
7945       PlayLevelSoundAction(x, y, ACTION_FILLING);
7946     }
7947     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7948              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7949     {
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952
7953       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7954       Store[x][y] = element;
7955
7956       PlayLevelSoundAction(x, y, ACTION_FILLING);
7957     }
7958     else if (element == EL_MAGIC_WALL_FULL)
7959     {
7960       if (IS_FREE(x, y + 1))
7961       {
7962         InitMovingField(x, y, MV_DOWN);
7963         started_moving = TRUE;
7964
7965         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7966         Store[x][y] = EL_CHANGED(Store[x][y]);
7967       }
7968       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7969       {
7970         if (!MovDelay[x][y])
7971           MovDelay[x][y] = TILEY / 4 + 1;
7972
7973         if (MovDelay[x][y])
7974         {
7975           MovDelay[x][y]--;
7976           if (MovDelay[x][y])
7977             return;
7978         }
7979
7980         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7981         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7982         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7983         Store[x][y] = 0;
7984       }
7985     }
7986     else if (element == EL_BD_MAGIC_WALL_FULL)
7987     {
7988       if (IS_FREE(x, y + 1))
7989       {
7990         InitMovingField(x, y, MV_DOWN);
7991         started_moving = TRUE;
7992
7993         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7994         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7995       }
7996       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7997       {
7998         if (!MovDelay[x][y])
7999           MovDelay[x][y] = TILEY / 4 + 1;
8000
8001         if (MovDelay[x][y])
8002         {
8003           MovDelay[x][y]--;
8004           if (MovDelay[x][y])
8005             return;
8006         }
8007
8008         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8009         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8010         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8011         Store[x][y] = 0;
8012       }
8013     }
8014     else if (element == EL_DC_MAGIC_WALL_FULL)
8015     {
8016       if (IS_FREE(x, y + 1))
8017       {
8018         InitMovingField(x, y, MV_DOWN);
8019         started_moving = TRUE;
8020
8021         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8022         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8023       }
8024       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8025       {
8026         if (!MovDelay[x][y])
8027           MovDelay[x][y] = TILEY / 4 + 1;
8028
8029         if (MovDelay[x][y])
8030         {
8031           MovDelay[x][y]--;
8032           if (MovDelay[x][y])
8033             return;
8034         }
8035
8036         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8037         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8038         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8039         Store[x][y] = 0;
8040       }
8041     }
8042     else if ((CAN_PASS_MAGIC_WALL(element) &&
8043               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8044                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8045              (CAN_PASS_DC_MAGIC_WALL(element) &&
8046               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8047
8048     {
8049       InitMovingField(x, y, MV_DOWN);
8050       started_moving = TRUE;
8051
8052       Tile[x][y] =
8053         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8054          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8055          EL_DC_MAGIC_WALL_FILLING);
8056       Store[x][y] = element;
8057     }
8058     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8059     {
8060       SplashAcid(x, y + 1);
8061
8062       InitMovingField(x, y, MV_DOWN);
8063       started_moving = TRUE;
8064
8065       Store[x][y] = EL_ACID;
8066     }
8067     else if (
8068              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8069               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8070              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8071               CAN_FALL(element) && WasJustFalling[x][y] &&
8072               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8073
8074              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8075               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8076               (Tile[x][y + 1] == EL_BLOCKED)))
8077     {
8078       /* this is needed for a special case not covered by calling "Impact()"
8079          from "ContinueMoving()": if an element moves to a tile directly below
8080          another element which was just falling on that tile (which was empty
8081          in the previous frame), the falling element above would just stop
8082          instead of smashing the element below (in previous version, the above
8083          element was just checked for "moving" instead of "falling", resulting
8084          in incorrect smashes caused by horizontal movement of the above
8085          element; also, the case of the player being the element to smash was
8086          simply not covered here... :-/ ) */
8087
8088       CheckCollision[x][y] = 0;
8089       CheckImpact[x][y] = 0;
8090
8091       Impact(x, y);
8092     }
8093     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8094     {
8095       if (MovDir[x][y] == MV_NONE)
8096       {
8097         InitMovingField(x, y, MV_DOWN);
8098         started_moving = TRUE;
8099       }
8100     }
8101     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8102     {
8103       if (WasJustFalling[x][y]) // prevent animation from being restarted
8104         MovDir[x][y] = MV_DOWN;
8105
8106       InitMovingField(x, y, MV_DOWN);
8107       started_moving = TRUE;
8108     }
8109     else if (element == EL_AMOEBA_DROP)
8110     {
8111       Tile[x][y] = EL_AMOEBA_GROWING;
8112       Store[x][y] = EL_AMOEBA_WET;
8113     }
8114     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8115               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8116              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8117              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8118     {
8119       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8120                                 (IS_FREE(x - 1, y + 1) ||
8121                                  Tile[x - 1][y + 1] == EL_ACID));
8122       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8123                                 (IS_FREE(x + 1, y + 1) ||
8124                                  Tile[x + 1][y + 1] == EL_ACID));
8125       boolean can_fall_any  = (can_fall_left || can_fall_right);
8126       boolean can_fall_both = (can_fall_left && can_fall_right);
8127       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8128
8129       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8130       {
8131         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8132           can_fall_right = FALSE;
8133         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8134           can_fall_left = FALSE;
8135         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8136           can_fall_right = FALSE;
8137         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8138           can_fall_left = FALSE;
8139
8140         can_fall_any  = (can_fall_left || can_fall_right);
8141         can_fall_both = FALSE;
8142       }
8143
8144       if (can_fall_both)
8145       {
8146         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8147           can_fall_right = FALSE;       // slip down on left side
8148         else
8149           can_fall_left = !(can_fall_right = RND(2));
8150
8151         can_fall_both = FALSE;
8152       }
8153
8154       if (can_fall_any)
8155       {
8156         // if not determined otherwise, prefer left side for slipping down
8157         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8158         started_moving = TRUE;
8159       }
8160     }
8161     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8162     {
8163       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8164       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8165       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8166       int belt_dir = game.belt_dir[belt_nr];
8167
8168       if ((belt_dir == MV_LEFT  && left_is_free) ||
8169           (belt_dir == MV_RIGHT && right_is_free))
8170       {
8171         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8172
8173         InitMovingField(x, y, belt_dir);
8174         started_moving = TRUE;
8175
8176         Pushed[x][y] = TRUE;
8177         Pushed[nextx][y] = TRUE;
8178
8179         GfxAction[x][y] = ACTION_DEFAULT;
8180       }
8181       else
8182       {
8183         MovDir[x][y] = 0;       // if element was moving, stop it
8184       }
8185     }
8186   }
8187
8188   // not "else if" because of elements that can fall and move (EL_SPRING)
8189   if (CAN_MOVE(element) && !started_moving)
8190   {
8191     int move_pattern = element_info[element].move_pattern;
8192     int newx, newy;
8193
8194     Moving2Blocked(x, y, &newx, &newy);
8195
8196     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8197       return;
8198
8199     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8200         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8201     {
8202       WasJustMoving[x][y] = 0;
8203       CheckCollision[x][y] = 0;
8204
8205       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8206
8207       if (Tile[x][y] != element)        // element has changed
8208         return;
8209     }
8210
8211     if (!MovDelay[x][y])        // start new movement phase
8212     {
8213       // all objects that can change their move direction after each step
8214       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8215
8216       if (element != EL_YAMYAM &&
8217           element != EL_DARK_YAMYAM &&
8218           element != EL_PACMAN &&
8219           !(move_pattern & MV_ANY_DIRECTION) &&
8220           move_pattern != MV_TURNING_LEFT &&
8221           move_pattern != MV_TURNING_RIGHT &&
8222           move_pattern != MV_TURNING_LEFT_RIGHT &&
8223           move_pattern != MV_TURNING_RIGHT_LEFT &&
8224           move_pattern != MV_TURNING_RANDOM)
8225       {
8226         TurnRound(x, y);
8227
8228         if (MovDelay[x][y] && (element == EL_BUG ||
8229                                element == EL_SPACESHIP ||
8230                                element == EL_SP_SNIKSNAK ||
8231                                element == EL_SP_ELECTRON ||
8232                                element == EL_MOLE))
8233           TEST_DrawLevelField(x, y);
8234       }
8235     }
8236
8237     if (MovDelay[x][y])         // wait some time before next movement
8238     {
8239       MovDelay[x][y]--;
8240
8241       if (element == EL_ROBOT ||
8242           element == EL_YAMYAM ||
8243           element == EL_DARK_YAMYAM)
8244       {
8245         DrawLevelElementAnimationIfNeeded(x, y, element);
8246         PlayLevelSoundAction(x, y, ACTION_WAITING);
8247       }
8248       else if (element == EL_SP_ELECTRON)
8249         DrawLevelElementAnimationIfNeeded(x, y, element);
8250       else if (element == EL_DRAGON)
8251       {
8252         int i;
8253         int dir = MovDir[x][y];
8254         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8255         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8256         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8257                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8258                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8259                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8260         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8261
8262         GfxAction[x][y] = ACTION_ATTACKING;
8263
8264         if (IS_PLAYER(x, y))
8265           DrawPlayerField(x, y);
8266         else
8267           TEST_DrawLevelField(x, y);
8268
8269         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8270
8271         for (i = 1; i <= 3; i++)
8272         {
8273           int xx = x + i * dx;
8274           int yy = y + i * dy;
8275           int sx = SCREENX(xx);
8276           int sy = SCREENY(yy);
8277           int flame_graphic = graphic + (i - 1);
8278
8279           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8280             break;
8281
8282           if (MovDelay[x][y])
8283           {
8284             int flamed = MovingOrBlocked2Element(xx, yy);
8285
8286             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8287               Bang(xx, yy);
8288             else
8289               RemoveMovingField(xx, yy);
8290
8291             ChangeDelay[xx][yy] = 0;
8292
8293             Tile[xx][yy] = EL_FLAMES;
8294
8295             if (IN_SCR_FIELD(sx, sy))
8296             {
8297               TEST_DrawLevelFieldCrumbled(xx, yy);
8298               DrawGraphic(sx, sy, flame_graphic, frame);
8299             }
8300           }
8301           else
8302           {
8303             if (Tile[xx][yy] == EL_FLAMES)
8304               Tile[xx][yy] = EL_EMPTY;
8305             TEST_DrawLevelField(xx, yy);
8306           }
8307         }
8308       }
8309
8310       if (MovDelay[x][y])       // element still has to wait some time
8311       {
8312         PlayLevelSoundAction(x, y, ACTION_WAITING);
8313
8314         return;
8315       }
8316     }
8317
8318     // now make next step
8319
8320     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8321
8322     if (DONT_COLLIDE_WITH(element) &&
8323         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8324         !PLAYER_ENEMY_PROTECTED(newx, newy))
8325     {
8326       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8327
8328       return;
8329     }
8330
8331     else if (CAN_MOVE_INTO_ACID(element) &&
8332              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8333              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8334              (MovDir[x][y] == MV_DOWN ||
8335               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8336     {
8337       SplashAcid(newx, newy);
8338       Store[x][y] = EL_ACID;
8339     }
8340     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8341     {
8342       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8343           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8344           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8345           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8346       {
8347         RemoveField(x, y);
8348         TEST_DrawLevelField(x, y);
8349
8350         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8351         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8352           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8353
8354         game.friends_still_needed--;
8355         if (!game.friends_still_needed &&
8356             !game.GameOver &&
8357             game.all_players_gone)
8358           LevelSolved();
8359
8360         return;
8361       }
8362       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8363       {
8364         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8365           TEST_DrawLevelField(newx, newy);
8366         else
8367           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8368       }
8369       else if (!IS_FREE(newx, newy))
8370       {
8371         GfxAction[x][y] = ACTION_WAITING;
8372
8373         if (IS_PLAYER(x, y))
8374           DrawPlayerField(x, y);
8375         else
8376           TEST_DrawLevelField(x, y);
8377
8378         return;
8379       }
8380     }
8381     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8382     {
8383       if (IS_FOOD_PIG(Tile[newx][newy]))
8384       {
8385         if (IS_MOVING(newx, newy))
8386           RemoveMovingField(newx, newy);
8387         else
8388         {
8389           Tile[newx][newy] = EL_EMPTY;
8390           TEST_DrawLevelField(newx, newy);
8391         }
8392
8393         PlayLevelSound(x, y, SND_PIG_DIGGING);
8394       }
8395       else if (!IS_FREE(newx, newy))
8396       {
8397         if (IS_PLAYER(x, y))
8398           DrawPlayerField(x, y);
8399         else
8400           TEST_DrawLevelField(x, y);
8401
8402         return;
8403       }
8404     }
8405     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8406     {
8407       if (Store[x][y] != EL_EMPTY)
8408       {
8409         boolean can_clone = FALSE;
8410         int xx, yy;
8411
8412         // check if element to clone is still there
8413         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8414         {
8415           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8416           {
8417             can_clone = TRUE;
8418
8419             break;
8420           }
8421         }
8422
8423         // cannot clone or target field not free anymore -- do not clone
8424         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8425           Store[x][y] = EL_EMPTY;
8426       }
8427
8428       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8429       {
8430         if (IS_MV_DIAGONAL(MovDir[x][y]))
8431         {
8432           int diagonal_move_dir = MovDir[x][y];
8433           int stored = Store[x][y];
8434           int change_delay = 8;
8435           int graphic;
8436
8437           // android is moving diagonally
8438
8439           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8440
8441           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8442           GfxElement[x][y] = EL_EMC_ANDROID;
8443           GfxAction[x][y] = ACTION_SHRINKING;
8444           GfxDir[x][y] = diagonal_move_dir;
8445           ChangeDelay[x][y] = change_delay;
8446
8447           if (Store[x][y] == EL_EMPTY)
8448             Store[x][y] = GfxElementEmpty[x][y];
8449
8450           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8451                                    GfxDir[x][y]);
8452
8453           DrawLevelGraphicAnimation(x, y, graphic);
8454           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8455
8456           if (Tile[newx][newy] == EL_ACID)
8457           {
8458             SplashAcid(newx, newy);
8459
8460             return;
8461           }
8462
8463           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8464
8465           Store[newx][newy] = EL_EMC_ANDROID;
8466           GfxElement[newx][newy] = EL_EMC_ANDROID;
8467           GfxAction[newx][newy] = ACTION_GROWING;
8468           GfxDir[newx][newy] = diagonal_move_dir;
8469           ChangeDelay[newx][newy] = change_delay;
8470
8471           graphic = el_act_dir2img(GfxElement[newx][newy],
8472                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8473
8474           DrawLevelGraphicAnimation(newx, newy, graphic);
8475           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8476
8477           return;
8478         }
8479         else
8480         {
8481           Tile[newx][newy] = EL_EMPTY;
8482           TEST_DrawLevelField(newx, newy);
8483
8484           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8485         }
8486       }
8487       else if (!IS_FREE(newx, newy))
8488       {
8489         return;
8490       }
8491     }
8492     else if (IS_CUSTOM_ELEMENT(element) &&
8493              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8494     {
8495       if (!DigFieldByCE(newx, newy, element))
8496         return;
8497
8498       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8499       {
8500         RunnerVisit[x][y] = FrameCounter;
8501         PlayerVisit[x][y] /= 8;         // expire player visit path
8502       }
8503     }
8504     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8505     {
8506       if (!IS_FREE(newx, newy))
8507       {
8508         if (IS_PLAYER(x, y))
8509           DrawPlayerField(x, y);
8510         else
8511           TEST_DrawLevelField(x, y);
8512
8513         return;
8514       }
8515       else
8516       {
8517         boolean wanna_flame = !RND(10);
8518         int dx = newx - x, dy = newy - y;
8519         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8520         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8521         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8522                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8523         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8524                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8525
8526         if ((wanna_flame ||
8527              IS_CLASSIC_ENEMY(element1) ||
8528              IS_CLASSIC_ENEMY(element2)) &&
8529             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8530             element1 != EL_FLAMES && element2 != EL_FLAMES)
8531         {
8532           ResetGfxAnimation(x, y);
8533           GfxAction[x][y] = ACTION_ATTACKING;
8534
8535           if (IS_PLAYER(x, y))
8536             DrawPlayerField(x, y);
8537           else
8538             TEST_DrawLevelField(x, y);
8539
8540           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8541
8542           MovDelay[x][y] = 50;
8543
8544           Tile[newx][newy] = EL_FLAMES;
8545           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8546             Tile[newx1][newy1] = EL_FLAMES;
8547           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8548             Tile[newx2][newy2] = EL_FLAMES;
8549
8550           return;
8551         }
8552       }
8553     }
8554     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8555              Tile[newx][newy] == EL_DIAMOND)
8556     {
8557       if (IS_MOVING(newx, newy))
8558         RemoveMovingField(newx, newy);
8559       else
8560       {
8561         Tile[newx][newy] = EL_EMPTY;
8562         TEST_DrawLevelField(newx, newy);
8563       }
8564
8565       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8566     }
8567     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8568              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8569     {
8570       if (AmoebaNr[newx][newy])
8571       {
8572         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8573         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8574             Tile[newx][newy] == EL_BD_AMOEBA)
8575           AmoebaCnt[AmoebaNr[newx][newy]]--;
8576       }
8577
8578       if (IS_MOVING(newx, newy))
8579       {
8580         RemoveMovingField(newx, newy);
8581       }
8582       else
8583       {
8584         Tile[newx][newy] = EL_EMPTY;
8585         TEST_DrawLevelField(newx, newy);
8586       }
8587
8588       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8589     }
8590     else if ((element == EL_PACMAN || element == EL_MOLE)
8591              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8592     {
8593       if (AmoebaNr[newx][newy])
8594       {
8595         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8596         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8597             Tile[newx][newy] == EL_BD_AMOEBA)
8598           AmoebaCnt[AmoebaNr[newx][newy]]--;
8599       }
8600
8601       if (element == EL_MOLE)
8602       {
8603         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8604         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8605
8606         ResetGfxAnimation(x, y);
8607         GfxAction[x][y] = ACTION_DIGGING;
8608         TEST_DrawLevelField(x, y);
8609
8610         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8611
8612         return;                         // wait for shrinking amoeba
8613       }
8614       else      // element == EL_PACMAN
8615       {
8616         Tile[newx][newy] = EL_EMPTY;
8617         TEST_DrawLevelField(newx, newy);
8618         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8619       }
8620     }
8621     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8622              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8623               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8624     {
8625       // wait for shrinking amoeba to completely disappear
8626       return;
8627     }
8628     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8629     {
8630       // object was running against a wall
8631
8632       TurnRound(x, y);
8633
8634       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8635         DrawLevelElementAnimation(x, y, element);
8636
8637       if (DONT_TOUCH(element))
8638         TestIfBadThingTouchesPlayer(x, y);
8639
8640       return;
8641     }
8642
8643     InitMovingField(x, y, MovDir[x][y]);
8644
8645     PlayLevelSoundAction(x, y, ACTION_MOVING);
8646   }
8647
8648   if (MovDir[x][y])
8649     ContinueMoving(x, y);
8650 }
8651
8652 void ContinueMoving(int x, int y)
8653 {
8654   int element = Tile[x][y];
8655   struct ElementInfo *ei = &element_info[element];
8656   int direction = MovDir[x][y];
8657   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8658   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8659   int newx = x + dx, newy = y + dy;
8660   int stored = Store[x][y];
8661   int stored_new = Store[newx][newy];
8662   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8663   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8664   boolean last_line = (newy == lev_fieldy - 1);
8665   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8666
8667   if (pushed_by_player)         // special case: moving object pushed by player
8668   {
8669     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8670   }
8671   else if (use_step_delay)      // special case: moving object has step delay
8672   {
8673     if (!MovDelay[x][y])
8674       MovPos[x][y] += getElementMoveStepsize(x, y);
8675
8676     if (MovDelay[x][y])
8677       MovDelay[x][y]--;
8678     else
8679       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8680
8681     if (MovDelay[x][y])
8682     {
8683       TEST_DrawLevelField(x, y);
8684
8685       return;   // element is still waiting
8686     }
8687   }
8688   else                          // normal case: generically moving object
8689   {
8690     MovPos[x][y] += getElementMoveStepsize(x, y);
8691   }
8692
8693   if (ABS(MovPos[x][y]) < TILEX)
8694   {
8695     TEST_DrawLevelField(x, y);
8696
8697     return;     // element is still moving
8698   }
8699
8700   // element reached destination field
8701
8702   Tile[x][y] = EL_EMPTY;
8703   Tile[newx][newy] = element;
8704   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8705
8706   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8707   {
8708     element = Tile[newx][newy] = EL_ACID;
8709   }
8710   else if (element == EL_MOLE)
8711   {
8712     Tile[x][y] = EL_SAND;
8713
8714     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8715   }
8716   else if (element == EL_QUICKSAND_FILLING)
8717   {
8718     element = Tile[newx][newy] = get_next_element(element);
8719     Store[newx][newy] = Store[x][y];
8720   }
8721   else if (element == EL_QUICKSAND_EMPTYING)
8722   {
8723     Tile[x][y] = get_next_element(element);
8724     element = Tile[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_QUICKSAND_FAST_FILLING)
8727   {
8728     element = Tile[newx][newy] = get_next_element(element);
8729     Store[newx][newy] = Store[x][y];
8730   }
8731   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8732   {
8733     Tile[x][y] = get_next_element(element);
8734     element = Tile[newx][newy] = Store[x][y];
8735   }
8736   else if (element == EL_MAGIC_WALL_FILLING)
8737   {
8738     element = Tile[newx][newy] = get_next_element(element);
8739     if (!game.magic_wall_active)
8740       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8741     Store[newx][newy] = Store[x][y];
8742   }
8743   else if (element == EL_MAGIC_WALL_EMPTYING)
8744   {
8745     Tile[x][y] = get_next_element(element);
8746     if (!game.magic_wall_active)
8747       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8748     element = Tile[newx][newy] = Store[x][y];
8749
8750     InitField(newx, newy, FALSE);
8751   }
8752   else if (element == EL_BD_MAGIC_WALL_FILLING)
8753   {
8754     element = Tile[newx][newy] = get_next_element(element);
8755     if (!game.magic_wall_active)
8756       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8757     Store[newx][newy] = Store[x][y];
8758   }
8759   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8760   {
8761     Tile[x][y] = get_next_element(element);
8762     if (!game.magic_wall_active)
8763       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8764     element = Tile[newx][newy] = Store[x][y];
8765
8766     InitField(newx, newy, FALSE);
8767   }
8768   else if (element == EL_DC_MAGIC_WALL_FILLING)
8769   {
8770     element = Tile[newx][newy] = get_next_element(element);
8771     if (!game.magic_wall_active)
8772       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8773     Store[newx][newy] = Store[x][y];
8774   }
8775   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8776   {
8777     Tile[x][y] = get_next_element(element);
8778     if (!game.magic_wall_active)
8779       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8780     element = Tile[newx][newy] = Store[x][y];
8781
8782     InitField(newx, newy, FALSE);
8783   }
8784   else if (element == EL_AMOEBA_DROPPING)
8785   {
8786     Tile[x][y] = get_next_element(element);
8787     element = Tile[newx][newy] = Store[x][y];
8788   }
8789   else if (element == EL_SOKOBAN_OBJECT)
8790   {
8791     if (Back[x][y])
8792       Tile[x][y] = Back[x][y];
8793
8794     if (Back[newx][newy])
8795       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8796
8797     Back[x][y] = Back[newx][newy] = 0;
8798   }
8799
8800   Store[x][y] = EL_EMPTY;
8801   MovPos[x][y] = 0;
8802   MovDir[x][y] = 0;
8803   MovDelay[x][y] = 0;
8804
8805   MovDelay[newx][newy] = 0;
8806
8807   if (CAN_CHANGE_OR_HAS_ACTION(element))
8808   {
8809     // copy element change control values to new field
8810     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8811     ChangePage[newx][newy]  = ChangePage[x][y];
8812     ChangeCount[newx][newy] = ChangeCount[x][y];
8813     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8814   }
8815
8816   CustomValue[newx][newy] = CustomValue[x][y];
8817
8818   ChangeDelay[x][y] = 0;
8819   ChangePage[x][y] = -1;
8820   ChangeCount[x][y] = 0;
8821   ChangeEvent[x][y] = -1;
8822
8823   CustomValue[x][y] = 0;
8824
8825   // copy animation control values to new field
8826   GfxFrame[newx][newy]  = GfxFrame[x][y];
8827   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8828   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8829   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8830
8831   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8832
8833   // some elements can leave other elements behind after moving
8834   if (ei->move_leave_element != EL_EMPTY &&
8835       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8836       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8837   {
8838     int move_leave_element = ei->move_leave_element;
8839
8840     // this makes it possible to leave the removed element again
8841     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8842       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8843
8844     Tile[x][y] = move_leave_element;
8845
8846     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8847       MovDir[x][y] = direction;
8848
8849     InitField(x, y, FALSE);
8850
8851     if (GFX_CRUMBLED(Tile[x][y]))
8852       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8853
8854     if (IS_PLAYER_ELEMENT(move_leave_element))
8855       RelocatePlayer(x, y, move_leave_element);
8856   }
8857
8858   // do this after checking for left-behind element
8859   ResetGfxAnimation(x, y);      // reset animation values for old field
8860
8861   if (!CAN_MOVE(element) ||
8862       (CAN_FALL(element) && direction == MV_DOWN &&
8863        (element == EL_SPRING ||
8864         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8865         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8866     GfxDir[x][y] = MovDir[newx][newy] = 0;
8867
8868   TEST_DrawLevelField(x, y);
8869   TEST_DrawLevelField(newx, newy);
8870
8871   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8872
8873   // prevent pushed element from moving on in pushed direction
8874   if (pushed_by_player && CAN_MOVE(element) &&
8875       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8876       !(element_info[element].move_pattern & direction))
8877     TurnRound(newx, newy);
8878
8879   // prevent elements on conveyor belt from moving on in last direction
8880   if (pushed_by_conveyor && CAN_FALL(element) &&
8881       direction & MV_HORIZONTAL)
8882     MovDir[newx][newy] = 0;
8883
8884   if (!pushed_by_player)
8885   {
8886     int nextx = newx + dx, nexty = newy + dy;
8887     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8888
8889     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8890
8891     if (CAN_FALL(element) && direction == MV_DOWN)
8892       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8893
8894     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8895       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8896
8897     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8898       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8899   }
8900
8901   if (DONT_TOUCH(element))      // object may be nasty to player or others
8902   {
8903     TestIfBadThingTouchesPlayer(newx, newy);
8904     TestIfBadThingTouchesFriend(newx, newy);
8905
8906     if (!IS_CUSTOM_ELEMENT(element))
8907       TestIfBadThingTouchesOtherBadThing(newx, newy);
8908   }
8909   else if (element == EL_PENGUIN)
8910     TestIfFriendTouchesBadThing(newx, newy);
8911
8912   if (DONT_GET_HIT_BY(element))
8913   {
8914     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8915   }
8916
8917   // give the player one last chance (one more frame) to move away
8918   if (CAN_FALL(element) && direction == MV_DOWN &&
8919       (last_line || (!IS_FREE(x, newy + 1) &&
8920                      (!IS_PLAYER(x, newy + 1) ||
8921                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8922     Impact(x, newy);
8923
8924   if (pushed_by_player && !game.use_change_when_pushing_bug)
8925   {
8926     int push_side = MV_DIR_OPPOSITE(direction);
8927     struct PlayerInfo *player = PLAYERINFO(x, y);
8928
8929     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8930                                player->index_bit, push_side);
8931     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8932                                         player->index_bit, push_side);
8933   }
8934
8935   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8936     MovDelay[newx][newy] = 1;
8937
8938   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8939
8940   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8941   TestIfElementHitsCustomElement(newx, newy, direction);
8942   TestIfPlayerTouchesCustomElement(newx, newy);
8943   TestIfElementTouchesCustomElement(newx, newy);
8944
8945   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8946       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8947     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8948                              MV_DIR_OPPOSITE(direction));
8949 }
8950
8951 int AmoebaNeighbourNr(int ax, int ay)
8952 {
8953   int i;
8954   int element = Tile[ax][ay];
8955   int group_nr = 0;
8956   static int xy[4][2] =
8957   {
8958     { 0, -1 },
8959     { -1, 0 },
8960     { +1, 0 },
8961     { 0, +1 }
8962   };
8963
8964   for (i = 0; i < NUM_DIRECTIONS; i++)
8965   {
8966     int x = ax + xy[i][0];
8967     int y = ay + xy[i][1];
8968
8969     if (!IN_LEV_FIELD(x, y))
8970       continue;
8971
8972     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8973       group_nr = AmoebaNr[x][y];
8974   }
8975
8976   return group_nr;
8977 }
8978
8979 static void AmoebaMerge(int ax, int ay)
8980 {
8981   int i, x, y, xx, yy;
8982   int new_group_nr = AmoebaNr[ax][ay];
8983   static int xy[4][2] =
8984   {
8985     { 0, -1 },
8986     { -1, 0 },
8987     { +1, 0 },
8988     { 0, +1 }
8989   };
8990
8991   if (new_group_nr == 0)
8992     return;
8993
8994   for (i = 0; i < NUM_DIRECTIONS; i++)
8995   {
8996     x = ax + xy[i][0];
8997     y = ay + xy[i][1];
8998
8999     if (!IN_LEV_FIELD(x, y))
9000       continue;
9001
9002     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9003          Tile[x][y] == EL_BD_AMOEBA ||
9004          Tile[x][y] == EL_AMOEBA_DEAD) &&
9005         AmoebaNr[x][y] != new_group_nr)
9006     {
9007       int old_group_nr = AmoebaNr[x][y];
9008
9009       if (old_group_nr == 0)
9010         return;
9011
9012       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9013       AmoebaCnt[old_group_nr] = 0;
9014       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9015       AmoebaCnt2[old_group_nr] = 0;
9016
9017       SCAN_PLAYFIELD(xx, yy)
9018       {
9019         if (AmoebaNr[xx][yy] == old_group_nr)
9020           AmoebaNr[xx][yy] = new_group_nr;
9021       }
9022     }
9023   }
9024 }
9025
9026 void AmoebaToDiamond(int ax, int ay)
9027 {
9028   int i, x, y;
9029
9030   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9031   {
9032     int group_nr = AmoebaNr[ax][ay];
9033
9034 #ifdef DEBUG
9035     if (group_nr == 0)
9036     {
9037       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9038       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9039
9040       return;
9041     }
9042 #endif
9043
9044     SCAN_PLAYFIELD(x, y)
9045     {
9046       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9047       {
9048         AmoebaNr[x][y] = 0;
9049         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9050       }
9051     }
9052
9053     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9054                             SND_AMOEBA_TURNING_TO_GEM :
9055                             SND_AMOEBA_TURNING_TO_ROCK));
9056     Bang(ax, ay);
9057   }
9058   else
9059   {
9060     static int xy[4][2] =
9061     {
9062       { 0, -1 },
9063       { -1, 0 },
9064       { +1, 0 },
9065       { 0, +1 }
9066     };
9067
9068     for (i = 0; i < NUM_DIRECTIONS; i++)
9069     {
9070       x = ax + xy[i][0];
9071       y = ay + xy[i][1];
9072
9073       if (!IN_LEV_FIELD(x, y))
9074         continue;
9075
9076       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9077       {
9078         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9079                               SND_AMOEBA_TURNING_TO_GEM :
9080                               SND_AMOEBA_TURNING_TO_ROCK));
9081         Bang(x, y);
9082       }
9083     }
9084   }
9085 }
9086
9087 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9088 {
9089   int x, y;
9090   int group_nr = AmoebaNr[ax][ay];
9091   boolean done = FALSE;
9092
9093 #ifdef DEBUG
9094   if (group_nr == 0)
9095   {
9096     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9097     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9098
9099     return;
9100   }
9101 #endif
9102
9103   SCAN_PLAYFIELD(x, y)
9104   {
9105     if (AmoebaNr[x][y] == group_nr &&
9106         (Tile[x][y] == EL_AMOEBA_DEAD ||
9107          Tile[x][y] == EL_BD_AMOEBA ||
9108          Tile[x][y] == EL_AMOEBA_GROWING))
9109     {
9110       AmoebaNr[x][y] = 0;
9111       Tile[x][y] = new_element;
9112       InitField(x, y, FALSE);
9113       TEST_DrawLevelField(x, y);
9114       done = TRUE;
9115     }
9116   }
9117
9118   if (done)
9119     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9120                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9121                             SND_BD_AMOEBA_TURNING_TO_GEM));
9122 }
9123
9124 static void AmoebaGrowing(int x, int y)
9125 {
9126   static unsigned int sound_delay = 0;
9127   static unsigned int sound_delay_value = 0;
9128
9129   if (!MovDelay[x][y])          // start new growing cycle
9130   {
9131     MovDelay[x][y] = 7;
9132
9133     if (DelayReached(&sound_delay, sound_delay_value))
9134     {
9135       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9136       sound_delay_value = 30;
9137     }
9138   }
9139
9140   if (MovDelay[x][y])           // wait some time before growing bigger
9141   {
9142     MovDelay[x][y]--;
9143     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9144     {
9145       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9146                                            6 - MovDelay[x][y]);
9147
9148       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9149     }
9150
9151     if (!MovDelay[x][y])
9152     {
9153       Tile[x][y] = Store[x][y];
9154       Store[x][y] = 0;
9155       TEST_DrawLevelField(x, y);
9156     }
9157   }
9158 }
9159
9160 static void AmoebaShrinking(int x, int y)
9161 {
9162   static unsigned int sound_delay = 0;
9163   static unsigned int sound_delay_value = 0;
9164
9165   if (!MovDelay[x][y])          // start new shrinking cycle
9166   {
9167     MovDelay[x][y] = 7;
9168
9169     if (DelayReached(&sound_delay, sound_delay_value))
9170       sound_delay_value = 30;
9171   }
9172
9173   if (MovDelay[x][y])           // wait some time before shrinking
9174   {
9175     MovDelay[x][y]--;
9176     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9177     {
9178       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9179                                            6 - MovDelay[x][y]);
9180
9181       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9182     }
9183
9184     if (!MovDelay[x][y])
9185     {
9186       Tile[x][y] = EL_EMPTY;
9187       TEST_DrawLevelField(x, y);
9188
9189       // don't let mole enter this field in this cycle;
9190       // (give priority to objects falling to this field from above)
9191       Stop[x][y] = TRUE;
9192     }
9193   }
9194 }
9195
9196 static void AmoebaReproduce(int ax, int ay)
9197 {
9198   int i;
9199   int element = Tile[ax][ay];
9200   int graphic = el2img(element);
9201   int newax = ax, neway = ay;
9202   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9203   static int xy[4][2] =
9204   {
9205     { 0, -1 },
9206     { -1, 0 },
9207     { +1, 0 },
9208     { 0, +1 }
9209   };
9210
9211   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9212   {
9213     Tile[ax][ay] = EL_AMOEBA_DEAD;
9214     TEST_DrawLevelField(ax, ay);
9215     return;
9216   }
9217
9218   if (IS_ANIMATED(graphic))
9219     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9220
9221   if (!MovDelay[ax][ay])        // start making new amoeba field
9222     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9223
9224   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9225   {
9226     MovDelay[ax][ay]--;
9227     if (MovDelay[ax][ay])
9228       return;
9229   }
9230
9231   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9232   {
9233     int start = RND(4);
9234     int x = ax + xy[start][0];
9235     int y = ay + xy[start][1];
9236
9237     if (!IN_LEV_FIELD(x, y))
9238       return;
9239
9240     if (IS_FREE(x, y) ||
9241         CAN_GROW_INTO(Tile[x][y]) ||
9242         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9243         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9244     {
9245       newax = x;
9246       neway = y;
9247     }
9248
9249     if (newax == ax && neway == ay)
9250       return;
9251   }
9252   else                          // normal or "filled" (BD style) amoeba
9253   {
9254     int start = RND(4);
9255     boolean waiting_for_player = FALSE;
9256
9257     for (i = 0; i < NUM_DIRECTIONS; i++)
9258     {
9259       int j = (start + i) % 4;
9260       int x = ax + xy[j][0];
9261       int y = ay + xy[j][1];
9262
9263       if (!IN_LEV_FIELD(x, y))
9264         continue;
9265
9266       if (IS_FREE(x, y) ||
9267           CAN_GROW_INTO(Tile[x][y]) ||
9268           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9269           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9270       {
9271         newax = x;
9272         neway = y;
9273         break;
9274       }
9275       else if (IS_PLAYER(x, y))
9276         waiting_for_player = TRUE;
9277     }
9278
9279     if (newax == ax && neway == ay)             // amoeba cannot grow
9280     {
9281       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9282       {
9283         Tile[ax][ay] = EL_AMOEBA_DEAD;
9284         TEST_DrawLevelField(ax, ay);
9285         AmoebaCnt[AmoebaNr[ax][ay]]--;
9286
9287         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9288         {
9289           if (element == EL_AMOEBA_FULL)
9290             AmoebaToDiamond(ax, ay);
9291           else if (element == EL_BD_AMOEBA)
9292             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9293         }
9294       }
9295       return;
9296     }
9297     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9298     {
9299       // amoeba gets larger by growing in some direction
9300
9301       int new_group_nr = AmoebaNr[ax][ay];
9302
9303 #ifdef DEBUG
9304   if (new_group_nr == 0)
9305   {
9306     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9307           newax, neway);
9308     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9309
9310     return;
9311   }
9312 #endif
9313
9314       AmoebaNr[newax][neway] = new_group_nr;
9315       AmoebaCnt[new_group_nr]++;
9316       AmoebaCnt2[new_group_nr]++;
9317
9318       // if amoeba touches other amoeba(s) after growing, unify them
9319       AmoebaMerge(newax, neway);
9320
9321       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9322       {
9323         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9324         return;
9325       }
9326     }
9327   }
9328
9329   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9330       (neway == lev_fieldy - 1 && newax != ax))
9331   {
9332     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9333     Store[newax][neway] = element;
9334   }
9335   else if (neway == ay || element == EL_EMC_DRIPPER)
9336   {
9337     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9338
9339     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9340   }
9341   else
9342   {
9343     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9344     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9345     Store[ax][ay] = EL_AMOEBA_DROP;
9346     ContinueMoving(ax, ay);
9347     return;
9348   }
9349
9350   TEST_DrawLevelField(newax, neway);
9351 }
9352
9353 static void Life(int ax, int ay)
9354 {
9355   int x1, y1, x2, y2;
9356   int life_time = 40;
9357   int element = Tile[ax][ay];
9358   int graphic = el2img(element);
9359   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9360                          level.biomaze);
9361   boolean changed = FALSE;
9362
9363   if (IS_ANIMATED(graphic))
9364     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9365
9366   if (Stop[ax][ay])
9367     return;
9368
9369   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9370     MovDelay[ax][ay] = life_time;
9371
9372   if (MovDelay[ax][ay])         // wait some time before next cycle
9373   {
9374     MovDelay[ax][ay]--;
9375     if (MovDelay[ax][ay])
9376       return;
9377   }
9378
9379   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9380   {
9381     int xx = ax+x1, yy = ay+y1;
9382     int old_element = Tile[xx][yy];
9383     int num_neighbours = 0;
9384
9385     if (!IN_LEV_FIELD(xx, yy))
9386       continue;
9387
9388     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9389     {
9390       int x = xx+x2, y = yy+y2;
9391
9392       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9393         continue;
9394
9395       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9396       boolean is_neighbour = FALSE;
9397
9398       if (level.use_life_bugs)
9399         is_neighbour =
9400           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9401            (IS_FREE(x, y)                             &&  Stop[x][y]));
9402       else
9403         is_neighbour =
9404           (Last[x][y] == element || is_player_cell);
9405
9406       if (is_neighbour)
9407         num_neighbours++;
9408     }
9409
9410     boolean is_free = FALSE;
9411
9412     if (level.use_life_bugs)
9413       is_free = (IS_FREE(xx, yy));
9414     else
9415       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9416
9417     if (xx == ax && yy == ay)           // field in the middle
9418     {
9419       if (num_neighbours < life_parameter[0] ||
9420           num_neighbours > life_parameter[1])
9421       {
9422         Tile[xx][yy] = EL_EMPTY;
9423         if (Tile[xx][yy] != old_element)
9424           TEST_DrawLevelField(xx, yy);
9425         Stop[xx][yy] = TRUE;
9426         changed = TRUE;
9427       }
9428     }
9429     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9430     {                                   // free border field
9431       if (num_neighbours >= life_parameter[2] &&
9432           num_neighbours <= life_parameter[3])
9433       {
9434         Tile[xx][yy] = element;
9435         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9436         if (Tile[xx][yy] != old_element)
9437           TEST_DrawLevelField(xx, yy);
9438         Stop[xx][yy] = TRUE;
9439         changed = TRUE;
9440       }
9441     }
9442   }
9443
9444   if (changed)
9445     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9446                    SND_GAME_OF_LIFE_GROWING);
9447 }
9448
9449 static void InitRobotWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunRobotWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9457 }
9458
9459 static void StopRobotWheel(int x, int y)
9460 {
9461   if (game.robot_wheel_x == x &&
9462       game.robot_wheel_y == y)
9463   {
9464     game.robot_wheel_x = -1;
9465     game.robot_wheel_y = -1;
9466     game.robot_wheel_active = FALSE;
9467   }
9468 }
9469
9470 static void InitTimegateWheel(int x, int y)
9471 {
9472   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9473 }
9474
9475 static void RunTimegateWheel(int x, int y)
9476 {
9477   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9478 }
9479
9480 static void InitMagicBallDelay(int x, int y)
9481 {
9482   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9483 }
9484
9485 static void ActivateMagicBall(int bx, int by)
9486 {
9487   int x, y;
9488
9489   if (level.ball_random)
9490   {
9491     int pos_border = RND(8);    // select one of the eight border elements
9492     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9493     int xx = pos_content % 3;
9494     int yy = pos_content / 3;
9495
9496     x = bx - 1 + xx;
9497     y = by - 1 + yy;
9498
9499     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9500       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9501   }
9502   else
9503   {
9504     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9505     {
9506       int xx = x - bx + 1;
9507       int yy = y - by + 1;
9508
9509       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9510         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9511     }
9512   }
9513
9514   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9515 }
9516
9517 static void CheckExit(int x, int y)
9518 {
9519   if (game.gems_still_needed > 0 ||
9520       game.sokoban_fields_still_needed > 0 ||
9521       game.sokoban_objects_still_needed > 0 ||
9522       game.lights_still_needed > 0)
9523   {
9524     int element = Tile[x][y];
9525     int graphic = el2img(element);
9526
9527     if (IS_ANIMATED(graphic))
9528       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9529
9530     return;
9531   }
9532
9533   // do not re-open exit door closed after last player
9534   if (game.all_players_gone)
9535     return;
9536
9537   Tile[x][y] = EL_EXIT_OPENING;
9538
9539   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9540 }
9541
9542 static void CheckExitEM(int x, int y)
9543 {
9544   if (game.gems_still_needed > 0 ||
9545       game.sokoban_fields_still_needed > 0 ||
9546       game.sokoban_objects_still_needed > 0 ||
9547       game.lights_still_needed > 0)
9548   {
9549     int element = Tile[x][y];
9550     int graphic = el2img(element);
9551
9552     if (IS_ANIMATED(graphic))
9553       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9554
9555     return;
9556   }
9557
9558   // do not re-open exit door closed after last player
9559   if (game.all_players_gone)
9560     return;
9561
9562   Tile[x][y] = EL_EM_EXIT_OPENING;
9563
9564   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9565 }
9566
9567 static void CheckExitSteel(int x, int y)
9568 {
9569   if (game.gems_still_needed > 0 ||
9570       game.sokoban_fields_still_needed > 0 ||
9571       game.sokoban_objects_still_needed > 0 ||
9572       game.lights_still_needed > 0)
9573   {
9574     int element = Tile[x][y];
9575     int graphic = el2img(element);
9576
9577     if (IS_ANIMATED(graphic))
9578       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9579
9580     return;
9581   }
9582
9583   // do not re-open exit door closed after last player
9584   if (game.all_players_gone)
9585     return;
9586
9587   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9588
9589   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9590 }
9591
9592 static void CheckExitSteelEM(int x, int y)
9593 {
9594   if (game.gems_still_needed > 0 ||
9595       game.sokoban_fields_still_needed > 0 ||
9596       game.sokoban_objects_still_needed > 0 ||
9597       game.lights_still_needed > 0)
9598   {
9599     int element = Tile[x][y];
9600     int graphic = el2img(element);
9601
9602     if (IS_ANIMATED(graphic))
9603       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9604
9605     return;
9606   }
9607
9608   // do not re-open exit door closed after last player
9609   if (game.all_players_gone)
9610     return;
9611
9612   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9613
9614   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9615 }
9616
9617 static void CheckExitSP(int x, int y)
9618 {
9619   if (game.gems_still_needed > 0)
9620   {
9621     int element = Tile[x][y];
9622     int graphic = el2img(element);
9623
9624     if (IS_ANIMATED(graphic))
9625       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9626
9627     return;
9628   }
9629
9630   // do not re-open exit door closed after last player
9631   if (game.all_players_gone)
9632     return;
9633
9634   Tile[x][y] = EL_SP_EXIT_OPENING;
9635
9636   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9637 }
9638
9639 static void CloseAllOpenTimegates(void)
9640 {
9641   int x, y;
9642
9643   SCAN_PLAYFIELD(x, y)
9644   {
9645     int element = Tile[x][y];
9646
9647     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9648     {
9649       Tile[x][y] = EL_TIMEGATE_CLOSING;
9650
9651       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9652     }
9653   }
9654 }
9655
9656 static void DrawTwinkleOnField(int x, int y)
9657 {
9658   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9659     return;
9660
9661   if (Tile[x][y] == EL_BD_DIAMOND)
9662     return;
9663
9664   if (MovDelay[x][y] == 0)      // next animation frame
9665     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9666
9667   if (MovDelay[x][y] != 0)      // wait some time before next frame
9668   {
9669     MovDelay[x][y]--;
9670
9671     DrawLevelElementAnimation(x, y, Tile[x][y]);
9672
9673     if (MovDelay[x][y] != 0)
9674     {
9675       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9676                                            10 - MovDelay[x][y]);
9677
9678       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9679     }
9680   }
9681 }
9682
9683 static void MauerWaechst(int x, int y)
9684 {
9685   int delay = 6;
9686
9687   if (!MovDelay[x][y])          // next animation frame
9688     MovDelay[x][y] = 3 * delay;
9689
9690   if (MovDelay[x][y])           // wait some time before next frame
9691   {
9692     MovDelay[x][y]--;
9693
9694     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9695     {
9696       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9697       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9698
9699       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9700     }
9701
9702     if (!MovDelay[x][y])
9703     {
9704       if (MovDir[x][y] == MV_LEFT)
9705       {
9706         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9707           TEST_DrawLevelField(x - 1, y);
9708       }
9709       else if (MovDir[x][y] == MV_RIGHT)
9710       {
9711         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9712           TEST_DrawLevelField(x + 1, y);
9713       }
9714       else if (MovDir[x][y] == MV_UP)
9715       {
9716         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9717           TEST_DrawLevelField(x, y - 1);
9718       }
9719       else
9720       {
9721         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9722           TEST_DrawLevelField(x, y + 1);
9723       }
9724
9725       Tile[x][y] = Store[x][y];
9726       Store[x][y] = 0;
9727       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9728       TEST_DrawLevelField(x, y);
9729     }
9730   }
9731 }
9732
9733 static void MauerAbleger(int ax, int ay)
9734 {
9735   int element = Tile[ax][ay];
9736   int graphic = el2img(element);
9737   boolean oben_frei = FALSE, unten_frei = FALSE;
9738   boolean links_frei = FALSE, rechts_frei = FALSE;
9739   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9740   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9741   boolean new_wall = FALSE;
9742
9743   if (IS_ANIMATED(graphic))
9744     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9745
9746   if (!MovDelay[ax][ay])        // start building new wall
9747     MovDelay[ax][ay] = 6;
9748
9749   if (MovDelay[ax][ay])         // wait some time before building new wall
9750   {
9751     MovDelay[ax][ay]--;
9752     if (MovDelay[ax][ay])
9753       return;
9754   }
9755
9756   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9757     oben_frei = TRUE;
9758   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9759     unten_frei = TRUE;
9760   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9761     links_frei = TRUE;
9762   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9763     rechts_frei = TRUE;
9764
9765   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9766       element == EL_EXPANDABLE_WALL_ANY)
9767   {
9768     if (oben_frei)
9769     {
9770       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9771       Store[ax][ay-1] = element;
9772       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9773       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9774         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9775                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9776       new_wall = TRUE;
9777     }
9778     if (unten_frei)
9779     {
9780       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9781       Store[ax][ay+1] = element;
9782       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9783       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9784         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9785                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9786       new_wall = TRUE;
9787     }
9788   }
9789
9790   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9791       element == EL_EXPANDABLE_WALL_ANY ||
9792       element == EL_EXPANDABLE_WALL ||
9793       element == EL_BD_EXPANDABLE_WALL)
9794   {
9795     if (links_frei)
9796     {
9797       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9798       Store[ax-1][ay] = element;
9799       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9800       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9801         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9802                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9803       new_wall = TRUE;
9804     }
9805
9806     if (rechts_frei)
9807     {
9808       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9809       Store[ax+1][ay] = element;
9810       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9811       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9812         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9813                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9814       new_wall = TRUE;
9815     }
9816   }
9817
9818   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9819     TEST_DrawLevelField(ax, ay);
9820
9821   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9822     oben_massiv = TRUE;
9823   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9824     unten_massiv = TRUE;
9825   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9826     links_massiv = TRUE;
9827   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9828     rechts_massiv = TRUE;
9829
9830   if (((oben_massiv && unten_massiv) ||
9831        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9832        element == EL_EXPANDABLE_WALL) &&
9833       ((links_massiv && rechts_massiv) ||
9834        element == EL_EXPANDABLE_WALL_VERTICAL))
9835     Tile[ax][ay] = EL_WALL;
9836
9837   if (new_wall)
9838     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9839 }
9840
9841 static void MauerAblegerStahl(int ax, int ay)
9842 {
9843   int element = Tile[ax][ay];
9844   int graphic = el2img(element);
9845   boolean oben_frei = FALSE, unten_frei = FALSE;
9846   boolean links_frei = FALSE, rechts_frei = FALSE;
9847   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9848   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9849   boolean new_wall = FALSE;
9850
9851   if (IS_ANIMATED(graphic))
9852     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9853
9854   if (!MovDelay[ax][ay])        // start building new wall
9855     MovDelay[ax][ay] = 6;
9856
9857   if (MovDelay[ax][ay])         // wait some time before building new wall
9858   {
9859     MovDelay[ax][ay]--;
9860     if (MovDelay[ax][ay])
9861       return;
9862   }
9863
9864   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9865     oben_frei = TRUE;
9866   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9867     unten_frei = TRUE;
9868   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9869     links_frei = TRUE;
9870   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9871     rechts_frei = TRUE;
9872
9873   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9874       element == EL_EXPANDABLE_STEELWALL_ANY)
9875   {
9876     if (oben_frei)
9877     {
9878       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9879       Store[ax][ay-1] = element;
9880       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9881       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9882         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9883                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9884       new_wall = TRUE;
9885     }
9886     if (unten_frei)
9887     {
9888       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9889       Store[ax][ay+1] = element;
9890       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9891       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9892         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9893                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9894       new_wall = TRUE;
9895     }
9896   }
9897
9898   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9899       element == EL_EXPANDABLE_STEELWALL_ANY)
9900   {
9901     if (links_frei)
9902     {
9903       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9904       Store[ax-1][ay] = element;
9905       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9906       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9907         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9908                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9909       new_wall = TRUE;
9910     }
9911
9912     if (rechts_frei)
9913     {
9914       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9915       Store[ax+1][ay] = element;
9916       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9917       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9918         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9919                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9920       new_wall = TRUE;
9921     }
9922   }
9923
9924   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9925     oben_massiv = TRUE;
9926   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9927     unten_massiv = TRUE;
9928   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9929     links_massiv = TRUE;
9930   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9931     rechts_massiv = TRUE;
9932
9933   if (((oben_massiv && unten_massiv) ||
9934        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9935       ((links_massiv && rechts_massiv) ||
9936        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9937     Tile[ax][ay] = EL_STEELWALL;
9938
9939   if (new_wall)
9940     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9941 }
9942
9943 static void CheckForDragon(int x, int y)
9944 {
9945   int i, j;
9946   boolean dragon_found = FALSE;
9947   static int xy[4][2] =
9948   {
9949     { 0, -1 },
9950     { -1, 0 },
9951     { +1, 0 },
9952     { 0, +1 }
9953   };
9954
9955   for (i = 0; i < NUM_DIRECTIONS; i++)
9956   {
9957     for (j = 0; j < 4; j++)
9958     {
9959       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9960
9961       if (IN_LEV_FIELD(xx, yy) &&
9962           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9963       {
9964         if (Tile[xx][yy] == EL_DRAGON)
9965           dragon_found = TRUE;
9966       }
9967       else
9968         break;
9969     }
9970   }
9971
9972   if (!dragon_found)
9973   {
9974     for (i = 0; i < NUM_DIRECTIONS; i++)
9975     {
9976       for (j = 0; j < 3; j++)
9977       {
9978         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9979   
9980         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9981         {
9982           Tile[xx][yy] = EL_EMPTY;
9983           TEST_DrawLevelField(xx, yy);
9984         }
9985         else
9986           break;
9987       }
9988     }
9989   }
9990 }
9991
9992 static void InitBuggyBase(int x, int y)
9993 {
9994   int element = Tile[x][y];
9995   int activating_delay = FRAMES_PER_SECOND / 4;
9996
9997   ChangeDelay[x][y] =
9998     (element == EL_SP_BUGGY_BASE ?
9999      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10000      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10001      activating_delay :
10002      element == EL_SP_BUGGY_BASE_ACTIVE ?
10003      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10004 }
10005
10006 static void WarnBuggyBase(int x, int y)
10007 {
10008   int i;
10009   static int xy[4][2] =
10010   {
10011     { 0, -1 },
10012     { -1, 0 },
10013     { +1, 0 },
10014     { 0, +1 }
10015   };
10016
10017   for (i = 0; i < NUM_DIRECTIONS; i++)
10018   {
10019     int xx = x + xy[i][0];
10020     int yy = y + xy[i][1];
10021
10022     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10023     {
10024       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10025
10026       break;
10027     }
10028   }
10029 }
10030
10031 static void InitTrap(int x, int y)
10032 {
10033   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10034 }
10035
10036 static void ActivateTrap(int x, int y)
10037 {
10038   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10039 }
10040
10041 static void ChangeActiveTrap(int x, int y)
10042 {
10043   int graphic = IMG_TRAP_ACTIVE;
10044
10045   // if new animation frame was drawn, correct crumbled sand border
10046   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10047     TEST_DrawLevelFieldCrumbled(x, y);
10048 }
10049
10050 static int getSpecialActionElement(int element, int number, int base_element)
10051 {
10052   return (element != EL_EMPTY ? element :
10053           number != -1 ? base_element + number - 1 :
10054           EL_EMPTY);
10055 }
10056
10057 static int getModifiedActionNumber(int value_old, int operator, int operand,
10058                                    int value_min, int value_max)
10059 {
10060   int value_new = (operator == CA_MODE_SET      ? operand :
10061                    operator == CA_MODE_ADD      ? value_old + operand :
10062                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10063                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10064                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10065                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10066                    value_old);
10067
10068   return (value_new < value_min ? value_min :
10069           value_new > value_max ? value_max :
10070           value_new);
10071 }
10072
10073 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10074 {
10075   struct ElementInfo *ei = &element_info[element];
10076   struct ElementChangeInfo *change = &ei->change_page[page];
10077   int target_element = change->target_element;
10078   int action_type = change->action_type;
10079   int action_mode = change->action_mode;
10080   int action_arg = change->action_arg;
10081   int action_element = change->action_element;
10082   int i;
10083
10084   if (!change->has_action)
10085     return;
10086
10087   // ---------- determine action paramater values -----------------------------
10088
10089   int level_time_value =
10090     (level.time > 0 ? TimeLeft :
10091      TimePlayed);
10092
10093   int action_arg_element_raw =
10094     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10095      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10096      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10097      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10098      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10099      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10100      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10101      EL_EMPTY);
10102   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10103
10104   int action_arg_direction =
10105     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10106      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10107      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10108      change->actual_trigger_side :
10109      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10110      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10111      MV_NONE);
10112
10113   int action_arg_number_min =
10114     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10115      CA_ARG_MIN);
10116
10117   int action_arg_number_max =
10118     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10119      action_type == CA_SET_LEVEL_GEMS ? 999 :
10120      action_type == CA_SET_LEVEL_TIME ? 9999 :
10121      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10122      action_type == CA_SET_CE_VALUE ? 9999 :
10123      action_type == CA_SET_CE_SCORE ? 9999 :
10124      CA_ARG_MAX);
10125
10126   int action_arg_number_reset =
10127     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10128      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10129      action_type == CA_SET_LEVEL_TIME ? level.time :
10130      action_type == CA_SET_LEVEL_SCORE ? 0 :
10131      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10132      action_type == CA_SET_CE_SCORE ? 0 :
10133      0);
10134
10135   int action_arg_number =
10136     (action_arg <= CA_ARG_MAX ? action_arg :
10137      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10138      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10139      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10140      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10141      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10142      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10143      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10144      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10145      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10146      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10147      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10148      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10149      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10150      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10151      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10152      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10153      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10154      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10155      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10156      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10157      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10158      -1);
10159
10160   int action_arg_number_old =
10161     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10162      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10163      action_type == CA_SET_LEVEL_SCORE ? game.score :
10164      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10165      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10166      0);
10167
10168   int action_arg_number_new =
10169     getModifiedActionNumber(action_arg_number_old,
10170                             action_mode, action_arg_number,
10171                             action_arg_number_min, action_arg_number_max);
10172
10173   int trigger_player_bits =
10174     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10175      change->actual_trigger_player_bits : change->trigger_player);
10176
10177   int action_arg_player_bits =
10178     (action_arg >= CA_ARG_PLAYER_1 &&
10179      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10180      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10181      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10182      PLAYER_BITS_ANY);
10183
10184   // ---------- execute action  -----------------------------------------------
10185
10186   switch (action_type)
10187   {
10188     case CA_NO_ACTION:
10189     {
10190       return;
10191     }
10192
10193     // ---------- level actions  ----------------------------------------------
10194
10195     case CA_RESTART_LEVEL:
10196     {
10197       game.restart_level = TRUE;
10198
10199       break;
10200     }
10201
10202     case CA_SHOW_ENVELOPE:
10203     {
10204       int element = getSpecialActionElement(action_arg_element,
10205                                             action_arg_number, EL_ENVELOPE_1);
10206
10207       if (IS_ENVELOPE(element))
10208         local_player->show_envelope = element;
10209
10210       break;
10211     }
10212
10213     case CA_SET_LEVEL_TIME:
10214     {
10215       if (level.time > 0)       // only modify limited time value
10216       {
10217         TimeLeft = action_arg_number_new;
10218
10219         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10220
10221         DisplayGameControlValues();
10222
10223         if (!TimeLeft && setup.time_limit)
10224           for (i = 0; i < MAX_PLAYERS; i++)
10225             KillPlayer(&stored_player[i]);
10226       }
10227
10228       break;
10229     }
10230
10231     case CA_SET_LEVEL_SCORE:
10232     {
10233       game.score = action_arg_number_new;
10234
10235       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10236
10237       DisplayGameControlValues();
10238
10239       break;
10240     }
10241
10242     case CA_SET_LEVEL_GEMS:
10243     {
10244       game.gems_still_needed = action_arg_number_new;
10245
10246       game.snapshot.collected_item = TRUE;
10247
10248       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10249
10250       DisplayGameControlValues();
10251
10252       break;
10253     }
10254
10255     case CA_SET_LEVEL_WIND:
10256     {
10257       game.wind_direction = action_arg_direction;
10258
10259       break;
10260     }
10261
10262     case CA_SET_LEVEL_RANDOM_SEED:
10263     {
10264       // ensure that setting a new random seed while playing is predictable
10265       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10266
10267       break;
10268     }
10269
10270     // ---------- player actions  ---------------------------------------------
10271
10272     case CA_MOVE_PLAYER:
10273     case CA_MOVE_PLAYER_NEW:
10274     {
10275       // automatically move to the next field in specified direction
10276       for (i = 0; i < MAX_PLAYERS; i++)
10277         if (trigger_player_bits & (1 << i))
10278           if (action_type == CA_MOVE_PLAYER ||
10279               stored_player[i].MovPos == 0)
10280             stored_player[i].programmed_action = action_arg_direction;
10281
10282       break;
10283     }
10284
10285     case CA_EXIT_PLAYER:
10286     {
10287       for (i = 0; i < MAX_PLAYERS; i++)
10288         if (action_arg_player_bits & (1 << i))
10289           ExitPlayer(&stored_player[i]);
10290
10291       if (game.players_still_needed == 0)
10292         LevelSolved();
10293
10294       break;
10295     }
10296
10297     case CA_KILL_PLAYER:
10298     {
10299       for (i = 0; i < MAX_PLAYERS; i++)
10300         if (action_arg_player_bits & (1 << i))
10301           KillPlayer(&stored_player[i]);
10302
10303       break;
10304     }
10305
10306     case CA_SET_PLAYER_KEYS:
10307     {
10308       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10309       int element = getSpecialActionElement(action_arg_element,
10310                                             action_arg_number, EL_KEY_1);
10311
10312       if (IS_KEY(element))
10313       {
10314         for (i = 0; i < MAX_PLAYERS; i++)
10315         {
10316           if (trigger_player_bits & (1 << i))
10317           {
10318             stored_player[i].key[KEY_NR(element)] = key_state;
10319
10320             DrawGameDoorValues();
10321           }
10322         }
10323       }
10324
10325       break;
10326     }
10327
10328     case CA_SET_PLAYER_SPEED:
10329     {
10330       for (i = 0; i < MAX_PLAYERS; i++)
10331       {
10332         if (trigger_player_bits & (1 << i))
10333         {
10334           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10335
10336           if (action_arg == CA_ARG_SPEED_FASTER &&
10337               stored_player[i].cannot_move)
10338           {
10339             action_arg_number = STEPSIZE_VERY_SLOW;
10340           }
10341           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10342                    action_arg == CA_ARG_SPEED_FASTER)
10343           {
10344             action_arg_number = 2;
10345             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10346                            CA_MODE_MULTIPLY);
10347           }
10348           else if (action_arg == CA_ARG_NUMBER_RESET)
10349           {
10350             action_arg_number = level.initial_player_stepsize[i];
10351           }
10352
10353           move_stepsize =
10354             getModifiedActionNumber(move_stepsize,
10355                                     action_mode,
10356                                     action_arg_number,
10357                                     action_arg_number_min,
10358                                     action_arg_number_max);
10359
10360           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10361         }
10362       }
10363
10364       break;
10365     }
10366
10367     case CA_SET_PLAYER_SHIELD:
10368     {
10369       for (i = 0; i < MAX_PLAYERS; i++)
10370       {
10371         if (trigger_player_bits & (1 << i))
10372         {
10373           if (action_arg == CA_ARG_SHIELD_OFF)
10374           {
10375             stored_player[i].shield_normal_time_left = 0;
10376             stored_player[i].shield_deadly_time_left = 0;
10377           }
10378           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10379           {
10380             stored_player[i].shield_normal_time_left = 999999;
10381           }
10382           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10383           {
10384             stored_player[i].shield_normal_time_left = 999999;
10385             stored_player[i].shield_deadly_time_left = 999999;
10386           }
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_PLAYER_GRAVITY:
10394     {
10395       for (i = 0; i < MAX_PLAYERS; i++)
10396       {
10397         if (trigger_player_bits & (1 << i))
10398         {
10399           stored_player[i].gravity =
10400             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10401              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10402              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10403              stored_player[i].gravity);
10404         }
10405       }
10406
10407       break;
10408     }
10409
10410     case CA_SET_PLAYER_ARTWORK:
10411     {
10412       for (i = 0; i < MAX_PLAYERS; i++)
10413       {
10414         if (trigger_player_bits & (1 << i))
10415         {
10416           int artwork_element = action_arg_element;
10417
10418           if (action_arg == CA_ARG_ELEMENT_RESET)
10419             artwork_element =
10420               (level.use_artwork_element[i] ? level.artwork_element[i] :
10421                stored_player[i].element_nr);
10422
10423           if (stored_player[i].artwork_element != artwork_element)
10424             stored_player[i].Frame = 0;
10425
10426           stored_player[i].artwork_element = artwork_element;
10427
10428           SetPlayerWaiting(&stored_player[i], FALSE);
10429
10430           // set number of special actions for bored and sleeping animation
10431           stored_player[i].num_special_action_bored =
10432             get_num_special_action(artwork_element,
10433                                    ACTION_BORING_1, ACTION_BORING_LAST);
10434           stored_player[i].num_special_action_sleeping =
10435             get_num_special_action(artwork_element,
10436                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10437         }
10438       }
10439
10440       break;
10441     }
10442
10443     case CA_SET_PLAYER_INVENTORY:
10444     {
10445       for (i = 0; i < MAX_PLAYERS; i++)
10446       {
10447         struct PlayerInfo *player = &stored_player[i];
10448         int j, k;
10449
10450         if (trigger_player_bits & (1 << i))
10451         {
10452           int inventory_element = action_arg_element;
10453
10454           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10455               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10456               action_arg == CA_ARG_ELEMENT_ACTION)
10457           {
10458             int element = inventory_element;
10459             int collect_count = element_info[element].collect_count_initial;
10460
10461             if (!IS_CUSTOM_ELEMENT(element))
10462               collect_count = 1;
10463
10464             if (collect_count == 0)
10465               player->inventory_infinite_element = element;
10466             else
10467               for (k = 0; k < collect_count; k++)
10468                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10469                   player->inventory_element[player->inventory_size++] =
10470                     element;
10471           }
10472           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10473                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10474                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10475           {
10476             if (player->inventory_infinite_element != EL_UNDEFINED &&
10477                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10478                                      action_arg_element_raw))
10479               player->inventory_infinite_element = EL_UNDEFINED;
10480
10481             for (k = 0, j = 0; j < player->inventory_size; j++)
10482             {
10483               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10484                                         action_arg_element_raw))
10485                 player->inventory_element[k++] = player->inventory_element[j];
10486             }
10487
10488             player->inventory_size = k;
10489           }
10490           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10491           {
10492             if (player->inventory_size > 0)
10493             {
10494               for (j = 0; j < player->inventory_size - 1; j++)
10495                 player->inventory_element[j] = player->inventory_element[j + 1];
10496
10497               player->inventory_size--;
10498             }
10499           }
10500           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10501           {
10502             if (player->inventory_size > 0)
10503               player->inventory_size--;
10504           }
10505           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10506           {
10507             player->inventory_infinite_element = EL_UNDEFINED;
10508             player->inventory_size = 0;
10509           }
10510           else if (action_arg == CA_ARG_INVENTORY_RESET)
10511           {
10512             player->inventory_infinite_element = EL_UNDEFINED;
10513             player->inventory_size = 0;
10514
10515             if (level.use_initial_inventory[i])
10516             {
10517               for (j = 0; j < level.initial_inventory_size[i]; j++)
10518               {
10519                 int element = level.initial_inventory_content[i][j];
10520                 int collect_count = element_info[element].collect_count_initial;
10521
10522                 if (!IS_CUSTOM_ELEMENT(element))
10523                   collect_count = 1;
10524
10525                 if (collect_count == 0)
10526                   player->inventory_infinite_element = element;
10527                 else
10528                   for (k = 0; k < collect_count; k++)
10529                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10530                       player->inventory_element[player->inventory_size++] =
10531                         element;
10532               }
10533             }
10534           }
10535         }
10536       }
10537
10538       break;
10539     }
10540
10541     // ---------- CE actions  -------------------------------------------------
10542
10543     case CA_SET_CE_VALUE:
10544     {
10545       int last_ce_value = CustomValue[x][y];
10546
10547       CustomValue[x][y] = action_arg_number_new;
10548
10549       if (CustomValue[x][y] != last_ce_value)
10550       {
10551         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10552         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10553
10554         if (CustomValue[x][y] == 0)
10555         {
10556           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10557           ChangeCount[x][y] = 0;        // allow at least one more change
10558
10559           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10560           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10561         }
10562       }
10563
10564       break;
10565     }
10566
10567     case CA_SET_CE_SCORE:
10568     {
10569       int last_ce_score = ei->collect_score;
10570
10571       ei->collect_score = action_arg_number_new;
10572
10573       if (ei->collect_score != last_ce_score)
10574       {
10575         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10576         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10577
10578         if (ei->collect_score == 0)
10579         {
10580           int xx, yy;
10581
10582           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10583           ChangeCount[x][y] = 0;        // allow at least one more change
10584
10585           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10586           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10587
10588           /*
10589             This is a very special case that seems to be a mixture between
10590             CheckElementChange() and CheckTriggeredElementChange(): while
10591             the first one only affects single elements that are triggered
10592             directly, the second one affects multiple elements in the playfield
10593             that are triggered indirectly by another element. This is a third
10594             case: Changing the CE score always affects multiple identical CEs,
10595             so every affected CE must be checked, not only the single CE for
10596             which the CE score was changed in the first place (as every instance
10597             of that CE shares the same CE score, and therefore also can change)!
10598           */
10599           SCAN_PLAYFIELD(xx, yy)
10600           {
10601             if (Tile[xx][yy] == element)
10602               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10603                                  CE_SCORE_GETS_ZERO);
10604           }
10605         }
10606       }
10607
10608       break;
10609     }
10610
10611     case CA_SET_CE_ARTWORK:
10612     {
10613       int artwork_element = action_arg_element;
10614       boolean reset_frame = FALSE;
10615       int xx, yy;
10616
10617       if (action_arg == CA_ARG_ELEMENT_RESET)
10618         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10619                            element);
10620
10621       if (ei->gfx_element != artwork_element)
10622         reset_frame = TRUE;
10623
10624       ei->gfx_element = artwork_element;
10625
10626       SCAN_PLAYFIELD(xx, yy)
10627       {
10628         if (Tile[xx][yy] == element)
10629         {
10630           if (reset_frame)
10631           {
10632             ResetGfxAnimation(xx, yy);
10633             ResetRandomAnimationValue(xx, yy);
10634           }
10635
10636           TEST_DrawLevelField(xx, yy);
10637         }
10638       }
10639
10640       break;
10641     }
10642
10643     // ---------- engine actions  ---------------------------------------------
10644
10645     case CA_SET_ENGINE_SCAN_MODE:
10646     {
10647       InitPlayfieldScanMode(action_arg);
10648
10649       break;
10650     }
10651
10652     default:
10653       break;
10654   }
10655 }
10656
10657 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10658 {
10659   int old_element = Tile[x][y];
10660   int new_element = GetElementFromGroupElement(element);
10661   int previous_move_direction = MovDir[x][y];
10662   int last_ce_value = CustomValue[x][y];
10663   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10664   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10665   boolean add_player_onto_element = (new_element_is_player &&
10666                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10667                                      IS_WALKABLE(old_element));
10668
10669   if (!add_player_onto_element)
10670   {
10671     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10672       RemoveMovingField(x, y);
10673     else
10674       RemoveField(x, y);
10675
10676     Tile[x][y] = new_element;
10677
10678     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10679       MovDir[x][y] = previous_move_direction;
10680
10681     if (element_info[new_element].use_last_ce_value)
10682       CustomValue[x][y] = last_ce_value;
10683
10684     InitField_WithBug1(x, y, FALSE);
10685
10686     new_element = Tile[x][y];   // element may have changed
10687
10688     ResetGfxAnimation(x, y);
10689     ResetRandomAnimationValue(x, y);
10690
10691     TEST_DrawLevelField(x, y);
10692
10693     if (GFX_CRUMBLED(new_element))
10694       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10695   }
10696
10697   // check if element under the player changes from accessible to unaccessible
10698   // (needed for special case of dropping element which then changes)
10699   // (must be checked after creating new element for walkable group elements)
10700   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10701       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10702   {
10703     Bang(x, y);
10704
10705     return;
10706   }
10707
10708   // "ChangeCount" not set yet to allow "entered by player" change one time
10709   if (new_element_is_player)
10710     RelocatePlayer(x, y, new_element);
10711
10712   if (is_change)
10713     ChangeCount[x][y]++;        // count number of changes in the same frame
10714
10715   TestIfBadThingTouchesPlayer(x, y);
10716   TestIfPlayerTouchesCustomElement(x, y);
10717   TestIfElementTouchesCustomElement(x, y);
10718 }
10719
10720 static void CreateField(int x, int y, int element)
10721 {
10722   CreateFieldExt(x, y, element, FALSE);
10723 }
10724
10725 static void CreateElementFromChange(int x, int y, int element)
10726 {
10727   element = GET_VALID_RUNTIME_ELEMENT(element);
10728
10729   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10730   {
10731     int old_element = Tile[x][y];
10732
10733     // prevent changed element from moving in same engine frame
10734     // unless both old and new element can either fall or move
10735     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10736         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10737       Stop[x][y] = TRUE;
10738   }
10739
10740   CreateFieldExt(x, y, element, TRUE);
10741 }
10742
10743 static boolean ChangeElement(int x, int y, int element, int page)
10744 {
10745   struct ElementInfo *ei = &element_info[element];
10746   struct ElementChangeInfo *change = &ei->change_page[page];
10747   int ce_value = CustomValue[x][y];
10748   int ce_score = ei->collect_score;
10749   int target_element;
10750   int old_element = Tile[x][y];
10751
10752   // always use default change event to prevent running into a loop
10753   if (ChangeEvent[x][y] == -1)
10754     ChangeEvent[x][y] = CE_DELAY;
10755
10756   if (ChangeEvent[x][y] == CE_DELAY)
10757   {
10758     // reset actual trigger element, trigger player and action element
10759     change->actual_trigger_element = EL_EMPTY;
10760     change->actual_trigger_player = EL_EMPTY;
10761     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10762     change->actual_trigger_side = CH_SIDE_NONE;
10763     change->actual_trigger_ce_value = 0;
10764     change->actual_trigger_ce_score = 0;
10765   }
10766
10767   // do not change elements more than a specified maximum number of changes
10768   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10769     return FALSE;
10770
10771   ChangeCount[x][y]++;          // count number of changes in the same frame
10772
10773   if (change->explode)
10774   {
10775     Bang(x, y);
10776
10777     return TRUE;
10778   }
10779
10780   if (change->use_target_content)
10781   {
10782     boolean complete_replace = TRUE;
10783     boolean can_replace[3][3];
10784     int xx, yy;
10785
10786     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10787     {
10788       boolean is_empty;
10789       boolean is_walkable;
10790       boolean is_diggable;
10791       boolean is_collectible;
10792       boolean is_removable;
10793       boolean is_destructible;
10794       int ex = x + xx - 1;
10795       int ey = y + yy - 1;
10796       int content_element = change->target_content.e[xx][yy];
10797       int e;
10798
10799       can_replace[xx][yy] = TRUE;
10800
10801       if (ex == x && ey == y)   // do not check changing element itself
10802         continue;
10803
10804       if (content_element == EL_EMPTY_SPACE)
10805       {
10806         can_replace[xx][yy] = FALSE;    // do not replace border with space
10807
10808         continue;
10809       }
10810
10811       if (!IN_LEV_FIELD(ex, ey))
10812       {
10813         can_replace[xx][yy] = FALSE;
10814         complete_replace = FALSE;
10815
10816         continue;
10817       }
10818
10819       e = Tile[ex][ey];
10820
10821       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10822         e = MovingOrBlocked2Element(ex, ey);
10823
10824       is_empty = (IS_FREE(ex, ey) ||
10825                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10826
10827       is_walkable     = (is_empty || IS_WALKABLE(e));
10828       is_diggable     = (is_empty || IS_DIGGABLE(e));
10829       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10830       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10831       is_removable    = (is_diggable || is_collectible);
10832
10833       can_replace[xx][yy] =
10834         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10835           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10836           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10837           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10838           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10839           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10840          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10841
10842       if (!can_replace[xx][yy])
10843         complete_replace = FALSE;
10844     }
10845
10846     if (!change->only_if_complete || complete_replace)
10847     {
10848       boolean something_has_changed = FALSE;
10849
10850       if (change->only_if_complete && change->use_random_replace &&
10851           RND(100) < change->random_percentage)
10852         return FALSE;
10853
10854       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10855       {
10856         int ex = x + xx - 1;
10857         int ey = y + yy - 1;
10858         int content_element;
10859
10860         if (can_replace[xx][yy] && (!change->use_random_replace ||
10861                                     RND(100) < change->random_percentage))
10862         {
10863           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10864             RemoveMovingField(ex, ey);
10865
10866           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10867
10868           content_element = change->target_content.e[xx][yy];
10869           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10870                                               ce_value, ce_score);
10871
10872           CreateElementFromChange(ex, ey, target_element);
10873
10874           something_has_changed = TRUE;
10875
10876           // for symmetry reasons, freeze newly created border elements
10877           if (ex != x || ey != y)
10878             Stop[ex][ey] = TRUE;        // no more moving in this frame
10879         }
10880       }
10881
10882       if (something_has_changed)
10883       {
10884         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10885         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10886       }
10887     }
10888   }
10889   else
10890   {
10891     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10892                                         ce_value, ce_score);
10893
10894     if (element == EL_DIAGONAL_GROWING ||
10895         element == EL_DIAGONAL_SHRINKING)
10896     {
10897       target_element = Store[x][y];
10898
10899       Store[x][y] = EL_EMPTY;
10900     }
10901
10902     // special case: element changes to player (and may be kept if walkable)
10903     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10904       CreateElementFromChange(x, y, EL_EMPTY);
10905
10906     CreateElementFromChange(x, y, target_element);
10907
10908     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10909     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10910   }
10911
10912   // this uses direct change before indirect change
10913   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10914
10915   return TRUE;
10916 }
10917
10918 static void HandleElementChange(int x, int y, int page)
10919 {
10920   int element = MovingOrBlocked2Element(x, y);
10921   struct ElementInfo *ei = &element_info[element];
10922   struct ElementChangeInfo *change = &ei->change_page[page];
10923   boolean handle_action_before_change = FALSE;
10924
10925 #ifdef DEBUG
10926   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10927       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10928   {
10929     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10930           x, y, element, element_info[element].token_name);
10931     Debug("game:playing:HandleElementChange", "This should never happen!");
10932   }
10933 #endif
10934
10935   // this can happen with classic bombs on walkable, changing elements
10936   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10937   {
10938     return;
10939   }
10940
10941   if (ChangeDelay[x][y] == 0)           // initialize element change
10942   {
10943     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10944
10945     if (change->can_change)
10946     {
10947       // !!! not clear why graphic animation should be reset at all here !!!
10948       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10949       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10950
10951       /*
10952         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10953
10954         When using an animation frame delay of 1 (this only happens with
10955         "sp_zonk.moving.left/right" in the classic graphics), the default
10956         (non-moving) animation shows wrong animation frames (while the
10957         moving animation, like "sp_zonk.moving.left/right", is correct,
10958         so this graphical bug never shows up with the classic graphics).
10959         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10960         be drawn instead of the correct frames 0,1,2,3. This is caused by
10961         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10962         an element change: First when the change delay ("ChangeDelay[][]")
10963         counter has reached zero after decrementing, then a second time in
10964         the next frame (after "GfxFrame[][]" was already incremented) when
10965         "ChangeDelay[][]" is reset to the initial delay value again.
10966
10967         This causes frame 0 to be drawn twice, while the last frame won't
10968         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10969
10970         As some animations may already be cleverly designed around this bug
10971         (at least the "Snake Bite" snake tail animation does this), it cannot
10972         simply be fixed here without breaking such existing animations.
10973         Unfortunately, it cannot easily be detected if a graphics set was
10974         designed "before" or "after" the bug was fixed. As a workaround,
10975         a new graphics set option "game.graphics_engine_version" was added
10976         to be able to specify the game's major release version for which the
10977         graphics set was designed, which can then be used to decide if the
10978         bugfix should be used (version 4 and above) or not (version 3 or
10979         below, or if no version was specified at all, as with old sets).
10980
10981         (The wrong/fixed animation frames can be tested with the test level set
10982         "test_gfxframe" and level "000", which contains a specially prepared
10983         custom element at level position (x/y) == (11/9) which uses the zonk
10984         animation mentioned above. Using "game.graphics_engine_version: 4"
10985         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10986         This can also be seen from the debug output for this test element.)
10987       */
10988
10989       // when a custom element is about to change (for example by change delay),
10990       // do not reset graphic animation when the custom element is moving
10991       if (game.graphics_engine_version < 4 &&
10992           !IS_MOVING(x, y))
10993       {
10994         ResetGfxAnimation(x, y);
10995         ResetRandomAnimationValue(x, y);
10996       }
10997
10998       if (change->pre_change_function)
10999         change->pre_change_function(x, y);
11000     }
11001   }
11002
11003   ChangeDelay[x][y]--;
11004
11005   if (ChangeDelay[x][y] != 0)           // continue element change
11006   {
11007     if (change->can_change)
11008     {
11009       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11010
11011       if (IS_ANIMATED(graphic))
11012         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11013
11014       if (change->change_function)
11015         change->change_function(x, y);
11016     }
11017   }
11018   else                                  // finish element change
11019   {
11020     if (ChangePage[x][y] != -1)         // remember page from delayed change
11021     {
11022       page = ChangePage[x][y];
11023       ChangePage[x][y] = -1;
11024
11025       change = &ei->change_page[page];
11026     }
11027
11028     if (IS_MOVING(x, y))                // never change a running system ;-)
11029     {
11030       ChangeDelay[x][y] = 1;            // try change after next move step
11031       ChangePage[x][y] = page;          // remember page to use for change
11032
11033       return;
11034     }
11035
11036     // special case: set new level random seed before changing element
11037     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11038       handle_action_before_change = TRUE;
11039
11040     if (change->has_action && handle_action_before_change)
11041       ExecuteCustomElementAction(x, y, element, page);
11042
11043     if (change->can_change)
11044     {
11045       if (ChangeElement(x, y, element, page))
11046       {
11047         if (change->post_change_function)
11048           change->post_change_function(x, y);
11049       }
11050     }
11051
11052     if (change->has_action && !handle_action_before_change)
11053       ExecuteCustomElementAction(x, y, element, page);
11054   }
11055 }
11056
11057 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11058                                               int trigger_element,
11059                                               int trigger_event,
11060                                               int trigger_player,
11061                                               int trigger_side,
11062                                               int trigger_page)
11063 {
11064   boolean change_done_any = FALSE;
11065   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11066   int i;
11067
11068   if (!(trigger_events[trigger_element][trigger_event]))
11069     return FALSE;
11070
11071   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11072
11073   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11074   {
11075     int element = EL_CUSTOM_START + i;
11076     boolean change_done = FALSE;
11077     int p;
11078
11079     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11080         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11081       continue;
11082
11083     for (p = 0; p < element_info[element].num_change_pages; p++)
11084     {
11085       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11086
11087       if (change->can_change_or_has_action &&
11088           change->has_event[trigger_event] &&
11089           change->trigger_side & trigger_side &&
11090           change->trigger_player & trigger_player &&
11091           change->trigger_page & trigger_page_bits &&
11092           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11093       {
11094         change->actual_trigger_element = trigger_element;
11095         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11096         change->actual_trigger_player_bits = trigger_player;
11097         change->actual_trigger_side = trigger_side;
11098         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11099         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11100
11101         if ((change->can_change && !change_done) || change->has_action)
11102         {
11103           int x, y;
11104
11105           SCAN_PLAYFIELD(x, y)
11106           {
11107             if (Tile[x][y] == element)
11108             {
11109               if (change->can_change && !change_done)
11110               {
11111                 // if element already changed in this frame, not only prevent
11112                 // another element change (checked in ChangeElement()), but
11113                 // also prevent additional element actions for this element
11114
11115                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11116                     !level.use_action_after_change_bug)
11117                   continue;
11118
11119                 ChangeDelay[x][y] = 1;
11120                 ChangeEvent[x][y] = trigger_event;
11121
11122                 HandleElementChange(x, y, p);
11123               }
11124               else if (change->has_action)
11125               {
11126                 // if element already changed in this frame, not only prevent
11127                 // another element change (checked in ChangeElement()), but
11128                 // also prevent additional element actions for this element
11129
11130                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11131                     !level.use_action_after_change_bug)
11132                   continue;
11133
11134                 ExecuteCustomElementAction(x, y, element, p);
11135                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11136               }
11137             }
11138           }
11139
11140           if (change->can_change)
11141           {
11142             change_done = TRUE;
11143             change_done_any = TRUE;
11144           }
11145         }
11146       }
11147     }
11148   }
11149
11150   RECURSION_LOOP_DETECTION_END();
11151
11152   return change_done_any;
11153 }
11154
11155 static boolean CheckElementChangeExt(int x, int y,
11156                                      int element,
11157                                      int trigger_element,
11158                                      int trigger_event,
11159                                      int trigger_player,
11160                                      int trigger_side)
11161 {
11162   boolean change_done = FALSE;
11163   int p;
11164
11165   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11166       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11167     return FALSE;
11168
11169   if (Tile[x][y] == EL_BLOCKED)
11170   {
11171     Blocked2Moving(x, y, &x, &y);
11172     element = Tile[x][y];
11173   }
11174
11175   // check if element has already changed or is about to change after moving
11176   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11177        Tile[x][y] != element) ||
11178
11179       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11180        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11181         ChangePage[x][y] != -1)))
11182     return FALSE;
11183
11184   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11185
11186   for (p = 0; p < element_info[element].num_change_pages; p++)
11187   {
11188     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11189
11190     /* check trigger element for all events where the element that is checked
11191        for changing interacts with a directly adjacent element -- this is
11192        different to element changes that affect other elements to change on the
11193        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11194     boolean check_trigger_element =
11195       (trigger_event == CE_NEXT_TO_X ||
11196        trigger_event == CE_TOUCHING_X ||
11197        trigger_event == CE_HITTING_X ||
11198        trigger_event == CE_HIT_BY_X ||
11199        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11200
11201     if (change->can_change_or_has_action &&
11202         change->has_event[trigger_event] &&
11203         change->trigger_side & trigger_side &&
11204         change->trigger_player & trigger_player &&
11205         (!check_trigger_element ||
11206          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11207     {
11208       change->actual_trigger_element = trigger_element;
11209       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11210       change->actual_trigger_player_bits = trigger_player;
11211       change->actual_trigger_side = trigger_side;
11212       change->actual_trigger_ce_value = CustomValue[x][y];
11213       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11214
11215       // special case: trigger element not at (x,y) position for some events
11216       if (check_trigger_element)
11217       {
11218         static struct
11219         {
11220           int dx, dy;
11221         } move_xy[] =
11222           {
11223             {  0,  0 },
11224             { -1,  0 },
11225             { +1,  0 },
11226             {  0,  0 },
11227             {  0, -1 },
11228             {  0,  0 }, { 0, 0 }, { 0, 0 },
11229             {  0, +1 }
11230           };
11231
11232         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11233         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11234
11235         change->actual_trigger_ce_value = CustomValue[xx][yy];
11236         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11237       }
11238
11239       if (change->can_change && !change_done)
11240       {
11241         ChangeDelay[x][y] = 1;
11242         ChangeEvent[x][y] = trigger_event;
11243
11244         HandleElementChange(x, y, p);
11245
11246         change_done = TRUE;
11247       }
11248       else if (change->has_action)
11249       {
11250         ExecuteCustomElementAction(x, y, element, p);
11251         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11252       }
11253     }
11254   }
11255
11256   RECURSION_LOOP_DETECTION_END();
11257
11258   return change_done;
11259 }
11260
11261 static void PlayPlayerSound(struct PlayerInfo *player)
11262 {
11263   int jx = player->jx, jy = player->jy;
11264   int sound_element = player->artwork_element;
11265   int last_action = player->last_action_waiting;
11266   int action = player->action_waiting;
11267
11268   if (player->is_waiting)
11269   {
11270     if (action != last_action)
11271       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11272     else
11273       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11274   }
11275   else
11276   {
11277     if (action != last_action)
11278       StopSound(element_info[sound_element].sound[last_action]);
11279
11280     if (last_action == ACTION_SLEEPING)
11281       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11282   }
11283 }
11284
11285 static void PlayAllPlayersSound(void)
11286 {
11287   int i;
11288
11289   for (i = 0; i < MAX_PLAYERS; i++)
11290     if (stored_player[i].active)
11291       PlayPlayerSound(&stored_player[i]);
11292 }
11293
11294 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11295 {
11296   boolean last_waiting = player->is_waiting;
11297   int move_dir = player->MovDir;
11298
11299   player->dir_waiting = move_dir;
11300   player->last_action_waiting = player->action_waiting;
11301
11302   if (is_waiting)
11303   {
11304     if (!last_waiting)          // not waiting -> waiting
11305     {
11306       player->is_waiting = TRUE;
11307
11308       player->frame_counter_bored =
11309         FrameCounter +
11310         game.player_boring_delay_fixed +
11311         GetSimpleRandom(game.player_boring_delay_random);
11312       player->frame_counter_sleeping =
11313         FrameCounter +
11314         game.player_sleeping_delay_fixed +
11315         GetSimpleRandom(game.player_sleeping_delay_random);
11316
11317       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11318     }
11319
11320     if (game.player_sleeping_delay_fixed +
11321         game.player_sleeping_delay_random > 0 &&
11322         player->anim_delay_counter == 0 &&
11323         player->post_delay_counter == 0 &&
11324         FrameCounter >= player->frame_counter_sleeping)
11325       player->is_sleeping = TRUE;
11326     else if (game.player_boring_delay_fixed +
11327              game.player_boring_delay_random > 0 &&
11328              FrameCounter >= player->frame_counter_bored)
11329       player->is_bored = TRUE;
11330
11331     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11332                               player->is_bored ? ACTION_BORING :
11333                               ACTION_WAITING);
11334
11335     if (player->is_sleeping && player->use_murphy)
11336     {
11337       // special case for sleeping Murphy when leaning against non-free tile
11338
11339       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11340           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11341            !IS_MOVING(player->jx - 1, player->jy)))
11342         move_dir = MV_LEFT;
11343       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11344                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11345                 !IS_MOVING(player->jx + 1, player->jy)))
11346         move_dir = MV_RIGHT;
11347       else
11348         player->is_sleeping = FALSE;
11349
11350       player->dir_waiting = move_dir;
11351     }
11352
11353     if (player->is_sleeping)
11354     {
11355       if (player->num_special_action_sleeping > 0)
11356       {
11357         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11358         {
11359           int last_special_action = player->special_action_sleeping;
11360           int num_special_action = player->num_special_action_sleeping;
11361           int special_action =
11362             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11363              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11364              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11365              last_special_action + 1 : ACTION_SLEEPING);
11366           int special_graphic =
11367             el_act_dir2img(player->artwork_element, special_action, move_dir);
11368
11369           player->anim_delay_counter =
11370             graphic_info[special_graphic].anim_delay_fixed +
11371             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11372           player->post_delay_counter =
11373             graphic_info[special_graphic].post_delay_fixed +
11374             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11375
11376           player->special_action_sleeping = special_action;
11377         }
11378
11379         if (player->anim_delay_counter > 0)
11380         {
11381           player->action_waiting = player->special_action_sleeping;
11382           player->anim_delay_counter--;
11383         }
11384         else if (player->post_delay_counter > 0)
11385         {
11386           player->post_delay_counter--;
11387         }
11388       }
11389     }
11390     else if (player->is_bored)
11391     {
11392       if (player->num_special_action_bored > 0)
11393       {
11394         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11395         {
11396           int special_action =
11397             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11398           int special_graphic =
11399             el_act_dir2img(player->artwork_element, special_action, move_dir);
11400
11401           player->anim_delay_counter =
11402             graphic_info[special_graphic].anim_delay_fixed +
11403             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11404           player->post_delay_counter =
11405             graphic_info[special_graphic].post_delay_fixed +
11406             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11407
11408           player->special_action_bored = special_action;
11409         }
11410
11411         if (player->anim_delay_counter > 0)
11412         {
11413           player->action_waiting = player->special_action_bored;
11414           player->anim_delay_counter--;
11415         }
11416         else if (player->post_delay_counter > 0)
11417         {
11418           player->post_delay_counter--;
11419         }
11420       }
11421     }
11422   }
11423   else if (last_waiting)        // waiting -> not waiting
11424   {
11425     player->is_waiting = FALSE;
11426     player->is_bored = FALSE;
11427     player->is_sleeping = FALSE;
11428
11429     player->frame_counter_bored = -1;
11430     player->frame_counter_sleeping = -1;
11431
11432     player->anim_delay_counter = 0;
11433     player->post_delay_counter = 0;
11434
11435     player->dir_waiting = player->MovDir;
11436     player->action_waiting = ACTION_DEFAULT;
11437
11438     player->special_action_bored = ACTION_DEFAULT;
11439     player->special_action_sleeping = ACTION_DEFAULT;
11440   }
11441 }
11442
11443 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11444 {
11445   if ((!player->is_moving  && player->was_moving) ||
11446       (player->MovPos == 0 && player->was_moving) ||
11447       (player->is_snapping && !player->was_snapping) ||
11448       (player->is_dropping && !player->was_dropping))
11449   {
11450     if (!CheckSaveEngineSnapshotToList())
11451       return;
11452
11453     player->was_moving = FALSE;
11454     player->was_snapping = TRUE;
11455     player->was_dropping = TRUE;
11456   }
11457   else
11458   {
11459     if (player->is_moving)
11460       player->was_moving = TRUE;
11461
11462     if (!player->is_snapping)
11463       player->was_snapping = FALSE;
11464
11465     if (!player->is_dropping)
11466       player->was_dropping = FALSE;
11467   }
11468
11469   static struct MouseActionInfo mouse_action_last = { 0 };
11470   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11471   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11472
11473   if (new_released)
11474     CheckSaveEngineSnapshotToList();
11475
11476   mouse_action_last = mouse_action;
11477 }
11478
11479 static void CheckSingleStepMode(struct PlayerInfo *player)
11480 {
11481   if (tape.single_step && tape.recording && !tape.pausing)
11482   {
11483     // as it is called "single step mode", just return to pause mode when the
11484     // player stopped moving after one tile (or never starts moving at all)
11485     // (reverse logic needed here in case single step mode used in team mode)
11486     if (player->is_moving ||
11487         player->is_pushing ||
11488         player->is_dropping_pressed ||
11489         player->effective_mouse_action.button)
11490       game.enter_single_step_mode = FALSE;
11491   }
11492
11493   CheckSaveEngineSnapshot(player);
11494 }
11495
11496 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11497 {
11498   int left      = player_action & JOY_LEFT;
11499   int right     = player_action & JOY_RIGHT;
11500   int up        = player_action & JOY_UP;
11501   int down      = player_action & JOY_DOWN;
11502   int button1   = player_action & JOY_BUTTON_1;
11503   int button2   = player_action & JOY_BUTTON_2;
11504   int dx        = (left ? -1 : right ? 1 : 0);
11505   int dy        = (up   ? -1 : down  ? 1 : 0);
11506
11507   if (!player->active || tape.pausing)
11508     return 0;
11509
11510   if (player_action)
11511   {
11512     if (button1)
11513       SnapField(player, dx, dy);
11514     else
11515     {
11516       if (button2)
11517         DropElement(player);
11518
11519       MovePlayer(player, dx, dy);
11520     }
11521
11522     CheckSingleStepMode(player);
11523
11524     SetPlayerWaiting(player, FALSE);
11525
11526     return player_action;
11527   }
11528   else
11529   {
11530     // no actions for this player (no input at player's configured device)
11531
11532     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11533     SnapField(player, 0, 0);
11534     CheckGravityMovementWhenNotMoving(player);
11535
11536     if (player->MovPos == 0)
11537       SetPlayerWaiting(player, TRUE);
11538
11539     if (player->MovPos == 0)    // needed for tape.playing
11540       player->is_moving = FALSE;
11541
11542     player->is_dropping = FALSE;
11543     player->is_dropping_pressed = FALSE;
11544     player->drop_pressed_delay = 0;
11545
11546     CheckSingleStepMode(player);
11547
11548     return 0;
11549   }
11550 }
11551
11552 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11553                                          byte *tape_action)
11554 {
11555   if (!tape.use_mouse_actions)
11556     return;
11557
11558   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11559   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11560   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11561 }
11562
11563 static void SetTapeActionFromMouseAction(byte *tape_action,
11564                                          struct MouseActionInfo *mouse_action)
11565 {
11566   if (!tape.use_mouse_actions)
11567     return;
11568
11569   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11570   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11571   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11572 }
11573
11574 static void CheckLevelSolved(void)
11575 {
11576   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11577   {
11578     if (game_em.level_solved &&
11579         !game_em.game_over)                             // game won
11580     {
11581       LevelSolved();
11582
11583       game_em.game_over = TRUE;
11584
11585       game.all_players_gone = TRUE;
11586     }
11587
11588     if (game_em.game_over)                              // game lost
11589       game.all_players_gone = TRUE;
11590   }
11591   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11592   {
11593     if (game_sp.level_solved &&
11594         !game_sp.game_over)                             // game won
11595     {
11596       LevelSolved();
11597
11598       game_sp.game_over = TRUE;
11599
11600       game.all_players_gone = TRUE;
11601     }
11602
11603     if (game_sp.game_over)                              // game lost
11604       game.all_players_gone = TRUE;
11605   }
11606   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11607   {
11608     if (game_mm.level_solved &&
11609         !game_mm.game_over)                             // game won
11610     {
11611       LevelSolved();
11612
11613       game_mm.game_over = TRUE;
11614
11615       game.all_players_gone = TRUE;
11616     }
11617
11618     if (game_mm.game_over)                              // game lost
11619       game.all_players_gone = TRUE;
11620   }
11621 }
11622
11623 static void CheckLevelTime_StepCounter(void)
11624 {
11625   int i;
11626
11627   TimePlayed++;
11628
11629   if (TimeLeft > 0)
11630   {
11631     TimeLeft--;
11632
11633     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11634       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11635
11636     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11637
11638     DisplayGameControlValues();
11639
11640     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11641       for (i = 0; i < MAX_PLAYERS; i++)
11642         KillPlayer(&stored_player[i]);
11643   }
11644   else if (game.no_time_limit && !game.all_players_gone)
11645   {
11646     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11647
11648     DisplayGameControlValues();
11649   }
11650 }
11651
11652 static void CheckLevelTime(void)
11653 {
11654   int i;
11655
11656   if (TimeFrames >= FRAMES_PER_SECOND)
11657   {
11658     TimeFrames = 0;
11659     TapeTime++;
11660
11661     for (i = 0; i < MAX_PLAYERS; i++)
11662     {
11663       struct PlayerInfo *player = &stored_player[i];
11664
11665       if (SHIELD_ON(player))
11666       {
11667         player->shield_normal_time_left--;
11668
11669         if (player->shield_deadly_time_left > 0)
11670           player->shield_deadly_time_left--;
11671       }
11672     }
11673
11674     if (!game.LevelSolved && !level.use_step_counter)
11675     {
11676       TimePlayed++;
11677
11678       if (TimeLeft > 0)
11679       {
11680         TimeLeft--;
11681
11682         if (TimeLeft <= 10 && setup.time_limit)
11683           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11684
11685         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11686            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11687
11688         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11689
11690         if (!TimeLeft && setup.time_limit)
11691         {
11692           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11693             game_em.lev->killed_out_of_time = TRUE;
11694           else
11695             for (i = 0; i < MAX_PLAYERS; i++)
11696               KillPlayer(&stored_player[i]);
11697         }
11698       }
11699       else if (game.no_time_limit && !game.all_players_gone)
11700       {
11701         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11702       }
11703
11704       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11705     }
11706
11707     if (tape.recording || tape.playing)
11708       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11709   }
11710
11711   if (tape.recording || tape.playing)
11712     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11713
11714   UpdateAndDisplayGameControlValues();
11715 }
11716
11717 void AdvanceFrameAndPlayerCounters(int player_nr)
11718 {
11719   int i;
11720
11721   // advance frame counters (global frame counter and time frame counter)
11722   FrameCounter++;
11723   TimeFrames++;
11724
11725   // advance player counters (counters for move delay, move animation etc.)
11726   for (i = 0; i < MAX_PLAYERS; i++)
11727   {
11728     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11729     int move_delay_value = stored_player[i].move_delay_value;
11730     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11731
11732     if (!advance_player_counters)       // not all players may be affected
11733       continue;
11734
11735     if (move_frames == 0)       // less than one move per game frame
11736     {
11737       int stepsize = TILEX / move_delay_value;
11738       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11739       int count = (stored_player[i].is_moving ?
11740                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11741
11742       if (count % delay == 0)
11743         move_frames = 1;
11744     }
11745
11746     stored_player[i].Frame += move_frames;
11747
11748     if (stored_player[i].MovPos != 0)
11749       stored_player[i].StepFrame += move_frames;
11750
11751     if (stored_player[i].move_delay > 0)
11752       stored_player[i].move_delay--;
11753
11754     // due to bugs in previous versions, counter must count up, not down
11755     if (stored_player[i].push_delay != -1)
11756       stored_player[i].push_delay++;
11757
11758     if (stored_player[i].drop_delay > 0)
11759       stored_player[i].drop_delay--;
11760
11761     if (stored_player[i].is_dropping_pressed)
11762       stored_player[i].drop_pressed_delay++;
11763   }
11764 }
11765
11766 void StartGameActions(boolean init_network_game, boolean record_tape,
11767                       int random_seed)
11768 {
11769   unsigned int new_random_seed = InitRND(random_seed);
11770
11771   if (record_tape)
11772     TapeStartRecording(new_random_seed);
11773
11774   if (init_network_game)
11775   {
11776     SendToServer_LevelFile();
11777     SendToServer_StartPlaying();
11778
11779     return;
11780   }
11781
11782   InitGame();
11783 }
11784
11785 static void GameActionsExt(void)
11786 {
11787 #if 0
11788   static unsigned int game_frame_delay = 0;
11789 #endif
11790   unsigned int game_frame_delay_value;
11791   byte *recorded_player_action;
11792   byte summarized_player_action = 0;
11793   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11794   int i;
11795
11796   // detect endless loops, caused by custom element programming
11797   if (recursion_loop_detected && recursion_loop_depth == 0)
11798   {
11799     char *message = getStringCat3("Internal Error! Element ",
11800                                   EL_NAME(recursion_loop_element),
11801                                   " caused endless loop! Quit the game?");
11802
11803     Warn("element '%s' caused endless loop in game engine",
11804          EL_NAME(recursion_loop_element));
11805
11806     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11807
11808     recursion_loop_detected = FALSE;    // if game should be continued
11809
11810     free(message);
11811
11812     return;
11813   }
11814
11815   if (game.restart_level)
11816     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11817
11818   CheckLevelSolved();
11819
11820   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11821     GameWon();
11822
11823   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11824     TapeStop();
11825
11826   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11827     return;
11828
11829   game_frame_delay_value =
11830     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11831
11832   if (tape.playing && tape.warp_forward && !tape.pausing)
11833     game_frame_delay_value = 0;
11834
11835   SetVideoFrameDelay(game_frame_delay_value);
11836
11837   // (de)activate virtual buttons depending on current game status
11838   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11839   {
11840     if (game.all_players_gone)  // if no players there to be controlled anymore
11841       SetOverlayActive(FALSE);
11842     else if (!tape.playing)     // if game continues after tape stopped playing
11843       SetOverlayActive(TRUE);
11844   }
11845
11846 #if 0
11847 #if 0
11848   // ---------- main game synchronization point ----------
11849
11850   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11851
11852   Debug("game:playing:skip", "skip == %d", skip);
11853
11854 #else
11855   // ---------- main game synchronization point ----------
11856
11857   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11858 #endif
11859 #endif
11860
11861   if (network_playing && !network_player_action_received)
11862   {
11863     // try to get network player actions in time
11864
11865     // last chance to get network player actions without main loop delay
11866     HandleNetworking();
11867
11868     // game was quit by network peer
11869     if (game_status != GAME_MODE_PLAYING)
11870       return;
11871
11872     // check if network player actions still missing and game still running
11873     if (!network_player_action_received && !checkGameEnded())
11874       return;           // failed to get network player actions in time
11875
11876     // do not yet reset "network_player_action_received" (for tape.pausing)
11877   }
11878
11879   if (tape.pausing)
11880     return;
11881
11882   // at this point we know that we really continue executing the game
11883
11884   network_player_action_received = FALSE;
11885
11886   // when playing tape, read previously recorded player input from tape data
11887   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11888
11889   local_player->effective_mouse_action = local_player->mouse_action;
11890
11891   if (recorded_player_action != NULL)
11892     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11893                                  recorded_player_action);
11894
11895   // TapePlayAction() may return NULL when toggling to "pause before death"
11896   if (tape.pausing)
11897     return;
11898
11899   if (tape.set_centered_player)
11900   {
11901     game.centered_player_nr_next = tape.centered_player_nr_next;
11902     game.set_centered_player = TRUE;
11903   }
11904
11905   for (i = 0; i < MAX_PLAYERS; i++)
11906   {
11907     summarized_player_action |= stored_player[i].action;
11908
11909     if (!network_playing && (game.team_mode || tape.playing))
11910       stored_player[i].effective_action = stored_player[i].action;
11911   }
11912
11913   if (network_playing && !checkGameEnded())
11914     SendToServer_MovePlayer(summarized_player_action);
11915
11916   // summarize all actions at local players mapped input device position
11917   // (this allows using different input devices in single player mode)
11918   if (!network.enabled && !game.team_mode)
11919     stored_player[map_player_action[local_player->index_nr]].effective_action =
11920       summarized_player_action;
11921
11922   // summarize all actions at centered player in local team mode
11923   if (tape.recording &&
11924       setup.team_mode && !network.enabled &&
11925       setup.input_on_focus &&
11926       game.centered_player_nr != -1)
11927   {
11928     for (i = 0; i < MAX_PLAYERS; i++)
11929       stored_player[map_player_action[i]].effective_action =
11930         (i == game.centered_player_nr ? summarized_player_action : 0);
11931   }
11932
11933   if (recorded_player_action != NULL)
11934     for (i = 0; i < MAX_PLAYERS; i++)
11935       stored_player[i].effective_action = recorded_player_action[i];
11936
11937   for (i = 0; i < MAX_PLAYERS; i++)
11938   {
11939     tape_action[i] = stored_player[i].effective_action;
11940
11941     /* (this may happen in the RND game engine if a player was not present on
11942        the playfield on level start, but appeared later from a custom element */
11943     if (setup.team_mode &&
11944         tape.recording &&
11945         tape_action[i] &&
11946         !tape.player_participates[i])
11947       tape.player_participates[i] = TRUE;
11948   }
11949
11950   SetTapeActionFromMouseAction(tape_action,
11951                                &local_player->effective_mouse_action);
11952
11953   // only record actions from input devices, but not programmed actions
11954   if (tape.recording)
11955     TapeRecordAction(tape_action);
11956
11957   // remember if game was played (especially after tape stopped playing)
11958   if (!tape.playing && summarized_player_action)
11959     game.GamePlayed = TRUE;
11960
11961 #if USE_NEW_PLAYER_ASSIGNMENTS
11962   // !!! also map player actions in single player mode !!!
11963   // if (game.team_mode)
11964   if (1)
11965   {
11966     byte mapped_action[MAX_PLAYERS];
11967
11968 #if DEBUG_PLAYER_ACTIONS
11969     for (i = 0; i < MAX_PLAYERS; i++)
11970       DebugContinued("", "%d, ", stored_player[i].effective_action);
11971 #endif
11972
11973     for (i = 0; i < MAX_PLAYERS; i++)
11974       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11975
11976     for (i = 0; i < MAX_PLAYERS; i++)
11977       stored_player[i].effective_action = mapped_action[i];
11978
11979 #if DEBUG_PLAYER_ACTIONS
11980     DebugContinued("", "=> ");
11981     for (i = 0; i < MAX_PLAYERS; i++)
11982       DebugContinued("", "%d, ", stored_player[i].effective_action);
11983     DebugContinued("game:playing:player", "\n");
11984 #endif
11985   }
11986 #if DEBUG_PLAYER_ACTIONS
11987   else
11988   {
11989     for (i = 0; i < MAX_PLAYERS; i++)
11990       DebugContinued("", "%d, ", stored_player[i].effective_action);
11991     DebugContinued("game:playing:player", "\n");
11992   }
11993 #endif
11994 #endif
11995
11996   for (i = 0; i < MAX_PLAYERS; i++)
11997   {
11998     // allow engine snapshot in case of changed movement attempt
11999     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12000         (stored_player[i].effective_action & KEY_MOTION))
12001       game.snapshot.changed_action = TRUE;
12002
12003     // allow engine snapshot in case of snapping/dropping attempt
12004     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12005         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12006       game.snapshot.changed_action = TRUE;
12007
12008     game.snapshot.last_action[i] = stored_player[i].effective_action;
12009   }
12010
12011   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12012   {
12013     GameActions_EM_Main();
12014   }
12015   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12016   {
12017     GameActions_SP_Main();
12018   }
12019   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12020   {
12021     GameActions_MM_Main();
12022   }
12023   else
12024   {
12025     GameActions_RND_Main();
12026   }
12027
12028   BlitScreenToBitmap(backbuffer);
12029
12030   CheckLevelSolved();
12031   CheckLevelTime();
12032
12033   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12034
12035   if (global.show_frames_per_second)
12036   {
12037     static unsigned int fps_counter = 0;
12038     static int fps_frames = 0;
12039     unsigned int fps_delay_ms = Counter() - fps_counter;
12040
12041     fps_frames++;
12042
12043     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12044     {
12045       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12046
12047       fps_frames = 0;
12048       fps_counter = Counter();
12049
12050       // always draw FPS to screen after FPS value was updated
12051       redraw_mask |= REDRAW_FPS;
12052     }
12053
12054     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12055     if (GetDrawDeactivationMask() == REDRAW_NONE)
12056       redraw_mask |= REDRAW_FPS;
12057   }
12058 }
12059
12060 static void GameActions_CheckSaveEngineSnapshot(void)
12061 {
12062   if (!game.snapshot.save_snapshot)
12063     return;
12064
12065   // clear flag for saving snapshot _before_ saving snapshot
12066   game.snapshot.save_snapshot = FALSE;
12067
12068   SaveEngineSnapshotToList();
12069 }
12070
12071 void GameActions(void)
12072 {
12073   GameActionsExt();
12074
12075   GameActions_CheckSaveEngineSnapshot();
12076 }
12077
12078 void GameActions_EM_Main(void)
12079 {
12080   byte effective_action[MAX_PLAYERS];
12081   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12082   int i;
12083
12084   for (i = 0; i < MAX_PLAYERS; i++)
12085     effective_action[i] = stored_player[i].effective_action;
12086
12087   GameActions_EM(effective_action, warp_mode);
12088 }
12089
12090 void GameActions_SP_Main(void)
12091 {
12092   byte effective_action[MAX_PLAYERS];
12093   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12094   int i;
12095
12096   for (i = 0; i < MAX_PLAYERS; i++)
12097     effective_action[i] = stored_player[i].effective_action;
12098
12099   GameActions_SP(effective_action, warp_mode);
12100
12101   for (i = 0; i < MAX_PLAYERS; i++)
12102   {
12103     if (stored_player[i].force_dropping)
12104       stored_player[i].action |= KEY_BUTTON_DROP;
12105
12106     stored_player[i].force_dropping = FALSE;
12107   }
12108 }
12109
12110 void GameActions_MM_Main(void)
12111 {
12112   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12113
12114   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12115 }
12116
12117 void GameActions_RND_Main(void)
12118 {
12119   GameActions_RND();
12120 }
12121
12122 void GameActions_RND(void)
12123 {
12124   static struct MouseActionInfo mouse_action_last = { 0 };
12125   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12126   int magic_wall_x = 0, magic_wall_y = 0;
12127   int i, x, y, element, graphic, last_gfx_frame;
12128
12129   InitPlayfieldScanModeVars();
12130
12131   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12132   {
12133     SCAN_PLAYFIELD(x, y)
12134     {
12135       ChangeCount[x][y] = 0;
12136       ChangeEvent[x][y] = -1;
12137     }
12138   }
12139
12140   if (game.set_centered_player)
12141   {
12142     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12143
12144     // switching to "all players" only possible if all players fit to screen
12145     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12146     {
12147       game.centered_player_nr_next = game.centered_player_nr;
12148       game.set_centered_player = FALSE;
12149     }
12150
12151     // do not switch focus to non-existing (or non-active) player
12152     if (game.centered_player_nr_next >= 0 &&
12153         !stored_player[game.centered_player_nr_next].active)
12154     {
12155       game.centered_player_nr_next = game.centered_player_nr;
12156       game.set_centered_player = FALSE;
12157     }
12158   }
12159
12160   if (game.set_centered_player &&
12161       ScreenMovPos == 0)        // screen currently aligned at tile position
12162   {
12163     int sx, sy;
12164
12165     if (game.centered_player_nr_next == -1)
12166     {
12167       setScreenCenteredToAllPlayers(&sx, &sy);
12168     }
12169     else
12170     {
12171       sx = stored_player[game.centered_player_nr_next].jx;
12172       sy = stored_player[game.centered_player_nr_next].jy;
12173     }
12174
12175     game.centered_player_nr = game.centered_player_nr_next;
12176     game.set_centered_player = FALSE;
12177
12178     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12179     DrawGameDoorValues();
12180   }
12181
12182   // check single step mode (set flag and clear again if any player is active)
12183   game.enter_single_step_mode =
12184     (tape.single_step && tape.recording && !tape.pausing);
12185
12186   for (i = 0; i < MAX_PLAYERS; i++)
12187   {
12188     int actual_player_action = stored_player[i].effective_action;
12189
12190 #if 1
12191     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12192        - rnd_equinox_tetrachloride 048
12193        - rnd_equinox_tetrachloride_ii 096
12194        - rnd_emanuel_schmieg 002
12195        - doctor_sloan_ww 001, 020
12196     */
12197     if (stored_player[i].MovPos == 0)
12198       CheckGravityMovement(&stored_player[i]);
12199 #endif
12200
12201     // overwrite programmed action with tape action
12202     if (stored_player[i].programmed_action)
12203       actual_player_action = stored_player[i].programmed_action;
12204
12205     PlayerActions(&stored_player[i], actual_player_action);
12206
12207     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12208   }
12209
12210   // single step pause mode may already have been toggled by "ScrollPlayer()"
12211   if (game.enter_single_step_mode && !tape.pausing)
12212     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12213
12214   ScrollScreen(NULL, SCROLL_GO_ON);
12215
12216   /* for backwards compatibility, the following code emulates a fixed bug that
12217      occured when pushing elements (causing elements that just made their last
12218      pushing step to already (if possible) make their first falling step in the
12219      same game frame, which is bad); this code is also needed to use the famous
12220      "spring push bug" which is used in older levels and might be wanted to be
12221      used also in newer levels, but in this case the buggy pushing code is only
12222      affecting the "spring" element and no other elements */
12223
12224   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12225   {
12226     for (i = 0; i < MAX_PLAYERS; i++)
12227     {
12228       struct PlayerInfo *player = &stored_player[i];
12229       int x = player->jx;
12230       int y = player->jy;
12231
12232       if (player->active && player->is_pushing && player->is_moving &&
12233           IS_MOVING(x, y) &&
12234           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12235            Tile[x][y] == EL_SPRING))
12236       {
12237         ContinueMoving(x, y);
12238
12239         // continue moving after pushing (this is actually a bug)
12240         if (!IS_MOVING(x, y))
12241           Stop[x][y] = FALSE;
12242       }
12243     }
12244   }
12245
12246   SCAN_PLAYFIELD(x, y)
12247   {
12248     Last[x][y] = Tile[x][y];
12249
12250     ChangeCount[x][y] = 0;
12251     ChangeEvent[x][y] = -1;
12252
12253     // this must be handled before main playfield loop
12254     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12255     {
12256       MovDelay[x][y]--;
12257       if (MovDelay[x][y] <= 0)
12258         RemoveField(x, y);
12259     }
12260
12261     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12262     {
12263       MovDelay[x][y]--;
12264       if (MovDelay[x][y] <= 0)
12265       {
12266         int element = Store[x][y];
12267         int move_direction = MovDir[x][y];
12268         int player_index_bit = Store2[x][y];
12269
12270         Store[x][y] = 0;
12271         Store2[x][y] = 0;
12272
12273         RemoveField(x, y);
12274         TEST_DrawLevelField(x, y);
12275
12276         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12277
12278         if (IS_ENVELOPE(element))
12279           local_player->show_envelope = element;
12280       }
12281     }
12282
12283 #if DEBUG
12284     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12285     {
12286       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12287             x, y);
12288       Debug("game:playing:GameActions_RND", "This should never happen!");
12289
12290       ChangePage[x][y] = -1;
12291     }
12292 #endif
12293
12294     Stop[x][y] = FALSE;
12295     if (WasJustMoving[x][y] > 0)
12296       WasJustMoving[x][y]--;
12297     if (WasJustFalling[x][y] > 0)
12298       WasJustFalling[x][y]--;
12299     if (CheckCollision[x][y] > 0)
12300       CheckCollision[x][y]--;
12301     if (CheckImpact[x][y] > 0)
12302       CheckImpact[x][y]--;
12303
12304     GfxFrame[x][y]++;
12305
12306     /* reset finished pushing action (not done in ContinueMoving() to allow
12307        continuous pushing animation for elements with zero push delay) */
12308     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12309     {
12310       ResetGfxAnimation(x, y);
12311       TEST_DrawLevelField(x, y);
12312     }
12313
12314 #if DEBUG
12315     if (IS_BLOCKED(x, y))
12316     {
12317       int oldx, oldy;
12318
12319       Blocked2Moving(x, y, &oldx, &oldy);
12320       if (!IS_MOVING(oldx, oldy))
12321       {
12322         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12323         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12324         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12325         Debug("game:playing:GameActions_RND", "This should never happen!");
12326       }
12327     }
12328 #endif
12329   }
12330
12331   if (mouse_action.button)
12332   {
12333     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12334     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12335
12336     x = mouse_action.lx;
12337     y = mouse_action.ly;
12338     element = Tile[x][y];
12339
12340     if (new_button)
12341     {
12342       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12343       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12344                                          ch_button);
12345     }
12346
12347     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12348     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12349                                        ch_button);
12350
12351     if (level.use_step_counter)
12352     {
12353       boolean counted_click = FALSE;
12354
12355       // element clicked that can change when clicked/pressed
12356       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12357           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12358            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12359         counted_click = TRUE;
12360
12361       // element clicked that can trigger change when clicked/pressed
12362       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12363           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12364         counted_click = TRUE;
12365
12366       if (new_button && counted_click)
12367         CheckLevelTime_StepCounter();
12368     }
12369   }
12370
12371   SCAN_PLAYFIELD(x, y)
12372   {
12373     element = Tile[x][y];
12374     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12375     last_gfx_frame = GfxFrame[x][y];
12376
12377     if (element == EL_EMPTY)
12378       graphic = el2img(GfxElementEmpty[x][y]);
12379
12380     ResetGfxFrame(x, y);
12381
12382     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12383       DrawLevelGraphicAnimation(x, y, graphic);
12384
12385     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12386         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12387       ResetRandomAnimationValue(x, y);
12388
12389     SetRandomAnimationValue(x, y);
12390
12391     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12392
12393     if (IS_INACTIVE(element))
12394     {
12395       if (IS_ANIMATED(graphic))
12396         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12397
12398       continue;
12399     }
12400
12401     // this may take place after moving, so 'element' may have changed
12402     if (IS_CHANGING(x, y) &&
12403         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12404     {
12405       int page = element_info[element].event_page_nr[CE_DELAY];
12406
12407       HandleElementChange(x, y, page);
12408
12409       element = Tile[x][y];
12410       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12411     }
12412
12413     CheckNextToConditions(x, y);
12414
12415     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12416     {
12417       StartMoving(x, y);
12418
12419       element = Tile[x][y];
12420       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12421
12422       if (IS_ANIMATED(graphic) &&
12423           !IS_MOVING(x, y) &&
12424           !Stop[x][y])
12425         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12426
12427       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12428         TEST_DrawTwinkleOnField(x, y);
12429     }
12430     else if (element == EL_ACID)
12431     {
12432       if (!Stop[x][y])
12433         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12434     }
12435     else if ((element == EL_EXIT_OPEN ||
12436               element == EL_EM_EXIT_OPEN ||
12437               element == EL_SP_EXIT_OPEN ||
12438               element == EL_STEEL_EXIT_OPEN ||
12439               element == EL_EM_STEEL_EXIT_OPEN ||
12440               element == EL_SP_TERMINAL ||
12441               element == EL_SP_TERMINAL_ACTIVE ||
12442               element == EL_EXTRA_TIME ||
12443               element == EL_SHIELD_NORMAL ||
12444               element == EL_SHIELD_DEADLY) &&
12445              IS_ANIMATED(graphic))
12446       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12447     else if (IS_MOVING(x, y))
12448       ContinueMoving(x, y);
12449     else if (IS_ACTIVE_BOMB(element))
12450       CheckDynamite(x, y);
12451     else if (element == EL_AMOEBA_GROWING)
12452       AmoebaGrowing(x, y);
12453     else if (element == EL_AMOEBA_SHRINKING)
12454       AmoebaShrinking(x, y);
12455
12456 #if !USE_NEW_AMOEBA_CODE
12457     else if (IS_AMOEBALIVE(element))
12458       AmoebaReproduce(x, y);
12459 #endif
12460
12461     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12462       Life(x, y);
12463     else if (element == EL_EXIT_CLOSED)
12464       CheckExit(x, y);
12465     else if (element == EL_EM_EXIT_CLOSED)
12466       CheckExitEM(x, y);
12467     else if (element == EL_STEEL_EXIT_CLOSED)
12468       CheckExitSteel(x, y);
12469     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12470       CheckExitSteelEM(x, y);
12471     else if (element == EL_SP_EXIT_CLOSED)
12472       CheckExitSP(x, y);
12473     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12474              element == EL_EXPANDABLE_STEELWALL_GROWING)
12475       MauerWaechst(x, y);
12476     else if (element == EL_EXPANDABLE_WALL ||
12477              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12478              element == EL_EXPANDABLE_WALL_VERTICAL ||
12479              element == EL_EXPANDABLE_WALL_ANY ||
12480              element == EL_BD_EXPANDABLE_WALL)
12481       MauerAbleger(x, y);
12482     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12483              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12484              element == EL_EXPANDABLE_STEELWALL_ANY)
12485       MauerAblegerStahl(x, y);
12486     else if (element == EL_FLAMES)
12487       CheckForDragon(x, y);
12488     else if (element == EL_EXPLOSION)
12489       ; // drawing of correct explosion animation is handled separately
12490     else if (element == EL_ELEMENT_SNAPPING ||
12491              element == EL_DIAGONAL_SHRINKING ||
12492              element == EL_DIAGONAL_GROWING)
12493     {
12494       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12495
12496       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12497     }
12498     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12499       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12500
12501     if (IS_BELT_ACTIVE(element))
12502       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12503
12504     if (game.magic_wall_active)
12505     {
12506       int jx = local_player->jx, jy = local_player->jy;
12507
12508       // play the element sound at the position nearest to the player
12509       if ((element == EL_MAGIC_WALL_FULL ||
12510            element == EL_MAGIC_WALL_ACTIVE ||
12511            element == EL_MAGIC_WALL_EMPTYING ||
12512            element == EL_BD_MAGIC_WALL_FULL ||
12513            element == EL_BD_MAGIC_WALL_ACTIVE ||
12514            element == EL_BD_MAGIC_WALL_EMPTYING ||
12515            element == EL_DC_MAGIC_WALL_FULL ||
12516            element == EL_DC_MAGIC_WALL_ACTIVE ||
12517            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12518           ABS(x - jx) + ABS(y - jy) <
12519           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12520       {
12521         magic_wall_x = x;
12522         magic_wall_y = y;
12523       }
12524     }
12525   }
12526
12527 #if USE_NEW_AMOEBA_CODE
12528   // new experimental amoeba growth stuff
12529   if (!(FrameCounter % 8))
12530   {
12531     static unsigned int random = 1684108901;
12532
12533     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12534     {
12535       x = RND(lev_fieldx);
12536       y = RND(lev_fieldy);
12537       element = Tile[x][y];
12538
12539       if (!IS_PLAYER(x,y) &&
12540           (element == EL_EMPTY ||
12541            CAN_GROW_INTO(element) ||
12542            element == EL_QUICKSAND_EMPTY ||
12543            element == EL_QUICKSAND_FAST_EMPTY ||
12544            element == EL_ACID_SPLASH_LEFT ||
12545            element == EL_ACID_SPLASH_RIGHT))
12546       {
12547         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12548             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12549             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12550             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12551           Tile[x][y] = EL_AMOEBA_DROP;
12552       }
12553
12554       random = random * 129 + 1;
12555     }
12556   }
12557 #endif
12558
12559   game.explosions_delayed = FALSE;
12560
12561   SCAN_PLAYFIELD(x, y)
12562   {
12563     element = Tile[x][y];
12564
12565     if (ExplodeField[x][y])
12566       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12567     else if (element == EL_EXPLOSION)
12568       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12569
12570     ExplodeField[x][y] = EX_TYPE_NONE;
12571   }
12572
12573   game.explosions_delayed = TRUE;
12574
12575   if (game.magic_wall_active)
12576   {
12577     if (!(game.magic_wall_time_left % 4))
12578     {
12579       int element = Tile[magic_wall_x][magic_wall_y];
12580
12581       if (element == EL_BD_MAGIC_WALL_FULL ||
12582           element == EL_BD_MAGIC_WALL_ACTIVE ||
12583           element == EL_BD_MAGIC_WALL_EMPTYING)
12584         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12585       else if (element == EL_DC_MAGIC_WALL_FULL ||
12586                element == EL_DC_MAGIC_WALL_ACTIVE ||
12587                element == EL_DC_MAGIC_WALL_EMPTYING)
12588         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12589       else
12590         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12591     }
12592
12593     if (game.magic_wall_time_left > 0)
12594     {
12595       game.magic_wall_time_left--;
12596
12597       if (!game.magic_wall_time_left)
12598       {
12599         SCAN_PLAYFIELD(x, y)
12600         {
12601           element = Tile[x][y];
12602
12603           if (element == EL_MAGIC_WALL_ACTIVE ||
12604               element == EL_MAGIC_WALL_FULL)
12605           {
12606             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12607             TEST_DrawLevelField(x, y);
12608           }
12609           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12610                    element == EL_BD_MAGIC_WALL_FULL)
12611           {
12612             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12613             TEST_DrawLevelField(x, y);
12614           }
12615           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12616                    element == EL_DC_MAGIC_WALL_FULL)
12617           {
12618             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12619             TEST_DrawLevelField(x, y);
12620           }
12621         }
12622
12623         game.magic_wall_active = FALSE;
12624       }
12625     }
12626   }
12627
12628   if (game.light_time_left > 0)
12629   {
12630     game.light_time_left--;
12631
12632     if (game.light_time_left == 0)
12633       RedrawAllLightSwitchesAndInvisibleElements();
12634   }
12635
12636   if (game.timegate_time_left > 0)
12637   {
12638     game.timegate_time_left--;
12639
12640     if (game.timegate_time_left == 0)
12641       CloseAllOpenTimegates();
12642   }
12643
12644   if (game.lenses_time_left > 0)
12645   {
12646     game.lenses_time_left--;
12647
12648     if (game.lenses_time_left == 0)
12649       RedrawAllInvisibleElementsForLenses();
12650   }
12651
12652   if (game.magnify_time_left > 0)
12653   {
12654     game.magnify_time_left--;
12655
12656     if (game.magnify_time_left == 0)
12657       RedrawAllInvisibleElementsForMagnifier();
12658   }
12659
12660   for (i = 0; i < MAX_PLAYERS; i++)
12661   {
12662     struct PlayerInfo *player = &stored_player[i];
12663
12664     if (SHIELD_ON(player))
12665     {
12666       if (player->shield_deadly_time_left)
12667         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12668       else if (player->shield_normal_time_left)
12669         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12670     }
12671   }
12672
12673 #if USE_DELAYED_GFX_REDRAW
12674   SCAN_PLAYFIELD(x, y)
12675   {
12676     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12677     {
12678       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12679          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12680
12681       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12682         DrawLevelField(x, y);
12683
12684       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12685         DrawLevelFieldCrumbled(x, y);
12686
12687       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12688         DrawLevelFieldCrumbledNeighbours(x, y);
12689
12690       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12691         DrawTwinkleOnField(x, y);
12692     }
12693
12694     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12695   }
12696 #endif
12697
12698   DrawAllPlayers();
12699   PlayAllPlayersSound();
12700
12701   for (i = 0; i < MAX_PLAYERS; i++)
12702   {
12703     struct PlayerInfo *player = &stored_player[i];
12704
12705     if (player->show_envelope != 0 && (!player->active ||
12706                                        player->MovPos == 0))
12707     {
12708       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12709
12710       player->show_envelope = 0;
12711     }
12712   }
12713
12714   // use random number generator in every frame to make it less predictable
12715   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12716     RND(1);
12717
12718   mouse_action_last = mouse_action;
12719 }
12720
12721 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12722 {
12723   int min_x = x, min_y = y, max_x = x, max_y = y;
12724   int scr_fieldx = getScreenFieldSizeX();
12725   int scr_fieldy = getScreenFieldSizeY();
12726   int i;
12727
12728   for (i = 0; i < MAX_PLAYERS; i++)
12729   {
12730     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12731
12732     if (!stored_player[i].active || &stored_player[i] == player)
12733       continue;
12734
12735     min_x = MIN(min_x, jx);
12736     min_y = MIN(min_y, jy);
12737     max_x = MAX(max_x, jx);
12738     max_y = MAX(max_y, jy);
12739   }
12740
12741   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12742 }
12743
12744 static boolean AllPlayersInVisibleScreen(void)
12745 {
12746   int i;
12747
12748   for (i = 0; i < MAX_PLAYERS; i++)
12749   {
12750     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12751
12752     if (!stored_player[i].active)
12753       continue;
12754
12755     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12756       return FALSE;
12757   }
12758
12759   return TRUE;
12760 }
12761
12762 void ScrollLevel(int dx, int dy)
12763 {
12764   int scroll_offset = 2 * TILEX_VAR;
12765   int x, y;
12766
12767   BlitBitmap(drawto_field, drawto_field,
12768              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12769              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12770              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12771              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12772              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12773              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12774
12775   if (dx != 0)
12776   {
12777     x = (dx == 1 ? BX1 : BX2);
12778     for (y = BY1; y <= BY2; y++)
12779       DrawScreenField(x, y);
12780   }
12781
12782   if (dy != 0)
12783   {
12784     y = (dy == 1 ? BY1 : BY2);
12785     for (x = BX1; x <= BX2; x++)
12786       DrawScreenField(x, y);
12787   }
12788
12789   redraw_mask |= REDRAW_FIELD;
12790 }
12791
12792 static boolean canFallDown(struct PlayerInfo *player)
12793 {
12794   int jx = player->jx, jy = player->jy;
12795
12796   return (IN_LEV_FIELD(jx, jy + 1) &&
12797           (IS_FREE(jx, jy + 1) ||
12798            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12799           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12800           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12801 }
12802
12803 static boolean canPassField(int x, int y, int move_dir)
12804 {
12805   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12806   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12807   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12808   int nextx = x + dx;
12809   int nexty = y + dy;
12810   int element = Tile[x][y];
12811
12812   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12813           !CAN_MOVE(element) &&
12814           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12815           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12816           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12817 }
12818
12819 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12820 {
12821   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12822   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12823   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12824   int newx = x + dx;
12825   int newy = y + dy;
12826
12827   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12828           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12829           (IS_DIGGABLE(Tile[newx][newy]) ||
12830            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12831            canPassField(newx, newy, move_dir)));
12832 }
12833
12834 static void CheckGravityMovement(struct PlayerInfo *player)
12835 {
12836   if (player->gravity && !player->programmed_action)
12837   {
12838     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12839     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12840     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12841     int jx = player->jx, jy = player->jy;
12842     boolean player_is_moving_to_valid_field =
12843       (!player_is_snapping &&
12844        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12845         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12846     boolean player_can_fall_down = canFallDown(player);
12847
12848     if (player_can_fall_down &&
12849         !player_is_moving_to_valid_field)
12850       player->programmed_action = MV_DOWN;
12851   }
12852 }
12853
12854 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12855 {
12856   return CheckGravityMovement(player);
12857
12858   if (player->gravity && !player->programmed_action)
12859   {
12860     int jx = player->jx, jy = player->jy;
12861     boolean field_under_player_is_free =
12862       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12863     boolean player_is_standing_on_valid_field =
12864       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12865        (IS_WALKABLE(Tile[jx][jy]) &&
12866         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12867
12868     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12869       player->programmed_action = MV_DOWN;
12870   }
12871 }
12872
12873 /*
12874   MovePlayerOneStep()
12875   -----------------------------------------------------------------------------
12876   dx, dy:               direction (non-diagonal) to try to move the player to
12877   real_dx, real_dy:     direction as read from input device (can be diagonal)
12878 */
12879
12880 boolean MovePlayerOneStep(struct PlayerInfo *player,
12881                           int dx, int dy, int real_dx, int real_dy)
12882 {
12883   int jx = player->jx, jy = player->jy;
12884   int new_jx = jx + dx, new_jy = jy + dy;
12885   int can_move;
12886   boolean player_can_move = !player->cannot_move;
12887
12888   if (!player->active || (!dx && !dy))
12889     return MP_NO_ACTION;
12890
12891   player->MovDir = (dx < 0 ? MV_LEFT :
12892                     dx > 0 ? MV_RIGHT :
12893                     dy < 0 ? MV_UP :
12894                     dy > 0 ? MV_DOWN :  MV_NONE);
12895
12896   if (!IN_LEV_FIELD(new_jx, new_jy))
12897     return MP_NO_ACTION;
12898
12899   if (!player_can_move)
12900   {
12901     if (player->MovPos == 0)
12902     {
12903       player->is_moving = FALSE;
12904       player->is_digging = FALSE;
12905       player->is_collecting = FALSE;
12906       player->is_snapping = FALSE;
12907       player->is_pushing = FALSE;
12908     }
12909   }
12910
12911   if (!network.enabled && game.centered_player_nr == -1 &&
12912       !AllPlayersInSight(player, new_jx, new_jy))
12913     return MP_NO_ACTION;
12914
12915   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12916   if (can_move != MP_MOVING)
12917     return can_move;
12918
12919   // check if DigField() has caused relocation of the player
12920   if (player->jx != jx || player->jy != jy)
12921     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12922
12923   StorePlayer[jx][jy] = 0;
12924   player->last_jx = jx;
12925   player->last_jy = jy;
12926   player->jx = new_jx;
12927   player->jy = new_jy;
12928   StorePlayer[new_jx][new_jy] = player->element_nr;
12929
12930   if (player->move_delay_value_next != -1)
12931   {
12932     player->move_delay_value = player->move_delay_value_next;
12933     player->move_delay_value_next = -1;
12934   }
12935
12936   player->MovPos =
12937     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12938
12939   player->step_counter++;
12940
12941   PlayerVisit[jx][jy] = FrameCounter;
12942
12943   player->is_moving = TRUE;
12944
12945 #if 1
12946   // should better be called in MovePlayer(), but this breaks some tapes
12947   ScrollPlayer(player, SCROLL_INIT);
12948 #endif
12949
12950   return MP_MOVING;
12951 }
12952
12953 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12954 {
12955   int jx = player->jx, jy = player->jy;
12956   int old_jx = jx, old_jy = jy;
12957   int moved = MP_NO_ACTION;
12958
12959   if (!player->active)
12960     return FALSE;
12961
12962   if (!dx && !dy)
12963   {
12964     if (player->MovPos == 0)
12965     {
12966       player->is_moving = FALSE;
12967       player->is_digging = FALSE;
12968       player->is_collecting = FALSE;
12969       player->is_snapping = FALSE;
12970       player->is_pushing = FALSE;
12971     }
12972
12973     return FALSE;
12974   }
12975
12976   if (player->move_delay > 0)
12977     return FALSE;
12978
12979   player->move_delay = -1;              // set to "uninitialized" value
12980
12981   // store if player is automatically moved to next field
12982   player->is_auto_moving = (player->programmed_action != MV_NONE);
12983
12984   // remove the last programmed player action
12985   player->programmed_action = 0;
12986
12987   if (player->MovPos)
12988   {
12989     // should only happen if pre-1.2 tape recordings are played
12990     // this is only for backward compatibility
12991
12992     int original_move_delay_value = player->move_delay_value;
12993
12994 #if DEBUG
12995     Debug("game:playing:MovePlayer",
12996           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12997           tape.counter);
12998 #endif
12999
13000     // scroll remaining steps with finest movement resolution
13001     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13002
13003     while (player->MovPos)
13004     {
13005       ScrollPlayer(player, SCROLL_GO_ON);
13006       ScrollScreen(NULL, SCROLL_GO_ON);
13007
13008       AdvanceFrameAndPlayerCounters(player->index_nr);
13009
13010       DrawAllPlayers();
13011       BackToFront_WithFrameDelay(0);
13012     }
13013
13014     player->move_delay_value = original_move_delay_value;
13015   }
13016
13017   player->is_active = FALSE;
13018
13019   if (player->last_move_dir & MV_HORIZONTAL)
13020   {
13021     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13022       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13023   }
13024   else
13025   {
13026     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13027       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13028   }
13029
13030   if (!moved && !player->is_active)
13031   {
13032     player->is_moving = FALSE;
13033     player->is_digging = FALSE;
13034     player->is_collecting = FALSE;
13035     player->is_snapping = FALSE;
13036     player->is_pushing = FALSE;
13037   }
13038
13039   jx = player->jx;
13040   jy = player->jy;
13041
13042   if (moved & MP_MOVING && !ScreenMovPos &&
13043       (player->index_nr == game.centered_player_nr ||
13044        game.centered_player_nr == -1))
13045   {
13046     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13047
13048     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13049     {
13050       // actual player has left the screen -- scroll in that direction
13051       if (jx != old_jx)         // player has moved horizontally
13052         scroll_x += (jx - old_jx);
13053       else                      // player has moved vertically
13054         scroll_y += (jy - old_jy);
13055     }
13056     else
13057     {
13058       int offset_raw = game.scroll_delay_value;
13059
13060       if (jx != old_jx)         // player has moved horizontally
13061       {
13062         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13063         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13064         int new_scroll_x = jx - MIDPOSX + offset_x;
13065
13066         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13067             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13068           scroll_x = new_scroll_x;
13069
13070         // don't scroll over playfield boundaries
13071         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13072
13073         // don't scroll more than one field at a time
13074         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13075
13076         // don't scroll against the player's moving direction
13077         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13078             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13079           scroll_x = old_scroll_x;
13080       }
13081       else                      // player has moved vertically
13082       {
13083         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13084         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13085         int new_scroll_y = jy - MIDPOSY + offset_y;
13086
13087         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13088             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13089           scroll_y = new_scroll_y;
13090
13091         // don't scroll over playfield boundaries
13092         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13093
13094         // don't scroll more than one field at a time
13095         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13096
13097         // don't scroll against the player's moving direction
13098         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13099             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13100           scroll_y = old_scroll_y;
13101       }
13102     }
13103
13104     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13105     {
13106       if (!network.enabled && game.centered_player_nr == -1 &&
13107           !AllPlayersInVisibleScreen())
13108       {
13109         scroll_x = old_scroll_x;
13110         scroll_y = old_scroll_y;
13111       }
13112       else
13113       {
13114         ScrollScreen(player, SCROLL_INIT);
13115         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13116       }
13117     }
13118   }
13119
13120   player->StepFrame = 0;
13121
13122   if (moved & MP_MOVING)
13123   {
13124     if (old_jx != jx && old_jy == jy)
13125       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13126     else if (old_jx == jx && old_jy != jy)
13127       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13128
13129     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13130
13131     player->last_move_dir = player->MovDir;
13132     player->is_moving = TRUE;
13133     player->is_snapping = FALSE;
13134     player->is_switching = FALSE;
13135     player->is_dropping = FALSE;
13136     player->is_dropping_pressed = FALSE;
13137     player->drop_pressed_delay = 0;
13138
13139 #if 0
13140     // should better be called here than above, but this breaks some tapes
13141     ScrollPlayer(player, SCROLL_INIT);
13142 #endif
13143   }
13144   else
13145   {
13146     CheckGravityMovementWhenNotMoving(player);
13147
13148     player->is_moving = FALSE;
13149
13150     /* at this point, the player is allowed to move, but cannot move right now
13151        (e.g. because of something blocking the way) -- ensure that the player
13152        is also allowed to move in the next frame (in old versions before 3.1.1,
13153        the player was forced to wait again for eight frames before next try) */
13154
13155     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13156       player->move_delay = 0;   // allow direct movement in the next frame
13157   }
13158
13159   if (player->move_delay == -1)         // not yet initialized by DigField()
13160     player->move_delay = player->move_delay_value;
13161
13162   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13163   {
13164     TestIfPlayerTouchesBadThing(jx, jy);
13165     TestIfPlayerTouchesCustomElement(jx, jy);
13166   }
13167
13168   if (!player->active)
13169     RemovePlayer(player);
13170
13171   return moved;
13172 }
13173
13174 void ScrollPlayer(struct PlayerInfo *player, int mode)
13175 {
13176   int jx = player->jx, jy = player->jy;
13177   int last_jx = player->last_jx, last_jy = player->last_jy;
13178   int move_stepsize = TILEX / player->move_delay_value;
13179
13180   if (!player->active)
13181     return;
13182
13183   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13184     return;
13185
13186   if (mode == SCROLL_INIT)
13187   {
13188     player->actual_frame_counter = FrameCounter;
13189     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13190
13191     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13192         Tile[last_jx][last_jy] == EL_EMPTY)
13193     {
13194       int last_field_block_delay = 0;   // start with no blocking at all
13195       int block_delay_adjustment = player->block_delay_adjustment;
13196
13197       // if player blocks last field, add delay for exactly one move
13198       if (player->block_last_field)
13199       {
13200         last_field_block_delay += player->move_delay_value;
13201
13202         // when blocking enabled, prevent moving up despite gravity
13203         if (player->gravity && player->MovDir == MV_UP)
13204           block_delay_adjustment = -1;
13205       }
13206
13207       // add block delay adjustment (also possible when not blocking)
13208       last_field_block_delay += block_delay_adjustment;
13209
13210       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13211       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13212     }
13213
13214     if (player->MovPos != 0)    // player has not yet reached destination
13215       return;
13216   }
13217   else if (!FrameReached(&player->actual_frame_counter, 1))
13218     return;
13219
13220   if (player->MovPos != 0)
13221   {
13222     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13223     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13224
13225     // before DrawPlayer() to draw correct player graphic for this case
13226     if (player->MovPos == 0)
13227       CheckGravityMovement(player);
13228   }
13229
13230   if (player->MovPos == 0)      // player reached destination field
13231   {
13232     if (player->move_delay_reset_counter > 0)
13233     {
13234       player->move_delay_reset_counter--;
13235
13236       if (player->move_delay_reset_counter == 0)
13237       {
13238         // continue with normal speed after quickly moving through gate
13239         HALVE_PLAYER_SPEED(player);
13240
13241         // be able to make the next move without delay
13242         player->move_delay = 0;
13243       }
13244     }
13245
13246     player->last_jx = jx;
13247     player->last_jy = jy;
13248
13249     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13250         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13251         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13252         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13253         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13254         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13255         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13256         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13257     {
13258       ExitPlayer(player);
13259
13260       if (game.players_still_needed == 0 &&
13261           (game.friends_still_needed == 0 ||
13262            IS_SP_ELEMENT(Tile[jx][jy])))
13263         LevelSolved();
13264     }
13265
13266     // this breaks one level: "machine", level 000
13267     {
13268       int move_direction = player->MovDir;
13269       int enter_side = MV_DIR_OPPOSITE(move_direction);
13270       int leave_side = move_direction;
13271       int old_jx = last_jx;
13272       int old_jy = last_jy;
13273       int old_element = Tile[old_jx][old_jy];
13274       int new_element = Tile[jx][jy];
13275
13276       if (IS_CUSTOM_ELEMENT(old_element))
13277         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13278                                    CE_LEFT_BY_PLAYER,
13279                                    player->index_bit, leave_side);
13280
13281       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13282                                           CE_PLAYER_LEAVES_X,
13283                                           player->index_bit, leave_side);
13284
13285       if (IS_CUSTOM_ELEMENT(new_element))
13286         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13287                                    player->index_bit, enter_side);
13288
13289       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13290                                           CE_PLAYER_ENTERS_X,
13291                                           player->index_bit, enter_side);
13292
13293       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13294                                         CE_MOVE_OF_X, move_direction);
13295     }
13296
13297     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13298     {
13299       TestIfPlayerTouchesBadThing(jx, jy);
13300       TestIfPlayerTouchesCustomElement(jx, jy);
13301
13302       /* needed because pushed element has not yet reached its destination,
13303          so it would trigger a change event at its previous field location */
13304       if (!player->is_pushing)
13305         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13306
13307       if (level.finish_dig_collect &&
13308           (player->is_digging || player->is_collecting))
13309       {
13310         int last_element = player->last_removed_element;
13311         int move_direction = player->MovDir;
13312         int enter_side = MV_DIR_OPPOSITE(move_direction);
13313         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13314                             CE_PLAYER_COLLECTS_X);
13315
13316         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13317                                             player->index_bit, enter_side);
13318
13319         player->last_removed_element = EL_UNDEFINED;
13320       }
13321
13322       if (!player->active)
13323         RemovePlayer(player);
13324     }
13325
13326     if (level.use_step_counter)
13327       CheckLevelTime_StepCounter();
13328
13329     if (tape.single_step && tape.recording && !tape.pausing &&
13330         !player->programmed_action)
13331       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13332
13333     if (!player->programmed_action)
13334       CheckSaveEngineSnapshot(player);
13335   }
13336 }
13337
13338 void ScrollScreen(struct PlayerInfo *player, int mode)
13339 {
13340   static unsigned int screen_frame_counter = 0;
13341
13342   if (mode == SCROLL_INIT)
13343   {
13344     // set scrolling step size according to actual player's moving speed
13345     ScrollStepSize = TILEX / player->move_delay_value;
13346
13347     screen_frame_counter = FrameCounter;
13348     ScreenMovDir = player->MovDir;
13349     ScreenMovPos = player->MovPos;
13350     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13351     return;
13352   }
13353   else if (!FrameReached(&screen_frame_counter, 1))
13354     return;
13355
13356   if (ScreenMovPos)
13357   {
13358     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13359     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13360     redraw_mask |= REDRAW_FIELD;
13361   }
13362   else
13363     ScreenMovDir = MV_NONE;
13364 }
13365
13366 void CheckNextToConditions(int x, int y)
13367 {
13368   int element = Tile[x][y];
13369
13370   if (IS_PLAYER(x, y))
13371     TestIfPlayerNextToCustomElement(x, y);
13372
13373   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13374       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13375     TestIfElementNextToCustomElement(x, y);
13376 }
13377
13378 void TestIfPlayerNextToCustomElement(int x, int y)
13379 {
13380   static int xy[4][2] =
13381   {
13382     { 0, -1 },
13383     { -1, 0 },
13384     { +1, 0 },
13385     { 0, +1 }
13386   };
13387   static int trigger_sides[4][2] =
13388   {
13389     // center side       border side
13390     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13391     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13392     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13393     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13394   };
13395   int i;
13396
13397   if (!IS_PLAYER(x, y))
13398     return;
13399
13400   struct PlayerInfo *player = PLAYERINFO(x, y);
13401
13402   if (player->is_moving)
13403     return;
13404
13405   for (i = 0; i < NUM_DIRECTIONS; i++)
13406   {
13407     int xx = x + xy[i][0];
13408     int yy = y + xy[i][1];
13409     int border_side = trigger_sides[i][1];
13410     int border_element;
13411
13412     if (!IN_LEV_FIELD(xx, yy))
13413       continue;
13414
13415     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13416       continue;         // center and border element not connected
13417
13418     border_element = Tile[xx][yy];
13419
13420     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13421                                player->index_bit, border_side);
13422     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13423                                         CE_PLAYER_NEXT_TO_X,
13424                                         player->index_bit, border_side);
13425
13426     /* use player element that is initially defined in the level playfield,
13427        not the player element that corresponds to the runtime player number
13428        (example: a level that contains EL_PLAYER_3 as the only player would
13429        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13430
13431     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13432                              CE_NEXT_TO_X, border_side);
13433   }
13434 }
13435
13436 void TestIfPlayerTouchesCustomElement(int x, int y)
13437 {
13438   static int xy[4][2] =
13439   {
13440     { 0, -1 },
13441     { -1, 0 },
13442     { +1, 0 },
13443     { 0, +1 }
13444   };
13445   static int trigger_sides[4][2] =
13446   {
13447     // center side       border side
13448     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13449     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13450     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13451     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13452   };
13453   static int touch_dir[4] =
13454   {
13455     MV_LEFT | MV_RIGHT,
13456     MV_UP   | MV_DOWN,
13457     MV_UP   | MV_DOWN,
13458     MV_LEFT | MV_RIGHT
13459   };
13460   int center_element = Tile[x][y];      // should always be non-moving!
13461   int i;
13462
13463   for (i = 0; i < NUM_DIRECTIONS; i++)
13464   {
13465     int xx = x + xy[i][0];
13466     int yy = y + xy[i][1];
13467     int center_side = trigger_sides[i][0];
13468     int border_side = trigger_sides[i][1];
13469     int border_element;
13470
13471     if (!IN_LEV_FIELD(xx, yy))
13472       continue;
13473
13474     if (IS_PLAYER(x, y))                // player found at center element
13475     {
13476       struct PlayerInfo *player = PLAYERINFO(x, y);
13477
13478       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13479         border_element = Tile[xx][yy];          // may be moving!
13480       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13481         border_element = Tile[xx][yy];
13482       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13483         border_element = MovingOrBlocked2Element(xx, yy);
13484       else
13485         continue;               // center and border element do not touch
13486
13487       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13488                                  player->index_bit, border_side);
13489       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13490                                           CE_PLAYER_TOUCHES_X,
13491                                           player->index_bit, border_side);
13492
13493       {
13494         /* use player element that is initially defined in the level playfield,
13495            not the player element that corresponds to the runtime player number
13496            (example: a level that contains EL_PLAYER_3 as the only player would
13497            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13498         int player_element = PLAYERINFO(x, y)->initial_element;
13499
13500         CheckElementChangeBySide(xx, yy, border_element, player_element,
13501                                  CE_TOUCHING_X, border_side);
13502       }
13503     }
13504     else if (IS_PLAYER(xx, yy))         // player found at border element
13505     {
13506       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13507
13508       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13509       {
13510         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13511           continue;             // center and border element do not touch
13512       }
13513
13514       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13515                                  player->index_bit, center_side);
13516       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13517                                           CE_PLAYER_TOUCHES_X,
13518                                           player->index_bit, center_side);
13519
13520       {
13521         /* use player element that is initially defined in the level playfield,
13522            not the player element that corresponds to the runtime player number
13523            (example: a level that contains EL_PLAYER_3 as the only player would
13524            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13525         int player_element = PLAYERINFO(xx, yy)->initial_element;
13526
13527         CheckElementChangeBySide(x, y, center_element, player_element,
13528                                  CE_TOUCHING_X, center_side);
13529       }
13530
13531       break;
13532     }
13533   }
13534 }
13535
13536 void TestIfElementNextToCustomElement(int x, int y)
13537 {
13538   static int xy[4][2] =
13539   {
13540     { 0, -1 },
13541     { -1, 0 },
13542     { +1, 0 },
13543     { 0, +1 }
13544   };
13545   static int trigger_sides[4][2] =
13546   {
13547     // center side      border side
13548     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13549     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13550     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13551     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13552   };
13553   int center_element = Tile[x][y];      // should always be non-moving!
13554   int i;
13555
13556   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13557     return;
13558
13559   for (i = 0; i < NUM_DIRECTIONS; i++)
13560   {
13561     int xx = x + xy[i][0];
13562     int yy = y + xy[i][1];
13563     int border_side = trigger_sides[i][1];
13564     int border_element;
13565
13566     if (!IN_LEV_FIELD(xx, yy))
13567       continue;
13568
13569     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13570       continue;                 // center and border element not connected
13571
13572     border_element = Tile[xx][yy];
13573
13574     // check for change of center element (but change it only once)
13575     if (CheckElementChangeBySide(x, y, center_element, border_element,
13576                                  CE_NEXT_TO_X, border_side))
13577       break;
13578   }
13579 }
13580
13581 void TestIfElementTouchesCustomElement(int x, int y)
13582 {
13583   static int xy[4][2] =
13584   {
13585     { 0, -1 },
13586     { -1, 0 },
13587     { +1, 0 },
13588     { 0, +1 }
13589   };
13590   static int trigger_sides[4][2] =
13591   {
13592     // center side      border side
13593     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13594     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13595     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13596     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13597   };
13598   static int touch_dir[4] =
13599   {
13600     MV_LEFT | MV_RIGHT,
13601     MV_UP   | MV_DOWN,
13602     MV_UP   | MV_DOWN,
13603     MV_LEFT | MV_RIGHT
13604   };
13605   boolean change_center_element = FALSE;
13606   int center_element = Tile[x][y];      // should always be non-moving!
13607   int border_element_old[NUM_DIRECTIONS];
13608   int i;
13609
13610   for (i = 0; i < NUM_DIRECTIONS; i++)
13611   {
13612     int xx = x + xy[i][0];
13613     int yy = y + xy[i][1];
13614     int border_element;
13615
13616     border_element_old[i] = -1;
13617
13618     if (!IN_LEV_FIELD(xx, yy))
13619       continue;
13620
13621     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13622       border_element = Tile[xx][yy];    // may be moving!
13623     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13624       border_element = Tile[xx][yy];
13625     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13626       border_element = MovingOrBlocked2Element(xx, yy);
13627     else
13628       continue;                 // center and border element do not touch
13629
13630     border_element_old[i] = border_element;
13631   }
13632
13633   for (i = 0; i < NUM_DIRECTIONS; i++)
13634   {
13635     int xx = x + xy[i][0];
13636     int yy = y + xy[i][1];
13637     int center_side = trigger_sides[i][0];
13638     int border_element = border_element_old[i];
13639
13640     if (border_element == -1)
13641       continue;
13642
13643     // check for change of border element
13644     CheckElementChangeBySide(xx, yy, border_element, center_element,
13645                              CE_TOUCHING_X, center_side);
13646
13647     // (center element cannot be player, so we dont have to check this here)
13648   }
13649
13650   for (i = 0; i < NUM_DIRECTIONS; i++)
13651   {
13652     int xx = x + xy[i][0];
13653     int yy = y + xy[i][1];
13654     int border_side = trigger_sides[i][1];
13655     int border_element = border_element_old[i];
13656
13657     if (border_element == -1)
13658       continue;
13659
13660     // check for change of center element (but change it only once)
13661     if (!change_center_element)
13662       change_center_element =
13663         CheckElementChangeBySide(x, y, center_element, border_element,
13664                                  CE_TOUCHING_X, border_side);
13665
13666     if (IS_PLAYER(xx, yy))
13667     {
13668       /* use player element that is initially defined in the level playfield,
13669          not the player element that corresponds to the runtime player number
13670          (example: a level that contains EL_PLAYER_3 as the only player would
13671          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13672       int player_element = PLAYERINFO(xx, yy)->initial_element;
13673
13674       CheckElementChangeBySide(x, y, center_element, player_element,
13675                                CE_TOUCHING_X, border_side);
13676     }
13677   }
13678 }
13679
13680 void TestIfElementHitsCustomElement(int x, int y, int direction)
13681 {
13682   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13683   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13684   int hitx = x + dx, hity = y + dy;
13685   int hitting_element = Tile[x][y];
13686   int touched_element;
13687
13688   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13689     return;
13690
13691   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13692                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13693
13694   if (IN_LEV_FIELD(hitx, hity))
13695   {
13696     int opposite_direction = MV_DIR_OPPOSITE(direction);
13697     int hitting_side = direction;
13698     int touched_side = opposite_direction;
13699     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13700                           MovDir[hitx][hity] != direction ||
13701                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13702
13703     object_hit = TRUE;
13704
13705     if (object_hit)
13706     {
13707       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13708                                CE_HITTING_X, touched_side);
13709
13710       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13711                                CE_HIT_BY_X, hitting_side);
13712
13713       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13714                                CE_HIT_BY_SOMETHING, opposite_direction);
13715
13716       if (IS_PLAYER(hitx, hity))
13717       {
13718         /* use player element that is initially defined in the level playfield,
13719            not the player element that corresponds to the runtime player number
13720            (example: a level that contains EL_PLAYER_3 as the only player would
13721            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13722         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13723
13724         CheckElementChangeBySide(x, y, hitting_element, player_element,
13725                                  CE_HITTING_X, touched_side);
13726       }
13727     }
13728   }
13729
13730   // "hitting something" is also true when hitting the playfield border
13731   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13732                            CE_HITTING_SOMETHING, direction);
13733 }
13734
13735 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13736 {
13737   int i, kill_x = -1, kill_y = -1;
13738
13739   int bad_element = -1;
13740   static int test_xy[4][2] =
13741   {
13742     { 0, -1 },
13743     { -1, 0 },
13744     { +1, 0 },
13745     { 0, +1 }
13746   };
13747   static int test_dir[4] =
13748   {
13749     MV_UP,
13750     MV_LEFT,
13751     MV_RIGHT,
13752     MV_DOWN
13753   };
13754
13755   for (i = 0; i < NUM_DIRECTIONS; i++)
13756   {
13757     int test_x, test_y, test_move_dir, test_element;
13758
13759     test_x = good_x + test_xy[i][0];
13760     test_y = good_y + test_xy[i][1];
13761
13762     if (!IN_LEV_FIELD(test_x, test_y))
13763       continue;
13764
13765     test_move_dir =
13766       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13767
13768     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13769
13770     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13771        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13772     */
13773     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13774         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13775     {
13776       kill_x = test_x;
13777       kill_y = test_y;
13778       bad_element = test_element;
13779
13780       break;
13781     }
13782   }
13783
13784   if (kill_x != -1 || kill_y != -1)
13785   {
13786     if (IS_PLAYER(good_x, good_y))
13787     {
13788       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13789
13790       if (player->shield_deadly_time_left > 0 &&
13791           !IS_INDESTRUCTIBLE(bad_element))
13792         Bang(kill_x, kill_y);
13793       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13794         KillPlayer(player);
13795     }
13796     else
13797       Bang(good_x, good_y);
13798   }
13799 }
13800
13801 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13802 {
13803   int i, kill_x = -1, kill_y = -1;
13804   int bad_element = Tile[bad_x][bad_y];
13805   static int test_xy[4][2] =
13806   {
13807     { 0, -1 },
13808     { -1, 0 },
13809     { +1, 0 },
13810     { 0, +1 }
13811   };
13812   static int touch_dir[4] =
13813   {
13814     MV_LEFT | MV_RIGHT,
13815     MV_UP   | MV_DOWN,
13816     MV_UP   | MV_DOWN,
13817     MV_LEFT | MV_RIGHT
13818   };
13819   static int test_dir[4] =
13820   {
13821     MV_UP,
13822     MV_LEFT,
13823     MV_RIGHT,
13824     MV_DOWN
13825   };
13826
13827   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13828     return;
13829
13830   for (i = 0; i < NUM_DIRECTIONS; i++)
13831   {
13832     int test_x, test_y, test_move_dir, test_element;
13833
13834     test_x = bad_x + test_xy[i][0];
13835     test_y = bad_y + test_xy[i][1];
13836
13837     if (!IN_LEV_FIELD(test_x, test_y))
13838       continue;
13839
13840     test_move_dir =
13841       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13842
13843     test_element = Tile[test_x][test_y];
13844
13845     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13846        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13847     */
13848     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13849         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13850     {
13851       // good thing is player or penguin that does not move away
13852       if (IS_PLAYER(test_x, test_y))
13853       {
13854         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13855
13856         if (bad_element == EL_ROBOT && player->is_moving)
13857           continue;     // robot does not kill player if he is moving
13858
13859         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13860         {
13861           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13862             continue;           // center and border element do not touch
13863         }
13864
13865         kill_x = test_x;
13866         kill_y = test_y;
13867
13868         break;
13869       }
13870       else if (test_element == EL_PENGUIN)
13871       {
13872         kill_x = test_x;
13873         kill_y = test_y;
13874
13875         break;
13876       }
13877     }
13878   }
13879
13880   if (kill_x != -1 || kill_y != -1)
13881   {
13882     if (IS_PLAYER(kill_x, kill_y))
13883     {
13884       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13885
13886       if (player->shield_deadly_time_left > 0 &&
13887           !IS_INDESTRUCTIBLE(bad_element))
13888         Bang(bad_x, bad_y);
13889       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13890         KillPlayer(player);
13891     }
13892     else
13893       Bang(kill_x, kill_y);
13894   }
13895 }
13896
13897 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13898 {
13899   int bad_element = Tile[bad_x][bad_y];
13900   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13901   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13902   int test_x = bad_x + dx, test_y = bad_y + dy;
13903   int test_move_dir, test_element;
13904   int kill_x = -1, kill_y = -1;
13905
13906   if (!IN_LEV_FIELD(test_x, test_y))
13907     return;
13908
13909   test_move_dir =
13910     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13911
13912   test_element = Tile[test_x][test_y];
13913
13914   if (test_move_dir != bad_move_dir)
13915   {
13916     // good thing can be player or penguin that does not move away
13917     if (IS_PLAYER(test_x, test_y))
13918     {
13919       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13920
13921       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13922          player as being hit when he is moving towards the bad thing, because
13923          the "get hit by" condition would be lost after the player stops) */
13924       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13925         return;         // player moves away from bad thing
13926
13927       kill_x = test_x;
13928       kill_y = test_y;
13929     }
13930     else if (test_element == EL_PENGUIN)
13931     {
13932       kill_x = test_x;
13933       kill_y = test_y;
13934     }
13935   }
13936
13937   if (kill_x != -1 || kill_y != -1)
13938   {
13939     if (IS_PLAYER(kill_x, kill_y))
13940     {
13941       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13942
13943       if (player->shield_deadly_time_left > 0 &&
13944           !IS_INDESTRUCTIBLE(bad_element))
13945         Bang(bad_x, bad_y);
13946       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13947         KillPlayer(player);
13948     }
13949     else
13950       Bang(kill_x, kill_y);
13951   }
13952 }
13953
13954 void TestIfPlayerTouchesBadThing(int x, int y)
13955 {
13956   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13957 }
13958
13959 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13960 {
13961   TestIfGoodThingHitsBadThing(x, y, move_dir);
13962 }
13963
13964 void TestIfBadThingTouchesPlayer(int x, int y)
13965 {
13966   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13967 }
13968
13969 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13970 {
13971   TestIfBadThingHitsGoodThing(x, y, move_dir);
13972 }
13973
13974 void TestIfFriendTouchesBadThing(int x, int y)
13975 {
13976   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13977 }
13978
13979 void TestIfBadThingTouchesFriend(int x, int y)
13980 {
13981   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13982 }
13983
13984 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13985 {
13986   int i, kill_x = bad_x, kill_y = bad_y;
13987   static int xy[4][2] =
13988   {
13989     { 0, -1 },
13990     { -1, 0 },
13991     { +1, 0 },
13992     { 0, +1 }
13993   };
13994
13995   for (i = 0; i < NUM_DIRECTIONS; i++)
13996   {
13997     int x, y, element;
13998
13999     x = bad_x + xy[i][0];
14000     y = bad_y + xy[i][1];
14001     if (!IN_LEV_FIELD(x, y))
14002       continue;
14003
14004     element = Tile[x][y];
14005     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14006         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14007     {
14008       kill_x = x;
14009       kill_y = y;
14010       break;
14011     }
14012   }
14013
14014   if (kill_x != bad_x || kill_y != bad_y)
14015     Bang(bad_x, bad_y);
14016 }
14017
14018 void KillPlayer(struct PlayerInfo *player)
14019 {
14020   int jx = player->jx, jy = player->jy;
14021
14022   if (!player->active)
14023     return;
14024
14025 #if 0
14026   Debug("game:playing:KillPlayer",
14027         "0: killed == %d, active == %d, reanimated == %d",
14028         player->killed, player->active, player->reanimated);
14029 #endif
14030
14031   /* the following code was introduced to prevent an infinite loop when calling
14032      -> Bang()
14033      -> CheckTriggeredElementChangeExt()
14034      -> ExecuteCustomElementAction()
14035      -> KillPlayer()
14036      -> (infinitely repeating the above sequence of function calls)
14037      which occurs when killing the player while having a CE with the setting
14038      "kill player X when explosion of <player X>"; the solution using a new
14039      field "player->killed" was chosen for backwards compatibility, although
14040      clever use of the fields "player->active" etc. would probably also work */
14041 #if 1
14042   if (player->killed)
14043     return;
14044 #endif
14045
14046   player->killed = TRUE;
14047
14048   // remove accessible field at the player's position
14049   Tile[jx][jy] = EL_EMPTY;
14050
14051   // deactivate shield (else Bang()/Explode() would not work right)
14052   player->shield_normal_time_left = 0;
14053   player->shield_deadly_time_left = 0;
14054
14055 #if 0
14056   Debug("game:playing:KillPlayer",
14057         "1: killed == %d, active == %d, reanimated == %d",
14058         player->killed, player->active, player->reanimated);
14059 #endif
14060
14061   Bang(jx, jy);
14062
14063 #if 0
14064   Debug("game:playing:KillPlayer",
14065         "2: killed == %d, active == %d, reanimated == %d",
14066         player->killed, player->active, player->reanimated);
14067 #endif
14068
14069   if (player->reanimated)       // killed player may have been reanimated
14070     player->killed = player->reanimated = FALSE;
14071   else
14072     BuryPlayer(player);
14073 }
14074
14075 static void KillPlayerUnlessEnemyProtected(int x, int y)
14076 {
14077   if (!PLAYER_ENEMY_PROTECTED(x, y))
14078     KillPlayer(PLAYERINFO(x, y));
14079 }
14080
14081 static void KillPlayerUnlessExplosionProtected(int x, int y)
14082 {
14083   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14084     KillPlayer(PLAYERINFO(x, y));
14085 }
14086
14087 void BuryPlayer(struct PlayerInfo *player)
14088 {
14089   int jx = player->jx, jy = player->jy;
14090
14091   if (!player->active)
14092     return;
14093
14094   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14095   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14096
14097   RemovePlayer(player);
14098
14099   player->buried = TRUE;
14100
14101   if (game.all_players_gone)
14102     game.GameOver = TRUE;
14103 }
14104
14105 void RemovePlayer(struct PlayerInfo *player)
14106 {
14107   int jx = player->jx, jy = player->jy;
14108   int i, found = FALSE;
14109
14110   player->present = FALSE;
14111   player->active = FALSE;
14112
14113   // required for some CE actions (even if the player is not active anymore)
14114   player->MovPos = 0;
14115
14116   if (!ExplodeField[jx][jy])
14117     StorePlayer[jx][jy] = 0;
14118
14119   if (player->is_moving)
14120     TEST_DrawLevelField(player->last_jx, player->last_jy);
14121
14122   for (i = 0; i < MAX_PLAYERS; i++)
14123     if (stored_player[i].active)
14124       found = TRUE;
14125
14126   if (!found)
14127   {
14128     game.all_players_gone = TRUE;
14129     game.GameOver = TRUE;
14130   }
14131
14132   game.exit_x = game.robot_wheel_x = jx;
14133   game.exit_y = game.robot_wheel_y = jy;
14134 }
14135
14136 void ExitPlayer(struct PlayerInfo *player)
14137 {
14138   DrawPlayer(player);   // needed here only to cleanup last field
14139   RemovePlayer(player);
14140
14141   if (game.players_still_needed > 0)
14142     game.players_still_needed--;
14143 }
14144
14145 static void SetFieldForSnapping(int x, int y, int element, int direction,
14146                                 int player_index_bit)
14147 {
14148   struct ElementInfo *ei = &element_info[element];
14149   int direction_bit = MV_DIR_TO_BIT(direction);
14150   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14151   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14152                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14153
14154   Tile[x][y] = EL_ELEMENT_SNAPPING;
14155   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14156   MovDir[x][y] = direction;
14157   Store[x][y] = element;
14158   Store2[x][y] = player_index_bit;
14159
14160   ResetGfxAnimation(x, y);
14161
14162   GfxElement[x][y] = element;
14163   GfxAction[x][y] = action;
14164   GfxDir[x][y] = direction;
14165   GfxFrame[x][y] = -1;
14166 }
14167
14168 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14169                                    int player_index_bit)
14170 {
14171   TestIfElementTouchesCustomElement(x, y);      // for empty space
14172
14173   if (level.finish_dig_collect)
14174   {
14175     int dig_side = MV_DIR_OPPOSITE(direction);
14176     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14177                         CE_PLAYER_COLLECTS_X);
14178
14179     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14180                                         player_index_bit, dig_side);
14181     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14182                                         player_index_bit, dig_side);
14183   }
14184 }
14185
14186 /*
14187   =============================================================================
14188   checkDiagonalPushing()
14189   -----------------------------------------------------------------------------
14190   check if diagonal input device direction results in pushing of object
14191   (by checking if the alternative direction is walkable, diggable, ...)
14192   =============================================================================
14193 */
14194
14195 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14196                                     int x, int y, int real_dx, int real_dy)
14197 {
14198   int jx, jy, dx, dy, xx, yy;
14199
14200   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14201     return TRUE;
14202
14203   // diagonal direction: check alternative direction
14204   jx = player->jx;
14205   jy = player->jy;
14206   dx = x - jx;
14207   dy = y - jy;
14208   xx = jx + (dx == 0 ? real_dx : 0);
14209   yy = jy + (dy == 0 ? real_dy : 0);
14210
14211   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14212 }
14213
14214 /*
14215   =============================================================================
14216   DigField()
14217   -----------------------------------------------------------------------------
14218   x, y:                 field next to player (non-diagonal) to try to dig to
14219   real_dx, real_dy:     direction as read from input device (can be diagonal)
14220   =============================================================================
14221 */
14222
14223 static int DigField(struct PlayerInfo *player,
14224                     int oldx, int oldy, int x, int y,
14225                     int real_dx, int real_dy, int mode)
14226 {
14227   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14228   boolean player_was_pushing = player->is_pushing;
14229   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14230   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14231   int jx = oldx, jy = oldy;
14232   int dx = x - jx, dy = y - jy;
14233   int nextx = x + dx, nexty = y + dy;
14234   int move_direction = (dx == -1 ? MV_LEFT  :
14235                         dx == +1 ? MV_RIGHT :
14236                         dy == -1 ? MV_UP    :
14237                         dy == +1 ? MV_DOWN  : MV_NONE);
14238   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14239   int dig_side = MV_DIR_OPPOSITE(move_direction);
14240   int old_element = Tile[jx][jy];
14241   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14242   int collect_count;
14243
14244   if (is_player)                // function can also be called by EL_PENGUIN
14245   {
14246     if (player->MovPos == 0)
14247     {
14248       player->is_digging = FALSE;
14249       player->is_collecting = FALSE;
14250     }
14251
14252     if (player->MovPos == 0)    // last pushing move finished
14253       player->is_pushing = FALSE;
14254
14255     if (mode == DF_NO_PUSH)     // player just stopped pushing
14256     {
14257       player->is_switching = FALSE;
14258       player->push_delay = -1;
14259
14260       return MP_NO_ACTION;
14261     }
14262   }
14263   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14264     old_element = Back[jx][jy];
14265
14266   // in case of element dropped at player position, check background
14267   else if (Back[jx][jy] != EL_EMPTY &&
14268            game.engine_version >= VERSION_IDENT(2,2,0,0))
14269     old_element = Back[jx][jy];
14270
14271   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14272     return MP_NO_ACTION;        // field has no opening in this direction
14273
14274   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14275     return MP_NO_ACTION;        // field has no opening in this direction
14276
14277   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14278   {
14279     SplashAcid(x, y);
14280
14281     Tile[jx][jy] = player->artwork_element;
14282     InitMovingField(jx, jy, MV_DOWN);
14283     Store[jx][jy] = EL_ACID;
14284     ContinueMoving(jx, jy);
14285     BuryPlayer(player);
14286
14287     return MP_DONT_RUN_INTO;
14288   }
14289
14290   if (player_can_move && DONT_RUN_INTO(element))
14291   {
14292     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14293
14294     return MP_DONT_RUN_INTO;
14295   }
14296
14297   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14298     return MP_NO_ACTION;
14299
14300   collect_count = element_info[element].collect_count_initial;
14301
14302   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14303     return MP_NO_ACTION;
14304
14305   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14306     player_can_move = player_can_move_or_snap;
14307
14308   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14309       game.engine_version >= VERSION_IDENT(2,2,0,0))
14310   {
14311     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14312                                player->index_bit, dig_side);
14313     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14314                                         player->index_bit, dig_side);
14315
14316     if (element == EL_DC_LANDMINE)
14317       Bang(x, y);
14318
14319     if (Tile[x][y] != element)          // field changed by snapping
14320       return MP_ACTION;
14321
14322     return MP_NO_ACTION;
14323   }
14324
14325   if (player->gravity && is_player && !player->is_auto_moving &&
14326       canFallDown(player) && move_direction != MV_DOWN &&
14327       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14328     return MP_NO_ACTION;        // player cannot walk here due to gravity
14329
14330   if (player_can_move &&
14331       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14332   {
14333     int sound_element = SND_ELEMENT(element);
14334     int sound_action = ACTION_WALKING;
14335
14336     if (IS_RND_GATE(element))
14337     {
14338       if (!player->key[RND_GATE_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_RND_GATE_GRAY(element))
14342     {
14343       if (!player->key[RND_GATE_GRAY_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14347     {
14348       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (element == EL_EXIT_OPEN ||
14352              element == EL_EM_EXIT_OPEN ||
14353              element == EL_EM_EXIT_OPENING ||
14354              element == EL_STEEL_EXIT_OPEN ||
14355              element == EL_EM_STEEL_EXIT_OPEN ||
14356              element == EL_EM_STEEL_EXIT_OPENING ||
14357              element == EL_SP_EXIT_OPEN ||
14358              element == EL_SP_EXIT_OPENING)
14359     {
14360       sound_action = ACTION_PASSING;    // player is passing exit
14361     }
14362     else if (element == EL_EMPTY)
14363     {
14364       sound_action = ACTION_MOVING;             // nothing to walk on
14365     }
14366
14367     // play sound from background or player, whatever is available
14368     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14369       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14370     else
14371       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14372   }
14373   else if (player_can_move &&
14374            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14375   {
14376     if (!ACCESS_FROM(element, opposite_direction))
14377       return MP_NO_ACTION;      // field not accessible from this direction
14378
14379     if (CAN_MOVE(element))      // only fixed elements can be passed!
14380       return MP_NO_ACTION;
14381
14382     if (IS_EM_GATE(element))
14383     {
14384       if (!player->key[EM_GATE_NR(element)])
14385         return MP_NO_ACTION;
14386     }
14387     else if (IS_EM_GATE_GRAY(element))
14388     {
14389       if (!player->key[EM_GATE_GRAY_NR(element)])
14390         return MP_NO_ACTION;
14391     }
14392     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14393     {
14394       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EMC_GATE(element))
14398     {
14399       if (!player->key[EMC_GATE_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_EMC_GATE_GRAY(element))
14403     {
14404       if (!player->key[EMC_GATE_GRAY_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14408     {
14409       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14410         return MP_NO_ACTION;
14411     }
14412     else if (element == EL_DC_GATE_WHITE ||
14413              element == EL_DC_GATE_WHITE_GRAY ||
14414              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14415     {
14416       if (player->num_white_keys == 0)
14417         return MP_NO_ACTION;
14418
14419       player->num_white_keys--;
14420     }
14421     else if (IS_SP_PORT(element))
14422     {
14423       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14424           element == EL_SP_GRAVITY_PORT_RIGHT ||
14425           element == EL_SP_GRAVITY_PORT_UP ||
14426           element == EL_SP_GRAVITY_PORT_DOWN)
14427         player->gravity = !player->gravity;
14428       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14429                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14430                element == EL_SP_GRAVITY_ON_PORT_UP ||
14431                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14432         player->gravity = TRUE;
14433       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14434                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14435                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14436                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14437         player->gravity = FALSE;
14438     }
14439
14440     // automatically move to the next field with double speed
14441     player->programmed_action = move_direction;
14442
14443     if (player->move_delay_reset_counter == 0)
14444     {
14445       player->move_delay_reset_counter = 2;     // two double speed steps
14446
14447       DOUBLE_PLAYER_SPEED(player);
14448     }
14449
14450     PlayLevelSoundAction(x, y, ACTION_PASSING);
14451   }
14452   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14453   {
14454     RemoveField(x, y);
14455
14456     if (mode != DF_SNAP)
14457     {
14458       GfxElement[x][y] = GFX_ELEMENT(element);
14459       player->is_digging = TRUE;
14460     }
14461
14462     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14463
14464     // use old behaviour for old levels (digging)
14465     if (!level.finish_dig_collect)
14466     {
14467       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14468                                           player->index_bit, dig_side);
14469
14470       // if digging triggered player relocation, finish digging tile
14471       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14472         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14473     }
14474
14475     if (mode == DF_SNAP)
14476     {
14477       if (level.block_snap_field)
14478         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14479       else
14480         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14481
14482       // use old behaviour for old levels (snapping)
14483       if (!level.finish_dig_collect)
14484         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14485                                             player->index_bit, dig_side);
14486     }
14487   }
14488   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14489   {
14490     RemoveField(x, y);
14491
14492     if (is_player && mode != DF_SNAP)
14493     {
14494       GfxElement[x][y] = element;
14495       player->is_collecting = TRUE;
14496     }
14497
14498     if (element == EL_SPEED_PILL)
14499     {
14500       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14501     }
14502     else if (element == EL_EXTRA_TIME && level.time > 0)
14503     {
14504       TimeLeft += level.extra_time;
14505
14506       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14507
14508       DisplayGameControlValues();
14509     }
14510     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14511     {
14512       player->shield_normal_time_left += level.shield_normal_time;
14513       if (element == EL_SHIELD_DEADLY)
14514         player->shield_deadly_time_left += level.shield_deadly_time;
14515     }
14516     else if (element == EL_DYNAMITE ||
14517              element == EL_EM_DYNAMITE ||
14518              element == EL_SP_DISK_RED)
14519     {
14520       if (player->inventory_size < MAX_INVENTORY_SIZE)
14521         player->inventory_element[player->inventory_size++] = element;
14522
14523       DrawGameDoorValues();
14524     }
14525     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14526     {
14527       player->dynabomb_count++;
14528       player->dynabombs_left++;
14529     }
14530     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14531     {
14532       player->dynabomb_size++;
14533     }
14534     else if (element == EL_DYNABOMB_INCREASE_POWER)
14535     {
14536       player->dynabomb_xl = TRUE;
14537     }
14538     else if (IS_KEY(element))
14539     {
14540       player->key[KEY_NR(element)] = TRUE;
14541
14542       DrawGameDoorValues();
14543     }
14544     else if (element == EL_DC_KEY_WHITE)
14545     {
14546       player->num_white_keys++;
14547
14548       // display white keys?
14549       // DrawGameDoorValues();
14550     }
14551     else if (IS_ENVELOPE(element))
14552     {
14553       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14554
14555       if (!wait_for_snapping)
14556         player->show_envelope = element;
14557     }
14558     else if (element == EL_EMC_LENSES)
14559     {
14560       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14561
14562       RedrawAllInvisibleElementsForLenses();
14563     }
14564     else if (element == EL_EMC_MAGNIFIER)
14565     {
14566       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14567
14568       RedrawAllInvisibleElementsForMagnifier();
14569     }
14570     else if (IS_DROPPABLE(element) ||
14571              IS_THROWABLE(element))     // can be collected and dropped
14572     {
14573       int i;
14574
14575       if (collect_count == 0)
14576         player->inventory_infinite_element = element;
14577       else
14578         for (i = 0; i < collect_count; i++)
14579           if (player->inventory_size < MAX_INVENTORY_SIZE)
14580             player->inventory_element[player->inventory_size++] = element;
14581
14582       DrawGameDoorValues();
14583     }
14584     else if (collect_count > 0)
14585     {
14586       game.gems_still_needed -= collect_count;
14587       if (game.gems_still_needed < 0)
14588         game.gems_still_needed = 0;
14589
14590       game.snapshot.collected_item = TRUE;
14591
14592       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14593
14594       DisplayGameControlValues();
14595     }
14596
14597     RaiseScoreElement(element);
14598     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14599
14600     // use old behaviour for old levels (collecting)
14601     if (!level.finish_dig_collect && is_player)
14602     {
14603       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14604                                           player->index_bit, dig_side);
14605
14606       // if collecting triggered player relocation, finish collecting tile
14607       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14608         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14609     }
14610
14611     if (mode == DF_SNAP)
14612     {
14613       if (level.block_snap_field)
14614         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14615       else
14616         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14617
14618       // use old behaviour for old levels (snapping)
14619       if (!level.finish_dig_collect)
14620         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14621                                             player->index_bit, dig_side);
14622     }
14623   }
14624   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14625   {
14626     if (mode == DF_SNAP && element != EL_BD_ROCK)
14627       return MP_NO_ACTION;
14628
14629     if (CAN_FALL(element) && dy)
14630       return MP_NO_ACTION;
14631
14632     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14633         !(element == EL_SPRING && level.use_spring_bug))
14634       return MP_NO_ACTION;
14635
14636     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14637         ((move_direction & MV_VERTICAL &&
14638           ((element_info[element].move_pattern & MV_LEFT &&
14639             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14640            (element_info[element].move_pattern & MV_RIGHT &&
14641             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14642          (move_direction & MV_HORIZONTAL &&
14643           ((element_info[element].move_pattern & MV_UP &&
14644             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14645            (element_info[element].move_pattern & MV_DOWN &&
14646             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14647       return MP_NO_ACTION;
14648
14649     // do not push elements already moving away faster than player
14650     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14651         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14652       return MP_NO_ACTION;
14653
14654     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14655     {
14656       if (player->push_delay_value == -1 || !player_was_pushing)
14657         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14658     }
14659     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14660     {
14661       if (player->push_delay_value == -1)
14662         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14663     }
14664     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14665     {
14666       if (!player->is_pushing)
14667         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14668     }
14669
14670     player->is_pushing = TRUE;
14671     player->is_active = TRUE;
14672
14673     if (!(IN_LEV_FIELD(nextx, nexty) &&
14674           (IS_FREE(nextx, nexty) ||
14675            (IS_SB_ELEMENT(element) &&
14676             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14677            (IS_CUSTOM_ELEMENT(element) &&
14678             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14679       return MP_NO_ACTION;
14680
14681     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14682       return MP_NO_ACTION;
14683
14684     if (player->push_delay == -1)       // new pushing; restart delay
14685       player->push_delay = 0;
14686
14687     if (player->push_delay < player->push_delay_value &&
14688         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14689         element != EL_SPRING && element != EL_BALLOON)
14690     {
14691       // make sure that there is no move delay before next try to push
14692       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14693         player->move_delay = 0;
14694
14695       return MP_NO_ACTION;
14696     }
14697
14698     if (IS_CUSTOM_ELEMENT(element) &&
14699         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14700     {
14701       if (!DigFieldByCE(nextx, nexty, element))
14702         return MP_NO_ACTION;
14703     }
14704
14705     if (IS_SB_ELEMENT(element))
14706     {
14707       boolean sokoban_task_solved = FALSE;
14708
14709       if (element == EL_SOKOBAN_FIELD_FULL)
14710       {
14711         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14712
14713         IncrementSokobanFieldsNeeded();
14714         IncrementSokobanObjectsNeeded();
14715       }
14716
14717       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14718       {
14719         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14720
14721         DecrementSokobanFieldsNeeded();
14722         DecrementSokobanObjectsNeeded();
14723
14724         // sokoban object was pushed from empty field to sokoban field
14725         if (Back[x][y] == EL_EMPTY)
14726           sokoban_task_solved = TRUE;
14727       }
14728
14729       Tile[x][y] = EL_SOKOBAN_OBJECT;
14730
14731       if (Back[x][y] == Back[nextx][nexty])
14732         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14733       else if (Back[x][y] != 0)
14734         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14735                                     ACTION_EMPTYING);
14736       else
14737         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14738                                     ACTION_FILLING);
14739
14740       if (sokoban_task_solved &&
14741           game.sokoban_fields_still_needed == 0 &&
14742           game.sokoban_objects_still_needed == 0 &&
14743           level.auto_exit_sokoban)
14744       {
14745         game.players_still_needed = 0;
14746
14747         LevelSolved();
14748
14749         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14750       }
14751     }
14752     else
14753       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14754
14755     InitMovingField(x, y, move_direction);
14756     GfxAction[x][y] = ACTION_PUSHING;
14757
14758     if (mode == DF_SNAP)
14759       ContinueMoving(x, y);
14760     else
14761       MovPos[x][y] = (dx != 0 ? dx : dy);
14762
14763     Pushed[x][y] = TRUE;
14764     Pushed[nextx][nexty] = TRUE;
14765
14766     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14767       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14768     else
14769       player->push_delay_value = -1;    // get new value later
14770
14771     // check for element change _after_ element has been pushed
14772     if (game.use_change_when_pushing_bug)
14773     {
14774       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14775                                  player->index_bit, dig_side);
14776       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14777                                           player->index_bit, dig_side);
14778     }
14779   }
14780   else if (IS_SWITCHABLE(element))
14781   {
14782     if (PLAYER_SWITCHING(player, x, y))
14783     {
14784       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14785                                           player->index_bit, dig_side);
14786
14787       return MP_ACTION;
14788     }
14789
14790     player->is_switching = TRUE;
14791     player->switch_x = x;
14792     player->switch_y = y;
14793
14794     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14795
14796     if (element == EL_ROBOT_WHEEL)
14797     {
14798       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14799
14800       game.robot_wheel_x = x;
14801       game.robot_wheel_y = y;
14802       game.robot_wheel_active = TRUE;
14803
14804       TEST_DrawLevelField(x, y);
14805     }
14806     else if (element == EL_SP_TERMINAL)
14807     {
14808       int xx, yy;
14809
14810       SCAN_PLAYFIELD(xx, yy)
14811       {
14812         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14813         {
14814           Bang(xx, yy);
14815         }
14816         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14817         {
14818           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14819
14820           ResetGfxAnimation(xx, yy);
14821           TEST_DrawLevelField(xx, yy);
14822         }
14823       }
14824     }
14825     else if (IS_BELT_SWITCH(element))
14826     {
14827       ToggleBeltSwitch(x, y);
14828     }
14829     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14830              element == EL_SWITCHGATE_SWITCH_DOWN ||
14831              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14832              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14833     {
14834       ToggleSwitchgateSwitch(x, y);
14835     }
14836     else if (element == EL_LIGHT_SWITCH ||
14837              element == EL_LIGHT_SWITCH_ACTIVE)
14838     {
14839       ToggleLightSwitch(x, y);
14840     }
14841     else if (element == EL_TIMEGATE_SWITCH ||
14842              element == EL_DC_TIMEGATE_SWITCH)
14843     {
14844       ActivateTimegateSwitch(x, y);
14845     }
14846     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14847              element == EL_BALLOON_SWITCH_RIGHT ||
14848              element == EL_BALLOON_SWITCH_UP    ||
14849              element == EL_BALLOON_SWITCH_DOWN  ||
14850              element == EL_BALLOON_SWITCH_NONE  ||
14851              element == EL_BALLOON_SWITCH_ANY)
14852     {
14853       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14854                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14855                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14856                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14857                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14858                              move_direction);
14859     }
14860     else if (element == EL_LAMP)
14861     {
14862       Tile[x][y] = EL_LAMP_ACTIVE;
14863       game.lights_still_needed--;
14864
14865       ResetGfxAnimation(x, y);
14866       TEST_DrawLevelField(x, y);
14867     }
14868     else if (element == EL_TIME_ORB_FULL)
14869     {
14870       Tile[x][y] = EL_TIME_ORB_EMPTY;
14871
14872       if (level.time > 0 || level.use_time_orb_bug)
14873       {
14874         TimeLeft += level.time_orb_time;
14875         game.no_time_limit = FALSE;
14876
14877         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14878
14879         DisplayGameControlValues();
14880       }
14881
14882       ResetGfxAnimation(x, y);
14883       TEST_DrawLevelField(x, y);
14884     }
14885     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14886              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14887     {
14888       int xx, yy;
14889
14890       game.ball_active = !game.ball_active;
14891
14892       SCAN_PLAYFIELD(xx, yy)
14893       {
14894         int e = Tile[xx][yy];
14895
14896         if (game.ball_active)
14897         {
14898           if (e == EL_EMC_MAGIC_BALL)
14899             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14900           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14901             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14902         }
14903         else
14904         {
14905           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14906             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14907           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14908             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14909         }
14910       }
14911     }
14912
14913     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14914                                         player->index_bit, dig_side);
14915
14916     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14917                                         player->index_bit, dig_side);
14918
14919     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14920                                         player->index_bit, dig_side);
14921
14922     return MP_ACTION;
14923   }
14924   else
14925   {
14926     if (!PLAYER_SWITCHING(player, x, y))
14927     {
14928       player->is_switching = TRUE;
14929       player->switch_x = x;
14930       player->switch_y = y;
14931
14932       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14933                                  player->index_bit, dig_side);
14934       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14935                                           player->index_bit, dig_side);
14936
14937       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14938                                  player->index_bit, dig_side);
14939       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14940                                           player->index_bit, dig_side);
14941     }
14942
14943     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14944                                player->index_bit, dig_side);
14945     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14946                                         player->index_bit, dig_side);
14947
14948     return MP_NO_ACTION;
14949   }
14950
14951   player->push_delay = -1;
14952
14953   if (is_player)                // function can also be called by EL_PENGUIN
14954   {
14955     if (Tile[x][y] != element)          // really digged/collected something
14956     {
14957       player->is_collecting = !player->is_digging;
14958       player->is_active = TRUE;
14959
14960       player->last_removed_element = element;
14961     }
14962   }
14963
14964   return MP_MOVING;
14965 }
14966
14967 static boolean DigFieldByCE(int x, int y, int digging_element)
14968 {
14969   int element = Tile[x][y];
14970
14971   if (!IS_FREE(x, y))
14972   {
14973     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14974                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14975                   ACTION_BREAKING);
14976
14977     // no element can dig solid indestructible elements
14978     if (IS_INDESTRUCTIBLE(element) &&
14979         !IS_DIGGABLE(element) &&
14980         !IS_COLLECTIBLE(element))
14981       return FALSE;
14982
14983     if (AmoebaNr[x][y] &&
14984         (element == EL_AMOEBA_FULL ||
14985          element == EL_BD_AMOEBA ||
14986          element == EL_AMOEBA_GROWING))
14987     {
14988       AmoebaCnt[AmoebaNr[x][y]]--;
14989       AmoebaCnt2[AmoebaNr[x][y]]--;
14990     }
14991
14992     if (IS_MOVING(x, y))
14993       RemoveMovingField(x, y);
14994     else
14995     {
14996       RemoveField(x, y);
14997       TEST_DrawLevelField(x, y);
14998     }
14999
15000     // if digged element was about to explode, prevent the explosion
15001     ExplodeField[x][y] = EX_TYPE_NONE;
15002
15003     PlayLevelSoundAction(x, y, action);
15004   }
15005
15006   Store[x][y] = EL_EMPTY;
15007
15008   // this makes it possible to leave the removed element again
15009   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15010     Store[x][y] = element;
15011
15012   return TRUE;
15013 }
15014
15015 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15016 {
15017   int jx = player->jx, jy = player->jy;
15018   int x = jx + dx, y = jy + dy;
15019   int snap_direction = (dx == -1 ? MV_LEFT  :
15020                         dx == +1 ? MV_RIGHT :
15021                         dy == -1 ? MV_UP    :
15022                         dy == +1 ? MV_DOWN  : MV_NONE);
15023   boolean can_continue_snapping = (level.continuous_snapping &&
15024                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15025
15026   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15027     return FALSE;
15028
15029   if (!player->active || !IN_LEV_FIELD(x, y))
15030     return FALSE;
15031
15032   if (dx && dy)
15033     return FALSE;
15034
15035   if (!dx && !dy)
15036   {
15037     if (player->MovPos == 0)
15038       player->is_pushing = FALSE;
15039
15040     player->is_snapping = FALSE;
15041
15042     if (player->MovPos == 0)
15043     {
15044       player->is_moving = FALSE;
15045       player->is_digging = FALSE;
15046       player->is_collecting = FALSE;
15047     }
15048
15049     return FALSE;
15050   }
15051
15052   // prevent snapping with already pressed snap key when not allowed
15053   if (player->is_snapping && !can_continue_snapping)
15054     return FALSE;
15055
15056   player->MovDir = snap_direction;
15057
15058   if (player->MovPos == 0)
15059   {
15060     player->is_moving = FALSE;
15061     player->is_digging = FALSE;
15062     player->is_collecting = FALSE;
15063   }
15064
15065   player->is_dropping = FALSE;
15066   player->is_dropping_pressed = FALSE;
15067   player->drop_pressed_delay = 0;
15068
15069   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15070     return FALSE;
15071
15072   player->is_snapping = TRUE;
15073   player->is_active = TRUE;
15074
15075   if (player->MovPos == 0)
15076   {
15077     player->is_moving = FALSE;
15078     player->is_digging = FALSE;
15079     player->is_collecting = FALSE;
15080   }
15081
15082   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15083     TEST_DrawLevelField(player->last_jx, player->last_jy);
15084
15085   TEST_DrawLevelField(x, y);
15086
15087   return TRUE;
15088 }
15089
15090 static boolean DropElement(struct PlayerInfo *player)
15091 {
15092   int old_element, new_element;
15093   int dropx = player->jx, dropy = player->jy;
15094   int drop_direction = player->MovDir;
15095   int drop_side = drop_direction;
15096   int drop_element = get_next_dropped_element(player);
15097
15098   /* do not drop an element on top of another element; when holding drop key
15099      pressed without moving, dropped element must move away before the next
15100      element can be dropped (this is especially important if the next element
15101      is dynamite, which can be placed on background for historical reasons) */
15102   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15103     return MP_ACTION;
15104
15105   if (IS_THROWABLE(drop_element))
15106   {
15107     dropx += GET_DX_FROM_DIR(drop_direction);
15108     dropy += GET_DY_FROM_DIR(drop_direction);
15109
15110     if (!IN_LEV_FIELD(dropx, dropy))
15111       return FALSE;
15112   }
15113
15114   old_element = Tile[dropx][dropy];     // old element at dropping position
15115   new_element = drop_element;           // default: no change when dropping
15116
15117   // check if player is active, not moving and ready to drop
15118   if (!player->active || player->MovPos || player->drop_delay > 0)
15119     return FALSE;
15120
15121   // check if player has anything that can be dropped
15122   if (new_element == EL_UNDEFINED)
15123     return FALSE;
15124
15125   // only set if player has anything that can be dropped
15126   player->is_dropping_pressed = TRUE;
15127
15128   // check if drop key was pressed long enough for EM style dynamite
15129   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15130     return FALSE;
15131
15132   // check if anything can be dropped at the current position
15133   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15134     return FALSE;
15135
15136   // collected custom elements can only be dropped on empty fields
15137   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15138     return FALSE;
15139
15140   if (old_element != EL_EMPTY)
15141     Back[dropx][dropy] = old_element;   // store old element on this field
15142
15143   ResetGfxAnimation(dropx, dropy);
15144   ResetRandomAnimationValue(dropx, dropy);
15145
15146   if (player->inventory_size > 0 ||
15147       player->inventory_infinite_element != EL_UNDEFINED)
15148   {
15149     if (player->inventory_size > 0)
15150     {
15151       player->inventory_size--;
15152
15153       DrawGameDoorValues();
15154
15155       if (new_element == EL_DYNAMITE)
15156         new_element = EL_DYNAMITE_ACTIVE;
15157       else if (new_element == EL_EM_DYNAMITE)
15158         new_element = EL_EM_DYNAMITE_ACTIVE;
15159       else if (new_element == EL_SP_DISK_RED)
15160         new_element = EL_SP_DISK_RED_ACTIVE;
15161     }
15162
15163     Tile[dropx][dropy] = new_element;
15164
15165     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15166       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15167                           el2img(Tile[dropx][dropy]), 0);
15168
15169     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15170
15171     // needed if previous element just changed to "empty" in the last frame
15172     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15173
15174     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15175                                player->index_bit, drop_side);
15176     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15177                                         CE_PLAYER_DROPS_X,
15178                                         player->index_bit, drop_side);
15179
15180     TestIfElementTouchesCustomElement(dropx, dropy);
15181   }
15182   else          // player is dropping a dyna bomb
15183   {
15184     player->dynabombs_left--;
15185
15186     Tile[dropx][dropy] = new_element;
15187
15188     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15189       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15190                           el2img(Tile[dropx][dropy]), 0);
15191
15192     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15193   }
15194
15195   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15196     InitField_WithBug1(dropx, dropy, FALSE);
15197
15198   new_element = Tile[dropx][dropy];     // element might have changed
15199
15200   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15201       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15202   {
15203     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15204       MovDir[dropx][dropy] = drop_direction;
15205
15206     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15207
15208     // do not cause impact style collision by dropping elements that can fall
15209     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15210   }
15211
15212   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15213   player->is_dropping = TRUE;
15214
15215   player->drop_pressed_delay = 0;
15216   player->is_dropping_pressed = FALSE;
15217
15218   player->drop_x = dropx;
15219   player->drop_y = dropy;
15220
15221   return TRUE;
15222 }
15223
15224 // ----------------------------------------------------------------------------
15225 // game sound playing functions
15226 // ----------------------------------------------------------------------------
15227
15228 static int *loop_sound_frame = NULL;
15229 static int *loop_sound_volume = NULL;
15230
15231 void InitPlayLevelSound(void)
15232 {
15233   int num_sounds = getSoundListSize();
15234
15235   checked_free(loop_sound_frame);
15236   checked_free(loop_sound_volume);
15237
15238   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15239   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15240 }
15241
15242 static void PlayLevelSound(int x, int y, int nr)
15243 {
15244   int sx = SCREENX(x), sy = SCREENY(y);
15245   int volume, stereo_position;
15246   int max_distance = 8;
15247   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15248
15249   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15250       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15251     return;
15252
15253   if (!IN_LEV_FIELD(x, y) ||
15254       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15255       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15256     return;
15257
15258   volume = SOUND_MAX_VOLUME;
15259
15260   if (!IN_SCR_FIELD(sx, sy))
15261   {
15262     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15263     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15264
15265     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15266   }
15267
15268   stereo_position = (SOUND_MAX_LEFT +
15269                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15270                      (SCR_FIELDX + 2 * max_distance));
15271
15272   if (IS_LOOP_SOUND(nr))
15273   {
15274     /* This assures that quieter loop sounds do not overwrite louder ones,
15275        while restarting sound volume comparison with each new game frame. */
15276
15277     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15278       return;
15279
15280     loop_sound_volume[nr] = volume;
15281     loop_sound_frame[nr] = FrameCounter;
15282   }
15283
15284   PlaySoundExt(nr, volume, stereo_position, type);
15285 }
15286
15287 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15288 {
15289   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15290                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15291                  y < LEVELY(BY1) ? LEVELY(BY1) :
15292                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15293                  sound_action);
15294 }
15295
15296 static void PlayLevelSoundAction(int x, int y, int action)
15297 {
15298   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15299 }
15300
15301 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15302 {
15303   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15304
15305   if (sound_effect != SND_UNDEFINED)
15306     PlayLevelSound(x, y, sound_effect);
15307 }
15308
15309 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15310                                               int action)
15311 {
15312   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15313
15314   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15315     PlayLevelSound(x, y, sound_effect);
15316 }
15317
15318 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15319 {
15320   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15321
15322   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15323     PlayLevelSound(x, y, sound_effect);
15324 }
15325
15326 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15327 {
15328   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15329
15330   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15331     StopSound(sound_effect);
15332 }
15333
15334 static int getLevelMusicNr(void)
15335 {
15336   if (levelset.music[level_nr] != MUS_UNDEFINED)
15337     return levelset.music[level_nr];            // from config file
15338   else
15339     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15340 }
15341
15342 static void FadeLevelSounds(void)
15343 {
15344   FadeSounds();
15345 }
15346
15347 static void FadeLevelMusic(void)
15348 {
15349   int music_nr = getLevelMusicNr();
15350   char *curr_music = getCurrentlyPlayingMusicFilename();
15351   char *next_music = getMusicInfoEntryFilename(music_nr);
15352
15353   if (!strEqual(curr_music, next_music))
15354     FadeMusic();
15355 }
15356
15357 void FadeLevelSoundsAndMusic(void)
15358 {
15359   FadeLevelSounds();
15360   FadeLevelMusic();
15361 }
15362
15363 static void PlayLevelMusic(void)
15364 {
15365   int music_nr = getLevelMusicNr();
15366   char *curr_music = getCurrentlyPlayingMusicFilename();
15367   char *next_music = getMusicInfoEntryFilename(music_nr);
15368
15369   if (!strEqual(curr_music, next_music))
15370     PlayMusicLoop(music_nr);
15371 }
15372
15373 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15374 {
15375   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15376   int offset = 0;
15377   int x = xx - offset;
15378   int y = yy - offset;
15379
15380   switch (sample)
15381   {
15382     case SOUND_blank:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15384       break;
15385
15386     case SOUND_roll:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15388       break;
15389
15390     case SOUND_stone:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15392       break;
15393
15394     case SOUND_nut:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15396       break;
15397
15398     case SOUND_crack:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15400       break;
15401
15402     case SOUND_bug:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15404       break;
15405
15406     case SOUND_tank:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15408       break;
15409
15410     case SOUND_android_clone:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15412       break;
15413
15414     case SOUND_android_move:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15416       break;
15417
15418     case SOUND_spring:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15420       break;
15421
15422     case SOUND_slurp:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15424       break;
15425
15426     case SOUND_eater:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15428       break;
15429
15430     case SOUND_eater_eat:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15432       break;
15433
15434     case SOUND_alien:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15436       break;
15437
15438     case SOUND_collect:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15440       break;
15441
15442     case SOUND_diamond:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15444       break;
15445
15446     case SOUND_squash:
15447       // !!! CHECK THIS !!!
15448 #if 1
15449       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15450 #else
15451       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15452 #endif
15453       break;
15454
15455     case SOUND_wonderfall:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15457       break;
15458
15459     case SOUND_drip:
15460       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15461       break;
15462
15463     case SOUND_push:
15464       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15465       break;
15466
15467     case SOUND_dirt:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15469       break;
15470
15471     case SOUND_acid:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15473       break;
15474
15475     case SOUND_ball:
15476       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15477       break;
15478
15479     case SOUND_slide:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15481       break;
15482
15483     case SOUND_wonder:
15484       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15485       break;
15486
15487     case SOUND_door:
15488       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15489       break;
15490
15491     case SOUND_exit_open:
15492       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15493       break;
15494
15495     case SOUND_exit_leave:
15496       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15497       break;
15498
15499     case SOUND_dynamite:
15500       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15501       break;
15502
15503     case SOUND_tick:
15504       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15505       break;
15506
15507     case SOUND_press:
15508       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15509       break;
15510
15511     case SOUND_wheel:
15512       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15513       break;
15514
15515     case SOUND_boom:
15516       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15517       break;
15518
15519     case SOUND_die:
15520       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15521       break;
15522
15523     case SOUND_time:
15524       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15525       break;
15526
15527     default:
15528       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15529       break;
15530   }
15531 }
15532
15533 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15534 {
15535   int element = map_element_SP_to_RND(element_sp);
15536   int action = map_action_SP_to_RND(action_sp);
15537   int offset = (setup.sp_show_border_elements ? 0 : 1);
15538   int x = xx - offset;
15539   int y = yy - offset;
15540
15541   PlayLevelSoundElementAction(x, y, element, action);
15542 }
15543
15544 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15545 {
15546   int element = map_element_MM_to_RND(element_mm);
15547   int action = map_action_MM_to_RND(action_mm);
15548   int offset = 0;
15549   int x = xx - offset;
15550   int y = yy - offset;
15551
15552   if (!IS_MM_ELEMENT(element))
15553     element = EL_MM_DEFAULT;
15554
15555   PlayLevelSoundElementAction(x, y, element, action);
15556 }
15557
15558 void PlaySound_MM(int sound_mm)
15559 {
15560   int sound = map_sound_MM_to_RND(sound_mm);
15561
15562   if (sound == SND_UNDEFINED)
15563     return;
15564
15565   PlaySound(sound);
15566 }
15567
15568 void PlaySoundLoop_MM(int sound_mm)
15569 {
15570   int sound = map_sound_MM_to_RND(sound_mm);
15571
15572   if (sound == SND_UNDEFINED)
15573     return;
15574
15575   PlaySoundLoop(sound);
15576 }
15577
15578 void StopSound_MM(int sound_mm)
15579 {
15580   int sound = map_sound_MM_to_RND(sound_mm);
15581
15582   if (sound == SND_UNDEFINED)
15583     return;
15584
15585   StopSound(sound);
15586 }
15587
15588 void RaiseScore(int value)
15589 {
15590   game.score += value;
15591
15592   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15593
15594   DisplayGameControlValues();
15595 }
15596
15597 void RaiseScoreElement(int element)
15598 {
15599   switch (element)
15600   {
15601     case EL_EMERALD:
15602     case EL_BD_DIAMOND:
15603     case EL_EMERALD_YELLOW:
15604     case EL_EMERALD_RED:
15605     case EL_EMERALD_PURPLE:
15606     case EL_SP_INFOTRON:
15607       RaiseScore(level.score[SC_EMERALD]);
15608       break;
15609     case EL_DIAMOND:
15610       RaiseScore(level.score[SC_DIAMOND]);
15611       break;
15612     case EL_CRYSTAL:
15613       RaiseScore(level.score[SC_CRYSTAL]);
15614       break;
15615     case EL_PEARL:
15616       RaiseScore(level.score[SC_PEARL]);
15617       break;
15618     case EL_BUG:
15619     case EL_BD_BUTTERFLY:
15620     case EL_SP_ELECTRON:
15621       RaiseScore(level.score[SC_BUG]);
15622       break;
15623     case EL_SPACESHIP:
15624     case EL_BD_FIREFLY:
15625     case EL_SP_SNIKSNAK:
15626       RaiseScore(level.score[SC_SPACESHIP]);
15627       break;
15628     case EL_YAMYAM:
15629     case EL_DARK_YAMYAM:
15630       RaiseScore(level.score[SC_YAMYAM]);
15631       break;
15632     case EL_ROBOT:
15633       RaiseScore(level.score[SC_ROBOT]);
15634       break;
15635     case EL_PACMAN:
15636       RaiseScore(level.score[SC_PACMAN]);
15637       break;
15638     case EL_NUT:
15639       RaiseScore(level.score[SC_NUT]);
15640       break;
15641     case EL_DYNAMITE:
15642     case EL_EM_DYNAMITE:
15643     case EL_SP_DISK_RED:
15644     case EL_DYNABOMB_INCREASE_NUMBER:
15645     case EL_DYNABOMB_INCREASE_SIZE:
15646     case EL_DYNABOMB_INCREASE_POWER:
15647       RaiseScore(level.score[SC_DYNAMITE]);
15648       break;
15649     case EL_SHIELD_NORMAL:
15650     case EL_SHIELD_DEADLY:
15651       RaiseScore(level.score[SC_SHIELD]);
15652       break;
15653     case EL_EXTRA_TIME:
15654       RaiseScore(level.extra_time_score);
15655       break;
15656     case EL_KEY_1:
15657     case EL_KEY_2:
15658     case EL_KEY_3:
15659     case EL_KEY_4:
15660     case EL_EM_KEY_1:
15661     case EL_EM_KEY_2:
15662     case EL_EM_KEY_3:
15663     case EL_EM_KEY_4:
15664     case EL_EMC_KEY_5:
15665     case EL_EMC_KEY_6:
15666     case EL_EMC_KEY_7:
15667     case EL_EMC_KEY_8:
15668     case EL_DC_KEY_WHITE:
15669       RaiseScore(level.score[SC_KEY]);
15670       break;
15671     default:
15672       RaiseScore(element_info[element].collect_score);
15673       break;
15674   }
15675 }
15676
15677 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15678 {
15679   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15680   {
15681     if (!quick_quit)
15682     {
15683       // prevent short reactivation of overlay buttons while closing door
15684       SetOverlayActive(FALSE);
15685
15686       // door may still be open due to skipped or envelope style request
15687       CloseDoor(DOOR_CLOSE_1);
15688     }
15689
15690     if (network.enabled)
15691       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15692     else
15693     {
15694       if (quick_quit)
15695         FadeSkipNextFadeIn();
15696
15697       SetGameStatus(GAME_MODE_MAIN);
15698
15699       DrawMainMenu();
15700     }
15701   }
15702   else          // continue playing the game
15703   {
15704     if (tape.playing && tape.deactivate_display)
15705       TapeDeactivateDisplayOff(TRUE);
15706
15707     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15708
15709     if (tape.playing && tape.deactivate_display)
15710       TapeDeactivateDisplayOn();
15711   }
15712 }
15713
15714 void RequestQuitGame(boolean escape_key_pressed)
15715 {
15716   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15717   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15718                         level_editor_test_game);
15719   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15720                           quick_quit);
15721
15722   RequestQuitGameExt(skip_request, quick_quit,
15723                      "Do you really want to quit the game?");
15724 }
15725
15726 void RequestRestartGame(char *message)
15727 {
15728   game.restart_game_message = NULL;
15729
15730   boolean has_started_game = hasStartedNetworkGame();
15731   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15732
15733   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15734   {
15735     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15736   }
15737   else
15738   {
15739     // needed in case of envelope request to close game panel
15740     CloseDoor(DOOR_CLOSE_1);
15741
15742     SetGameStatus(GAME_MODE_MAIN);
15743
15744     DrawMainMenu();
15745   }
15746 }
15747
15748 void CheckGameOver(void)
15749 {
15750   static boolean last_game_over = FALSE;
15751   static int game_over_delay = 0;
15752   int game_over_delay_value = 50;
15753   boolean game_over = checkGameFailed();
15754
15755   // do not handle game over if request dialog is already active
15756   if (game.request_active)
15757     return;
15758
15759   // do not ask to play again if game was never actually played
15760   if (!game.GamePlayed)
15761     return;
15762
15763   if (!game_over)
15764   {
15765     last_game_over = FALSE;
15766     game_over_delay = game_over_delay_value;
15767
15768     return;
15769   }
15770
15771   if (game_over_delay > 0)
15772   {
15773     game_over_delay--;
15774
15775     return;
15776   }
15777
15778   if (last_game_over != game_over)
15779     game.restart_game_message = (hasStartedNetworkGame() ?
15780                                  "Game over! Play it again?" :
15781                                  "Game over!");
15782
15783   last_game_over = game_over;
15784 }
15785
15786 boolean checkGameSolved(void)
15787 {
15788   // set for all game engines if level was solved
15789   return game.LevelSolved_GameEnd;
15790 }
15791
15792 boolean checkGameFailed(void)
15793 {
15794   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15795     return (game_em.game_over && !game_em.level_solved);
15796   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15797     return (game_sp.game_over && !game_sp.level_solved);
15798   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15799     return (game_mm.game_over && !game_mm.level_solved);
15800   else                          // GAME_ENGINE_TYPE_RND
15801     return (game.GameOver && !game.LevelSolved);
15802 }
15803
15804 boolean checkGameEnded(void)
15805 {
15806   return (checkGameSolved() || checkGameFailed());
15807 }
15808
15809
15810 // ----------------------------------------------------------------------------
15811 // random generator functions
15812 // ----------------------------------------------------------------------------
15813
15814 unsigned int InitEngineRandom_RND(int seed)
15815 {
15816   game.num_random_calls = 0;
15817
15818   return InitEngineRandom(seed);
15819 }
15820
15821 unsigned int RND(int max)
15822 {
15823   if (max > 0)
15824   {
15825     game.num_random_calls++;
15826
15827     return GetEngineRandom(max);
15828   }
15829
15830   return 0;
15831 }
15832
15833
15834 // ----------------------------------------------------------------------------
15835 // game engine snapshot handling functions
15836 // ----------------------------------------------------------------------------
15837
15838 struct EngineSnapshotInfo
15839 {
15840   // runtime values for custom element collect score
15841   int collect_score[NUM_CUSTOM_ELEMENTS];
15842
15843   // runtime values for group element choice position
15844   int choice_pos[NUM_GROUP_ELEMENTS];
15845
15846   // runtime values for belt position animations
15847   int belt_graphic[4][NUM_BELT_PARTS];
15848   int belt_anim_mode[4][NUM_BELT_PARTS];
15849 };
15850
15851 static struct EngineSnapshotInfo engine_snapshot_rnd;
15852 static char *snapshot_level_identifier = NULL;
15853 static int snapshot_level_nr = -1;
15854
15855 static void SaveEngineSnapshotValues_RND(void)
15856 {
15857   static int belt_base_active_element[4] =
15858   {
15859     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15860     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15861     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15862     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15863   };
15864   int i, j;
15865
15866   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15867   {
15868     int element = EL_CUSTOM_START + i;
15869
15870     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15871   }
15872
15873   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15874   {
15875     int element = EL_GROUP_START + i;
15876
15877     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15878   }
15879
15880   for (i = 0; i < 4; i++)
15881   {
15882     for (j = 0; j < NUM_BELT_PARTS; j++)
15883     {
15884       int element = belt_base_active_element[i] + j;
15885       int graphic = el2img(element);
15886       int anim_mode = graphic_info[graphic].anim_mode;
15887
15888       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15889       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15890     }
15891   }
15892 }
15893
15894 static void LoadEngineSnapshotValues_RND(void)
15895 {
15896   unsigned int num_random_calls = game.num_random_calls;
15897   int i, j;
15898
15899   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15900   {
15901     int element = EL_CUSTOM_START + i;
15902
15903     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15904   }
15905
15906   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15907   {
15908     int element = EL_GROUP_START + i;
15909
15910     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15911   }
15912
15913   for (i = 0; i < 4; i++)
15914   {
15915     for (j = 0; j < NUM_BELT_PARTS; j++)
15916     {
15917       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15918       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15919
15920       graphic_info[graphic].anim_mode = anim_mode;
15921     }
15922   }
15923
15924   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15925   {
15926     InitRND(tape.random_seed);
15927     for (i = 0; i < num_random_calls; i++)
15928       RND(1);
15929   }
15930
15931   if (game.num_random_calls != num_random_calls)
15932   {
15933     Error("number of random calls out of sync");
15934     Error("number of random calls should be %d", num_random_calls);
15935     Error("number of random calls is %d", game.num_random_calls);
15936
15937     Fail("this should not happen -- please debug");
15938   }
15939 }
15940
15941 void FreeEngineSnapshotSingle(void)
15942 {
15943   FreeSnapshotSingle();
15944
15945   setString(&snapshot_level_identifier, NULL);
15946   snapshot_level_nr = -1;
15947 }
15948
15949 void FreeEngineSnapshotList(void)
15950 {
15951   FreeSnapshotList();
15952 }
15953
15954 static ListNode *SaveEngineSnapshotBuffers(void)
15955 {
15956   ListNode *buffers = NULL;
15957
15958   // copy some special values to a structure better suited for the snapshot
15959
15960   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15961     SaveEngineSnapshotValues_RND();
15962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15963     SaveEngineSnapshotValues_EM();
15964   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15965     SaveEngineSnapshotValues_SP(&buffers);
15966   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15967     SaveEngineSnapshotValues_MM(&buffers);
15968
15969   // save values stored in special snapshot structure
15970
15971   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15972     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15973   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15974     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15975   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15976     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15977   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15978     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15979
15980   // save further RND engine values
15981
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15985
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15991
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15995
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15997
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16019
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16022
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16026
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16029
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16036
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16039
16040 #if 0
16041   ListNode *node = engine_snapshot_list_rnd;
16042   int num_bytes = 0;
16043
16044   while (node != NULL)
16045   {
16046     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16047
16048     node = node->next;
16049   }
16050
16051   Debug("game:playing:SaveEngineSnapshotBuffers",
16052         "size of engine snapshot: %d bytes", num_bytes);
16053 #endif
16054
16055   return buffers;
16056 }
16057
16058 void SaveEngineSnapshotSingle(void)
16059 {
16060   ListNode *buffers = SaveEngineSnapshotBuffers();
16061
16062   // finally save all snapshot buffers to single snapshot
16063   SaveSnapshotSingle(buffers);
16064
16065   // save level identification information
16066   setString(&snapshot_level_identifier, leveldir_current->identifier);
16067   snapshot_level_nr = level_nr;
16068 }
16069
16070 boolean CheckSaveEngineSnapshotToList(void)
16071 {
16072   boolean save_snapshot =
16073     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16074      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16075       game.snapshot.changed_action) ||
16076      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16077       game.snapshot.collected_item));
16078
16079   game.snapshot.changed_action = FALSE;
16080   game.snapshot.collected_item = FALSE;
16081   game.snapshot.save_snapshot = save_snapshot;
16082
16083   return save_snapshot;
16084 }
16085
16086 void SaveEngineSnapshotToList(void)
16087 {
16088   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16089       tape.quick_resume)
16090     return;
16091
16092   ListNode *buffers = SaveEngineSnapshotBuffers();
16093
16094   // finally save all snapshot buffers to snapshot list
16095   SaveSnapshotToList(buffers);
16096 }
16097
16098 void SaveEngineSnapshotToListInitial(void)
16099 {
16100   FreeEngineSnapshotList();
16101
16102   SaveEngineSnapshotToList();
16103 }
16104
16105 static void LoadEngineSnapshotValues(void)
16106 {
16107   // restore special values from snapshot structure
16108
16109   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16110     LoadEngineSnapshotValues_RND();
16111   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16112     LoadEngineSnapshotValues_EM();
16113   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16114     LoadEngineSnapshotValues_SP();
16115   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16116     LoadEngineSnapshotValues_MM();
16117 }
16118
16119 void LoadEngineSnapshotSingle(void)
16120 {
16121   LoadSnapshotSingle();
16122
16123   LoadEngineSnapshotValues();
16124 }
16125
16126 static void LoadEngineSnapshot_Undo(int steps)
16127 {
16128   LoadSnapshotFromList_Older(steps);
16129
16130   LoadEngineSnapshotValues();
16131 }
16132
16133 static void LoadEngineSnapshot_Redo(int steps)
16134 {
16135   LoadSnapshotFromList_Newer(steps);
16136
16137   LoadEngineSnapshotValues();
16138 }
16139
16140 boolean CheckEngineSnapshotSingle(void)
16141 {
16142   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16143           snapshot_level_nr == level_nr);
16144 }
16145
16146 boolean CheckEngineSnapshotList(void)
16147 {
16148   return CheckSnapshotList();
16149 }
16150
16151
16152 // ---------- new game button stuff -------------------------------------------
16153
16154 static struct
16155 {
16156   int graphic;
16157   struct XY *pos;
16158   int gadget_id;
16159   boolean *setup_value;
16160   boolean allowed_on_tape;
16161   boolean is_touch_button;
16162   char *infotext;
16163 } gamebutton_info[NUM_GAME_BUTTONS] =
16164 {
16165   {
16166     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16167     GAME_CTRL_ID_STOP,                          NULL,
16168     TRUE, FALSE,                                "stop game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16172     GAME_CTRL_ID_PAUSE,                         NULL,
16173     TRUE, FALSE,                                "pause game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16177     GAME_CTRL_ID_PLAY,                          NULL,
16178     TRUE, FALSE,                                "play game"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16182     GAME_CTRL_ID_UNDO,                          NULL,
16183     TRUE, FALSE,                                "undo step"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16187     GAME_CTRL_ID_REDO,                          NULL,
16188     TRUE, FALSE,                                "redo step"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16192     GAME_CTRL_ID_SAVE,                          NULL,
16193     TRUE, FALSE,                                "save game"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16197     GAME_CTRL_ID_PAUSE2,                        NULL,
16198     TRUE, FALSE,                                "pause game"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16202     GAME_CTRL_ID_LOAD,                          NULL,
16203     TRUE, FALSE,                                "load game"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16207     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16208     FALSE, FALSE,                               "stop game"
16209   },
16210   {
16211     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16212     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16213     FALSE, FALSE,                               "pause game"
16214   },
16215   {
16216     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16217     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16218     FALSE, FALSE,                               "play game"
16219   },
16220   {
16221     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16222     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16223     FALSE, TRUE,                                "stop game"
16224   },
16225   {
16226     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16227     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16228     FALSE, TRUE,                                "pause game"
16229   },
16230   {
16231     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16232     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16233     TRUE, FALSE,                                "background music on/off"
16234   },
16235   {
16236     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16237     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16238     TRUE, FALSE,                                "sound loops on/off"
16239   },
16240   {
16241     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16242     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16243     TRUE, FALSE,                                "normal sounds on/off"
16244   },
16245   {
16246     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16247     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16248     FALSE, FALSE,                               "background music on/off"
16249   },
16250   {
16251     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16252     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16253     FALSE, FALSE,                               "sound loops on/off"
16254   },
16255   {
16256     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16257     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16258     FALSE, FALSE,                               "normal sounds on/off"
16259   }
16260 };
16261
16262 void CreateGameButtons(void)
16263 {
16264   int i;
16265
16266   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16267   {
16268     int graphic = gamebutton_info[i].graphic;
16269     struct GraphicInfo *gfx = &graphic_info[graphic];
16270     struct XY *pos = gamebutton_info[i].pos;
16271     struct GadgetInfo *gi;
16272     int button_type;
16273     boolean checked;
16274     unsigned int event_mask;
16275     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16276     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16277     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16278     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16279     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16280     int gd_x   = gfx->src_x;
16281     int gd_y   = gfx->src_y;
16282     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16283     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16284     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16285     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16286     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16287     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16288     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16289     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16290     int id = i;
16291
16292     if (gfx->bitmap == NULL)
16293     {
16294       game_gadget[id] = NULL;
16295
16296       continue;
16297     }
16298
16299     if (id == GAME_CTRL_ID_STOP ||
16300         id == GAME_CTRL_ID_PANEL_STOP ||
16301         id == GAME_CTRL_ID_TOUCH_STOP ||
16302         id == GAME_CTRL_ID_PLAY ||
16303         id == GAME_CTRL_ID_PANEL_PLAY ||
16304         id == GAME_CTRL_ID_SAVE ||
16305         id == GAME_CTRL_ID_LOAD)
16306     {
16307       button_type = GD_TYPE_NORMAL_BUTTON;
16308       checked = FALSE;
16309       event_mask = GD_EVENT_RELEASED;
16310     }
16311     else if (id == GAME_CTRL_ID_UNDO ||
16312              id == GAME_CTRL_ID_REDO)
16313     {
16314       button_type = GD_TYPE_NORMAL_BUTTON;
16315       checked = FALSE;
16316       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16317     }
16318     else
16319     {
16320       button_type = GD_TYPE_CHECK_BUTTON;
16321       checked = (gamebutton_info[i].setup_value != NULL ?
16322                  *gamebutton_info[i].setup_value : FALSE);
16323       event_mask = GD_EVENT_PRESSED;
16324     }
16325
16326     gi = CreateGadget(GDI_CUSTOM_ID, id,
16327                       GDI_IMAGE_ID, graphic,
16328                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16329                       GDI_X, base_x + x,
16330                       GDI_Y, base_y + y,
16331                       GDI_WIDTH, gfx->width,
16332                       GDI_HEIGHT, gfx->height,
16333                       GDI_TYPE, button_type,
16334                       GDI_STATE, GD_BUTTON_UNPRESSED,
16335                       GDI_CHECKED, checked,
16336                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16337                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16338                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16339                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16340                       GDI_DIRECT_DRAW, FALSE,
16341                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16342                       GDI_EVENT_MASK, event_mask,
16343                       GDI_CALLBACK_ACTION, HandleGameButtons,
16344                       GDI_END);
16345
16346     if (gi == NULL)
16347       Fail("cannot create gadget");
16348
16349     game_gadget[id] = gi;
16350   }
16351 }
16352
16353 void FreeGameButtons(void)
16354 {
16355   int i;
16356
16357   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16358     FreeGadget(game_gadget[i]);
16359 }
16360
16361 static void UnmapGameButtonsAtSamePosition(int id)
16362 {
16363   int i;
16364
16365   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16366     if (i != id &&
16367         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16368         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16369       UnmapGadget(game_gadget[i]);
16370 }
16371
16372 static void UnmapGameButtonsAtSamePosition_All(void)
16373 {
16374   if (setup.show_load_save_buttons)
16375   {
16376     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16377     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16378     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16379   }
16380   else if (setup.show_undo_redo_buttons)
16381   {
16382     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16383     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16384     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16385   }
16386   else
16387   {
16388     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16389     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16390     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16391
16392     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16393     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16394     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16395   }
16396 }
16397
16398 void MapLoadSaveButtons(void)
16399 {
16400   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16401   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16402
16403   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16404   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16405 }
16406
16407 void MapUndoRedoButtons(void)
16408 {
16409   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16410   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16411
16412   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16413   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16414 }
16415
16416 void ModifyPauseButtons(void)
16417 {
16418   static int ids[] =
16419   {
16420     GAME_CTRL_ID_PAUSE,
16421     GAME_CTRL_ID_PAUSE2,
16422     GAME_CTRL_ID_PANEL_PAUSE,
16423     GAME_CTRL_ID_TOUCH_PAUSE,
16424     -1
16425   };
16426   int i;
16427
16428   for (i = 0; ids[i] > -1; i++)
16429     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16430 }
16431
16432 static void MapGameButtonsExt(boolean on_tape)
16433 {
16434   int i;
16435
16436   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16437     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16438       MapGadget(game_gadget[i]);
16439
16440   UnmapGameButtonsAtSamePosition_All();
16441
16442   RedrawGameButtons();
16443 }
16444
16445 static void UnmapGameButtonsExt(boolean on_tape)
16446 {
16447   int i;
16448
16449   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16450     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16451       UnmapGadget(game_gadget[i]);
16452 }
16453
16454 static void RedrawGameButtonsExt(boolean on_tape)
16455 {
16456   int i;
16457
16458   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16459     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16460       RedrawGadget(game_gadget[i]);
16461 }
16462
16463 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16464 {
16465   if (gi == NULL)
16466     return;
16467
16468   gi->checked = state;
16469 }
16470
16471 static void RedrawSoundButtonGadget(int id)
16472 {
16473   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16474              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16475              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16476              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16477              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16478              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16479              id);
16480
16481   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16482   RedrawGadget(game_gadget[id2]);
16483 }
16484
16485 void MapGameButtons(void)
16486 {
16487   MapGameButtonsExt(FALSE);
16488 }
16489
16490 void UnmapGameButtons(void)
16491 {
16492   UnmapGameButtonsExt(FALSE);
16493 }
16494
16495 void RedrawGameButtons(void)
16496 {
16497   RedrawGameButtonsExt(FALSE);
16498 }
16499
16500 void MapGameButtonsOnTape(void)
16501 {
16502   MapGameButtonsExt(TRUE);
16503 }
16504
16505 void UnmapGameButtonsOnTape(void)
16506 {
16507   UnmapGameButtonsExt(TRUE);
16508 }
16509
16510 void RedrawGameButtonsOnTape(void)
16511 {
16512   RedrawGameButtonsExt(TRUE);
16513 }
16514
16515 static void GameUndoRedoExt(void)
16516 {
16517   ClearPlayerAction();
16518
16519   tape.pausing = TRUE;
16520
16521   RedrawPlayfield();
16522   UpdateAndDisplayGameControlValues();
16523
16524   DrawCompleteVideoDisplay();
16525   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16526   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16527   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16528
16529   ModifyPauseButtons();
16530
16531   BackToFront();
16532 }
16533
16534 static void GameUndo(int steps)
16535 {
16536   if (!CheckEngineSnapshotList())
16537     return;
16538
16539   int tape_property_bits = tape.property_bits;
16540
16541   LoadEngineSnapshot_Undo(steps);
16542
16543   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16544
16545   GameUndoRedoExt();
16546 }
16547
16548 static void GameRedo(int steps)
16549 {
16550   if (!CheckEngineSnapshotList())
16551     return;
16552
16553   int tape_property_bits = tape.property_bits;
16554
16555   LoadEngineSnapshot_Redo(steps);
16556
16557   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16558
16559   GameUndoRedoExt();
16560 }
16561
16562 static void HandleGameButtonsExt(int id, int button)
16563 {
16564   static boolean game_undo_executed = FALSE;
16565   int steps = BUTTON_STEPSIZE(button);
16566   boolean handle_game_buttons =
16567     (game_status == GAME_MODE_PLAYING ||
16568      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16569
16570   if (!handle_game_buttons)
16571     return;
16572
16573   switch (id)
16574   {
16575     case GAME_CTRL_ID_STOP:
16576     case GAME_CTRL_ID_PANEL_STOP:
16577     case GAME_CTRL_ID_TOUCH_STOP:
16578       if (game_status == GAME_MODE_MAIN)
16579         break;
16580
16581       if (tape.playing)
16582         TapeStop();
16583       else
16584         RequestQuitGame(FALSE);
16585
16586       break;
16587
16588     case GAME_CTRL_ID_PAUSE:
16589     case GAME_CTRL_ID_PAUSE2:
16590     case GAME_CTRL_ID_PANEL_PAUSE:
16591     case GAME_CTRL_ID_TOUCH_PAUSE:
16592       if (network.enabled && game_status == GAME_MODE_PLAYING)
16593       {
16594         if (tape.pausing)
16595           SendToServer_ContinuePlaying();
16596         else
16597           SendToServer_PausePlaying();
16598       }
16599       else
16600         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16601
16602       game_undo_executed = FALSE;
16603
16604       break;
16605
16606     case GAME_CTRL_ID_PLAY:
16607     case GAME_CTRL_ID_PANEL_PLAY:
16608       if (game_status == GAME_MODE_MAIN)
16609       {
16610         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16611       }
16612       else if (tape.pausing)
16613       {
16614         if (network.enabled)
16615           SendToServer_ContinuePlaying();
16616         else
16617           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16618       }
16619       break;
16620
16621     case GAME_CTRL_ID_UNDO:
16622       // Important: When using "save snapshot when collecting an item" mode,
16623       // load last (current) snapshot for first "undo" after pressing "pause"
16624       // (else the last-but-one snapshot would be loaded, because the snapshot
16625       // pointer already points to the last snapshot when pressing "pause",
16626       // which is fine for "every step/move" mode, but not for "every collect")
16627       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16628           !game_undo_executed)
16629         steps--;
16630
16631       game_undo_executed = TRUE;
16632
16633       GameUndo(steps);
16634       break;
16635
16636     case GAME_CTRL_ID_REDO:
16637       GameRedo(steps);
16638       break;
16639
16640     case GAME_CTRL_ID_SAVE:
16641       TapeQuickSave();
16642       break;
16643
16644     case GAME_CTRL_ID_LOAD:
16645       TapeQuickLoad();
16646       break;
16647
16648     case SOUND_CTRL_ID_MUSIC:
16649     case SOUND_CTRL_ID_PANEL_MUSIC:
16650       if (setup.sound_music)
16651       { 
16652         setup.sound_music = FALSE;
16653
16654         FadeMusic();
16655       }
16656       else if (audio.music_available)
16657       { 
16658         setup.sound = setup.sound_music = TRUE;
16659
16660         SetAudioMode(setup.sound);
16661
16662         if (game_status == GAME_MODE_PLAYING)
16663           PlayLevelMusic();
16664       }
16665
16666       RedrawSoundButtonGadget(id);
16667
16668       break;
16669
16670     case SOUND_CTRL_ID_LOOPS:
16671     case SOUND_CTRL_ID_PANEL_LOOPS:
16672       if (setup.sound_loops)
16673         setup.sound_loops = FALSE;
16674       else if (audio.loops_available)
16675       {
16676         setup.sound = setup.sound_loops = TRUE;
16677
16678         SetAudioMode(setup.sound);
16679       }
16680
16681       RedrawSoundButtonGadget(id);
16682
16683       break;
16684
16685     case SOUND_CTRL_ID_SIMPLE:
16686     case SOUND_CTRL_ID_PANEL_SIMPLE:
16687       if (setup.sound_simple)
16688         setup.sound_simple = FALSE;
16689       else if (audio.sound_available)
16690       {
16691         setup.sound = setup.sound_simple = TRUE;
16692
16693         SetAudioMode(setup.sound);
16694       }
16695
16696       RedrawSoundButtonGadget(id);
16697
16698       break;
16699
16700     default:
16701       break;
16702   }
16703 }
16704
16705 static void HandleGameButtons(struct GadgetInfo *gi)
16706 {
16707   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16708 }
16709
16710 void HandleSoundButtonKeys(Key key)
16711 {
16712   if (key == setup.shortcut.sound_simple)
16713     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16714   else if (key == setup.shortcut.sound_loops)
16715     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16716   else if (key == setup.shortcut.sound_music)
16717     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16718 }