fixed bug with player moving when releasing one of several pressed snap keys
[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 //                  http://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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckFadeAll())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   if (!level_editor_test_game)
3369     FadeOut(fade_mask);
3370
3371   // needed if different viewport properties defined for playing
3372   ChangeViewportPropertiesIfNeeded();
3373
3374   ClearField();
3375
3376   DrawCompleteVideoDisplay();
3377
3378   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3379
3380   InitGameEngine();
3381   InitGameControlValues();
3382
3383   // don't play tapes over network
3384   network_playing = (network.enabled && !tape.playing);
3385
3386   for (i = 0; i < MAX_PLAYERS; i++)
3387   {
3388     struct PlayerInfo *player = &stored_player[i];
3389
3390     player->index_nr = i;
3391     player->index_bit = (1 << i);
3392     player->element_nr = EL_PLAYER_1 + i;
3393
3394     player->present = FALSE;
3395     player->active = FALSE;
3396     player->mapped = FALSE;
3397
3398     player->killed = FALSE;
3399     player->reanimated = FALSE;
3400     player->buried = FALSE;
3401
3402     player->action = 0;
3403     player->effective_action = 0;
3404     player->programmed_action = 0;
3405     player->snap_action = 0;
3406
3407     player->mouse_action.lx = 0;
3408     player->mouse_action.ly = 0;
3409     player->mouse_action.button = 0;
3410     player->mouse_action.button_hint = 0;
3411
3412     player->effective_mouse_action.lx = 0;
3413     player->effective_mouse_action.ly = 0;
3414     player->effective_mouse_action.button = 0;
3415     player->effective_mouse_action.button_hint = 0;
3416
3417     for (j = 0; j < MAX_NUM_KEYS; j++)
3418       player->key[j] = FALSE;
3419
3420     player->num_white_keys = 0;
3421
3422     player->dynabomb_count = 0;
3423     player->dynabomb_size = 1;
3424     player->dynabombs_left = 0;
3425     player->dynabomb_xl = FALSE;
3426
3427     player->MovDir = initial_move_dir;
3428     player->MovPos = 0;
3429     player->GfxPos = 0;
3430     player->GfxDir = initial_move_dir;
3431     player->GfxAction = ACTION_DEFAULT;
3432     player->Frame = 0;
3433     player->StepFrame = 0;
3434
3435     player->initial_element = player->element_nr;
3436     player->artwork_element =
3437       (level.use_artwork_element[i] ? level.artwork_element[i] :
3438        player->element_nr);
3439     player->use_murphy = FALSE;
3440
3441     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3442     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3443
3444     player->gravity = level.initial_player_gravity[i];
3445
3446     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3447
3448     player->actual_frame_counter = 0;
3449
3450     player->step_counter = 0;
3451
3452     player->last_move_dir = initial_move_dir;
3453
3454     player->is_active = FALSE;
3455
3456     player->is_waiting = FALSE;
3457     player->is_moving = FALSE;
3458     player->is_auto_moving = FALSE;
3459     player->is_digging = FALSE;
3460     player->is_snapping = FALSE;
3461     player->is_collecting = FALSE;
3462     player->is_pushing = FALSE;
3463     player->is_switching = FALSE;
3464     player->is_dropping = FALSE;
3465     player->is_dropping_pressed = FALSE;
3466
3467     player->is_bored = FALSE;
3468     player->is_sleeping = FALSE;
3469
3470     player->was_waiting = TRUE;
3471     player->was_moving = FALSE;
3472     player->was_snapping = FALSE;
3473     player->was_dropping = FALSE;
3474
3475     player->force_dropping = FALSE;
3476
3477     player->frame_counter_bored = -1;
3478     player->frame_counter_sleeping = -1;
3479
3480     player->anim_delay_counter = 0;
3481     player->post_delay_counter = 0;
3482
3483     player->dir_waiting = initial_move_dir;
3484     player->action_waiting = ACTION_DEFAULT;
3485     player->last_action_waiting = ACTION_DEFAULT;
3486     player->special_action_bored = ACTION_DEFAULT;
3487     player->special_action_sleeping = ACTION_DEFAULT;
3488
3489     player->switch_x = -1;
3490     player->switch_y = -1;
3491
3492     player->drop_x = -1;
3493     player->drop_y = -1;
3494
3495     player->show_envelope = 0;
3496
3497     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3498
3499     player->push_delay       = -1;      // initialized when pushing starts
3500     player->push_delay_value = game.initial_push_delay_value;
3501
3502     player->drop_delay = 0;
3503     player->drop_pressed_delay = 0;
3504
3505     player->last_jx = -1;
3506     player->last_jy = -1;
3507     player->jx = -1;
3508     player->jy = -1;
3509
3510     player->shield_normal_time_left = 0;
3511     player->shield_deadly_time_left = 0;
3512
3513     player->inventory_infinite_element = EL_UNDEFINED;
3514     player->inventory_size = 0;
3515
3516     if (level.use_initial_inventory[i])
3517     {
3518       for (j = 0; j < level.initial_inventory_size[i]; j++)
3519       {
3520         int element = level.initial_inventory_content[i][j];
3521         int collect_count = element_info[element].collect_count_initial;
3522         int k;
3523
3524         if (!IS_CUSTOM_ELEMENT(element))
3525           collect_count = 1;
3526
3527         if (collect_count == 0)
3528           player->inventory_infinite_element = element;
3529         else
3530           for (k = 0; k < collect_count; k++)
3531             if (player->inventory_size < MAX_INVENTORY_SIZE)
3532               player->inventory_element[player->inventory_size++] = element;
3533       }
3534     }
3535
3536     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3537     SnapField(player, 0, 0);
3538
3539     map_player_action[i] = i;
3540   }
3541
3542   network_player_action_received = FALSE;
3543
3544   // initial null action
3545   if (network_playing)
3546     SendToServer_MovePlayer(MV_NONE);
3547
3548   FrameCounter = 0;
3549   TimeFrames = 0;
3550   TimePlayed = 0;
3551   TimeLeft = level.time;
3552   TapeTime = 0;
3553
3554   ScreenMovDir = MV_NONE;
3555   ScreenMovPos = 0;
3556   ScreenGfxPos = 0;
3557
3558   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3559
3560   game.robot_wheel_x = -1;
3561   game.robot_wheel_y = -1;
3562
3563   game.exit_x = -1;
3564   game.exit_y = -1;
3565
3566   game.all_players_gone = FALSE;
3567
3568   game.LevelSolved = FALSE;
3569   game.GameOver = FALSE;
3570
3571   game.GamePlayed = !tape.playing;
3572
3573   game.LevelSolved_GameWon = FALSE;
3574   game.LevelSolved_GameEnd = FALSE;
3575   game.LevelSolved_SaveTape = FALSE;
3576   game.LevelSolved_SaveScore = FALSE;
3577
3578   game.LevelSolved_CountingTime = 0;
3579   game.LevelSolved_CountingScore = 0;
3580   game.LevelSolved_CountingHealth = 0;
3581
3582   game.panel.active = TRUE;
3583
3584   game.no_time_limit = (level.time == 0);
3585
3586   game.yamyam_content_nr = 0;
3587   game.robot_wheel_active = FALSE;
3588   game.magic_wall_active = FALSE;
3589   game.magic_wall_time_left = 0;
3590   game.light_time_left = 0;
3591   game.timegate_time_left = 0;
3592   game.switchgate_pos = 0;
3593   game.wind_direction = level.wind_direction_initial;
3594
3595   game.score = 0;
3596   game.score_final = 0;
3597
3598   game.health = MAX_HEALTH;
3599   game.health_final = MAX_HEALTH;
3600
3601   game.gems_still_needed = level.gems_needed;
3602   game.sokoban_fields_still_needed = 0;
3603   game.sokoban_objects_still_needed = 0;
3604   game.lights_still_needed = 0;
3605   game.players_still_needed = 0;
3606   game.friends_still_needed = 0;
3607
3608   game.lenses_time_left = 0;
3609   game.magnify_time_left = 0;
3610
3611   game.ball_state = level.ball_state_initial;
3612   game.ball_content_nr = 0;
3613
3614   game.explosions_delayed = TRUE;
3615
3616   game.envelope_active = FALSE;
3617
3618   for (i = 0; i < NUM_BELTS; i++)
3619   {
3620     game.belt_dir[i] = MV_NONE;
3621     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3622   }
3623
3624   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3625     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3626
3627 #if DEBUG_INIT_PLAYER
3628   DebugPrintPlayerStatus("Player status at level initialization");
3629 #endif
3630
3631   SCAN_PLAYFIELD(x, y)
3632   {
3633     Feld[x][y] = Last[x][y] = level.field[x][y];
3634     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3635     ChangeDelay[x][y] = 0;
3636     ChangePage[x][y] = -1;
3637     CustomValue[x][y] = 0;              // initialized in InitField()
3638     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3639     AmoebaNr[x][y] = 0;
3640     WasJustMoving[x][y] = 0;
3641     WasJustFalling[x][y] = 0;
3642     CheckCollision[x][y] = 0;
3643     CheckImpact[x][y] = 0;
3644     Stop[x][y] = FALSE;
3645     Pushed[x][y] = FALSE;
3646
3647     ChangeCount[x][y] = 0;
3648     ChangeEvent[x][y] = -1;
3649
3650     ExplodePhase[x][y] = 0;
3651     ExplodeDelay[x][y] = 0;
3652     ExplodeField[x][y] = EX_TYPE_NONE;
3653
3654     RunnerVisit[x][y] = 0;
3655     PlayerVisit[x][y] = 0;
3656
3657     GfxFrame[x][y] = 0;
3658     GfxRandom[x][y] = INIT_GFX_RANDOM();
3659     GfxElement[x][y] = EL_UNDEFINED;
3660     GfxAction[x][y] = ACTION_DEFAULT;
3661     GfxDir[x][y] = MV_NONE;
3662     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3663   }
3664
3665   SCAN_PLAYFIELD(x, y)
3666   {
3667     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3668       emulate_bd = FALSE;
3669     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3670       emulate_sb = FALSE;
3671     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3672       emulate_sp = FALSE;
3673
3674     InitField(x, y, TRUE);
3675
3676     ResetGfxAnimation(x, y);
3677   }
3678
3679   InitBeltMovement();
3680
3681   for (i = 0; i < MAX_PLAYERS; i++)
3682   {
3683     struct PlayerInfo *player = &stored_player[i];
3684
3685     // set number of special actions for bored and sleeping animation
3686     player->num_special_action_bored =
3687       get_num_special_action(player->artwork_element,
3688                              ACTION_BORING_1, ACTION_BORING_LAST);
3689     player->num_special_action_sleeping =
3690       get_num_special_action(player->artwork_element,
3691                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3692   }
3693
3694   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3695                     emulate_sb ? EMU_SOKOBAN :
3696                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3697
3698   // initialize type of slippery elements
3699   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3700   {
3701     if (!IS_CUSTOM_ELEMENT(i))
3702     {
3703       // default: elements slip down either to the left or right randomly
3704       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3705
3706       // SP style elements prefer to slip down on the left side
3707       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3708         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3709
3710       // BD style elements prefer to slip down on the left side
3711       if (game.emulation == EMU_BOULDERDASH)
3712         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3713     }
3714   }
3715
3716   // initialize explosion and ignition delay
3717   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3718   {
3719     if (!IS_CUSTOM_ELEMENT(i))
3720     {
3721       int num_phase = 8;
3722       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3723                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3724                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3725       int last_phase = (num_phase + 1) * delay;
3726       int half_phase = (num_phase / 2) * delay;
3727
3728       element_info[i].explosion_delay = last_phase - 1;
3729       element_info[i].ignition_delay = half_phase;
3730
3731       if (i == EL_BLACK_ORB)
3732         element_info[i].ignition_delay = 1;
3733     }
3734   }
3735
3736   // correct non-moving belts to start moving left
3737   for (i = 0; i < NUM_BELTS; i++)
3738     if (game.belt_dir[i] == MV_NONE)
3739       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3740
3741 #if USE_NEW_PLAYER_ASSIGNMENTS
3742   // use preferred player also in local single-player mode
3743   if (!network.enabled && !game.team_mode)
3744   {
3745     int old_index_nr = local_player->index_nr;
3746     int new_index_nr = setup.network_player_nr;
3747
3748     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3749     {
3750       stored_player[old_index_nr].connected_locally = FALSE;
3751       stored_player[new_index_nr].connected_locally = TRUE;
3752     }
3753   }
3754
3755   for (i = 0; i < MAX_PLAYERS; i++)
3756   {
3757     stored_player[i].connected = FALSE;
3758
3759     // in network game mode, the local player might not be the first player
3760     if (stored_player[i].connected_locally)
3761       local_player = &stored_player[i];
3762   }
3763
3764   if (!network.enabled)
3765     local_player->connected = TRUE;
3766
3767   if (tape.playing)
3768   {
3769     for (i = 0; i < MAX_PLAYERS; i++)
3770       stored_player[i].connected = tape.player_participates[i];
3771   }
3772   else if (network.enabled)
3773   {
3774     // add team mode players connected over the network (needed for correct
3775     // assignment of player figures from level to locally playing players)
3776
3777     for (i = 0; i < MAX_PLAYERS; i++)
3778       if (stored_player[i].connected_network)
3779         stored_player[i].connected = TRUE;
3780   }
3781   else if (game.team_mode)
3782   {
3783     // try to guess locally connected team mode players (needed for correct
3784     // assignment of player figures from level to locally playing players)
3785
3786     for (i = 0; i < MAX_PLAYERS; i++)
3787       if (setup.input[i].use_joystick ||
3788           setup.input[i].key.left != KSYM_UNDEFINED)
3789         stored_player[i].connected = TRUE;
3790   }
3791
3792 #if DEBUG_INIT_PLAYER
3793   DebugPrintPlayerStatus("Player status after level initialization");
3794 #endif
3795
3796 #if DEBUG_INIT_PLAYER
3797   if (options.debug)
3798     printf("Reassigning players ...\n");
3799 #endif
3800
3801   // check if any connected player was not found in playfield
3802   for (i = 0; i < MAX_PLAYERS; i++)
3803   {
3804     struct PlayerInfo *player = &stored_player[i];
3805
3806     if (player->connected && !player->present)
3807     {
3808       struct PlayerInfo *field_player = NULL;
3809
3810 #if DEBUG_INIT_PLAYER
3811       if (options.debug)
3812         printf("- looking for field player for player %d ...\n", i + 1);
3813 #endif
3814
3815       // assign first free player found that is present in the playfield
3816
3817       // first try: look for unmapped playfield player that is not connected
3818       for (j = 0; j < MAX_PLAYERS; j++)
3819         if (field_player == NULL &&
3820             stored_player[j].present &&
3821             !stored_player[j].mapped &&
3822             !stored_player[j].connected)
3823           field_player = &stored_player[j];
3824
3825       // second try: look for *any* unmapped playfield player
3826       for (j = 0; j < MAX_PLAYERS; j++)
3827         if (field_player == NULL &&
3828             stored_player[j].present &&
3829             !stored_player[j].mapped)
3830           field_player = &stored_player[j];
3831
3832       if (field_player != NULL)
3833       {
3834         int jx = field_player->jx, jy = field_player->jy;
3835
3836 #if DEBUG_INIT_PLAYER
3837         if (options.debug)
3838           printf("- found player %d\n", field_player->index_nr + 1);
3839 #endif
3840
3841         player->present = FALSE;
3842         player->active = FALSE;
3843
3844         field_player->present = TRUE;
3845         field_player->active = TRUE;
3846
3847         /*
3848         player->initial_element = field_player->initial_element;
3849         player->artwork_element = field_player->artwork_element;
3850
3851         player->block_last_field       = field_player->block_last_field;
3852         player->block_delay_adjustment = field_player->block_delay_adjustment;
3853         */
3854
3855         StorePlayer[jx][jy] = field_player->element_nr;
3856
3857         field_player->jx = field_player->last_jx = jx;
3858         field_player->jy = field_player->last_jy = jy;
3859
3860         if (local_player == player)
3861           local_player = field_player;
3862
3863         map_player_action[field_player->index_nr] = i;
3864
3865         field_player->mapped = TRUE;
3866
3867 #if DEBUG_INIT_PLAYER
3868         if (options.debug)
3869           printf("- map_player_action[%d] == %d\n",
3870                  field_player->index_nr + 1, i + 1);
3871 #endif
3872       }
3873     }
3874
3875     if (player->connected && player->present)
3876       player->mapped = TRUE;
3877   }
3878
3879 #if DEBUG_INIT_PLAYER
3880   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3881 #endif
3882
3883 #else
3884
3885   // check if any connected player was not found in playfield
3886   for (i = 0; i < MAX_PLAYERS; i++)
3887   {
3888     struct PlayerInfo *player = &stored_player[i];
3889
3890     if (player->connected && !player->present)
3891     {
3892       for (j = 0; j < MAX_PLAYERS; j++)
3893       {
3894         struct PlayerInfo *field_player = &stored_player[j];
3895         int jx = field_player->jx, jy = field_player->jy;
3896
3897         // assign first free player found that is present in the playfield
3898         if (field_player->present && !field_player->connected)
3899         {
3900           player->present = TRUE;
3901           player->active = TRUE;
3902
3903           field_player->present = FALSE;
3904           field_player->active = FALSE;
3905
3906           player->initial_element = field_player->initial_element;
3907           player->artwork_element = field_player->artwork_element;
3908
3909           player->block_last_field       = field_player->block_last_field;
3910           player->block_delay_adjustment = field_player->block_delay_adjustment;
3911
3912           StorePlayer[jx][jy] = player->element_nr;
3913
3914           player->jx = player->last_jx = jx;
3915           player->jy = player->last_jy = jy;
3916
3917           break;
3918         }
3919       }
3920     }
3921   }
3922 #endif
3923
3924 #if 0
3925   printf("::: local_player->present == %d\n", local_player->present);
3926 #endif
3927
3928   // set focus to local player for network games, else to all players
3929   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3930   game.centered_player_nr_next = game.centered_player_nr;
3931   game.set_centered_player = FALSE;
3932
3933   if (network_playing && tape.recording)
3934   {
3935     // store client dependent player focus when recording network games
3936     tape.centered_player_nr_next = game.centered_player_nr_next;
3937     tape.set_centered_player = TRUE;
3938   }
3939
3940   if (tape.playing)
3941   {
3942     // when playing a tape, eliminate all players who do not participate
3943
3944 #if USE_NEW_PLAYER_ASSIGNMENTS
3945
3946     if (!game.team_mode)
3947     {
3948       for (i = 0; i < MAX_PLAYERS; i++)
3949       {
3950         if (stored_player[i].active &&
3951             !tape.player_participates[map_player_action[i]])
3952         {
3953           struct PlayerInfo *player = &stored_player[i];
3954           int jx = player->jx, jy = player->jy;
3955
3956 #if DEBUG_INIT_PLAYER
3957           if (options.debug)
3958             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3959 #endif
3960
3961           player->active = FALSE;
3962           StorePlayer[jx][jy] = 0;
3963           Feld[jx][jy] = EL_EMPTY;
3964         }
3965       }
3966     }
3967
3968 #else
3969
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971     {
3972       if (stored_player[i].active &&
3973           !tape.player_participates[i])
3974       {
3975         struct PlayerInfo *player = &stored_player[i];
3976         int jx = player->jx, jy = player->jy;
3977
3978         player->active = FALSE;
3979         StorePlayer[jx][jy] = 0;
3980         Feld[jx][jy] = EL_EMPTY;
3981       }
3982     }
3983 #endif
3984   }
3985   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3986   {
3987     // when in single player mode, eliminate all but the local player
3988
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990     {
3991       struct PlayerInfo *player = &stored_player[i];
3992
3993       if (player->active && player != local_player)
3994       {
3995         int jx = player->jx, jy = player->jy;
3996
3997         player->active = FALSE;
3998         player->present = FALSE;
3999
4000         StorePlayer[jx][jy] = 0;
4001         Feld[jx][jy] = EL_EMPTY;
4002       }
4003     }
4004   }
4005
4006   for (i = 0; i < MAX_PLAYERS; i++)
4007     if (stored_player[i].active)
4008       game.players_still_needed++;
4009
4010   if (level.solved_by_one_player)
4011     game.players_still_needed = 1;
4012
4013   // when recording the game, store which players take part in the game
4014   if (tape.recording)
4015   {
4016 #if USE_NEW_PLAYER_ASSIGNMENTS
4017     for (i = 0; i < MAX_PLAYERS; i++)
4018       if (stored_player[i].connected)
4019         tape.player_participates[i] = TRUE;
4020 #else
4021     for (i = 0; i < MAX_PLAYERS; i++)
4022       if (stored_player[i].active)
4023         tape.player_participates[i] = TRUE;
4024 #endif
4025   }
4026
4027 #if DEBUG_INIT_PLAYER
4028   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4029 #endif
4030
4031   if (BorderElement == EL_EMPTY)
4032   {
4033     SBX_Left = 0;
4034     SBX_Right = lev_fieldx - SCR_FIELDX;
4035     SBY_Upper = 0;
4036     SBY_Lower = lev_fieldy - SCR_FIELDY;
4037   }
4038   else
4039   {
4040     SBX_Left = -1;
4041     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4042     SBY_Upper = -1;
4043     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4044   }
4045
4046   if (full_lev_fieldx <= SCR_FIELDX)
4047     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4048   if (full_lev_fieldy <= SCR_FIELDY)
4049     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4050
4051   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4052     SBX_Left--;
4053   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4054     SBY_Upper--;
4055
4056   // if local player not found, look for custom element that might create
4057   // the player (make some assumptions about the right custom element)
4058   if (!local_player->present)
4059   {
4060     int start_x = 0, start_y = 0;
4061     int found_rating = 0;
4062     int found_element = EL_UNDEFINED;
4063     int player_nr = local_player->index_nr;
4064
4065     SCAN_PLAYFIELD(x, y)
4066     {
4067       int element = Feld[x][y];
4068       int content;
4069       int xx, yy;
4070       boolean is_player;
4071
4072       if (level.use_start_element[player_nr] &&
4073           level.start_element[player_nr] == element &&
4074           found_rating < 4)
4075       {
4076         start_x = x;
4077         start_y = y;
4078
4079         found_rating = 4;
4080         found_element = element;
4081       }
4082
4083       if (!IS_CUSTOM_ELEMENT(element))
4084         continue;
4085
4086       if (CAN_CHANGE(element))
4087       {
4088         for (i = 0; i < element_info[element].num_change_pages; i++)
4089         {
4090           // check for player created from custom element as single target
4091           content = element_info[element].change_page[i].target_element;
4092           is_player = ELEM_IS_PLAYER(content);
4093
4094           if (is_player && (found_rating < 3 ||
4095                             (found_rating == 3 && element < found_element)))
4096           {
4097             start_x = x;
4098             start_y = y;
4099
4100             found_rating = 3;
4101             found_element = element;
4102           }
4103         }
4104       }
4105
4106       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4107       {
4108         // check for player created from custom element as explosion content
4109         content = element_info[element].content.e[xx][yy];
4110         is_player = ELEM_IS_PLAYER(content);
4111
4112         if (is_player && (found_rating < 2 ||
4113                           (found_rating == 2 && element < found_element)))
4114         {
4115           start_x = x + xx - 1;
4116           start_y = y + yy - 1;
4117
4118           found_rating = 2;
4119           found_element = element;
4120         }
4121
4122         if (!CAN_CHANGE(element))
4123           continue;
4124
4125         for (i = 0; i < element_info[element].num_change_pages; i++)
4126         {
4127           // check for player created from custom element as extended target
4128           content =
4129             element_info[element].change_page[i].target_content.e[xx][yy];
4130
4131           is_player = ELEM_IS_PLAYER(content);
4132
4133           if (is_player && (found_rating < 1 ||
4134                             (found_rating == 1 && element < found_element)))
4135           {
4136             start_x = x + xx - 1;
4137             start_y = y + yy - 1;
4138
4139             found_rating = 1;
4140             found_element = element;
4141           }
4142         }
4143       }
4144     }
4145
4146     scroll_x = SCROLL_POSITION_X(start_x);
4147     scroll_y = SCROLL_POSITION_Y(start_y);
4148   }
4149   else
4150   {
4151     scroll_x = SCROLL_POSITION_X(local_player->jx);
4152     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4153   }
4154
4155   // !!! FIX THIS (START) !!!
4156   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4157   {
4158     InitGameEngine_EM();
4159   }
4160   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4161   {
4162     InitGameEngine_SP();
4163   }
4164   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4165   {
4166     InitGameEngine_MM();
4167   }
4168   else
4169   {
4170     DrawLevel(REDRAW_FIELD);
4171     DrawAllPlayers();
4172
4173     // after drawing the level, correct some elements
4174     if (game.timegate_time_left == 0)
4175       CloseAllOpenTimegates();
4176   }
4177
4178   // blit playfield from scroll buffer to normal back buffer for fading in
4179   BlitScreenToBitmap(backbuffer);
4180   // !!! FIX THIS (END) !!!
4181
4182   DrawMaskedBorder(fade_mask);
4183
4184   FadeIn(fade_mask);
4185
4186 #if 1
4187   // full screen redraw is required at this point in the following cases:
4188   // - special editor door undrawn when game was started from level editor
4189   // - drawing area (playfield) was changed and has to be removed completely
4190   redraw_mask = REDRAW_ALL;
4191   BackToFront();
4192 #endif
4193
4194   if (!game.restart_level)
4195   {
4196     // copy default game door content to main double buffer
4197
4198     // !!! CHECK AGAIN !!!
4199     SetPanelBackground();
4200     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4201     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4202   }
4203
4204   SetPanelBackground();
4205   SetDrawBackgroundMask(REDRAW_DOOR_1);
4206
4207   UpdateAndDisplayGameControlValues();
4208
4209   if (!game.restart_level)
4210   {
4211     UnmapGameButtons();
4212     UnmapTapeButtons();
4213
4214     FreeGameButtons();
4215     CreateGameButtons();
4216
4217     MapGameButtons();
4218     MapTapeButtons();
4219
4220     // copy actual game door content to door double buffer for OpenDoor()
4221     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4222
4223     OpenDoor(DOOR_OPEN_ALL);
4224
4225     KeyboardAutoRepeatOffUnlessAutoplay();
4226
4227 #if DEBUG_INIT_PLAYER
4228     DebugPrintPlayerStatus("Player status (final)");
4229 #endif
4230   }
4231
4232   UnmapAllGadgets();
4233
4234   MapGameButtons();
4235   MapTapeButtons();
4236
4237   if (!game.restart_level && !tape.playing)
4238   {
4239     LevelStats_incPlayed(level_nr);
4240
4241     SaveLevelSetup_SeriesInfo();
4242   }
4243
4244   game.restart_level = FALSE;
4245   game.restart_game_message = NULL;
4246   game.request_active = FALSE;
4247
4248   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4249     InitGameActions_MM();
4250
4251   SaveEngineSnapshotToListInitial();
4252
4253   if (!game.restart_level)
4254   {
4255     PlaySound(SND_GAME_STARTING);
4256
4257     if (setup.sound_music)
4258       PlayLevelMusic();
4259   }
4260 }
4261
4262 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4263                         int actual_player_x, int actual_player_y)
4264 {
4265   // this is used for non-R'n'D game engines to update certain engine values
4266
4267   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4268   {
4269     actual_player_x = correctLevelPosX_EM(actual_player_x);
4270     actual_player_y = correctLevelPosY_EM(actual_player_y);
4271   }
4272
4273   // needed to determine if sounds are played within the visible screen area
4274   scroll_x = actual_scroll_x;
4275   scroll_y = actual_scroll_y;
4276
4277   // needed to get player position for "follow finger" playing input method
4278   local_player->jx = actual_player_x;
4279   local_player->jy = actual_player_y;
4280 }
4281
4282 void InitMovDir(int x, int y)
4283 {
4284   int i, element = Feld[x][y];
4285   static int xy[4][2] =
4286   {
4287     {  0, +1 },
4288     { +1,  0 },
4289     {  0, -1 },
4290     { -1,  0 }
4291   };
4292   static int direction[3][4] =
4293   {
4294     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4295     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4296     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4297   };
4298
4299   switch (element)
4300   {
4301     case EL_BUG_RIGHT:
4302     case EL_BUG_UP:
4303     case EL_BUG_LEFT:
4304     case EL_BUG_DOWN:
4305       Feld[x][y] = EL_BUG;
4306       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4307       break;
4308
4309     case EL_SPACESHIP_RIGHT:
4310     case EL_SPACESHIP_UP:
4311     case EL_SPACESHIP_LEFT:
4312     case EL_SPACESHIP_DOWN:
4313       Feld[x][y] = EL_SPACESHIP;
4314       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4315       break;
4316
4317     case EL_BD_BUTTERFLY_RIGHT:
4318     case EL_BD_BUTTERFLY_UP:
4319     case EL_BD_BUTTERFLY_LEFT:
4320     case EL_BD_BUTTERFLY_DOWN:
4321       Feld[x][y] = EL_BD_BUTTERFLY;
4322       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4323       break;
4324
4325     case EL_BD_FIREFLY_RIGHT:
4326     case EL_BD_FIREFLY_UP:
4327     case EL_BD_FIREFLY_LEFT:
4328     case EL_BD_FIREFLY_DOWN:
4329       Feld[x][y] = EL_BD_FIREFLY;
4330       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4331       break;
4332
4333     case EL_PACMAN_RIGHT:
4334     case EL_PACMAN_UP:
4335     case EL_PACMAN_LEFT:
4336     case EL_PACMAN_DOWN:
4337       Feld[x][y] = EL_PACMAN;
4338       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4339       break;
4340
4341     case EL_YAMYAM_LEFT:
4342     case EL_YAMYAM_RIGHT:
4343     case EL_YAMYAM_UP:
4344     case EL_YAMYAM_DOWN:
4345       Feld[x][y] = EL_YAMYAM;
4346       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4347       break;
4348
4349     case EL_SP_SNIKSNAK:
4350       MovDir[x][y] = MV_UP;
4351       break;
4352
4353     case EL_SP_ELECTRON:
4354       MovDir[x][y] = MV_LEFT;
4355       break;
4356
4357     case EL_MOLE_LEFT:
4358     case EL_MOLE_RIGHT:
4359     case EL_MOLE_UP:
4360     case EL_MOLE_DOWN:
4361       Feld[x][y] = EL_MOLE;
4362       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4363       break;
4364
4365     default:
4366       if (IS_CUSTOM_ELEMENT(element))
4367       {
4368         struct ElementInfo *ei = &element_info[element];
4369         int move_direction_initial = ei->move_direction_initial;
4370         int move_pattern = ei->move_pattern;
4371
4372         if (move_direction_initial == MV_START_PREVIOUS)
4373         {
4374           if (MovDir[x][y] != MV_NONE)
4375             return;
4376
4377           move_direction_initial = MV_START_AUTOMATIC;
4378         }
4379
4380         if (move_direction_initial == MV_START_RANDOM)
4381           MovDir[x][y] = 1 << RND(4);
4382         else if (move_direction_initial & MV_ANY_DIRECTION)
4383           MovDir[x][y] = move_direction_initial;
4384         else if (move_pattern == MV_ALL_DIRECTIONS ||
4385                  move_pattern == MV_TURNING_LEFT ||
4386                  move_pattern == MV_TURNING_RIGHT ||
4387                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4388                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4389                  move_pattern == MV_TURNING_RANDOM)
4390           MovDir[x][y] = 1 << RND(4);
4391         else if (move_pattern == MV_HORIZONTAL)
4392           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4393         else if (move_pattern == MV_VERTICAL)
4394           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4395         else if (move_pattern & MV_ANY_DIRECTION)
4396           MovDir[x][y] = element_info[element].move_pattern;
4397         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4398                  move_pattern == MV_ALONG_RIGHT_SIDE)
4399         {
4400           // use random direction as default start direction
4401           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4402             MovDir[x][y] = 1 << RND(4);
4403
4404           for (i = 0; i < NUM_DIRECTIONS; i++)
4405           {
4406             int x1 = x + xy[i][0];
4407             int y1 = y + xy[i][1];
4408
4409             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4410             {
4411               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4412                 MovDir[x][y] = direction[0][i];
4413               else
4414                 MovDir[x][y] = direction[1][i];
4415
4416               break;
4417             }
4418           }
4419         }                
4420       }
4421       else
4422       {
4423         MovDir[x][y] = 1 << RND(4);
4424
4425         if (element != EL_BUG &&
4426             element != EL_SPACESHIP &&
4427             element != EL_BD_BUTTERFLY &&
4428             element != EL_BD_FIREFLY)
4429           break;
4430
4431         for (i = 0; i < NUM_DIRECTIONS; i++)
4432         {
4433           int x1 = x + xy[i][0];
4434           int y1 = y + xy[i][1];
4435
4436           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4437           {
4438             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4439             {
4440               MovDir[x][y] = direction[0][i];
4441               break;
4442             }
4443             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4444                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4445             {
4446               MovDir[x][y] = direction[1][i];
4447               break;
4448             }
4449           }
4450         }
4451       }
4452       break;
4453   }
4454
4455   GfxDir[x][y] = MovDir[x][y];
4456 }
4457
4458 void InitAmoebaNr(int x, int y)
4459 {
4460   int i;
4461   int group_nr = AmoebeNachbarNr(x, y);
4462
4463   if (group_nr == 0)
4464   {
4465     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4466     {
4467       if (AmoebaCnt[i] == 0)
4468       {
4469         group_nr = i;
4470         break;
4471       }
4472     }
4473   }
4474
4475   AmoebaNr[x][y] = group_nr;
4476   AmoebaCnt[group_nr]++;
4477   AmoebaCnt2[group_nr]++;
4478 }
4479
4480 static void LevelSolved(void)
4481 {
4482   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4483       game.players_still_needed > 0)
4484     return;
4485
4486   game.LevelSolved = TRUE;
4487   game.GameOver = TRUE;
4488
4489   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4490                       level.native_em_level->lev->score :
4491                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4492                       game_mm.score :
4493                       game.score);
4494   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4495                        MM_HEALTH(game_mm.laser_overload_value) :
4496                        game.health);
4497
4498   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4499   game.LevelSolved_CountingScore = game.score_final;
4500   game.LevelSolved_CountingHealth = game.health_final;
4501 }
4502
4503 void GameWon(void)
4504 {
4505   static int time_count_steps;
4506   static int time, time_final;
4507   static int score, score_final;
4508   static int health, health_final;
4509   static int game_over_delay_1 = 0;
4510   static int game_over_delay_2 = 0;
4511   static int game_over_delay_3 = 0;
4512   int game_over_delay_value_1 = 50;
4513   int game_over_delay_value_2 = 25;
4514   int game_over_delay_value_3 = 50;
4515
4516   if (!game.LevelSolved_GameWon)
4517   {
4518     int i;
4519
4520     // do not start end game actions before the player stops moving (to exit)
4521     if (local_player->active && local_player->MovPos)
4522       return;
4523
4524     game.LevelSolved_GameWon = TRUE;
4525     game.LevelSolved_SaveTape = tape.recording;
4526     game.LevelSolved_SaveScore = !tape.playing;
4527
4528     if (!tape.playing)
4529     {
4530       LevelStats_incSolved(level_nr);
4531
4532       SaveLevelSetup_SeriesInfo();
4533     }
4534
4535     if (tape.auto_play)         // tape might already be stopped here
4536       tape.auto_play_level_solved = TRUE;
4537
4538     TapeStop();
4539
4540     game_over_delay_1 = 0;
4541     game_over_delay_2 = 0;
4542     game_over_delay_3 = game_over_delay_value_3;
4543
4544     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4545     score = score_final = game.score_final;
4546     health = health_final = game.health_final;
4547
4548     if (level.score[SC_TIME_BONUS] > 0)
4549     {
4550       if (TimeLeft > 0)
4551       {
4552         time_final = 0;
4553         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4554       }
4555       else if (game.no_time_limit && TimePlayed < 999)
4556       {
4557         time_final = 999;
4558         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4559       }
4560
4561       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4562
4563       game_over_delay_1 = game_over_delay_value_1;
4564
4565       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4566       {
4567         health_final = 0;
4568         score_final += health * level.score[SC_TIME_BONUS];
4569
4570         game_over_delay_2 = game_over_delay_value_2;
4571       }
4572
4573       game.score_final = score_final;
4574       game.health_final = health_final;
4575     }
4576
4577     if (level_editor_test_game)
4578     {
4579       time = time_final;
4580       score = score_final;
4581
4582       game.LevelSolved_CountingTime = time;
4583       game.LevelSolved_CountingScore = score;
4584
4585       game_panel_controls[GAME_PANEL_TIME].value = time;
4586       game_panel_controls[GAME_PANEL_SCORE].value = score;
4587
4588       DisplayGameControlValues();
4589     }
4590
4591     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4592     {
4593       // check if last player has left the level
4594       if (game.exit_x >= 0 &&
4595           game.exit_y >= 0)
4596       {
4597         int x = game.exit_x;
4598         int y = game.exit_y;
4599         int element = Feld[x][y];
4600
4601         // close exit door after last player
4602         if ((game.all_players_gone &&
4603              (element == EL_EXIT_OPEN ||
4604               element == EL_SP_EXIT_OPEN ||
4605               element == EL_STEEL_EXIT_OPEN)) ||
4606             element == EL_EM_EXIT_OPEN ||
4607             element == EL_EM_STEEL_EXIT_OPEN)
4608         {
4609
4610           Feld[x][y] =
4611             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4612              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4613              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4614              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4615              EL_EM_STEEL_EXIT_CLOSING);
4616
4617           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4618         }
4619
4620         // player disappears
4621         DrawLevelField(x, y);
4622       }
4623
4624       for (i = 0; i < MAX_PLAYERS; i++)
4625       {
4626         struct PlayerInfo *player = &stored_player[i];
4627
4628         if (player->present)
4629         {
4630           RemovePlayer(player);
4631
4632           // player disappears
4633           DrawLevelField(player->jx, player->jy);
4634         }
4635       }
4636     }
4637
4638     PlaySound(SND_GAME_WINNING);
4639   }
4640
4641   if (game_over_delay_1 > 0)
4642   {
4643     game_over_delay_1--;
4644
4645     return;
4646   }
4647
4648   if (time != time_final)
4649   {
4650     int time_to_go = ABS(time_final - time);
4651     int time_count_dir = (time < time_final ? +1 : -1);
4652
4653     if (time_to_go < time_count_steps)
4654       time_count_steps = 1;
4655
4656     time  += time_count_steps * time_count_dir;
4657     score += time_count_steps * level.score[SC_TIME_BONUS];
4658
4659     game.LevelSolved_CountingTime = time;
4660     game.LevelSolved_CountingScore = score;
4661
4662     game_panel_controls[GAME_PANEL_TIME].value = time;
4663     game_panel_controls[GAME_PANEL_SCORE].value = score;
4664
4665     DisplayGameControlValues();
4666
4667     if (time == time_final)
4668       StopSound(SND_GAME_LEVELTIME_BONUS);
4669     else if (setup.sound_loops)
4670       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4671     else
4672       PlaySound(SND_GAME_LEVELTIME_BONUS);
4673
4674     return;
4675   }
4676
4677   if (game_over_delay_2 > 0)
4678   {
4679     game_over_delay_2--;
4680
4681     return;
4682   }
4683
4684   if (health != health_final)
4685   {
4686     int health_count_dir = (health < health_final ? +1 : -1);
4687
4688     health += health_count_dir;
4689     score  += level.score[SC_TIME_BONUS];
4690
4691     game.LevelSolved_CountingHealth = health;
4692     game.LevelSolved_CountingScore = score;
4693
4694     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4695     game_panel_controls[GAME_PANEL_SCORE].value = score;
4696
4697     DisplayGameControlValues();
4698
4699     if (health == health_final)
4700       StopSound(SND_GAME_LEVELTIME_BONUS);
4701     else if (setup.sound_loops)
4702       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4703     else
4704       PlaySound(SND_GAME_LEVELTIME_BONUS);
4705
4706     return;
4707   }
4708
4709   game.panel.active = FALSE;
4710
4711   if (game_over_delay_3 > 0)
4712   {
4713     game_over_delay_3--;
4714
4715     return;
4716   }
4717
4718   GameEnd();
4719 }
4720
4721 void GameEnd(void)
4722 {
4723   // used instead of "level_nr" (needed for network games)
4724   int last_level_nr = levelset.level_nr;
4725   int hi_pos;
4726
4727   game.LevelSolved_GameEnd = TRUE;
4728
4729   if (game.LevelSolved_SaveTape)
4730   {
4731     // make sure that request dialog to save tape does not open door again
4732     if (!global.use_envelope_request)
4733       CloseDoor(DOOR_CLOSE_1);
4734
4735     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4736   }
4737
4738   // if no tape is to be saved, close both doors simultaneously
4739   CloseDoor(DOOR_CLOSE_ALL);
4740
4741   if (level_editor_test_game)
4742   {
4743     SetGameStatus(GAME_MODE_MAIN);
4744
4745     DrawMainMenu();
4746
4747     return;
4748   }
4749
4750   if (!game.LevelSolved_SaveScore)
4751   {
4752     SetGameStatus(GAME_MODE_MAIN);
4753
4754     DrawMainMenu();
4755
4756     return;
4757   }
4758
4759   if (level_nr == leveldir_current->handicap_level)
4760   {
4761     leveldir_current->handicap_level++;
4762
4763     SaveLevelSetup_SeriesInfo();
4764   }
4765
4766   if (setup.increment_levels &&
4767       level_nr < leveldir_current->last_level &&
4768       !network_playing)
4769   {
4770     level_nr++;         // advance to next level
4771     TapeErase();        // start with empty tape
4772
4773     if (setup.auto_play_next_level)
4774     {
4775       LoadLevel(level_nr);
4776
4777       SaveLevelSetup_SeriesInfo();
4778     }
4779   }
4780
4781   hi_pos = NewHiScore(last_level_nr);
4782
4783   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4784   {
4785     SetGameStatus(GAME_MODE_SCORES);
4786
4787     DrawHallOfFame(last_level_nr, hi_pos);
4788   }
4789   else if (setup.auto_play_next_level && setup.increment_levels &&
4790            last_level_nr < leveldir_current->last_level &&
4791            !network_playing)
4792   {
4793     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4794   }
4795   else
4796   {
4797     SetGameStatus(GAME_MODE_MAIN);
4798
4799     DrawMainMenu();
4800   }
4801 }
4802
4803 int NewHiScore(int level_nr)
4804 {
4805   int k, l;
4806   int position = -1;
4807   boolean one_score_entry_per_name = !program.many_scores_per_name;
4808
4809   LoadScore(level_nr);
4810
4811   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4812       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4813     return -1;
4814
4815   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4816   {
4817     if (game.score_final > highscore[k].Score)
4818     {
4819       // player has made it to the hall of fame
4820
4821       if (k < MAX_SCORE_ENTRIES - 1)
4822       {
4823         int m = MAX_SCORE_ENTRIES - 1;
4824
4825         if (one_score_entry_per_name)
4826         {
4827           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4828             if (strEqual(setup.player_name, highscore[l].Name))
4829               m = l;
4830
4831           if (m == k)   // player's new highscore overwrites his old one
4832             goto put_into_list;
4833         }
4834
4835         for (l = m; l > k; l--)
4836         {
4837           strcpy(highscore[l].Name, highscore[l - 1].Name);
4838           highscore[l].Score = highscore[l - 1].Score;
4839         }
4840       }
4841
4842       put_into_list:
4843
4844       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4845       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4846       highscore[k].Score = game.score_final;
4847       position = k;
4848
4849       break;
4850     }
4851     else if (one_score_entry_per_name &&
4852              !strncmp(setup.player_name, highscore[k].Name,
4853                       MAX_PLAYER_NAME_LEN))
4854       break;    // player already there with a higher score
4855   }
4856
4857   if (position >= 0) 
4858     SaveScore(level_nr);
4859
4860   return position;
4861 }
4862
4863 static int getElementMoveStepsizeExt(int x, int y, int direction)
4864 {
4865   int element = Feld[x][y];
4866   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4867   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4868   int horiz_move = (dx != 0);
4869   int sign = (horiz_move ? dx : dy);
4870   int step = sign * element_info[element].move_stepsize;
4871
4872   // special values for move stepsize for spring and things on conveyor belt
4873   if (horiz_move)
4874   {
4875     if (CAN_FALL(element) &&
4876         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4877       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4878     else if (element == EL_SPRING)
4879       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4880   }
4881
4882   return step;
4883 }
4884
4885 static int getElementMoveStepsize(int x, int y)
4886 {
4887   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4888 }
4889
4890 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4891 {
4892   if (player->GfxAction != action || player->GfxDir != dir)
4893   {
4894     player->GfxAction = action;
4895     player->GfxDir = dir;
4896     player->Frame = 0;
4897     player->StepFrame = 0;
4898   }
4899 }
4900
4901 static void ResetGfxFrame(int x, int y)
4902 {
4903   // profiling showed that "autotest" spends 10~20% of its time in this function
4904   if (DrawingDeactivatedField())
4905     return;
4906
4907   int element = Feld[x][y];
4908   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4909
4910   if (graphic_info[graphic].anim_global_sync)
4911     GfxFrame[x][y] = FrameCounter;
4912   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4913     GfxFrame[x][y] = CustomValue[x][y];
4914   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4915     GfxFrame[x][y] = element_info[element].collect_score;
4916   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4917     GfxFrame[x][y] = ChangeDelay[x][y];
4918 }
4919
4920 static void ResetGfxAnimation(int x, int y)
4921 {
4922   GfxAction[x][y] = ACTION_DEFAULT;
4923   GfxDir[x][y] = MovDir[x][y];
4924   GfxFrame[x][y] = 0;
4925
4926   ResetGfxFrame(x, y);
4927 }
4928
4929 static void ResetRandomAnimationValue(int x, int y)
4930 {
4931   GfxRandom[x][y] = INIT_GFX_RANDOM();
4932 }
4933
4934 static void InitMovingField(int x, int y, int direction)
4935 {
4936   int element = Feld[x][y];
4937   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4938   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4939   int newx = x + dx;
4940   int newy = y + dy;
4941   boolean is_moving_before, is_moving_after;
4942
4943   // check if element was/is moving or being moved before/after mode change
4944   is_moving_before = (WasJustMoving[x][y] != 0);
4945   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4946
4947   // reset animation only for moving elements which change direction of moving
4948   // or which just started or stopped moving
4949   // (else CEs with property "can move" / "not moving" are reset each frame)
4950   if (is_moving_before != is_moving_after ||
4951       direction != MovDir[x][y])
4952     ResetGfxAnimation(x, y);
4953
4954   MovDir[x][y] = direction;
4955   GfxDir[x][y] = direction;
4956
4957   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4958                      direction == MV_DOWN && CAN_FALL(element) ?
4959                      ACTION_FALLING : ACTION_MOVING);
4960
4961   // this is needed for CEs with property "can move" / "not moving"
4962
4963   if (is_moving_after)
4964   {
4965     if (Feld[newx][newy] == EL_EMPTY)
4966       Feld[newx][newy] = EL_BLOCKED;
4967
4968     MovDir[newx][newy] = MovDir[x][y];
4969
4970     CustomValue[newx][newy] = CustomValue[x][y];
4971
4972     GfxFrame[newx][newy] = GfxFrame[x][y];
4973     GfxRandom[newx][newy] = GfxRandom[x][y];
4974     GfxAction[newx][newy] = GfxAction[x][y];
4975     GfxDir[newx][newy] = GfxDir[x][y];
4976   }
4977 }
4978
4979 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4980 {
4981   int direction = MovDir[x][y];
4982   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4983   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4984
4985   *goes_to_x = newx;
4986   *goes_to_y = newy;
4987 }
4988
4989 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4990 {
4991   int oldx = x, oldy = y;
4992   int direction = MovDir[x][y];
4993
4994   if (direction == MV_LEFT)
4995     oldx++;
4996   else if (direction == MV_RIGHT)
4997     oldx--;
4998   else if (direction == MV_UP)
4999     oldy++;
5000   else if (direction == MV_DOWN)
5001     oldy--;
5002
5003   *comes_from_x = oldx;
5004   *comes_from_y = oldy;
5005 }
5006
5007 static int MovingOrBlocked2Element(int x, int y)
5008 {
5009   int element = Feld[x][y];
5010
5011   if (element == EL_BLOCKED)
5012   {
5013     int oldx, oldy;
5014
5015     Blocked2Moving(x, y, &oldx, &oldy);
5016     return Feld[oldx][oldy];
5017   }
5018   else
5019     return element;
5020 }
5021
5022 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5023 {
5024   // like MovingOrBlocked2Element(), but if element is moving
5025   // and (x,y) is the field the moving element is just leaving,
5026   // return EL_BLOCKED instead of the element value
5027   int element = Feld[x][y];
5028
5029   if (IS_MOVING(x, y))
5030   {
5031     if (element == EL_BLOCKED)
5032     {
5033       int oldx, oldy;
5034
5035       Blocked2Moving(x, y, &oldx, &oldy);
5036       return Feld[oldx][oldy];
5037     }
5038     else
5039       return EL_BLOCKED;
5040   }
5041   else
5042     return element;
5043 }
5044
5045 static void RemoveField(int x, int y)
5046 {
5047   Feld[x][y] = EL_EMPTY;
5048
5049   MovPos[x][y] = 0;
5050   MovDir[x][y] = 0;
5051   MovDelay[x][y] = 0;
5052
5053   CustomValue[x][y] = 0;
5054
5055   AmoebaNr[x][y] = 0;
5056   ChangeDelay[x][y] = 0;
5057   ChangePage[x][y] = -1;
5058   Pushed[x][y] = FALSE;
5059
5060   GfxElement[x][y] = EL_UNDEFINED;
5061   GfxAction[x][y] = ACTION_DEFAULT;
5062   GfxDir[x][y] = MV_NONE;
5063 }
5064
5065 static void RemoveMovingField(int x, int y)
5066 {
5067   int oldx = x, oldy = y, newx = x, newy = y;
5068   int element = Feld[x][y];
5069   int next_element = EL_UNDEFINED;
5070
5071   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5072     return;
5073
5074   if (IS_MOVING(x, y))
5075   {
5076     Moving2Blocked(x, y, &newx, &newy);
5077
5078     if (Feld[newx][newy] != EL_BLOCKED)
5079     {
5080       // element is moving, but target field is not free (blocked), but
5081       // already occupied by something different (example: acid pool);
5082       // in this case, only remove the moving field, but not the target
5083
5084       RemoveField(oldx, oldy);
5085
5086       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5087
5088       TEST_DrawLevelField(oldx, oldy);
5089
5090       return;
5091     }
5092   }
5093   else if (element == EL_BLOCKED)
5094   {
5095     Blocked2Moving(x, y, &oldx, &oldy);
5096     if (!IS_MOVING(oldx, oldy))
5097       return;
5098   }
5099
5100   if (element == EL_BLOCKED &&
5101       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5102        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5103        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5104        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5105        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5106        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5107     next_element = get_next_element(Feld[oldx][oldy]);
5108
5109   RemoveField(oldx, oldy);
5110   RemoveField(newx, newy);
5111
5112   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5113
5114   if (next_element != EL_UNDEFINED)
5115     Feld[oldx][oldy] = next_element;
5116
5117   TEST_DrawLevelField(oldx, oldy);
5118   TEST_DrawLevelField(newx, newy);
5119 }
5120
5121 void DrawDynamite(int x, int y)
5122 {
5123   int sx = SCREENX(x), sy = SCREENY(y);
5124   int graphic = el2img(Feld[x][y]);
5125   int frame;
5126
5127   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5128     return;
5129
5130   if (IS_WALKABLE_INSIDE(Back[x][y]))
5131     return;
5132
5133   if (Back[x][y])
5134     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5135   else if (Store[x][y])
5136     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5137
5138   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5139
5140   if (Back[x][y] || Store[x][y])
5141     DrawGraphicThruMask(sx, sy, graphic, frame);
5142   else
5143     DrawGraphic(sx, sy, graphic, frame);
5144 }
5145
5146 static void CheckDynamite(int x, int y)
5147 {
5148   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5149   {
5150     MovDelay[x][y]--;
5151
5152     if (MovDelay[x][y] != 0)
5153     {
5154       DrawDynamite(x, y);
5155       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5156
5157       return;
5158     }
5159   }
5160
5161   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5162
5163   Bang(x, y);
5164 }
5165
5166 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5167 {
5168   boolean num_checked_players = 0;
5169   int i;
5170
5171   for (i = 0; i < MAX_PLAYERS; i++)
5172   {
5173     if (stored_player[i].active)
5174     {
5175       int sx = stored_player[i].jx;
5176       int sy = stored_player[i].jy;
5177
5178       if (num_checked_players == 0)
5179       {
5180         *sx1 = *sx2 = sx;
5181         *sy1 = *sy2 = sy;
5182       }
5183       else
5184       {
5185         *sx1 = MIN(*sx1, sx);
5186         *sy1 = MIN(*sy1, sy);
5187         *sx2 = MAX(*sx2, sx);
5188         *sy2 = MAX(*sy2, sy);
5189       }
5190
5191       num_checked_players++;
5192     }
5193   }
5194 }
5195
5196 static boolean checkIfAllPlayersFitToScreen_RND(void)
5197 {
5198   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5199
5200   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5201
5202   return (sx2 - sx1 < SCR_FIELDX &&
5203           sy2 - sy1 < SCR_FIELDY);
5204 }
5205
5206 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5207 {
5208   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5209
5210   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5211
5212   *sx = (sx1 + sx2) / 2;
5213   *sy = (sy1 + sy2) / 2;
5214 }
5215
5216 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5217                                boolean center_screen, boolean quick_relocation)
5218 {
5219   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5220   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5221   boolean no_delay = (tape.warp_forward);
5222   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5223   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5224   int new_scroll_x, new_scroll_y;
5225
5226   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5227   {
5228     // case 1: quick relocation inside visible screen (without scrolling)
5229
5230     RedrawPlayfield();
5231
5232     return;
5233   }
5234
5235   if (!level.shifted_relocation || center_screen)
5236   {
5237     // relocation _with_ centering of screen
5238
5239     new_scroll_x = SCROLL_POSITION_X(x);
5240     new_scroll_y = SCROLL_POSITION_Y(y);
5241   }
5242   else
5243   {
5244     // relocation _without_ centering of screen
5245
5246     int center_scroll_x = SCROLL_POSITION_X(old_x);
5247     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5248     int offset_x = x + (scroll_x - center_scroll_x);
5249     int offset_y = y + (scroll_y - center_scroll_y);
5250
5251     // for new screen position, apply previous offset to center position
5252     new_scroll_x = SCROLL_POSITION_X(offset_x);
5253     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5254   }
5255
5256   if (quick_relocation)
5257   {
5258     // case 2: quick relocation (redraw without visible scrolling)
5259
5260     scroll_x = new_scroll_x;
5261     scroll_y = new_scroll_y;
5262
5263     RedrawPlayfield();
5264
5265     return;
5266   }
5267
5268   // case 3: visible relocation (with scrolling to new position)
5269
5270   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5271
5272   SetVideoFrameDelay(wait_delay_value);
5273
5274   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5275   {
5276     int dx = 0, dy = 0;
5277     int fx = FX, fy = FY;
5278
5279     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5280     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5281
5282     if (dx == 0 && dy == 0)             // no scrolling needed at all
5283       break;
5284
5285     scroll_x -= dx;
5286     scroll_y -= dy;
5287
5288     fx += dx * TILEX / 2;
5289     fy += dy * TILEY / 2;
5290
5291     ScrollLevel(dx, dy);
5292     DrawAllPlayers();
5293
5294     // scroll in two steps of half tile size to make things smoother
5295     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5296
5297     // scroll second step to align at full tile size
5298     BlitScreenToBitmap(window);
5299   }
5300
5301   DrawAllPlayers();
5302   BackToFront();
5303
5304   SetVideoFrameDelay(frame_delay_value_old);
5305 }
5306
5307 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5308 {
5309   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5310   int player_nr = GET_PLAYER_NR(el_player);
5311   struct PlayerInfo *player = &stored_player[player_nr];
5312   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5313   boolean no_delay = (tape.warp_forward);
5314   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5315   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5316   int old_jx = player->jx;
5317   int old_jy = player->jy;
5318   int old_element = Feld[old_jx][old_jy];
5319   int element = Feld[jx][jy];
5320   boolean player_relocated = (old_jx != jx || old_jy != jy);
5321
5322   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5323   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5324   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5325   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5326   int leave_side_horiz = move_dir_horiz;
5327   int leave_side_vert  = move_dir_vert;
5328   int enter_side = enter_side_horiz | enter_side_vert;
5329   int leave_side = leave_side_horiz | leave_side_vert;
5330
5331   if (player->buried)           // do not reanimate dead player
5332     return;
5333
5334   if (!player_relocated)        // no need to relocate the player
5335     return;
5336
5337   if (IS_PLAYER(jx, jy))        // player already placed at new position
5338   {
5339     RemoveField(jx, jy);        // temporarily remove newly placed player
5340     DrawLevelField(jx, jy);
5341   }
5342
5343   if (player->present)
5344   {
5345     while (player->MovPos)
5346     {
5347       ScrollPlayer(player, SCROLL_GO_ON);
5348       ScrollScreen(NULL, SCROLL_GO_ON);
5349
5350       AdvanceFrameAndPlayerCounters(player->index_nr);
5351
5352       DrawPlayer(player);
5353
5354       BackToFront_WithFrameDelay(wait_delay_value);
5355     }
5356
5357     DrawPlayer(player);         // needed here only to cleanup last field
5358     DrawLevelField(player->jx, player->jy);     // remove player graphic
5359
5360     player->is_moving = FALSE;
5361   }
5362
5363   if (IS_CUSTOM_ELEMENT(old_element))
5364     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5365                                CE_LEFT_BY_PLAYER,
5366                                player->index_bit, leave_side);
5367
5368   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5369                                       CE_PLAYER_LEAVES_X,
5370                                       player->index_bit, leave_side);
5371
5372   Feld[jx][jy] = el_player;
5373   InitPlayerField(jx, jy, el_player, TRUE);
5374
5375   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5376      possible that the relocation target field did not contain a player element,
5377      but a walkable element, to which the new player was relocated -- in this
5378      case, restore that (already initialized!) element on the player field */
5379   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5380   {
5381     Feld[jx][jy] = element;     // restore previously existing element
5382   }
5383
5384   // only visually relocate centered player
5385   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5386                      FALSE, level.instant_relocation);
5387
5388   TestIfPlayerTouchesBadThing(jx, jy);
5389   TestIfPlayerTouchesCustomElement(jx, jy);
5390
5391   if (IS_CUSTOM_ELEMENT(element))
5392     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5393                                player->index_bit, enter_side);
5394
5395   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5396                                       player->index_bit, enter_side);
5397
5398   if (player->is_switching)
5399   {
5400     /* ensure that relocation while still switching an element does not cause
5401        a new element to be treated as also switched directly after relocation
5402        (this is important for teleporter switches that teleport the player to
5403        a place where another teleporter switch is in the same direction, which
5404        would then incorrectly be treated as immediately switched before the
5405        direction key that caused the switch was released) */
5406
5407     player->switch_x += jx - old_jx;
5408     player->switch_y += jy - old_jy;
5409   }
5410 }
5411
5412 static void Explode(int ex, int ey, int phase, int mode)
5413 {
5414   int x, y;
5415   int last_phase;
5416   int border_element;
5417
5418   // !!! eliminate this variable !!!
5419   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5420
5421   if (game.explosions_delayed)
5422   {
5423     ExplodeField[ex][ey] = mode;
5424     return;
5425   }
5426
5427   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5428   {
5429     int center_element = Feld[ex][ey];
5430     int artwork_element, explosion_element;     // set these values later
5431
5432     // remove things displayed in background while burning dynamite
5433     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5434       Back[ex][ey] = 0;
5435
5436     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5437     {
5438       // put moving element to center field (and let it explode there)
5439       center_element = MovingOrBlocked2Element(ex, ey);
5440       RemoveMovingField(ex, ey);
5441       Feld[ex][ey] = center_element;
5442     }
5443
5444     // now "center_element" is finally determined -- set related values now
5445     artwork_element = center_element;           // for custom player artwork
5446     explosion_element = center_element;         // for custom player artwork
5447
5448     if (IS_PLAYER(ex, ey))
5449     {
5450       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5451
5452       artwork_element = stored_player[player_nr].artwork_element;
5453
5454       if (level.use_explosion_element[player_nr])
5455       {
5456         explosion_element = level.explosion_element[player_nr];
5457         artwork_element = explosion_element;
5458       }
5459     }
5460
5461     if (mode == EX_TYPE_NORMAL ||
5462         mode == EX_TYPE_CENTER ||
5463         mode == EX_TYPE_CROSS)
5464       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5465
5466     last_phase = element_info[explosion_element].explosion_delay + 1;
5467
5468     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5469     {
5470       int xx = x - ex + 1;
5471       int yy = y - ey + 1;
5472       int element;
5473
5474       if (!IN_LEV_FIELD(x, y) ||
5475           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5476           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5477         continue;
5478
5479       element = Feld[x][y];
5480
5481       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5482       {
5483         element = MovingOrBlocked2Element(x, y);
5484
5485         if (!IS_EXPLOSION_PROOF(element))
5486           RemoveMovingField(x, y);
5487       }
5488
5489       // indestructible elements can only explode in center (but not flames)
5490       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5491                                            mode == EX_TYPE_BORDER)) ||
5492           element == EL_FLAMES)
5493         continue;
5494
5495       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5496          behaviour, for example when touching a yamyam that explodes to rocks
5497          with active deadly shield, a rock is created under the player !!! */
5498       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5499 #if 0
5500       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5501           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5502            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5503 #else
5504       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5505 #endif
5506       {
5507         if (IS_ACTIVE_BOMB(element))
5508         {
5509           // re-activate things under the bomb like gate or penguin
5510           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5511           Back[x][y] = 0;
5512         }
5513
5514         continue;
5515       }
5516
5517       // save walkable background elements while explosion on same tile
5518       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5519           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5520         Back[x][y] = element;
5521
5522       // ignite explodable elements reached by other explosion
5523       if (element == EL_EXPLOSION)
5524         element = Store2[x][y];
5525
5526       if (AmoebaNr[x][y] &&
5527           (element == EL_AMOEBA_FULL ||
5528            element == EL_BD_AMOEBA ||
5529            element == EL_AMOEBA_GROWING))
5530       {
5531         AmoebaCnt[AmoebaNr[x][y]]--;
5532         AmoebaCnt2[AmoebaNr[x][y]]--;
5533       }
5534
5535       RemoveField(x, y);
5536
5537       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5538       {
5539         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5540
5541         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5542
5543         if (PLAYERINFO(ex, ey)->use_murphy)
5544           Store[x][y] = EL_EMPTY;
5545       }
5546
5547       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5548       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5549       else if (ELEM_IS_PLAYER(center_element))
5550         Store[x][y] = EL_EMPTY;
5551       else if (center_element == EL_YAMYAM)
5552         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5553       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5554         Store[x][y] = element_info[center_element].content.e[xx][yy];
5555 #if 1
5556       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5557       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5558       // otherwise) -- FIX THIS !!!
5559       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5560         Store[x][y] = element_info[element].content.e[1][1];
5561 #else
5562       else if (!CAN_EXPLODE(element))
5563         Store[x][y] = element_info[element].content.e[1][1];
5564 #endif
5565       else
5566         Store[x][y] = EL_EMPTY;
5567
5568       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5569           center_element == EL_AMOEBA_TO_DIAMOND)
5570         Store2[x][y] = element;
5571
5572       Feld[x][y] = EL_EXPLOSION;
5573       GfxElement[x][y] = artwork_element;
5574
5575       ExplodePhase[x][y] = 1;
5576       ExplodeDelay[x][y] = last_phase;
5577
5578       Stop[x][y] = TRUE;
5579     }
5580
5581     if (center_element == EL_YAMYAM)
5582       game.yamyam_content_nr =
5583         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5584
5585     return;
5586   }
5587
5588   if (Stop[ex][ey])
5589     return;
5590
5591   x = ex;
5592   y = ey;
5593
5594   if (phase == 1)
5595     GfxFrame[x][y] = 0;         // restart explosion animation
5596
5597   last_phase = ExplodeDelay[x][y];
5598
5599   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5600
5601   // this can happen if the player leaves an explosion just in time
5602   if (GfxElement[x][y] == EL_UNDEFINED)
5603     GfxElement[x][y] = EL_EMPTY;
5604
5605   border_element = Store2[x][y];
5606   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5607     border_element = StorePlayer[x][y];
5608
5609   if (phase == element_info[border_element].ignition_delay ||
5610       phase == last_phase)
5611   {
5612     boolean border_explosion = FALSE;
5613
5614     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5615         !PLAYER_EXPLOSION_PROTECTED(x, y))
5616     {
5617       KillPlayerUnlessExplosionProtected(x, y);
5618       border_explosion = TRUE;
5619     }
5620     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5621     {
5622       Feld[x][y] = Store2[x][y];
5623       Store2[x][y] = 0;
5624       Bang(x, y);
5625       border_explosion = TRUE;
5626     }
5627     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5628     {
5629       AmoebeUmwandeln(x, y);
5630       Store2[x][y] = 0;
5631       border_explosion = TRUE;
5632     }
5633
5634     // if an element just explodes due to another explosion (chain-reaction),
5635     // do not immediately end the new explosion when it was the last frame of
5636     // the explosion (as it would be done in the following "if"-statement!)
5637     if (border_explosion && phase == last_phase)
5638       return;
5639   }
5640
5641   if (phase == last_phase)
5642   {
5643     int element;
5644
5645     element = Feld[x][y] = Store[x][y];
5646     Store[x][y] = Store2[x][y] = 0;
5647     GfxElement[x][y] = EL_UNDEFINED;
5648
5649     // player can escape from explosions and might therefore be still alive
5650     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5651         element <= EL_PLAYER_IS_EXPLODING_4)
5652     {
5653       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5654       int explosion_element = EL_PLAYER_1 + player_nr;
5655       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5656       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5657
5658       if (level.use_explosion_element[player_nr])
5659         explosion_element = level.explosion_element[player_nr];
5660
5661       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5662                     element_info[explosion_element].content.e[xx][yy]);
5663     }
5664
5665     // restore probably existing indestructible background element
5666     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5667       element = Feld[x][y] = Back[x][y];
5668     Back[x][y] = 0;
5669
5670     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5671     GfxDir[x][y] = MV_NONE;
5672     ChangeDelay[x][y] = 0;
5673     ChangePage[x][y] = -1;
5674
5675     CustomValue[x][y] = 0;
5676
5677     InitField_WithBug2(x, y, FALSE);
5678
5679     TEST_DrawLevelField(x, y);
5680
5681     TestIfElementTouchesCustomElement(x, y);
5682
5683     if (GFX_CRUMBLED(element))
5684       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5685
5686     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5687       StorePlayer[x][y] = 0;
5688
5689     if (ELEM_IS_PLAYER(element))
5690       RelocatePlayer(x, y, element);
5691   }
5692   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5693   {
5694     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5695     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5696
5697     if (phase == delay)
5698       TEST_DrawLevelFieldCrumbled(x, y);
5699
5700     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5701     {
5702       DrawLevelElement(x, y, Back[x][y]);
5703       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5704     }
5705     else if (IS_WALKABLE_UNDER(Back[x][y]))
5706     {
5707       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5708       DrawLevelElementThruMask(x, y, Back[x][y]);
5709     }
5710     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5711       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5712   }
5713 }
5714
5715 static void DynaExplode(int ex, int ey)
5716 {
5717   int i, j;
5718   int dynabomb_element = Feld[ex][ey];
5719   int dynabomb_size = 1;
5720   boolean dynabomb_xl = FALSE;
5721   struct PlayerInfo *player;
5722   static int xy[4][2] =
5723   {
5724     { 0, -1 },
5725     { -1, 0 },
5726     { +1, 0 },
5727     { 0, +1 }
5728   };
5729
5730   if (IS_ACTIVE_BOMB(dynabomb_element))
5731   {
5732     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5733     dynabomb_size = player->dynabomb_size;
5734     dynabomb_xl = player->dynabomb_xl;
5735     player->dynabombs_left++;
5736   }
5737
5738   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5739
5740   for (i = 0; i < NUM_DIRECTIONS; i++)
5741   {
5742     for (j = 1; j <= dynabomb_size; j++)
5743     {
5744       int x = ex + j * xy[i][0];
5745       int y = ey + j * xy[i][1];
5746       int element;
5747
5748       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5749         break;
5750
5751       element = Feld[x][y];
5752
5753       // do not restart explosions of fields with active bombs
5754       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5755         continue;
5756
5757       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5758
5759       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5760           !IS_DIGGABLE(element) && !dynabomb_xl)
5761         break;
5762     }
5763   }
5764 }
5765
5766 void Bang(int x, int y)
5767 {
5768   int element = MovingOrBlocked2Element(x, y);
5769   int explosion_type = EX_TYPE_NORMAL;
5770
5771   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5772   {
5773     struct PlayerInfo *player = PLAYERINFO(x, y);
5774
5775     element = Feld[x][y] = player->initial_element;
5776
5777     if (level.use_explosion_element[player->index_nr])
5778     {
5779       int explosion_element = level.explosion_element[player->index_nr];
5780
5781       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5782         explosion_type = EX_TYPE_CROSS;
5783       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5784         explosion_type = EX_TYPE_CENTER;
5785     }
5786   }
5787
5788   switch (element)
5789   {
5790     case EL_BUG:
5791     case EL_SPACESHIP:
5792     case EL_BD_BUTTERFLY:
5793     case EL_BD_FIREFLY:
5794     case EL_YAMYAM:
5795     case EL_DARK_YAMYAM:
5796     case EL_ROBOT:
5797     case EL_PACMAN:
5798     case EL_MOLE:
5799       RaiseScoreElement(element);
5800       break;
5801
5802     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5803     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5804     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5805     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5806     case EL_DYNABOMB_INCREASE_NUMBER:
5807     case EL_DYNABOMB_INCREASE_SIZE:
5808     case EL_DYNABOMB_INCREASE_POWER:
5809       explosion_type = EX_TYPE_DYNA;
5810       break;
5811
5812     case EL_DC_LANDMINE:
5813       explosion_type = EX_TYPE_CENTER;
5814       break;
5815
5816     case EL_PENGUIN:
5817     case EL_LAMP:
5818     case EL_LAMP_ACTIVE:
5819     case EL_AMOEBA_TO_DIAMOND:
5820       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5821         explosion_type = EX_TYPE_CENTER;
5822       break;
5823
5824     default:
5825       if (element_info[element].explosion_type == EXPLODES_CROSS)
5826         explosion_type = EX_TYPE_CROSS;
5827       else if (element_info[element].explosion_type == EXPLODES_1X1)
5828         explosion_type = EX_TYPE_CENTER;
5829       break;
5830   }
5831
5832   if (explosion_type == EX_TYPE_DYNA)
5833     DynaExplode(x, y);
5834   else
5835     Explode(x, y, EX_PHASE_START, explosion_type);
5836
5837   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5838 }
5839
5840 static void SplashAcid(int x, int y)
5841 {
5842   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5843       (!IN_LEV_FIELD(x - 1, y - 2) ||
5844        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5845     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5846
5847   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5848       (!IN_LEV_FIELD(x + 1, y - 2) ||
5849        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5850     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5851
5852   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5853 }
5854
5855 static void InitBeltMovement(void)
5856 {
5857   static int belt_base_element[4] =
5858   {
5859     EL_CONVEYOR_BELT_1_LEFT,
5860     EL_CONVEYOR_BELT_2_LEFT,
5861     EL_CONVEYOR_BELT_3_LEFT,
5862     EL_CONVEYOR_BELT_4_LEFT
5863   };
5864   static int belt_base_active_element[4] =
5865   {
5866     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5867     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5868     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5869     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5870   };
5871
5872   int x, y, i, j;
5873
5874   // set frame order for belt animation graphic according to belt direction
5875   for (i = 0; i < NUM_BELTS; i++)
5876   {
5877     int belt_nr = i;
5878
5879     for (j = 0; j < NUM_BELT_PARTS; j++)
5880     {
5881       int element = belt_base_active_element[belt_nr] + j;
5882       int graphic_1 = el2img(element);
5883       int graphic_2 = el2panelimg(element);
5884
5885       if (game.belt_dir[i] == MV_LEFT)
5886       {
5887         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5888         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5889       }
5890       else
5891       {
5892         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5893         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5894       }
5895     }
5896   }
5897
5898   SCAN_PLAYFIELD(x, y)
5899   {
5900     int element = Feld[x][y];
5901
5902     for (i = 0; i < NUM_BELTS; i++)
5903     {
5904       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5905       {
5906         int e_belt_nr = getBeltNrFromBeltElement(element);
5907         int belt_nr = i;
5908
5909         if (e_belt_nr == belt_nr)
5910         {
5911           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5912
5913           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5914         }
5915       }
5916     }
5917   }
5918 }
5919
5920 static void ToggleBeltSwitch(int x, int y)
5921 {
5922   static int belt_base_element[4] =
5923   {
5924     EL_CONVEYOR_BELT_1_LEFT,
5925     EL_CONVEYOR_BELT_2_LEFT,
5926     EL_CONVEYOR_BELT_3_LEFT,
5927     EL_CONVEYOR_BELT_4_LEFT
5928   };
5929   static int belt_base_active_element[4] =
5930   {
5931     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5932     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5933     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5934     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5935   };
5936   static int belt_base_switch_element[4] =
5937   {
5938     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5939     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5940     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5941     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5942   };
5943   static int belt_move_dir[4] =
5944   {
5945     MV_LEFT,
5946     MV_NONE,
5947     MV_RIGHT,
5948     MV_NONE,
5949   };
5950
5951   int element = Feld[x][y];
5952   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5953   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5954   int belt_dir = belt_move_dir[belt_dir_nr];
5955   int xx, yy, i;
5956
5957   if (!IS_BELT_SWITCH(element))
5958     return;
5959
5960   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5961   game.belt_dir[belt_nr] = belt_dir;
5962
5963   if (belt_dir_nr == 3)
5964     belt_dir_nr = 1;
5965
5966   // set frame order for belt animation graphic according to belt direction
5967   for (i = 0; i < NUM_BELT_PARTS; i++)
5968   {
5969     int element = belt_base_active_element[belt_nr] + i;
5970     int graphic_1 = el2img(element);
5971     int graphic_2 = el2panelimg(element);
5972
5973     if (belt_dir == MV_LEFT)
5974     {
5975       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5976       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5977     }
5978     else
5979     {
5980       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5981       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5982     }
5983   }
5984
5985   SCAN_PLAYFIELD(xx, yy)
5986   {
5987     int element = Feld[xx][yy];
5988
5989     if (IS_BELT_SWITCH(element))
5990     {
5991       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5992
5993       if (e_belt_nr == belt_nr)
5994       {
5995         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5996         TEST_DrawLevelField(xx, yy);
5997       }
5998     }
5999     else if (IS_BELT(element) && belt_dir != MV_NONE)
6000     {
6001       int e_belt_nr = getBeltNrFromBeltElement(element);
6002
6003       if (e_belt_nr == belt_nr)
6004       {
6005         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6006
6007         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6008         TEST_DrawLevelField(xx, yy);
6009       }
6010     }
6011     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6012     {
6013       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6014
6015       if (e_belt_nr == belt_nr)
6016       {
6017         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6018
6019         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6020         TEST_DrawLevelField(xx, yy);
6021       }
6022     }
6023   }
6024 }
6025
6026 static void ToggleSwitchgateSwitch(int x, int y)
6027 {
6028   int xx, yy;
6029
6030   game.switchgate_pos = !game.switchgate_pos;
6031
6032   SCAN_PLAYFIELD(xx, yy)
6033   {
6034     int element = Feld[xx][yy];
6035
6036     if (element == EL_SWITCHGATE_SWITCH_UP)
6037     {
6038       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6039       TEST_DrawLevelField(xx, yy);
6040     }
6041     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6042     {
6043       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6044       TEST_DrawLevelField(xx, yy);
6045     }
6046     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6047     {
6048       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6049       TEST_DrawLevelField(xx, yy);
6050     }
6051     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6052     {
6053       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6054       TEST_DrawLevelField(xx, yy);
6055     }
6056     else if (element == EL_SWITCHGATE_OPEN ||
6057              element == EL_SWITCHGATE_OPENING)
6058     {
6059       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6060
6061       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6062     }
6063     else if (element == EL_SWITCHGATE_CLOSED ||
6064              element == EL_SWITCHGATE_CLOSING)
6065     {
6066       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6067
6068       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6069     }
6070   }
6071 }
6072
6073 static int getInvisibleActiveFromInvisibleElement(int element)
6074 {
6075   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6076           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6077           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6078           element);
6079 }
6080
6081 static int getInvisibleFromInvisibleActiveElement(int element)
6082 {
6083   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6084           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6085           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6086           element);
6087 }
6088
6089 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6090 {
6091   int x, y;
6092
6093   SCAN_PLAYFIELD(x, y)
6094   {
6095     int element = Feld[x][y];
6096
6097     if (element == EL_LIGHT_SWITCH &&
6098         game.light_time_left > 0)
6099     {
6100       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6101       TEST_DrawLevelField(x, y);
6102     }
6103     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6104              game.light_time_left == 0)
6105     {
6106       Feld[x][y] = EL_LIGHT_SWITCH;
6107       TEST_DrawLevelField(x, y);
6108     }
6109     else if (element == EL_EMC_DRIPPER &&
6110              game.light_time_left > 0)
6111     {
6112       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6113       TEST_DrawLevelField(x, y);
6114     }
6115     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6116              game.light_time_left == 0)
6117     {
6118       Feld[x][y] = EL_EMC_DRIPPER;
6119       TEST_DrawLevelField(x, y);
6120     }
6121     else if (element == EL_INVISIBLE_STEELWALL ||
6122              element == EL_INVISIBLE_WALL ||
6123              element == EL_INVISIBLE_SAND)
6124     {
6125       if (game.light_time_left > 0)
6126         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6127
6128       TEST_DrawLevelField(x, y);
6129
6130       // uncrumble neighbour fields, if needed
6131       if (element == EL_INVISIBLE_SAND)
6132         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6133     }
6134     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6135              element == EL_INVISIBLE_WALL_ACTIVE ||
6136              element == EL_INVISIBLE_SAND_ACTIVE)
6137     {
6138       if (game.light_time_left == 0)
6139         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6140
6141       TEST_DrawLevelField(x, y);
6142
6143       // re-crumble neighbour fields, if needed
6144       if (element == EL_INVISIBLE_SAND)
6145         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6146     }
6147   }
6148 }
6149
6150 static void RedrawAllInvisibleElementsForLenses(void)
6151 {
6152   int x, y;
6153
6154   SCAN_PLAYFIELD(x, y)
6155   {
6156     int element = Feld[x][y];
6157
6158     if (element == EL_EMC_DRIPPER &&
6159         game.lenses_time_left > 0)
6160     {
6161       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6162       TEST_DrawLevelField(x, y);
6163     }
6164     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6165              game.lenses_time_left == 0)
6166     {
6167       Feld[x][y] = EL_EMC_DRIPPER;
6168       TEST_DrawLevelField(x, y);
6169     }
6170     else if (element == EL_INVISIBLE_STEELWALL ||
6171              element == EL_INVISIBLE_WALL ||
6172              element == EL_INVISIBLE_SAND)
6173     {
6174       if (game.lenses_time_left > 0)
6175         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6176
6177       TEST_DrawLevelField(x, y);
6178
6179       // uncrumble neighbour fields, if needed
6180       if (element == EL_INVISIBLE_SAND)
6181         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6182     }
6183     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6184              element == EL_INVISIBLE_WALL_ACTIVE ||
6185              element == EL_INVISIBLE_SAND_ACTIVE)
6186     {
6187       if (game.lenses_time_left == 0)
6188         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6189
6190       TEST_DrawLevelField(x, y);
6191
6192       // re-crumble neighbour fields, if needed
6193       if (element == EL_INVISIBLE_SAND)
6194         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6195     }
6196   }
6197 }
6198
6199 static void RedrawAllInvisibleElementsForMagnifier(void)
6200 {
6201   int x, y;
6202
6203   SCAN_PLAYFIELD(x, y)
6204   {
6205     int element = Feld[x][y];
6206
6207     if (element == EL_EMC_FAKE_GRASS &&
6208         game.magnify_time_left > 0)
6209     {
6210       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6211       TEST_DrawLevelField(x, y);
6212     }
6213     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6214              game.magnify_time_left == 0)
6215     {
6216       Feld[x][y] = EL_EMC_FAKE_GRASS;
6217       TEST_DrawLevelField(x, y);
6218     }
6219     else if (IS_GATE_GRAY(element) &&
6220              game.magnify_time_left > 0)
6221     {
6222       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6223                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6224                     IS_EM_GATE_GRAY(element) ?
6225                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6226                     IS_EMC_GATE_GRAY(element) ?
6227                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6228                     IS_DC_GATE_GRAY(element) ?
6229                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6230                     element);
6231       TEST_DrawLevelField(x, y);
6232     }
6233     else if (IS_GATE_GRAY_ACTIVE(element) &&
6234              game.magnify_time_left == 0)
6235     {
6236       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6237                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6238                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6239                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6240                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6241                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6242                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6243                     EL_DC_GATE_WHITE_GRAY :
6244                     element);
6245       TEST_DrawLevelField(x, y);
6246     }
6247   }
6248 }
6249
6250 static void ToggleLightSwitch(int x, int y)
6251 {
6252   int element = Feld[x][y];
6253
6254   game.light_time_left =
6255     (element == EL_LIGHT_SWITCH ?
6256      level.time_light * FRAMES_PER_SECOND : 0);
6257
6258   RedrawAllLightSwitchesAndInvisibleElements();
6259 }
6260
6261 static void ActivateTimegateSwitch(int x, int y)
6262 {
6263   int xx, yy;
6264
6265   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6266
6267   SCAN_PLAYFIELD(xx, yy)
6268   {
6269     int element = Feld[xx][yy];
6270
6271     if (element == EL_TIMEGATE_CLOSED ||
6272         element == EL_TIMEGATE_CLOSING)
6273     {
6274       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6275       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6276     }
6277
6278     /*
6279     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6280     {
6281       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6282       TEST_DrawLevelField(xx, yy);
6283     }
6284     */
6285
6286   }
6287
6288   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6289                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6290 }
6291
6292 static void Impact(int x, int y)
6293 {
6294   boolean last_line = (y == lev_fieldy - 1);
6295   boolean object_hit = FALSE;
6296   boolean impact = (last_line || object_hit);
6297   int element = Feld[x][y];
6298   int smashed = EL_STEELWALL;
6299
6300   if (!last_line)       // check if element below was hit
6301   {
6302     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6303       return;
6304
6305     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6306                                          MovDir[x][y + 1] != MV_DOWN ||
6307                                          MovPos[x][y + 1] <= TILEY / 2));
6308
6309     // do not smash moving elements that left the smashed field in time
6310     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6311         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6312       object_hit = FALSE;
6313
6314 #if USE_QUICKSAND_IMPACT_BUGFIX
6315     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6316     {
6317       RemoveMovingField(x, y + 1);
6318       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6319       Feld[x][y + 2] = EL_ROCK;
6320       TEST_DrawLevelField(x, y + 2);
6321
6322       object_hit = TRUE;
6323     }
6324
6325     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6326     {
6327       RemoveMovingField(x, y + 1);
6328       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6329       Feld[x][y + 2] = EL_ROCK;
6330       TEST_DrawLevelField(x, y + 2);
6331
6332       object_hit = TRUE;
6333     }
6334 #endif
6335
6336     if (object_hit)
6337       smashed = MovingOrBlocked2Element(x, y + 1);
6338
6339     impact = (last_line || object_hit);
6340   }
6341
6342   if (!last_line && smashed == EL_ACID) // element falls into acid
6343   {
6344     SplashAcid(x, y + 1);
6345     return;
6346   }
6347
6348   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6349   // only reset graphic animation if graphic really changes after impact
6350   if (impact &&
6351       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6352   {
6353     ResetGfxAnimation(x, y);
6354     TEST_DrawLevelField(x, y);
6355   }
6356
6357   if (impact && CAN_EXPLODE_IMPACT(element))
6358   {
6359     Bang(x, y);
6360     return;
6361   }
6362   else if (impact && element == EL_PEARL &&
6363            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6364   {
6365     ResetGfxAnimation(x, y);
6366
6367     Feld[x][y] = EL_PEARL_BREAKING;
6368     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6369     return;
6370   }
6371   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6372   {
6373     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6374
6375     return;
6376   }
6377
6378   if (impact && element == EL_AMOEBA_DROP)
6379   {
6380     if (object_hit && IS_PLAYER(x, y + 1))
6381       KillPlayerUnlessEnemyProtected(x, y + 1);
6382     else if (object_hit && smashed == EL_PENGUIN)
6383       Bang(x, y + 1);
6384     else
6385     {
6386       Feld[x][y] = EL_AMOEBA_GROWING;
6387       Store[x][y] = EL_AMOEBA_WET;
6388
6389       ResetRandomAnimationValue(x, y);
6390     }
6391     return;
6392   }
6393
6394   if (object_hit)               // check which object was hit
6395   {
6396     if ((CAN_PASS_MAGIC_WALL(element) && 
6397          (smashed == EL_MAGIC_WALL ||
6398           smashed == EL_BD_MAGIC_WALL)) ||
6399         (CAN_PASS_DC_MAGIC_WALL(element) &&
6400          smashed == EL_DC_MAGIC_WALL))
6401     {
6402       int xx, yy;
6403       int activated_magic_wall =
6404         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6405          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6406          EL_DC_MAGIC_WALL_ACTIVE);
6407
6408       // activate magic wall / mill
6409       SCAN_PLAYFIELD(xx, yy)
6410       {
6411         if (Feld[xx][yy] == smashed)
6412           Feld[xx][yy] = activated_magic_wall;
6413       }
6414
6415       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6416       game.magic_wall_active = TRUE;
6417
6418       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6419                             SND_MAGIC_WALL_ACTIVATING :
6420                             smashed == EL_BD_MAGIC_WALL ?
6421                             SND_BD_MAGIC_WALL_ACTIVATING :
6422                             SND_DC_MAGIC_WALL_ACTIVATING));
6423     }
6424
6425     if (IS_PLAYER(x, y + 1))
6426     {
6427       if (CAN_SMASH_PLAYER(element))
6428       {
6429         KillPlayerUnlessEnemyProtected(x, y + 1);
6430         return;
6431       }
6432     }
6433     else if (smashed == EL_PENGUIN)
6434     {
6435       if (CAN_SMASH_PLAYER(element))
6436       {
6437         Bang(x, y + 1);
6438         return;
6439       }
6440     }
6441     else if (element == EL_BD_DIAMOND)
6442     {
6443       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6444       {
6445         Bang(x, y + 1);
6446         return;
6447       }
6448     }
6449     else if (((element == EL_SP_INFOTRON ||
6450                element == EL_SP_ZONK) &&
6451               (smashed == EL_SP_SNIKSNAK ||
6452                smashed == EL_SP_ELECTRON ||
6453                smashed == EL_SP_DISK_ORANGE)) ||
6454              (element == EL_SP_INFOTRON &&
6455               smashed == EL_SP_DISK_YELLOW))
6456     {
6457       Bang(x, y + 1);
6458       return;
6459     }
6460     else if (CAN_SMASH_EVERYTHING(element))
6461     {
6462       if (IS_CLASSIC_ENEMY(smashed) ||
6463           CAN_EXPLODE_SMASHED(smashed))
6464       {
6465         Bang(x, y + 1);
6466         return;
6467       }
6468       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6469       {
6470         if (smashed == EL_LAMP ||
6471             smashed == EL_LAMP_ACTIVE)
6472         {
6473           Bang(x, y + 1);
6474           return;
6475         }
6476         else if (smashed == EL_NUT)
6477         {
6478           Feld[x][y + 1] = EL_NUT_BREAKING;
6479           PlayLevelSound(x, y, SND_NUT_BREAKING);
6480           RaiseScoreElement(EL_NUT);
6481           return;
6482         }
6483         else if (smashed == EL_PEARL)
6484         {
6485           ResetGfxAnimation(x, y);
6486
6487           Feld[x][y + 1] = EL_PEARL_BREAKING;
6488           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6489           return;
6490         }
6491         else if (smashed == EL_DIAMOND)
6492         {
6493           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6494           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6495           return;
6496         }
6497         else if (IS_BELT_SWITCH(smashed))
6498         {
6499           ToggleBeltSwitch(x, y + 1);
6500         }
6501         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6502                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6503                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6504                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6505         {
6506           ToggleSwitchgateSwitch(x, y + 1);
6507         }
6508         else if (smashed == EL_LIGHT_SWITCH ||
6509                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6510         {
6511           ToggleLightSwitch(x, y + 1);
6512         }
6513         else
6514         {
6515           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6516
6517           CheckElementChangeBySide(x, y + 1, smashed, element,
6518                                    CE_SWITCHED, CH_SIDE_TOP);
6519           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6520                                             CH_SIDE_TOP);
6521         }
6522       }
6523       else
6524       {
6525         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6526       }
6527     }
6528   }
6529
6530   // play sound of magic wall / mill
6531   if (!last_line &&
6532       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6533        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6534        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6535   {
6536     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6537       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6538     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6539       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6540     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6541       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6542
6543     return;
6544   }
6545
6546   // play sound of object that hits the ground
6547   if (last_line || object_hit)
6548     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6549 }
6550
6551 static void TurnRoundExt(int x, int y)
6552 {
6553   static struct
6554   {
6555     int dx, dy;
6556   } move_xy[] =
6557   {
6558     {  0,  0 },
6559     { -1,  0 },
6560     { +1,  0 },
6561     {  0,  0 },
6562     {  0, -1 },
6563     {  0,  0 }, { 0, 0 }, { 0, 0 },
6564     {  0, +1 }
6565   };
6566   static struct
6567   {
6568     int left, right, back;
6569   } turn[] =
6570   {
6571     { 0,        0,              0        },
6572     { MV_DOWN,  MV_UP,          MV_RIGHT },
6573     { MV_UP,    MV_DOWN,        MV_LEFT  },
6574     { 0,        0,              0        },
6575     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6576     { 0,        0,              0        },
6577     { 0,        0,              0        },
6578     { 0,        0,              0        },
6579     { MV_RIGHT, MV_LEFT,        MV_UP    }
6580   };
6581
6582   int element = Feld[x][y];
6583   int move_pattern = element_info[element].move_pattern;
6584
6585   int old_move_dir = MovDir[x][y];
6586   int left_dir  = turn[old_move_dir].left;
6587   int right_dir = turn[old_move_dir].right;
6588   int back_dir  = turn[old_move_dir].back;
6589
6590   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6591   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6592   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6593   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6594
6595   int left_x  = x + left_dx,  left_y  = y + left_dy;
6596   int right_x = x + right_dx, right_y = y + right_dy;
6597   int move_x  = x + move_dx,  move_y  = y + move_dy;
6598
6599   int xx, yy;
6600
6601   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6602   {
6603     TestIfBadThingTouchesOtherBadThing(x, y);
6604
6605     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6606       MovDir[x][y] = right_dir;
6607     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6608       MovDir[x][y] = left_dir;
6609
6610     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6611       MovDelay[x][y] = 9;
6612     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6613       MovDelay[x][y] = 1;
6614   }
6615   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6616   {
6617     TestIfBadThingTouchesOtherBadThing(x, y);
6618
6619     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6620       MovDir[x][y] = left_dir;
6621     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6622       MovDir[x][y] = right_dir;
6623
6624     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6625       MovDelay[x][y] = 9;
6626     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6627       MovDelay[x][y] = 1;
6628   }
6629   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6630   {
6631     TestIfBadThingTouchesOtherBadThing(x, y);
6632
6633     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6634       MovDir[x][y] = left_dir;
6635     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6636       MovDir[x][y] = right_dir;
6637
6638     if (MovDir[x][y] != old_move_dir)
6639       MovDelay[x][y] = 9;
6640   }
6641   else if (element == EL_YAMYAM)
6642   {
6643     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6644     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6645
6646     if (can_turn_left && can_turn_right)
6647       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6648     else if (can_turn_left)
6649       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6650     else if (can_turn_right)
6651       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6652     else
6653       MovDir[x][y] = back_dir;
6654
6655     MovDelay[x][y] = 16 + 16 * RND(3);
6656   }
6657   else if (element == EL_DARK_YAMYAM)
6658   {
6659     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6660                                                          left_x, left_y);
6661     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6662                                                          right_x, right_y);
6663
6664     if (can_turn_left && can_turn_right)
6665       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6666     else if (can_turn_left)
6667       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6668     else if (can_turn_right)
6669       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6670     else
6671       MovDir[x][y] = back_dir;
6672
6673     MovDelay[x][y] = 16 + 16 * RND(3);
6674   }
6675   else if (element == EL_PACMAN)
6676   {
6677     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6678     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6679
6680     if (can_turn_left && can_turn_right)
6681       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6682     else if (can_turn_left)
6683       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6684     else if (can_turn_right)
6685       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6686     else
6687       MovDir[x][y] = back_dir;
6688
6689     MovDelay[x][y] = 6 + RND(40);
6690   }
6691   else if (element == EL_PIG)
6692   {
6693     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6694     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6695     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6696     boolean should_turn_left, should_turn_right, should_move_on;
6697     int rnd_value = 24;
6698     int rnd = RND(rnd_value);
6699
6700     should_turn_left = (can_turn_left &&
6701                         (!can_move_on ||
6702                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6703                                                    y + back_dy + left_dy)));
6704     should_turn_right = (can_turn_right &&
6705                          (!can_move_on ||
6706                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6707                                                     y + back_dy + right_dy)));
6708     should_move_on = (can_move_on &&
6709                       (!can_turn_left ||
6710                        !can_turn_right ||
6711                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6712                                                  y + move_dy + left_dy) ||
6713                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6714                                                  y + move_dy + right_dy)));
6715
6716     if (should_turn_left || should_turn_right || should_move_on)
6717     {
6718       if (should_turn_left && should_turn_right && should_move_on)
6719         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6720                         rnd < 2 * rnd_value / 3 ? right_dir :
6721                         old_move_dir);
6722       else if (should_turn_left && should_turn_right)
6723         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6724       else if (should_turn_left && should_move_on)
6725         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6726       else if (should_turn_right && should_move_on)
6727         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6728       else if (should_turn_left)
6729         MovDir[x][y] = left_dir;
6730       else if (should_turn_right)
6731         MovDir[x][y] = right_dir;
6732       else if (should_move_on)
6733         MovDir[x][y] = old_move_dir;
6734     }
6735     else if (can_move_on && rnd > rnd_value / 8)
6736       MovDir[x][y] = old_move_dir;
6737     else if (can_turn_left && can_turn_right)
6738       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6739     else if (can_turn_left && rnd > rnd_value / 8)
6740       MovDir[x][y] = left_dir;
6741     else if (can_turn_right && rnd > rnd_value/8)
6742       MovDir[x][y] = right_dir;
6743     else
6744       MovDir[x][y] = back_dir;
6745
6746     xx = x + move_xy[MovDir[x][y]].dx;
6747     yy = y + move_xy[MovDir[x][y]].dy;
6748
6749     if (!IN_LEV_FIELD(xx, yy) ||
6750         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6751       MovDir[x][y] = old_move_dir;
6752
6753     MovDelay[x][y] = 0;
6754   }
6755   else if (element == EL_DRAGON)
6756   {
6757     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6758     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6759     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6760     int rnd_value = 24;
6761     int rnd = RND(rnd_value);
6762
6763     if (can_move_on && rnd > rnd_value / 8)
6764       MovDir[x][y] = old_move_dir;
6765     else if (can_turn_left && can_turn_right)
6766       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6767     else if (can_turn_left && rnd > rnd_value / 8)
6768       MovDir[x][y] = left_dir;
6769     else if (can_turn_right && rnd > rnd_value / 8)
6770       MovDir[x][y] = right_dir;
6771     else
6772       MovDir[x][y] = back_dir;
6773
6774     xx = x + move_xy[MovDir[x][y]].dx;
6775     yy = y + move_xy[MovDir[x][y]].dy;
6776
6777     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6778       MovDir[x][y] = old_move_dir;
6779
6780     MovDelay[x][y] = 0;
6781   }
6782   else if (element == EL_MOLE)
6783   {
6784     boolean can_move_on =
6785       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6786                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6787                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6788     if (!can_move_on)
6789     {
6790       boolean can_turn_left =
6791         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6792                               IS_AMOEBOID(Feld[left_x][left_y])));
6793
6794       boolean can_turn_right =
6795         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6796                               IS_AMOEBOID(Feld[right_x][right_y])));
6797
6798       if (can_turn_left && can_turn_right)
6799         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6800       else if (can_turn_left)
6801         MovDir[x][y] = left_dir;
6802       else
6803         MovDir[x][y] = right_dir;
6804     }
6805
6806     if (MovDir[x][y] != old_move_dir)
6807       MovDelay[x][y] = 9;
6808   }
6809   else if (element == EL_BALLOON)
6810   {
6811     MovDir[x][y] = game.wind_direction;
6812     MovDelay[x][y] = 0;
6813   }
6814   else if (element == EL_SPRING)
6815   {
6816     if (MovDir[x][y] & MV_HORIZONTAL)
6817     {
6818       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6819           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6820       {
6821         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6822         ResetGfxAnimation(move_x, move_y);
6823         TEST_DrawLevelField(move_x, move_y);
6824
6825         MovDir[x][y] = back_dir;
6826       }
6827       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6828                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6829         MovDir[x][y] = MV_NONE;
6830     }
6831
6832     MovDelay[x][y] = 0;
6833   }
6834   else if (element == EL_ROBOT ||
6835            element == EL_SATELLITE ||
6836            element == EL_PENGUIN ||
6837            element == EL_EMC_ANDROID)
6838   {
6839     int attr_x = -1, attr_y = -1;
6840
6841     if (game.all_players_gone)
6842     {
6843       attr_x = game.exit_x;
6844       attr_y = game.exit_y;
6845     }
6846     else
6847     {
6848       int i;
6849
6850       for (i = 0; i < MAX_PLAYERS; i++)
6851       {
6852         struct PlayerInfo *player = &stored_player[i];
6853         int jx = player->jx, jy = player->jy;
6854
6855         if (!player->active)
6856           continue;
6857
6858         if (attr_x == -1 ||
6859             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6860         {
6861           attr_x = jx;
6862           attr_y = jy;
6863         }
6864       }
6865     }
6866
6867     if (element == EL_ROBOT &&
6868         game.robot_wheel_x >= 0 &&
6869         game.robot_wheel_y >= 0 &&
6870         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6871          game.engine_version < VERSION_IDENT(3,1,0,0)))
6872     {
6873       attr_x = game.robot_wheel_x;
6874       attr_y = game.robot_wheel_y;
6875     }
6876
6877     if (element == EL_PENGUIN)
6878     {
6879       int i;
6880       static int xy[4][2] =
6881       {
6882         { 0, -1 },
6883         { -1, 0 },
6884         { +1, 0 },
6885         { 0, +1 }
6886       };
6887
6888       for (i = 0; i < NUM_DIRECTIONS; i++)
6889       {
6890         int ex = x + xy[i][0];
6891         int ey = y + xy[i][1];
6892
6893         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6894                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6895                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6896                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6897         {
6898           attr_x = ex;
6899           attr_y = ey;
6900           break;
6901         }
6902       }
6903     }
6904
6905     MovDir[x][y] = MV_NONE;
6906     if (attr_x < x)
6907       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6908     else if (attr_x > x)
6909       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6910     if (attr_y < y)
6911       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6912     else if (attr_y > y)
6913       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6914
6915     if (element == EL_ROBOT)
6916     {
6917       int newx, newy;
6918
6919       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6920         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6921       Moving2Blocked(x, y, &newx, &newy);
6922
6923       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6924         MovDelay[x][y] = 8 + 8 * !RND(3);
6925       else
6926         MovDelay[x][y] = 16;
6927     }
6928     else if (element == EL_PENGUIN)
6929     {
6930       int newx, newy;
6931
6932       MovDelay[x][y] = 1;
6933
6934       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6935       {
6936         boolean first_horiz = RND(2);
6937         int new_move_dir = MovDir[x][y];
6938
6939         MovDir[x][y] =
6940           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6941         Moving2Blocked(x, y, &newx, &newy);
6942
6943         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6944           return;
6945
6946         MovDir[x][y] =
6947           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6948         Moving2Blocked(x, y, &newx, &newy);
6949
6950         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6951           return;
6952
6953         MovDir[x][y] = old_move_dir;
6954         return;
6955       }
6956     }
6957     else if (element == EL_SATELLITE)
6958     {
6959       int newx, newy;
6960
6961       MovDelay[x][y] = 1;
6962
6963       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6964       {
6965         boolean first_horiz = RND(2);
6966         int new_move_dir = MovDir[x][y];
6967
6968         MovDir[x][y] =
6969           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6970         Moving2Blocked(x, y, &newx, &newy);
6971
6972         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6973           return;
6974
6975         MovDir[x][y] =
6976           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6977         Moving2Blocked(x, y, &newx, &newy);
6978
6979         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6980           return;
6981
6982         MovDir[x][y] = old_move_dir;
6983         return;
6984       }
6985     }
6986     else if (element == EL_EMC_ANDROID)
6987     {
6988       static int check_pos[16] =
6989       {
6990         -1,             //  0 => (invalid)
6991         7,              //  1 => MV_LEFT
6992         3,              //  2 => MV_RIGHT
6993         -1,             //  3 => (invalid)
6994         1,              //  4 =>            MV_UP
6995         0,              //  5 => MV_LEFT  | MV_UP
6996         2,              //  6 => MV_RIGHT | MV_UP
6997         -1,             //  7 => (invalid)
6998         5,              //  8 =>            MV_DOWN
6999         6,              //  9 => MV_LEFT  | MV_DOWN
7000         4,              // 10 => MV_RIGHT | MV_DOWN
7001         -1,             // 11 => (invalid)
7002         -1,             // 12 => (invalid)
7003         -1,             // 13 => (invalid)
7004         -1,             // 14 => (invalid)
7005         -1,             // 15 => (invalid)
7006       };
7007       static struct
7008       {
7009         int dx, dy;
7010         int dir;
7011       } check_xy[8] =
7012       {
7013         { -1, -1,       MV_LEFT  | MV_UP   },
7014         {  0, -1,                  MV_UP   },
7015         { +1, -1,       MV_RIGHT | MV_UP   },
7016         { +1,  0,       MV_RIGHT           },
7017         { +1, +1,       MV_RIGHT | MV_DOWN },
7018         {  0, +1,                  MV_DOWN },
7019         { -1, +1,       MV_LEFT  | MV_DOWN },
7020         { -1,  0,       MV_LEFT            },
7021       };
7022       int start_pos, check_order;
7023       boolean can_clone = FALSE;
7024       int i;
7025
7026       // check if there is any free field around current position
7027       for (i = 0; i < 8; i++)
7028       {
7029         int newx = x + check_xy[i].dx;
7030         int newy = y + check_xy[i].dy;
7031
7032         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7033         {
7034           can_clone = TRUE;
7035
7036           break;
7037         }
7038       }
7039
7040       if (can_clone)            // randomly find an element to clone
7041       {
7042         can_clone = FALSE;
7043
7044         start_pos = check_pos[RND(8)];
7045         check_order = (RND(2) ? -1 : +1);
7046
7047         for (i = 0; i < 8; i++)
7048         {
7049           int pos_raw = start_pos + i * check_order;
7050           int pos = (pos_raw + 8) % 8;
7051           int newx = x + check_xy[pos].dx;
7052           int newy = y + check_xy[pos].dy;
7053
7054           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7055           {
7056             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7057             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7058
7059             Store[x][y] = Feld[newx][newy];
7060
7061             can_clone = TRUE;
7062
7063             break;
7064           }
7065         }
7066       }
7067
7068       if (can_clone)            // randomly find a direction to move
7069       {
7070         can_clone = FALSE;
7071
7072         start_pos = check_pos[RND(8)];
7073         check_order = (RND(2) ? -1 : +1);
7074
7075         for (i = 0; i < 8; i++)
7076         {
7077           int pos_raw = start_pos + i * check_order;
7078           int pos = (pos_raw + 8) % 8;
7079           int newx = x + check_xy[pos].dx;
7080           int newy = y + check_xy[pos].dy;
7081           int new_move_dir = check_xy[pos].dir;
7082
7083           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7084           {
7085             MovDir[x][y] = new_move_dir;
7086             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7087
7088             can_clone = TRUE;
7089
7090             break;
7091           }
7092         }
7093       }
7094
7095       if (can_clone)            // cloning and moving successful
7096         return;
7097
7098       // cannot clone -- try to move towards player
7099
7100       start_pos = check_pos[MovDir[x][y] & 0x0f];
7101       check_order = (RND(2) ? -1 : +1);
7102
7103       for (i = 0; i < 3; i++)
7104       {
7105         // first check start_pos, then previous/next or (next/previous) pos
7106         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7107         int pos = (pos_raw + 8) % 8;
7108         int newx = x + check_xy[pos].dx;
7109         int newy = y + check_xy[pos].dy;
7110         int new_move_dir = check_xy[pos].dir;
7111
7112         if (IS_PLAYER(newx, newy))
7113           break;
7114
7115         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7116         {
7117           MovDir[x][y] = new_move_dir;
7118           MovDelay[x][y] = level.android_move_time * 8 + 1;
7119
7120           break;
7121         }
7122       }
7123     }
7124   }
7125   else if (move_pattern == MV_TURNING_LEFT ||
7126            move_pattern == MV_TURNING_RIGHT ||
7127            move_pattern == MV_TURNING_LEFT_RIGHT ||
7128            move_pattern == MV_TURNING_RIGHT_LEFT ||
7129            move_pattern == MV_TURNING_RANDOM ||
7130            move_pattern == MV_ALL_DIRECTIONS)
7131   {
7132     boolean can_turn_left =
7133       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7134     boolean can_turn_right =
7135       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7136
7137     if (element_info[element].move_stepsize == 0)       // "not moving"
7138       return;
7139
7140     if (move_pattern == MV_TURNING_LEFT)
7141       MovDir[x][y] = left_dir;
7142     else if (move_pattern == MV_TURNING_RIGHT)
7143       MovDir[x][y] = right_dir;
7144     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7145       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7146     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7147       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7148     else if (move_pattern == MV_TURNING_RANDOM)
7149       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7150                       can_turn_right && !can_turn_left ? right_dir :
7151                       RND(2) ? left_dir : right_dir);
7152     else if (can_turn_left && can_turn_right)
7153       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7154     else if (can_turn_left)
7155       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7156     else if (can_turn_right)
7157       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7158     else
7159       MovDir[x][y] = back_dir;
7160
7161     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7162   }
7163   else if (move_pattern == MV_HORIZONTAL ||
7164            move_pattern == MV_VERTICAL)
7165   {
7166     if (move_pattern & old_move_dir)
7167       MovDir[x][y] = back_dir;
7168     else if (move_pattern == MV_HORIZONTAL)
7169       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7170     else if (move_pattern == MV_VERTICAL)
7171       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7172
7173     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174   }
7175   else if (move_pattern & MV_ANY_DIRECTION)
7176   {
7177     MovDir[x][y] = move_pattern;
7178     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179   }
7180   else if (move_pattern & MV_WIND_DIRECTION)
7181   {
7182     MovDir[x][y] = game.wind_direction;
7183     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7184   }
7185   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7186   {
7187     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7188       MovDir[x][y] = left_dir;
7189     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7190       MovDir[x][y] = right_dir;
7191
7192     if (MovDir[x][y] != old_move_dir)
7193       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7194   }
7195   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7196   {
7197     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7198       MovDir[x][y] = right_dir;
7199     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7200       MovDir[x][y] = left_dir;
7201
7202     if (MovDir[x][y] != old_move_dir)
7203       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7204   }
7205   else if (move_pattern == MV_TOWARDS_PLAYER ||
7206            move_pattern == MV_AWAY_FROM_PLAYER)
7207   {
7208     int attr_x = -1, attr_y = -1;
7209     int newx, newy;
7210     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7211
7212     if (game.all_players_gone)
7213     {
7214       attr_x = game.exit_x;
7215       attr_y = game.exit_y;
7216     }
7217     else
7218     {
7219       int i;
7220
7221       for (i = 0; i < MAX_PLAYERS; i++)
7222       {
7223         struct PlayerInfo *player = &stored_player[i];
7224         int jx = player->jx, jy = player->jy;
7225
7226         if (!player->active)
7227           continue;
7228
7229         if (attr_x == -1 ||
7230             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7231         {
7232           attr_x = jx;
7233           attr_y = jy;
7234         }
7235       }
7236     }
7237
7238     MovDir[x][y] = MV_NONE;
7239     if (attr_x < x)
7240       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7241     else if (attr_x > x)
7242       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7243     if (attr_y < y)
7244       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7245     else if (attr_y > y)
7246       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7247
7248     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7249
7250     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7251     {
7252       boolean first_horiz = RND(2);
7253       int new_move_dir = MovDir[x][y];
7254
7255       if (element_info[element].move_stepsize == 0)     // "not moving"
7256       {
7257         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7258         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7259
7260         return;
7261       }
7262
7263       MovDir[x][y] =
7264         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7265       Moving2Blocked(x, y, &newx, &newy);
7266
7267       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7268         return;
7269
7270       MovDir[x][y] =
7271         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7272       Moving2Blocked(x, y, &newx, &newy);
7273
7274       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7275         return;
7276
7277       MovDir[x][y] = old_move_dir;
7278     }
7279   }
7280   else if (move_pattern == MV_WHEN_PUSHED ||
7281            move_pattern == MV_WHEN_DROPPED)
7282   {
7283     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7284       MovDir[x][y] = MV_NONE;
7285
7286     MovDelay[x][y] = 0;
7287   }
7288   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7289   {
7290     static int test_xy[7][2] =
7291     {
7292       { 0, -1 },
7293       { -1, 0 },
7294       { +1, 0 },
7295       { 0, +1 },
7296       { 0, -1 },
7297       { -1, 0 },
7298       { +1, 0 },
7299     };
7300     static int test_dir[7] =
7301     {
7302       MV_UP,
7303       MV_LEFT,
7304       MV_RIGHT,
7305       MV_DOWN,
7306       MV_UP,
7307       MV_LEFT,
7308       MV_RIGHT,
7309     };
7310     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7311     int move_preference = -1000000;     // start with very low preference
7312     int new_move_dir = MV_NONE;
7313     int start_test = RND(4);
7314     int i;
7315
7316     for (i = 0; i < NUM_DIRECTIONS; i++)
7317     {
7318       int move_dir = test_dir[start_test + i];
7319       int move_dir_preference;
7320
7321       xx = x + test_xy[start_test + i][0];
7322       yy = y + test_xy[start_test + i][1];
7323
7324       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7325           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7326       {
7327         new_move_dir = move_dir;
7328
7329         break;
7330       }
7331
7332       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7333         continue;
7334
7335       move_dir_preference = -1 * RunnerVisit[xx][yy];
7336       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7337         move_dir_preference = PlayerVisit[xx][yy];
7338
7339       if (move_dir_preference > move_preference)
7340       {
7341         // prefer field that has not been visited for the longest time
7342         move_preference = move_dir_preference;
7343         new_move_dir = move_dir;
7344       }
7345       else if (move_dir_preference == move_preference &&
7346                move_dir == old_move_dir)
7347       {
7348         // prefer last direction when all directions are preferred equally
7349         move_preference = move_dir_preference;
7350         new_move_dir = move_dir;
7351       }
7352     }
7353
7354     MovDir[x][y] = new_move_dir;
7355     if (old_move_dir != new_move_dir)
7356       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7357   }
7358 }
7359
7360 static void TurnRound(int x, int y)
7361 {
7362   int direction = MovDir[x][y];
7363
7364   TurnRoundExt(x, y);
7365
7366   GfxDir[x][y] = MovDir[x][y];
7367
7368   if (direction != MovDir[x][y])
7369     GfxFrame[x][y] = 0;
7370
7371   if (MovDelay[x][y])
7372     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7373
7374   ResetGfxFrame(x, y);
7375 }
7376
7377 static boolean JustBeingPushed(int x, int y)
7378 {
7379   int i;
7380
7381   for (i = 0; i < MAX_PLAYERS; i++)
7382   {
7383     struct PlayerInfo *player = &stored_player[i];
7384
7385     if (player->active && player->is_pushing && player->MovPos)
7386     {
7387       int next_jx = player->jx + (player->jx - player->last_jx);
7388       int next_jy = player->jy + (player->jy - player->last_jy);
7389
7390       if (x == next_jx && y == next_jy)
7391         return TRUE;
7392     }
7393   }
7394
7395   return FALSE;
7396 }
7397
7398 static void StartMoving(int x, int y)
7399 {
7400   boolean started_moving = FALSE;       // some elements can fall _and_ move
7401   int element = Feld[x][y];
7402
7403   if (Stop[x][y])
7404     return;
7405
7406   if (MovDelay[x][y] == 0)
7407     GfxAction[x][y] = ACTION_DEFAULT;
7408
7409   if (CAN_FALL(element) && y < lev_fieldy - 1)
7410   {
7411     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7412         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7413       if (JustBeingPushed(x, y))
7414         return;
7415
7416     if (element == EL_QUICKSAND_FULL)
7417     {
7418       if (IS_FREE(x, y + 1))
7419       {
7420         InitMovingField(x, y, MV_DOWN);
7421         started_moving = TRUE;
7422
7423         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7424 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7425         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7426           Store[x][y] = EL_ROCK;
7427 #else
7428         Store[x][y] = EL_ROCK;
7429 #endif
7430
7431         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7432       }
7433       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7434       {
7435         if (!MovDelay[x][y])
7436         {
7437           MovDelay[x][y] = TILEY + 1;
7438
7439           ResetGfxAnimation(x, y);
7440           ResetGfxAnimation(x, y + 1);
7441         }
7442
7443         if (MovDelay[x][y])
7444         {
7445           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7446           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7447
7448           MovDelay[x][y]--;
7449           if (MovDelay[x][y])
7450             return;
7451         }
7452
7453         Feld[x][y] = EL_QUICKSAND_EMPTY;
7454         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7455         Store[x][y + 1] = Store[x][y];
7456         Store[x][y] = 0;
7457
7458         PlayLevelSoundAction(x, y, ACTION_FILLING);
7459       }
7460       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7461       {
7462         if (!MovDelay[x][y])
7463         {
7464           MovDelay[x][y] = TILEY + 1;
7465
7466           ResetGfxAnimation(x, y);
7467           ResetGfxAnimation(x, y + 1);
7468         }
7469
7470         if (MovDelay[x][y])
7471         {
7472           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7473           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7474
7475           MovDelay[x][y]--;
7476           if (MovDelay[x][y])
7477             return;
7478         }
7479
7480         Feld[x][y] = EL_QUICKSAND_EMPTY;
7481         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7482         Store[x][y + 1] = Store[x][y];
7483         Store[x][y] = 0;
7484
7485         PlayLevelSoundAction(x, y, ACTION_FILLING);
7486       }
7487     }
7488     else if (element == EL_QUICKSAND_FAST_FULL)
7489     {
7490       if (IS_FREE(x, y + 1))
7491       {
7492         InitMovingField(x, y, MV_DOWN);
7493         started_moving = TRUE;
7494
7495         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7496 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7497         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7498           Store[x][y] = EL_ROCK;
7499 #else
7500         Store[x][y] = EL_ROCK;
7501 #endif
7502
7503         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7504       }
7505       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7506       {
7507         if (!MovDelay[x][y])
7508         {
7509           MovDelay[x][y] = TILEY + 1;
7510
7511           ResetGfxAnimation(x, y);
7512           ResetGfxAnimation(x, y + 1);
7513         }
7514
7515         if (MovDelay[x][y])
7516         {
7517           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7518           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7519
7520           MovDelay[x][y]--;
7521           if (MovDelay[x][y])
7522             return;
7523         }
7524
7525         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7526         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7527         Store[x][y + 1] = Store[x][y];
7528         Store[x][y] = 0;
7529
7530         PlayLevelSoundAction(x, y, ACTION_FILLING);
7531       }
7532       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7533       {
7534         if (!MovDelay[x][y])
7535         {
7536           MovDelay[x][y] = TILEY + 1;
7537
7538           ResetGfxAnimation(x, y);
7539           ResetGfxAnimation(x, y + 1);
7540         }
7541
7542         if (MovDelay[x][y])
7543         {
7544           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7545           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7546
7547           MovDelay[x][y]--;
7548           if (MovDelay[x][y])
7549             return;
7550         }
7551
7552         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7553         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7554         Store[x][y + 1] = Store[x][y];
7555         Store[x][y] = 0;
7556
7557         PlayLevelSoundAction(x, y, ACTION_FILLING);
7558       }
7559     }
7560     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7561              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7562     {
7563       InitMovingField(x, y, MV_DOWN);
7564       started_moving = TRUE;
7565
7566       Feld[x][y] = EL_QUICKSAND_FILLING;
7567       Store[x][y] = element;
7568
7569       PlayLevelSoundAction(x, y, ACTION_FILLING);
7570     }
7571     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7572              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7573     {
7574       InitMovingField(x, y, MV_DOWN);
7575       started_moving = TRUE;
7576
7577       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7578       Store[x][y] = element;
7579
7580       PlayLevelSoundAction(x, y, ACTION_FILLING);
7581     }
7582     else if (element == EL_MAGIC_WALL_FULL)
7583     {
7584       if (IS_FREE(x, y + 1))
7585       {
7586         InitMovingField(x, y, MV_DOWN);
7587         started_moving = TRUE;
7588
7589         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7590         Store[x][y] = EL_CHANGED(Store[x][y]);
7591       }
7592       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7593       {
7594         if (!MovDelay[x][y])
7595           MovDelay[x][y] = TILEY / 4 + 1;
7596
7597         if (MovDelay[x][y])
7598         {
7599           MovDelay[x][y]--;
7600           if (MovDelay[x][y])
7601             return;
7602         }
7603
7604         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7605         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7606         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7607         Store[x][y] = 0;
7608       }
7609     }
7610     else if (element == EL_BD_MAGIC_WALL_FULL)
7611     {
7612       if (IS_FREE(x, y + 1))
7613       {
7614         InitMovingField(x, y, MV_DOWN);
7615         started_moving = TRUE;
7616
7617         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7618         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7619       }
7620       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7621       {
7622         if (!MovDelay[x][y])
7623           MovDelay[x][y] = TILEY / 4 + 1;
7624
7625         if (MovDelay[x][y])
7626         {
7627           MovDelay[x][y]--;
7628           if (MovDelay[x][y])
7629             return;
7630         }
7631
7632         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7633         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7634         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7635         Store[x][y] = 0;
7636       }
7637     }
7638     else if (element == EL_DC_MAGIC_WALL_FULL)
7639     {
7640       if (IS_FREE(x, y + 1))
7641       {
7642         InitMovingField(x, y, MV_DOWN);
7643         started_moving = TRUE;
7644
7645         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7646         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7647       }
7648       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7649       {
7650         if (!MovDelay[x][y])
7651           MovDelay[x][y] = TILEY / 4 + 1;
7652
7653         if (MovDelay[x][y])
7654         {
7655           MovDelay[x][y]--;
7656           if (MovDelay[x][y])
7657             return;
7658         }
7659
7660         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7661         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7662         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7663         Store[x][y] = 0;
7664       }
7665     }
7666     else if ((CAN_PASS_MAGIC_WALL(element) &&
7667               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7668                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7669              (CAN_PASS_DC_MAGIC_WALL(element) &&
7670               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7671
7672     {
7673       InitMovingField(x, y, MV_DOWN);
7674       started_moving = TRUE;
7675
7676       Feld[x][y] =
7677         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7678          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7679          EL_DC_MAGIC_WALL_FILLING);
7680       Store[x][y] = element;
7681     }
7682     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7683     {
7684       SplashAcid(x, y + 1);
7685
7686       InitMovingField(x, y, MV_DOWN);
7687       started_moving = TRUE;
7688
7689       Store[x][y] = EL_ACID;
7690     }
7691     else if (
7692              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7693               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7694              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7695               CAN_FALL(element) && WasJustFalling[x][y] &&
7696               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7697
7698              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7699               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7700               (Feld[x][y + 1] == EL_BLOCKED)))
7701     {
7702       /* this is needed for a special case not covered by calling "Impact()"
7703          from "ContinueMoving()": if an element moves to a tile directly below
7704          another element which was just falling on that tile (which was empty
7705          in the previous frame), the falling element above would just stop
7706          instead of smashing the element below (in previous version, the above
7707          element was just checked for "moving" instead of "falling", resulting
7708          in incorrect smashes caused by horizontal movement of the above
7709          element; also, the case of the player being the element to smash was
7710          simply not covered here... :-/ ) */
7711
7712       CheckCollision[x][y] = 0;
7713       CheckImpact[x][y] = 0;
7714
7715       Impact(x, y);
7716     }
7717     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7718     {
7719       if (MovDir[x][y] == MV_NONE)
7720       {
7721         InitMovingField(x, y, MV_DOWN);
7722         started_moving = TRUE;
7723       }
7724     }
7725     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7726     {
7727       if (WasJustFalling[x][y]) // prevent animation from being restarted
7728         MovDir[x][y] = MV_DOWN;
7729
7730       InitMovingField(x, y, MV_DOWN);
7731       started_moving = TRUE;
7732     }
7733     else if (element == EL_AMOEBA_DROP)
7734     {
7735       Feld[x][y] = EL_AMOEBA_GROWING;
7736       Store[x][y] = EL_AMOEBA_WET;
7737     }
7738     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7739               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7740              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7741              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7742     {
7743       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7744                                 (IS_FREE(x - 1, y + 1) ||
7745                                  Feld[x - 1][y + 1] == EL_ACID));
7746       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7747                                 (IS_FREE(x + 1, y + 1) ||
7748                                  Feld[x + 1][y + 1] == EL_ACID));
7749       boolean can_fall_any  = (can_fall_left || can_fall_right);
7750       boolean can_fall_both = (can_fall_left && can_fall_right);
7751       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7752
7753       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7754       {
7755         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7756           can_fall_right = FALSE;
7757         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7758           can_fall_left = FALSE;
7759         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7760           can_fall_right = FALSE;
7761         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7762           can_fall_left = FALSE;
7763
7764         can_fall_any  = (can_fall_left || can_fall_right);
7765         can_fall_both = FALSE;
7766       }
7767
7768       if (can_fall_both)
7769       {
7770         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7771           can_fall_right = FALSE;       // slip down on left side
7772         else
7773           can_fall_left = !(can_fall_right = RND(2));
7774
7775         can_fall_both = FALSE;
7776       }
7777
7778       if (can_fall_any)
7779       {
7780         // if not determined otherwise, prefer left side for slipping down
7781         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7782         started_moving = TRUE;
7783       }
7784     }
7785     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7786     {
7787       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7788       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7789       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7790       int belt_dir = game.belt_dir[belt_nr];
7791
7792       if ((belt_dir == MV_LEFT  && left_is_free) ||
7793           (belt_dir == MV_RIGHT && right_is_free))
7794       {
7795         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7796
7797         InitMovingField(x, y, belt_dir);
7798         started_moving = TRUE;
7799
7800         Pushed[x][y] = TRUE;
7801         Pushed[nextx][y] = TRUE;
7802
7803         GfxAction[x][y] = ACTION_DEFAULT;
7804       }
7805       else
7806       {
7807         MovDir[x][y] = 0;       // if element was moving, stop it
7808       }
7809     }
7810   }
7811
7812   // not "else if" because of elements that can fall and move (EL_SPRING)
7813   if (CAN_MOVE(element) && !started_moving)
7814   {
7815     int move_pattern = element_info[element].move_pattern;
7816     int newx, newy;
7817
7818     Moving2Blocked(x, y, &newx, &newy);
7819
7820     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7821       return;
7822
7823     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7824         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7825     {
7826       WasJustMoving[x][y] = 0;
7827       CheckCollision[x][y] = 0;
7828
7829       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7830
7831       if (Feld[x][y] != element)        // element has changed
7832         return;
7833     }
7834
7835     if (!MovDelay[x][y])        // start new movement phase
7836     {
7837       // all objects that can change their move direction after each step
7838       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7839
7840       if (element != EL_YAMYAM &&
7841           element != EL_DARK_YAMYAM &&
7842           element != EL_PACMAN &&
7843           !(move_pattern & MV_ANY_DIRECTION) &&
7844           move_pattern != MV_TURNING_LEFT &&
7845           move_pattern != MV_TURNING_RIGHT &&
7846           move_pattern != MV_TURNING_LEFT_RIGHT &&
7847           move_pattern != MV_TURNING_RIGHT_LEFT &&
7848           move_pattern != MV_TURNING_RANDOM)
7849       {
7850         TurnRound(x, y);
7851
7852         if (MovDelay[x][y] && (element == EL_BUG ||
7853                                element == EL_SPACESHIP ||
7854                                element == EL_SP_SNIKSNAK ||
7855                                element == EL_SP_ELECTRON ||
7856                                element == EL_MOLE))
7857           TEST_DrawLevelField(x, y);
7858       }
7859     }
7860
7861     if (MovDelay[x][y])         // wait some time before next movement
7862     {
7863       MovDelay[x][y]--;
7864
7865       if (element == EL_ROBOT ||
7866           element == EL_YAMYAM ||
7867           element == EL_DARK_YAMYAM)
7868       {
7869         DrawLevelElementAnimationIfNeeded(x, y, element);
7870         PlayLevelSoundAction(x, y, ACTION_WAITING);
7871       }
7872       else if (element == EL_SP_ELECTRON)
7873         DrawLevelElementAnimationIfNeeded(x, y, element);
7874       else if (element == EL_DRAGON)
7875       {
7876         int i;
7877         int dir = MovDir[x][y];
7878         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7879         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7880         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7881                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7882                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7883                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7884         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7885
7886         GfxAction[x][y] = ACTION_ATTACKING;
7887
7888         if (IS_PLAYER(x, y))
7889           DrawPlayerField(x, y);
7890         else
7891           TEST_DrawLevelField(x, y);
7892
7893         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7894
7895         for (i = 1; i <= 3; i++)
7896         {
7897           int xx = x + i * dx;
7898           int yy = y + i * dy;
7899           int sx = SCREENX(xx);
7900           int sy = SCREENY(yy);
7901           int flame_graphic = graphic + (i - 1);
7902
7903           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7904             break;
7905
7906           if (MovDelay[x][y])
7907           {
7908             int flamed = MovingOrBlocked2Element(xx, yy);
7909
7910             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7911               Bang(xx, yy);
7912             else
7913               RemoveMovingField(xx, yy);
7914
7915             ChangeDelay[xx][yy] = 0;
7916
7917             Feld[xx][yy] = EL_FLAMES;
7918
7919             if (IN_SCR_FIELD(sx, sy))
7920             {
7921               TEST_DrawLevelFieldCrumbled(xx, yy);
7922               DrawGraphic(sx, sy, flame_graphic, frame);
7923             }
7924           }
7925           else
7926           {
7927             if (Feld[xx][yy] == EL_FLAMES)
7928               Feld[xx][yy] = EL_EMPTY;
7929             TEST_DrawLevelField(xx, yy);
7930           }
7931         }
7932       }
7933
7934       if (MovDelay[x][y])       // element still has to wait some time
7935       {
7936         PlayLevelSoundAction(x, y, ACTION_WAITING);
7937
7938         return;
7939       }
7940     }
7941
7942     // now make next step
7943
7944     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7945
7946     if (DONT_COLLIDE_WITH(element) &&
7947         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7948         !PLAYER_ENEMY_PROTECTED(newx, newy))
7949     {
7950       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7951
7952       return;
7953     }
7954
7955     else if (CAN_MOVE_INTO_ACID(element) &&
7956              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7957              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7958              (MovDir[x][y] == MV_DOWN ||
7959               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7960     {
7961       SplashAcid(newx, newy);
7962       Store[x][y] = EL_ACID;
7963     }
7964     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7965     {
7966       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7967           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7968           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7969           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7970       {
7971         RemoveField(x, y);
7972         TEST_DrawLevelField(x, y);
7973
7974         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7975         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7976           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7977
7978         game.friends_still_needed--;
7979         if (!game.friends_still_needed &&
7980             !game.GameOver &&
7981             game.all_players_gone)
7982           LevelSolved();
7983
7984         return;
7985       }
7986       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7987       {
7988         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7989           TEST_DrawLevelField(newx, newy);
7990         else
7991           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7992       }
7993       else if (!IS_FREE(newx, newy))
7994       {
7995         GfxAction[x][y] = ACTION_WAITING;
7996
7997         if (IS_PLAYER(x, y))
7998           DrawPlayerField(x, y);
7999         else
8000           TEST_DrawLevelField(x, y);
8001
8002         return;
8003       }
8004     }
8005     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8006     {
8007       if (IS_FOOD_PIG(Feld[newx][newy]))
8008       {
8009         if (IS_MOVING(newx, newy))
8010           RemoveMovingField(newx, newy);
8011         else
8012         {
8013           Feld[newx][newy] = EL_EMPTY;
8014           TEST_DrawLevelField(newx, newy);
8015         }
8016
8017         PlayLevelSound(x, y, SND_PIG_DIGGING);
8018       }
8019       else if (!IS_FREE(newx, newy))
8020       {
8021         if (IS_PLAYER(x, y))
8022           DrawPlayerField(x, y);
8023         else
8024           TEST_DrawLevelField(x, y);
8025
8026         return;
8027       }
8028     }
8029     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8030     {
8031       if (Store[x][y] != EL_EMPTY)
8032       {
8033         boolean can_clone = FALSE;
8034         int xx, yy;
8035
8036         // check if element to clone is still there
8037         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8038         {
8039           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8040           {
8041             can_clone = TRUE;
8042
8043             break;
8044           }
8045         }
8046
8047         // cannot clone or target field not free anymore -- do not clone
8048         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8049           Store[x][y] = EL_EMPTY;
8050       }
8051
8052       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8053       {
8054         if (IS_MV_DIAGONAL(MovDir[x][y]))
8055         {
8056           int diagonal_move_dir = MovDir[x][y];
8057           int stored = Store[x][y];
8058           int change_delay = 8;
8059           int graphic;
8060
8061           // android is moving diagonally
8062
8063           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8064
8065           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8066           GfxElement[x][y] = EL_EMC_ANDROID;
8067           GfxAction[x][y] = ACTION_SHRINKING;
8068           GfxDir[x][y] = diagonal_move_dir;
8069           ChangeDelay[x][y] = change_delay;
8070
8071           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8072                                    GfxDir[x][y]);
8073
8074           DrawLevelGraphicAnimation(x, y, graphic);
8075           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8076
8077           if (Feld[newx][newy] == EL_ACID)
8078           {
8079             SplashAcid(newx, newy);
8080
8081             return;
8082           }
8083
8084           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8085
8086           Store[newx][newy] = EL_EMC_ANDROID;
8087           GfxElement[newx][newy] = EL_EMC_ANDROID;
8088           GfxAction[newx][newy] = ACTION_GROWING;
8089           GfxDir[newx][newy] = diagonal_move_dir;
8090           ChangeDelay[newx][newy] = change_delay;
8091
8092           graphic = el_act_dir2img(GfxElement[newx][newy],
8093                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8094
8095           DrawLevelGraphicAnimation(newx, newy, graphic);
8096           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8097
8098           return;
8099         }
8100         else
8101         {
8102           Feld[newx][newy] = EL_EMPTY;
8103           TEST_DrawLevelField(newx, newy);
8104
8105           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8106         }
8107       }
8108       else if (!IS_FREE(newx, newy))
8109       {
8110         return;
8111       }
8112     }
8113     else if (IS_CUSTOM_ELEMENT(element) &&
8114              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8115     {
8116       if (!DigFieldByCE(newx, newy, element))
8117         return;
8118
8119       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8120       {
8121         RunnerVisit[x][y] = FrameCounter;
8122         PlayerVisit[x][y] /= 8;         // expire player visit path
8123       }
8124     }
8125     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8126     {
8127       if (!IS_FREE(newx, newy))
8128       {
8129         if (IS_PLAYER(x, y))
8130           DrawPlayerField(x, y);
8131         else
8132           TEST_DrawLevelField(x, y);
8133
8134         return;
8135       }
8136       else
8137       {
8138         boolean wanna_flame = !RND(10);
8139         int dx = newx - x, dy = newy - y;
8140         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8141         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8142         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8143                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8144         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8145                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8146
8147         if ((wanna_flame ||
8148              IS_CLASSIC_ENEMY(element1) ||
8149              IS_CLASSIC_ENEMY(element2)) &&
8150             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8151             element1 != EL_FLAMES && element2 != EL_FLAMES)
8152         {
8153           ResetGfxAnimation(x, y);
8154           GfxAction[x][y] = ACTION_ATTACKING;
8155
8156           if (IS_PLAYER(x, y))
8157             DrawPlayerField(x, y);
8158           else
8159             TEST_DrawLevelField(x, y);
8160
8161           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8162
8163           MovDelay[x][y] = 50;
8164
8165           Feld[newx][newy] = EL_FLAMES;
8166           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8167             Feld[newx1][newy1] = EL_FLAMES;
8168           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8169             Feld[newx2][newy2] = EL_FLAMES;
8170
8171           return;
8172         }
8173       }
8174     }
8175     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8176              Feld[newx][newy] == EL_DIAMOND)
8177     {
8178       if (IS_MOVING(newx, newy))
8179         RemoveMovingField(newx, newy);
8180       else
8181       {
8182         Feld[newx][newy] = EL_EMPTY;
8183         TEST_DrawLevelField(newx, newy);
8184       }
8185
8186       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8187     }
8188     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8189              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8190     {
8191       if (AmoebaNr[newx][newy])
8192       {
8193         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8194         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8195             Feld[newx][newy] == EL_BD_AMOEBA)
8196           AmoebaCnt[AmoebaNr[newx][newy]]--;
8197       }
8198
8199       if (IS_MOVING(newx, newy))
8200       {
8201         RemoveMovingField(newx, newy);
8202       }
8203       else
8204       {
8205         Feld[newx][newy] = EL_EMPTY;
8206         TEST_DrawLevelField(newx, newy);
8207       }
8208
8209       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8210     }
8211     else if ((element == EL_PACMAN || element == EL_MOLE)
8212              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8213     {
8214       if (AmoebaNr[newx][newy])
8215       {
8216         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8217         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8218             Feld[newx][newy] == EL_BD_AMOEBA)
8219           AmoebaCnt[AmoebaNr[newx][newy]]--;
8220       }
8221
8222       if (element == EL_MOLE)
8223       {
8224         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8225         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8226
8227         ResetGfxAnimation(x, y);
8228         GfxAction[x][y] = ACTION_DIGGING;
8229         TEST_DrawLevelField(x, y);
8230
8231         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8232
8233         return;                         // wait for shrinking amoeba
8234       }
8235       else      // element == EL_PACMAN
8236       {
8237         Feld[newx][newy] = EL_EMPTY;
8238         TEST_DrawLevelField(newx, newy);
8239         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8240       }
8241     }
8242     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8243              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8244               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8245     {
8246       // wait for shrinking amoeba to completely disappear
8247       return;
8248     }
8249     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8250     {
8251       // object was running against a wall
8252
8253       TurnRound(x, y);
8254
8255       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8256         DrawLevelElementAnimation(x, y, element);
8257
8258       if (DONT_TOUCH(element))
8259         TestIfBadThingTouchesPlayer(x, y);
8260
8261       return;
8262     }
8263
8264     InitMovingField(x, y, MovDir[x][y]);
8265
8266     PlayLevelSoundAction(x, y, ACTION_MOVING);
8267   }
8268
8269   if (MovDir[x][y])
8270     ContinueMoving(x, y);
8271 }
8272
8273 void ContinueMoving(int x, int y)
8274 {
8275   int element = Feld[x][y];
8276   struct ElementInfo *ei = &element_info[element];
8277   int direction = MovDir[x][y];
8278   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8279   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8280   int newx = x + dx, newy = y + dy;
8281   int stored = Store[x][y];
8282   int stored_new = Store[newx][newy];
8283   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8284   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8285   boolean last_line = (newy == lev_fieldy - 1);
8286
8287   MovPos[x][y] += getElementMoveStepsize(x, y);
8288
8289   if (pushed_by_player) // special case: moving object pushed by player
8290     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8291
8292   if (ABS(MovPos[x][y]) < TILEX)
8293   {
8294     TEST_DrawLevelField(x, y);
8295
8296     return;     // element is still moving
8297   }
8298
8299   // element reached destination field
8300
8301   Feld[x][y] = EL_EMPTY;
8302   Feld[newx][newy] = element;
8303   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8304
8305   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8306   {
8307     element = Feld[newx][newy] = EL_ACID;
8308   }
8309   else if (element == EL_MOLE)
8310   {
8311     Feld[x][y] = EL_SAND;
8312
8313     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8314   }
8315   else if (element == EL_QUICKSAND_FILLING)
8316   {
8317     element = Feld[newx][newy] = get_next_element(element);
8318     Store[newx][newy] = Store[x][y];
8319   }
8320   else if (element == EL_QUICKSAND_EMPTYING)
8321   {
8322     Feld[x][y] = get_next_element(element);
8323     element = Feld[newx][newy] = Store[x][y];
8324   }
8325   else if (element == EL_QUICKSAND_FAST_FILLING)
8326   {
8327     element = Feld[newx][newy] = get_next_element(element);
8328     Store[newx][newy] = Store[x][y];
8329   }
8330   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8331   {
8332     Feld[x][y] = get_next_element(element);
8333     element = Feld[newx][newy] = Store[x][y];
8334   }
8335   else if (element == EL_MAGIC_WALL_FILLING)
8336   {
8337     element = Feld[newx][newy] = get_next_element(element);
8338     if (!game.magic_wall_active)
8339       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8340     Store[newx][newy] = Store[x][y];
8341   }
8342   else if (element == EL_MAGIC_WALL_EMPTYING)
8343   {
8344     Feld[x][y] = get_next_element(element);
8345     if (!game.magic_wall_active)
8346       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8347     element = Feld[newx][newy] = Store[x][y];
8348
8349     InitField(newx, newy, FALSE);
8350   }
8351   else if (element == EL_BD_MAGIC_WALL_FILLING)
8352   {
8353     element = Feld[newx][newy] = get_next_element(element);
8354     if (!game.magic_wall_active)
8355       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8356     Store[newx][newy] = Store[x][y];
8357   }
8358   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8359   {
8360     Feld[x][y] = get_next_element(element);
8361     if (!game.magic_wall_active)
8362       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8363     element = Feld[newx][newy] = Store[x][y];
8364
8365     InitField(newx, newy, FALSE);
8366   }
8367   else if (element == EL_DC_MAGIC_WALL_FILLING)
8368   {
8369     element = Feld[newx][newy] = get_next_element(element);
8370     if (!game.magic_wall_active)
8371       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8372     Store[newx][newy] = Store[x][y];
8373   }
8374   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8375   {
8376     Feld[x][y] = get_next_element(element);
8377     if (!game.magic_wall_active)
8378       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8379     element = Feld[newx][newy] = Store[x][y];
8380
8381     InitField(newx, newy, FALSE);
8382   }
8383   else if (element == EL_AMOEBA_DROPPING)
8384   {
8385     Feld[x][y] = get_next_element(element);
8386     element = Feld[newx][newy] = Store[x][y];
8387   }
8388   else if (element == EL_SOKOBAN_OBJECT)
8389   {
8390     if (Back[x][y])
8391       Feld[x][y] = Back[x][y];
8392
8393     if (Back[newx][newy])
8394       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8395
8396     Back[x][y] = Back[newx][newy] = 0;
8397   }
8398
8399   Store[x][y] = EL_EMPTY;
8400   MovPos[x][y] = 0;
8401   MovDir[x][y] = 0;
8402   MovDelay[x][y] = 0;
8403
8404   MovDelay[newx][newy] = 0;
8405
8406   if (CAN_CHANGE_OR_HAS_ACTION(element))
8407   {
8408     // copy element change control values to new field
8409     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8410     ChangePage[newx][newy]  = ChangePage[x][y];
8411     ChangeCount[newx][newy] = ChangeCount[x][y];
8412     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8413   }
8414
8415   CustomValue[newx][newy] = CustomValue[x][y];
8416
8417   ChangeDelay[x][y] = 0;
8418   ChangePage[x][y] = -1;
8419   ChangeCount[x][y] = 0;
8420   ChangeEvent[x][y] = -1;
8421
8422   CustomValue[x][y] = 0;
8423
8424   // copy animation control values to new field
8425   GfxFrame[newx][newy]  = GfxFrame[x][y];
8426   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8427   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8428   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8429
8430   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8431
8432   // some elements can leave other elements behind after moving
8433   if (ei->move_leave_element != EL_EMPTY &&
8434       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8435       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8436   {
8437     int move_leave_element = ei->move_leave_element;
8438
8439     // this makes it possible to leave the removed element again
8440     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8441       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8442
8443     Feld[x][y] = move_leave_element;
8444
8445     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8446       MovDir[x][y] = direction;
8447
8448     InitField(x, y, FALSE);
8449
8450     if (GFX_CRUMBLED(Feld[x][y]))
8451       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8452
8453     if (ELEM_IS_PLAYER(move_leave_element))
8454       RelocatePlayer(x, y, move_leave_element);
8455   }
8456
8457   // do this after checking for left-behind element
8458   ResetGfxAnimation(x, y);      // reset animation values for old field
8459
8460   if (!CAN_MOVE(element) ||
8461       (CAN_FALL(element) && direction == MV_DOWN &&
8462        (element == EL_SPRING ||
8463         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8464         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8465     GfxDir[x][y] = MovDir[newx][newy] = 0;
8466
8467   TEST_DrawLevelField(x, y);
8468   TEST_DrawLevelField(newx, newy);
8469
8470   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8471
8472   // prevent pushed element from moving on in pushed direction
8473   if (pushed_by_player && CAN_MOVE(element) &&
8474       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8475       !(element_info[element].move_pattern & direction))
8476     TurnRound(newx, newy);
8477
8478   // prevent elements on conveyor belt from moving on in last direction
8479   if (pushed_by_conveyor && CAN_FALL(element) &&
8480       direction & MV_HORIZONTAL)
8481     MovDir[newx][newy] = 0;
8482
8483   if (!pushed_by_player)
8484   {
8485     int nextx = newx + dx, nexty = newy + dy;
8486     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8487
8488     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8489
8490     if (CAN_FALL(element) && direction == MV_DOWN)
8491       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8492
8493     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8494       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8495
8496     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8497       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8498   }
8499
8500   if (DONT_TOUCH(element))      // object may be nasty to player or others
8501   {
8502     TestIfBadThingTouchesPlayer(newx, newy);
8503     TestIfBadThingTouchesFriend(newx, newy);
8504
8505     if (!IS_CUSTOM_ELEMENT(element))
8506       TestIfBadThingTouchesOtherBadThing(newx, newy);
8507   }
8508   else if (element == EL_PENGUIN)
8509     TestIfFriendTouchesBadThing(newx, newy);
8510
8511   if (DONT_GET_HIT_BY(element))
8512   {
8513     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8514   }
8515
8516   // give the player one last chance (one more frame) to move away
8517   if (CAN_FALL(element) && direction == MV_DOWN &&
8518       (last_line || (!IS_FREE(x, newy + 1) &&
8519                      (!IS_PLAYER(x, newy + 1) ||
8520                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8521     Impact(x, newy);
8522
8523   if (pushed_by_player && !game.use_change_when_pushing_bug)
8524   {
8525     int push_side = MV_DIR_OPPOSITE(direction);
8526     struct PlayerInfo *player = PLAYERINFO(x, y);
8527
8528     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8529                                player->index_bit, push_side);
8530     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8531                                         player->index_bit, push_side);
8532   }
8533
8534   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8535     MovDelay[newx][newy] = 1;
8536
8537   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8538
8539   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8540   TestIfElementHitsCustomElement(newx, newy, direction);
8541   TestIfPlayerTouchesCustomElement(newx, newy);
8542   TestIfElementTouchesCustomElement(newx, newy);
8543
8544   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8545       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8546     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8547                              MV_DIR_OPPOSITE(direction));
8548 }
8549
8550 int AmoebeNachbarNr(int ax, int ay)
8551 {
8552   int i;
8553   int element = Feld[ax][ay];
8554   int group_nr = 0;
8555   static int xy[4][2] =
8556   {
8557     { 0, -1 },
8558     { -1, 0 },
8559     { +1, 0 },
8560     { 0, +1 }
8561   };
8562
8563   for (i = 0; i < NUM_DIRECTIONS; i++)
8564   {
8565     int x = ax + xy[i][0];
8566     int y = ay + xy[i][1];
8567
8568     if (!IN_LEV_FIELD(x, y))
8569       continue;
8570
8571     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8572       group_nr = AmoebaNr[x][y];
8573   }
8574
8575   return group_nr;
8576 }
8577
8578 static void AmoebenVereinigen(int ax, int ay)
8579 {
8580   int i, x, y, xx, yy;
8581   int new_group_nr = AmoebaNr[ax][ay];
8582   static int xy[4][2] =
8583   {
8584     { 0, -1 },
8585     { -1, 0 },
8586     { +1, 0 },
8587     { 0, +1 }
8588   };
8589
8590   if (new_group_nr == 0)
8591     return;
8592
8593   for (i = 0; i < NUM_DIRECTIONS; i++)
8594   {
8595     x = ax + xy[i][0];
8596     y = ay + xy[i][1];
8597
8598     if (!IN_LEV_FIELD(x, y))
8599       continue;
8600
8601     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8602          Feld[x][y] == EL_BD_AMOEBA ||
8603          Feld[x][y] == EL_AMOEBA_DEAD) &&
8604         AmoebaNr[x][y] != new_group_nr)
8605     {
8606       int old_group_nr = AmoebaNr[x][y];
8607
8608       if (old_group_nr == 0)
8609         return;
8610
8611       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8612       AmoebaCnt[old_group_nr] = 0;
8613       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8614       AmoebaCnt2[old_group_nr] = 0;
8615
8616       SCAN_PLAYFIELD(xx, yy)
8617       {
8618         if (AmoebaNr[xx][yy] == old_group_nr)
8619           AmoebaNr[xx][yy] = new_group_nr;
8620       }
8621     }
8622   }
8623 }
8624
8625 void AmoebeUmwandeln(int ax, int ay)
8626 {
8627   int i, x, y;
8628
8629   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8630   {
8631     int group_nr = AmoebaNr[ax][ay];
8632
8633 #ifdef DEBUG
8634     if (group_nr == 0)
8635     {
8636       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8637       printf("AmoebeUmwandeln(): This should never happen!\n");
8638       return;
8639     }
8640 #endif
8641
8642     SCAN_PLAYFIELD(x, y)
8643     {
8644       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8645       {
8646         AmoebaNr[x][y] = 0;
8647         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8648       }
8649     }
8650
8651     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8652                             SND_AMOEBA_TURNING_TO_GEM :
8653                             SND_AMOEBA_TURNING_TO_ROCK));
8654     Bang(ax, ay);
8655   }
8656   else
8657   {
8658     static int xy[4][2] =
8659     {
8660       { 0, -1 },
8661       { -1, 0 },
8662       { +1, 0 },
8663       { 0, +1 }
8664     };
8665
8666     for (i = 0; i < NUM_DIRECTIONS; i++)
8667     {
8668       x = ax + xy[i][0];
8669       y = ay + xy[i][1];
8670
8671       if (!IN_LEV_FIELD(x, y))
8672         continue;
8673
8674       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8675       {
8676         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8677                               SND_AMOEBA_TURNING_TO_GEM :
8678                               SND_AMOEBA_TURNING_TO_ROCK));
8679         Bang(x, y);
8680       }
8681     }
8682   }
8683 }
8684
8685 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8686 {
8687   int x, y;
8688   int group_nr = AmoebaNr[ax][ay];
8689   boolean done = FALSE;
8690
8691 #ifdef DEBUG
8692   if (group_nr == 0)
8693   {
8694     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8695     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8696     return;
8697   }
8698 #endif
8699
8700   SCAN_PLAYFIELD(x, y)
8701   {
8702     if (AmoebaNr[x][y] == group_nr &&
8703         (Feld[x][y] == EL_AMOEBA_DEAD ||
8704          Feld[x][y] == EL_BD_AMOEBA ||
8705          Feld[x][y] == EL_AMOEBA_GROWING))
8706     {
8707       AmoebaNr[x][y] = 0;
8708       Feld[x][y] = new_element;
8709       InitField(x, y, FALSE);
8710       TEST_DrawLevelField(x, y);
8711       done = TRUE;
8712     }
8713   }
8714
8715   if (done)
8716     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8717                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8718                             SND_BD_AMOEBA_TURNING_TO_GEM));
8719 }
8720
8721 static void AmoebeWaechst(int x, int y)
8722 {
8723   static unsigned int sound_delay = 0;
8724   static unsigned int sound_delay_value = 0;
8725
8726   if (!MovDelay[x][y])          // start new growing cycle
8727   {
8728     MovDelay[x][y] = 7;
8729
8730     if (DelayReached(&sound_delay, sound_delay_value))
8731     {
8732       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8733       sound_delay_value = 30;
8734     }
8735   }
8736
8737   if (MovDelay[x][y])           // wait some time before growing bigger
8738   {
8739     MovDelay[x][y]--;
8740     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8741     {
8742       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8743                                            6 - MovDelay[x][y]);
8744
8745       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8746     }
8747
8748     if (!MovDelay[x][y])
8749     {
8750       Feld[x][y] = Store[x][y];
8751       Store[x][y] = 0;
8752       TEST_DrawLevelField(x, y);
8753     }
8754   }
8755 }
8756
8757 static void AmoebaDisappearing(int x, int y)
8758 {
8759   static unsigned int sound_delay = 0;
8760   static unsigned int sound_delay_value = 0;
8761
8762   if (!MovDelay[x][y])          // start new shrinking cycle
8763   {
8764     MovDelay[x][y] = 7;
8765
8766     if (DelayReached(&sound_delay, sound_delay_value))
8767       sound_delay_value = 30;
8768   }
8769
8770   if (MovDelay[x][y])           // wait some time before shrinking
8771   {
8772     MovDelay[x][y]--;
8773     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8774     {
8775       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8776                                            6 - MovDelay[x][y]);
8777
8778       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8779     }
8780
8781     if (!MovDelay[x][y])
8782     {
8783       Feld[x][y] = EL_EMPTY;
8784       TEST_DrawLevelField(x, y);
8785
8786       // don't let mole enter this field in this cycle;
8787       // (give priority to objects falling to this field from above)
8788       Stop[x][y] = TRUE;
8789     }
8790   }
8791 }
8792
8793 static void AmoebeAbleger(int ax, int ay)
8794 {
8795   int i;
8796   int element = Feld[ax][ay];
8797   int graphic = el2img(element);
8798   int newax = ax, neway = ay;
8799   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8800   static int xy[4][2] =
8801   {
8802     { 0, -1 },
8803     { -1, 0 },
8804     { +1, 0 },
8805     { 0, +1 }
8806   };
8807
8808   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8809   {
8810     Feld[ax][ay] = EL_AMOEBA_DEAD;
8811     TEST_DrawLevelField(ax, ay);
8812     return;
8813   }
8814
8815   if (IS_ANIMATED(graphic))
8816     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8817
8818   if (!MovDelay[ax][ay])        // start making new amoeba field
8819     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8820
8821   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8822   {
8823     MovDelay[ax][ay]--;
8824     if (MovDelay[ax][ay])
8825       return;
8826   }
8827
8828   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8829   {
8830     int start = RND(4);
8831     int x = ax + xy[start][0];
8832     int y = ay + xy[start][1];
8833
8834     if (!IN_LEV_FIELD(x, y))
8835       return;
8836
8837     if (IS_FREE(x, y) ||
8838         CAN_GROW_INTO(Feld[x][y]) ||
8839         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8840         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8841     {
8842       newax = x;
8843       neway = y;
8844     }
8845
8846     if (newax == ax && neway == ay)
8847       return;
8848   }
8849   else                          // normal or "filled" (BD style) amoeba
8850   {
8851     int start = RND(4);
8852     boolean waiting_for_player = FALSE;
8853
8854     for (i = 0; i < NUM_DIRECTIONS; i++)
8855     {
8856       int j = (start + i) % 4;
8857       int x = ax + xy[j][0];
8858       int y = ay + xy[j][1];
8859
8860       if (!IN_LEV_FIELD(x, y))
8861         continue;
8862
8863       if (IS_FREE(x, y) ||
8864           CAN_GROW_INTO(Feld[x][y]) ||
8865           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8866           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8867       {
8868         newax = x;
8869         neway = y;
8870         break;
8871       }
8872       else if (IS_PLAYER(x, y))
8873         waiting_for_player = TRUE;
8874     }
8875
8876     if (newax == ax && neway == ay)             // amoeba cannot grow
8877     {
8878       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8879       {
8880         Feld[ax][ay] = EL_AMOEBA_DEAD;
8881         TEST_DrawLevelField(ax, ay);
8882         AmoebaCnt[AmoebaNr[ax][ay]]--;
8883
8884         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8885         {
8886           if (element == EL_AMOEBA_FULL)
8887             AmoebeUmwandeln(ax, ay);
8888           else if (element == EL_BD_AMOEBA)
8889             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8890         }
8891       }
8892       return;
8893     }
8894     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8895     {
8896       // amoeba gets larger by growing in some direction
8897
8898       int new_group_nr = AmoebaNr[ax][ay];
8899
8900 #ifdef DEBUG
8901   if (new_group_nr == 0)
8902   {
8903     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8904     printf("AmoebeAbleger(): This should never happen!\n");
8905     return;
8906   }
8907 #endif
8908
8909       AmoebaNr[newax][neway] = new_group_nr;
8910       AmoebaCnt[new_group_nr]++;
8911       AmoebaCnt2[new_group_nr]++;
8912
8913       // if amoeba touches other amoeba(s) after growing, unify them
8914       AmoebenVereinigen(newax, neway);
8915
8916       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8917       {
8918         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8919         return;
8920       }
8921     }
8922   }
8923
8924   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8925       (neway == lev_fieldy - 1 && newax != ax))
8926   {
8927     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8928     Store[newax][neway] = element;
8929   }
8930   else if (neway == ay || element == EL_EMC_DRIPPER)
8931   {
8932     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8933
8934     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8935   }
8936   else
8937   {
8938     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8939     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8940     Store[ax][ay] = EL_AMOEBA_DROP;
8941     ContinueMoving(ax, ay);
8942     return;
8943   }
8944
8945   TEST_DrawLevelField(newax, neway);
8946 }
8947
8948 static void Life(int ax, int ay)
8949 {
8950   int x1, y1, x2, y2;
8951   int life_time = 40;
8952   int element = Feld[ax][ay];
8953   int graphic = el2img(element);
8954   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8955                          level.biomaze);
8956   boolean changed = FALSE;
8957
8958   if (IS_ANIMATED(graphic))
8959     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8960
8961   if (Stop[ax][ay])
8962     return;
8963
8964   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8965     MovDelay[ax][ay] = life_time;
8966
8967   if (MovDelay[ax][ay])         // wait some time before next cycle
8968   {
8969     MovDelay[ax][ay]--;
8970     if (MovDelay[ax][ay])
8971       return;
8972   }
8973
8974   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8975   {
8976     int xx = ax+x1, yy = ay+y1;
8977     int old_element = Feld[xx][yy];
8978     int num_neighbours = 0;
8979
8980     if (!IN_LEV_FIELD(xx, yy))
8981       continue;
8982
8983     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8984     {
8985       int x = xx+x2, y = yy+y2;
8986
8987       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8988         continue;
8989
8990       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8991       boolean is_neighbour = FALSE;
8992
8993       if (level.use_life_bugs)
8994         is_neighbour =
8995           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8996            (IS_FREE(x, y)                             &&  Stop[x][y]));
8997       else
8998         is_neighbour =
8999           (Last[x][y] == element || is_player_cell);
9000
9001       if (is_neighbour)
9002         num_neighbours++;
9003     }
9004
9005     boolean is_free = FALSE;
9006
9007     if (level.use_life_bugs)
9008       is_free = (IS_FREE(xx, yy));
9009     else
9010       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9011
9012     if (xx == ax && yy == ay)           // field in the middle
9013     {
9014       if (num_neighbours < life_parameter[0] ||
9015           num_neighbours > life_parameter[1])
9016       {
9017         Feld[xx][yy] = EL_EMPTY;
9018         if (Feld[xx][yy] != old_element)
9019           TEST_DrawLevelField(xx, yy);
9020         Stop[xx][yy] = TRUE;
9021         changed = TRUE;
9022       }
9023     }
9024     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9025     {                                   // free border field
9026       if (num_neighbours >= life_parameter[2] &&
9027           num_neighbours <= life_parameter[3])
9028       {
9029         Feld[xx][yy] = element;
9030         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9031         if (Feld[xx][yy] != old_element)
9032           TEST_DrawLevelField(xx, yy);
9033         Stop[xx][yy] = TRUE;
9034         changed = TRUE;
9035       }
9036     }
9037   }
9038
9039   if (changed)
9040     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9041                    SND_GAME_OF_LIFE_GROWING);
9042 }
9043
9044 static void InitRobotWheel(int x, int y)
9045 {
9046   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9047 }
9048
9049 static void RunRobotWheel(int x, int y)
9050 {
9051   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9052 }
9053
9054 static void StopRobotWheel(int x, int y)
9055 {
9056   if (game.robot_wheel_x == x &&
9057       game.robot_wheel_y == y)
9058   {
9059     game.robot_wheel_x = -1;
9060     game.robot_wheel_y = -1;
9061     game.robot_wheel_active = FALSE;
9062   }
9063 }
9064
9065 static void InitTimegateWheel(int x, int y)
9066 {
9067   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9068 }
9069
9070 static void RunTimegateWheel(int x, int y)
9071 {
9072   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9073 }
9074
9075 static void InitMagicBallDelay(int x, int y)
9076 {
9077   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9078 }
9079
9080 static void ActivateMagicBall(int bx, int by)
9081 {
9082   int x, y;
9083
9084   if (level.ball_random)
9085   {
9086     int pos_border = RND(8);    // select one of the eight border elements
9087     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9088     int xx = pos_content % 3;
9089     int yy = pos_content / 3;
9090
9091     x = bx - 1 + xx;
9092     y = by - 1 + yy;
9093
9094     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9095       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9096   }
9097   else
9098   {
9099     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9100     {
9101       int xx = x - bx + 1;
9102       int yy = y - by + 1;
9103
9104       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9105         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9106     }
9107   }
9108
9109   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9110 }
9111
9112 static void CheckExit(int x, int y)
9113 {
9114   if (game.gems_still_needed > 0 ||
9115       game.sokoban_fields_still_needed > 0 ||
9116       game.sokoban_objects_still_needed > 0 ||
9117       game.lights_still_needed > 0)
9118   {
9119     int element = Feld[x][y];
9120     int graphic = el2img(element);
9121
9122     if (IS_ANIMATED(graphic))
9123       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9124
9125     return;
9126   }
9127
9128   // do not re-open exit door closed after last player
9129   if (game.all_players_gone)
9130     return;
9131
9132   Feld[x][y] = EL_EXIT_OPENING;
9133
9134   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9135 }
9136
9137 static void CheckExitEM(int x, int y)
9138 {
9139   if (game.gems_still_needed > 0 ||
9140       game.sokoban_fields_still_needed > 0 ||
9141       game.sokoban_objects_still_needed > 0 ||
9142       game.lights_still_needed > 0)
9143   {
9144     int element = Feld[x][y];
9145     int graphic = el2img(element);
9146
9147     if (IS_ANIMATED(graphic))
9148       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9149
9150     return;
9151   }
9152
9153   // do not re-open exit door closed after last player
9154   if (game.all_players_gone)
9155     return;
9156
9157   Feld[x][y] = EL_EM_EXIT_OPENING;
9158
9159   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9160 }
9161
9162 static void CheckExitSteel(int x, int y)
9163 {
9164   if (game.gems_still_needed > 0 ||
9165       game.sokoban_fields_still_needed > 0 ||
9166       game.sokoban_objects_still_needed > 0 ||
9167       game.lights_still_needed > 0)
9168   {
9169     int element = Feld[x][y];
9170     int graphic = el2img(element);
9171
9172     if (IS_ANIMATED(graphic))
9173       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9174
9175     return;
9176   }
9177
9178   // do not re-open exit door closed after last player
9179   if (game.all_players_gone)
9180     return;
9181
9182   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9183
9184   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9185 }
9186
9187 static void CheckExitSteelEM(int x, int y)
9188 {
9189   if (game.gems_still_needed > 0 ||
9190       game.sokoban_fields_still_needed > 0 ||
9191       game.sokoban_objects_still_needed > 0 ||
9192       game.lights_still_needed > 0)
9193   {
9194     int element = Feld[x][y];
9195     int graphic = el2img(element);
9196
9197     if (IS_ANIMATED(graphic))
9198       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9199
9200     return;
9201   }
9202
9203   // do not re-open exit door closed after last player
9204   if (game.all_players_gone)
9205     return;
9206
9207   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9208
9209   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9210 }
9211
9212 static void CheckExitSP(int x, int y)
9213 {
9214   if (game.gems_still_needed > 0)
9215   {
9216     int element = Feld[x][y];
9217     int graphic = el2img(element);
9218
9219     if (IS_ANIMATED(graphic))
9220       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9221
9222     return;
9223   }
9224
9225   // do not re-open exit door closed after last player
9226   if (game.all_players_gone)
9227     return;
9228
9229   Feld[x][y] = EL_SP_EXIT_OPENING;
9230
9231   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9232 }
9233
9234 static void CloseAllOpenTimegates(void)
9235 {
9236   int x, y;
9237
9238   SCAN_PLAYFIELD(x, y)
9239   {
9240     int element = Feld[x][y];
9241
9242     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9243     {
9244       Feld[x][y] = EL_TIMEGATE_CLOSING;
9245
9246       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9247     }
9248   }
9249 }
9250
9251 static void DrawTwinkleOnField(int x, int y)
9252 {
9253   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9254     return;
9255
9256   if (Feld[x][y] == EL_BD_DIAMOND)
9257     return;
9258
9259   if (MovDelay[x][y] == 0)      // next animation frame
9260     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9261
9262   if (MovDelay[x][y] != 0)      // wait some time before next frame
9263   {
9264     MovDelay[x][y]--;
9265
9266     DrawLevelElementAnimation(x, y, Feld[x][y]);
9267
9268     if (MovDelay[x][y] != 0)
9269     {
9270       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9271                                            10 - MovDelay[x][y]);
9272
9273       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9274     }
9275   }
9276 }
9277
9278 static void MauerWaechst(int x, int y)
9279 {
9280   int delay = 6;
9281
9282   if (!MovDelay[x][y])          // next animation frame
9283     MovDelay[x][y] = 3 * delay;
9284
9285   if (MovDelay[x][y])           // wait some time before next frame
9286   {
9287     MovDelay[x][y]--;
9288
9289     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9290     {
9291       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9292       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9293
9294       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9295     }
9296
9297     if (!MovDelay[x][y])
9298     {
9299       if (MovDir[x][y] == MV_LEFT)
9300       {
9301         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9302           TEST_DrawLevelField(x - 1, y);
9303       }
9304       else if (MovDir[x][y] == MV_RIGHT)
9305       {
9306         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9307           TEST_DrawLevelField(x + 1, y);
9308       }
9309       else if (MovDir[x][y] == MV_UP)
9310       {
9311         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9312           TEST_DrawLevelField(x, y - 1);
9313       }
9314       else
9315       {
9316         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9317           TEST_DrawLevelField(x, y + 1);
9318       }
9319
9320       Feld[x][y] = Store[x][y];
9321       Store[x][y] = 0;
9322       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9323       TEST_DrawLevelField(x, y);
9324     }
9325   }
9326 }
9327
9328 static void MauerAbleger(int ax, int ay)
9329 {
9330   int element = Feld[ax][ay];
9331   int graphic = el2img(element);
9332   boolean oben_frei = FALSE, unten_frei = FALSE;
9333   boolean links_frei = FALSE, rechts_frei = FALSE;
9334   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9335   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9336   boolean new_wall = FALSE;
9337
9338   if (IS_ANIMATED(graphic))
9339     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9340
9341   if (!MovDelay[ax][ay])        // start building new wall
9342     MovDelay[ax][ay] = 6;
9343
9344   if (MovDelay[ax][ay])         // wait some time before building new wall
9345   {
9346     MovDelay[ax][ay]--;
9347     if (MovDelay[ax][ay])
9348       return;
9349   }
9350
9351   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9352     oben_frei = TRUE;
9353   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9354     unten_frei = TRUE;
9355   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9356     links_frei = TRUE;
9357   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9358     rechts_frei = TRUE;
9359
9360   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9361       element == EL_EXPANDABLE_WALL_ANY)
9362   {
9363     if (oben_frei)
9364     {
9365       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9366       Store[ax][ay-1] = element;
9367       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9368       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9369         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9370                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9371       new_wall = TRUE;
9372     }
9373     if (unten_frei)
9374     {
9375       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9376       Store[ax][ay+1] = element;
9377       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9378       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9379         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9380                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9381       new_wall = TRUE;
9382     }
9383   }
9384
9385   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9386       element == EL_EXPANDABLE_WALL_ANY ||
9387       element == EL_EXPANDABLE_WALL ||
9388       element == EL_BD_EXPANDABLE_WALL)
9389   {
9390     if (links_frei)
9391     {
9392       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9393       Store[ax-1][ay] = element;
9394       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9395       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9396         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9397                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9398       new_wall = TRUE;
9399     }
9400
9401     if (rechts_frei)
9402     {
9403       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9404       Store[ax+1][ay] = element;
9405       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9406       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9407         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9408                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9409       new_wall = TRUE;
9410     }
9411   }
9412
9413   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9414     TEST_DrawLevelField(ax, ay);
9415
9416   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9417     oben_massiv = TRUE;
9418   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9419     unten_massiv = TRUE;
9420   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9421     links_massiv = TRUE;
9422   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9423     rechts_massiv = TRUE;
9424
9425   if (((oben_massiv && unten_massiv) ||
9426        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9427        element == EL_EXPANDABLE_WALL) &&
9428       ((links_massiv && rechts_massiv) ||
9429        element == EL_EXPANDABLE_WALL_VERTICAL))
9430     Feld[ax][ay] = EL_WALL;
9431
9432   if (new_wall)
9433     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9434 }
9435
9436 static void MauerAblegerStahl(int ax, int ay)
9437 {
9438   int element = Feld[ax][ay];
9439   int graphic = el2img(element);
9440   boolean oben_frei = FALSE, unten_frei = FALSE;
9441   boolean links_frei = FALSE, rechts_frei = FALSE;
9442   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9443   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9444   boolean new_wall = FALSE;
9445
9446   if (IS_ANIMATED(graphic))
9447     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9448
9449   if (!MovDelay[ax][ay])        // start building new wall
9450     MovDelay[ax][ay] = 6;
9451
9452   if (MovDelay[ax][ay])         // wait some time before building new wall
9453   {
9454     MovDelay[ax][ay]--;
9455     if (MovDelay[ax][ay])
9456       return;
9457   }
9458
9459   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9460     oben_frei = TRUE;
9461   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9462     unten_frei = TRUE;
9463   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9464     links_frei = TRUE;
9465   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9466     rechts_frei = TRUE;
9467
9468   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9469       element == EL_EXPANDABLE_STEELWALL_ANY)
9470   {
9471     if (oben_frei)
9472     {
9473       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9474       Store[ax][ay-1] = element;
9475       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9476       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9477         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9478                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9479       new_wall = TRUE;
9480     }
9481     if (unten_frei)
9482     {
9483       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9484       Store[ax][ay+1] = element;
9485       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9486       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9487         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9488                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9489       new_wall = TRUE;
9490     }
9491   }
9492
9493   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9494       element == EL_EXPANDABLE_STEELWALL_ANY)
9495   {
9496     if (links_frei)
9497     {
9498       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9499       Store[ax-1][ay] = element;
9500       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9501       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9502         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9503                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9504       new_wall = TRUE;
9505     }
9506
9507     if (rechts_frei)
9508     {
9509       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9510       Store[ax+1][ay] = element;
9511       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9512       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9513         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9514                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9515       new_wall = TRUE;
9516     }
9517   }
9518
9519   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9520     oben_massiv = TRUE;
9521   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9522     unten_massiv = TRUE;
9523   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9524     links_massiv = TRUE;
9525   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9526     rechts_massiv = TRUE;
9527
9528   if (((oben_massiv && unten_massiv) ||
9529        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9530       ((links_massiv && rechts_massiv) ||
9531        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9532     Feld[ax][ay] = EL_STEELWALL;
9533
9534   if (new_wall)
9535     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9536 }
9537
9538 static void CheckForDragon(int x, int y)
9539 {
9540   int i, j;
9541   boolean dragon_found = FALSE;
9542   static int xy[4][2] =
9543   {
9544     { 0, -1 },
9545     { -1, 0 },
9546     { +1, 0 },
9547     { 0, +1 }
9548   };
9549
9550   for (i = 0; i < NUM_DIRECTIONS; i++)
9551   {
9552     for (j = 0; j < 4; j++)
9553     {
9554       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9555
9556       if (IN_LEV_FIELD(xx, yy) &&
9557           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9558       {
9559         if (Feld[xx][yy] == EL_DRAGON)
9560           dragon_found = TRUE;
9561       }
9562       else
9563         break;
9564     }
9565   }
9566
9567   if (!dragon_found)
9568   {
9569     for (i = 0; i < NUM_DIRECTIONS; i++)
9570     {
9571       for (j = 0; j < 3; j++)
9572       {
9573         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9574   
9575         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9576         {
9577           Feld[xx][yy] = EL_EMPTY;
9578           TEST_DrawLevelField(xx, yy);
9579         }
9580         else
9581           break;
9582       }
9583     }
9584   }
9585 }
9586
9587 static void InitBuggyBase(int x, int y)
9588 {
9589   int element = Feld[x][y];
9590   int activating_delay = FRAMES_PER_SECOND / 4;
9591
9592   ChangeDelay[x][y] =
9593     (element == EL_SP_BUGGY_BASE ?
9594      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9595      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9596      activating_delay :
9597      element == EL_SP_BUGGY_BASE_ACTIVE ?
9598      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9599 }
9600
9601 static void WarnBuggyBase(int x, int y)
9602 {
9603   int i;
9604   static int xy[4][2] =
9605   {
9606     { 0, -1 },
9607     { -1, 0 },
9608     { +1, 0 },
9609     { 0, +1 }
9610   };
9611
9612   for (i = 0; i < NUM_DIRECTIONS; i++)
9613   {
9614     int xx = x + xy[i][0];
9615     int yy = y + xy[i][1];
9616
9617     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9618     {
9619       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9620
9621       break;
9622     }
9623   }
9624 }
9625
9626 static void InitTrap(int x, int y)
9627 {
9628   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9629 }
9630
9631 static void ActivateTrap(int x, int y)
9632 {
9633   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9634 }
9635
9636 static void ChangeActiveTrap(int x, int y)
9637 {
9638   int graphic = IMG_TRAP_ACTIVE;
9639
9640   // if new animation frame was drawn, correct crumbled sand border
9641   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9642     TEST_DrawLevelFieldCrumbled(x, y);
9643 }
9644
9645 static int getSpecialActionElement(int element, int number, int base_element)
9646 {
9647   return (element != EL_EMPTY ? element :
9648           number != -1 ? base_element + number - 1 :
9649           EL_EMPTY);
9650 }
9651
9652 static int getModifiedActionNumber(int value_old, int operator, int operand,
9653                                    int value_min, int value_max)
9654 {
9655   int value_new = (operator == CA_MODE_SET      ? operand :
9656                    operator == CA_MODE_ADD      ? value_old + operand :
9657                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9658                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9659                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9660                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9661                    value_old);
9662
9663   return (value_new < value_min ? value_min :
9664           value_new > value_max ? value_max :
9665           value_new);
9666 }
9667
9668 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9669 {
9670   struct ElementInfo *ei = &element_info[element];
9671   struct ElementChangeInfo *change = &ei->change_page[page];
9672   int target_element = change->target_element;
9673   int action_type = change->action_type;
9674   int action_mode = change->action_mode;
9675   int action_arg = change->action_arg;
9676   int action_element = change->action_element;
9677   int i;
9678
9679   if (!change->has_action)
9680     return;
9681
9682   // ---------- determine action paramater values -----------------------------
9683
9684   int level_time_value =
9685     (level.time > 0 ? TimeLeft :
9686      TimePlayed);
9687
9688   int action_arg_element_raw =
9689     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9690      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9691      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9692      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9693      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9694      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9695      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9696      EL_EMPTY);
9697   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9698
9699   int action_arg_direction =
9700     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9701      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9702      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9703      change->actual_trigger_side :
9704      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9705      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9706      MV_NONE);
9707
9708   int action_arg_number_min =
9709     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9710      CA_ARG_MIN);
9711
9712   int action_arg_number_max =
9713     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9714      action_type == CA_SET_LEVEL_GEMS ? 999 :
9715      action_type == CA_SET_LEVEL_TIME ? 9999 :
9716      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9717      action_type == CA_SET_CE_VALUE ? 9999 :
9718      action_type == CA_SET_CE_SCORE ? 9999 :
9719      CA_ARG_MAX);
9720
9721   int action_arg_number_reset =
9722     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9723      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9724      action_type == CA_SET_LEVEL_TIME ? level.time :
9725      action_type == CA_SET_LEVEL_SCORE ? 0 :
9726      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9727      action_type == CA_SET_CE_SCORE ? 0 :
9728      0);
9729
9730   int action_arg_number =
9731     (action_arg <= CA_ARG_MAX ? action_arg :
9732      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9733      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9734      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9735      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9736      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9737      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9738      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9739      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9740      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9741      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9742      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9743      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9744      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9745      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9746      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9747      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9748      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9749      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9750      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9751      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9752      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9753      -1);
9754
9755   int action_arg_number_old =
9756     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9757      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9758      action_type == CA_SET_LEVEL_SCORE ? game.score :
9759      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9760      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9761      0);
9762
9763   int action_arg_number_new =
9764     getModifiedActionNumber(action_arg_number_old,
9765                             action_mode, action_arg_number,
9766                             action_arg_number_min, action_arg_number_max);
9767
9768   int trigger_player_bits =
9769     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9770      change->actual_trigger_player_bits : change->trigger_player);
9771
9772   int action_arg_player_bits =
9773     (action_arg >= CA_ARG_PLAYER_1 &&
9774      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9775      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9776      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9777      PLAYER_BITS_ANY);
9778
9779   // ---------- execute action  -----------------------------------------------
9780
9781   switch (action_type)
9782   {
9783     case CA_NO_ACTION:
9784     {
9785       return;
9786     }
9787
9788     // ---------- level actions  ----------------------------------------------
9789
9790     case CA_RESTART_LEVEL:
9791     {
9792       game.restart_level = TRUE;
9793
9794       break;
9795     }
9796
9797     case CA_SHOW_ENVELOPE:
9798     {
9799       int element = getSpecialActionElement(action_arg_element,
9800                                             action_arg_number, EL_ENVELOPE_1);
9801
9802       if (IS_ENVELOPE(element))
9803         local_player->show_envelope = element;
9804
9805       break;
9806     }
9807
9808     case CA_SET_LEVEL_TIME:
9809     {
9810       if (level.time > 0)       // only modify limited time value
9811       {
9812         TimeLeft = action_arg_number_new;
9813
9814         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9815
9816         DisplayGameControlValues();
9817
9818         if (!TimeLeft && setup.time_limit)
9819           for (i = 0; i < MAX_PLAYERS; i++)
9820             KillPlayer(&stored_player[i]);
9821       }
9822
9823       break;
9824     }
9825
9826     case CA_SET_LEVEL_SCORE:
9827     {
9828       game.score = action_arg_number_new;
9829
9830       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9831
9832       DisplayGameControlValues();
9833
9834       break;
9835     }
9836
9837     case CA_SET_LEVEL_GEMS:
9838     {
9839       game.gems_still_needed = action_arg_number_new;
9840
9841       game.snapshot.collected_item = TRUE;
9842
9843       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9844
9845       DisplayGameControlValues();
9846
9847       break;
9848     }
9849
9850     case CA_SET_LEVEL_WIND:
9851     {
9852       game.wind_direction = action_arg_direction;
9853
9854       break;
9855     }
9856
9857     case CA_SET_LEVEL_RANDOM_SEED:
9858     {
9859       // ensure that setting a new random seed while playing is predictable
9860       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9861
9862       break;
9863     }
9864
9865     // ---------- player actions  ---------------------------------------------
9866
9867     case CA_MOVE_PLAYER:
9868     {
9869       // automatically move to the next field in specified direction
9870       for (i = 0; i < MAX_PLAYERS; i++)
9871         if (trigger_player_bits & (1 << i))
9872           stored_player[i].programmed_action = action_arg_direction;
9873
9874       break;
9875     }
9876
9877     case CA_EXIT_PLAYER:
9878     {
9879       for (i = 0; i < MAX_PLAYERS; i++)
9880         if (action_arg_player_bits & (1 << i))
9881           ExitPlayer(&stored_player[i]);
9882
9883       if (game.players_still_needed == 0)
9884         LevelSolved();
9885
9886       break;
9887     }
9888
9889     case CA_KILL_PLAYER:
9890     {
9891       for (i = 0; i < MAX_PLAYERS; i++)
9892         if (action_arg_player_bits & (1 << i))
9893           KillPlayer(&stored_player[i]);
9894
9895       break;
9896     }
9897
9898     case CA_SET_PLAYER_KEYS:
9899     {
9900       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9901       int element = getSpecialActionElement(action_arg_element,
9902                                             action_arg_number, EL_KEY_1);
9903
9904       if (IS_KEY(element))
9905       {
9906         for (i = 0; i < MAX_PLAYERS; i++)
9907         {
9908           if (trigger_player_bits & (1 << i))
9909           {
9910             stored_player[i].key[KEY_NR(element)] = key_state;
9911
9912             DrawGameDoorValues();
9913           }
9914         }
9915       }
9916
9917       break;
9918     }
9919
9920     case CA_SET_PLAYER_SPEED:
9921     {
9922       for (i = 0; i < MAX_PLAYERS; i++)
9923       {
9924         if (trigger_player_bits & (1 << i))
9925         {
9926           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9927
9928           if (action_arg == CA_ARG_SPEED_FASTER &&
9929               stored_player[i].cannot_move)
9930           {
9931             action_arg_number = STEPSIZE_VERY_SLOW;
9932           }
9933           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9934                    action_arg == CA_ARG_SPEED_FASTER)
9935           {
9936             action_arg_number = 2;
9937             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9938                            CA_MODE_MULTIPLY);
9939           }
9940           else if (action_arg == CA_ARG_NUMBER_RESET)
9941           {
9942             action_arg_number = level.initial_player_stepsize[i];
9943           }
9944
9945           move_stepsize =
9946             getModifiedActionNumber(move_stepsize,
9947                                     action_mode,
9948                                     action_arg_number,
9949                                     action_arg_number_min,
9950                                     action_arg_number_max);
9951
9952           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9953         }
9954       }
9955
9956       break;
9957     }
9958
9959     case CA_SET_PLAYER_SHIELD:
9960     {
9961       for (i = 0; i < MAX_PLAYERS; i++)
9962       {
9963         if (trigger_player_bits & (1 << i))
9964         {
9965           if (action_arg == CA_ARG_SHIELD_OFF)
9966           {
9967             stored_player[i].shield_normal_time_left = 0;
9968             stored_player[i].shield_deadly_time_left = 0;
9969           }
9970           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9971           {
9972             stored_player[i].shield_normal_time_left = 999999;
9973           }
9974           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9975           {
9976             stored_player[i].shield_normal_time_left = 999999;
9977             stored_player[i].shield_deadly_time_left = 999999;
9978           }
9979         }
9980       }
9981
9982       break;
9983     }
9984
9985     case CA_SET_PLAYER_GRAVITY:
9986     {
9987       for (i = 0; i < MAX_PLAYERS; i++)
9988       {
9989         if (trigger_player_bits & (1 << i))
9990         {
9991           stored_player[i].gravity =
9992             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9993              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9994              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9995              stored_player[i].gravity);
9996         }
9997       }
9998
9999       break;
10000     }
10001
10002     case CA_SET_PLAYER_ARTWORK:
10003     {
10004       for (i = 0; i < MAX_PLAYERS; i++)
10005       {
10006         if (trigger_player_bits & (1 << i))
10007         {
10008           int artwork_element = action_arg_element;
10009
10010           if (action_arg == CA_ARG_ELEMENT_RESET)
10011             artwork_element =
10012               (level.use_artwork_element[i] ? level.artwork_element[i] :
10013                stored_player[i].element_nr);
10014
10015           if (stored_player[i].artwork_element != artwork_element)
10016             stored_player[i].Frame = 0;
10017
10018           stored_player[i].artwork_element = artwork_element;
10019
10020           SetPlayerWaiting(&stored_player[i], FALSE);
10021
10022           // set number of special actions for bored and sleeping animation
10023           stored_player[i].num_special_action_bored =
10024             get_num_special_action(artwork_element,
10025                                    ACTION_BORING_1, ACTION_BORING_LAST);
10026           stored_player[i].num_special_action_sleeping =
10027             get_num_special_action(artwork_element,
10028                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10029         }
10030       }
10031
10032       break;
10033     }
10034
10035     case CA_SET_PLAYER_INVENTORY:
10036     {
10037       for (i = 0; i < MAX_PLAYERS; i++)
10038       {
10039         struct PlayerInfo *player = &stored_player[i];
10040         int j, k;
10041
10042         if (trigger_player_bits & (1 << i))
10043         {
10044           int inventory_element = action_arg_element;
10045
10046           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10047               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10048               action_arg == CA_ARG_ELEMENT_ACTION)
10049           {
10050             int element = inventory_element;
10051             int collect_count = element_info[element].collect_count_initial;
10052
10053             if (!IS_CUSTOM_ELEMENT(element))
10054               collect_count = 1;
10055
10056             if (collect_count == 0)
10057               player->inventory_infinite_element = element;
10058             else
10059               for (k = 0; k < collect_count; k++)
10060                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10061                   player->inventory_element[player->inventory_size++] =
10062                     element;
10063           }
10064           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10065                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10066                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10067           {
10068             if (player->inventory_infinite_element != EL_UNDEFINED &&
10069                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10070                                      action_arg_element_raw))
10071               player->inventory_infinite_element = EL_UNDEFINED;
10072
10073             for (k = 0, j = 0; j < player->inventory_size; j++)
10074             {
10075               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10076                                         action_arg_element_raw))
10077                 player->inventory_element[k++] = player->inventory_element[j];
10078             }
10079
10080             player->inventory_size = k;
10081           }
10082           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10083           {
10084             if (player->inventory_size > 0)
10085             {
10086               for (j = 0; j < player->inventory_size - 1; j++)
10087                 player->inventory_element[j] = player->inventory_element[j + 1];
10088
10089               player->inventory_size--;
10090             }
10091           }
10092           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10093           {
10094             if (player->inventory_size > 0)
10095               player->inventory_size--;
10096           }
10097           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10098           {
10099             player->inventory_infinite_element = EL_UNDEFINED;
10100             player->inventory_size = 0;
10101           }
10102           else if (action_arg == CA_ARG_INVENTORY_RESET)
10103           {
10104             player->inventory_infinite_element = EL_UNDEFINED;
10105             player->inventory_size = 0;
10106
10107             if (level.use_initial_inventory[i])
10108             {
10109               for (j = 0; j < level.initial_inventory_size[i]; j++)
10110               {
10111                 int element = level.initial_inventory_content[i][j];
10112                 int collect_count = element_info[element].collect_count_initial;
10113
10114                 if (!IS_CUSTOM_ELEMENT(element))
10115                   collect_count = 1;
10116
10117                 if (collect_count == 0)
10118                   player->inventory_infinite_element = element;
10119                 else
10120                   for (k = 0; k < collect_count; k++)
10121                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10122                       player->inventory_element[player->inventory_size++] =
10123                         element;
10124               }
10125             }
10126           }
10127         }
10128       }
10129
10130       break;
10131     }
10132
10133     // ---------- CE actions  -------------------------------------------------
10134
10135     case CA_SET_CE_VALUE:
10136     {
10137       int last_ce_value = CustomValue[x][y];
10138
10139       CustomValue[x][y] = action_arg_number_new;
10140
10141       if (CustomValue[x][y] != last_ce_value)
10142       {
10143         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10144         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10145
10146         if (CustomValue[x][y] == 0)
10147         {
10148           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10149           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10150         }
10151       }
10152
10153       break;
10154     }
10155
10156     case CA_SET_CE_SCORE:
10157     {
10158       int last_ce_score = ei->collect_score;
10159
10160       ei->collect_score = action_arg_number_new;
10161
10162       if (ei->collect_score != last_ce_score)
10163       {
10164         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10165         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10166
10167         if (ei->collect_score == 0)
10168         {
10169           int xx, yy;
10170
10171           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10172           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10173
10174           /*
10175             This is a very special case that seems to be a mixture between
10176             CheckElementChange() and CheckTriggeredElementChange(): while
10177             the first one only affects single elements that are triggered
10178             directly, the second one affects multiple elements in the playfield
10179             that are triggered indirectly by another element. This is a third
10180             case: Changing the CE score always affects multiple identical CEs,
10181             so every affected CE must be checked, not only the single CE for
10182             which the CE score was changed in the first place (as every instance
10183             of that CE shares the same CE score, and therefore also can change)!
10184           */
10185           SCAN_PLAYFIELD(xx, yy)
10186           {
10187             if (Feld[xx][yy] == element)
10188               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10189                                  CE_SCORE_GETS_ZERO);
10190           }
10191         }
10192       }
10193
10194       break;
10195     }
10196
10197     case CA_SET_CE_ARTWORK:
10198     {
10199       int artwork_element = action_arg_element;
10200       boolean reset_frame = FALSE;
10201       int xx, yy;
10202
10203       if (action_arg == CA_ARG_ELEMENT_RESET)
10204         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10205                            element);
10206
10207       if (ei->gfx_element != artwork_element)
10208         reset_frame = TRUE;
10209
10210       ei->gfx_element = artwork_element;
10211
10212       SCAN_PLAYFIELD(xx, yy)
10213       {
10214         if (Feld[xx][yy] == element)
10215         {
10216           if (reset_frame)
10217           {
10218             ResetGfxAnimation(xx, yy);
10219             ResetRandomAnimationValue(xx, yy);
10220           }
10221
10222           TEST_DrawLevelField(xx, yy);
10223         }
10224       }
10225
10226       break;
10227     }
10228
10229     // ---------- engine actions  ---------------------------------------------
10230
10231     case CA_SET_ENGINE_SCAN_MODE:
10232     {
10233       InitPlayfieldScanMode(action_arg);
10234
10235       break;
10236     }
10237
10238     default:
10239       break;
10240   }
10241 }
10242
10243 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10244 {
10245   int old_element = Feld[x][y];
10246   int new_element = GetElementFromGroupElement(element);
10247   int previous_move_direction = MovDir[x][y];
10248   int last_ce_value = CustomValue[x][y];
10249   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10250   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10251   boolean add_player_onto_element = (new_element_is_player &&
10252                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10253                                      IS_WALKABLE(old_element));
10254
10255   if (!add_player_onto_element)
10256   {
10257     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10258       RemoveMovingField(x, y);
10259     else
10260       RemoveField(x, y);
10261
10262     Feld[x][y] = new_element;
10263
10264     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10265       MovDir[x][y] = previous_move_direction;
10266
10267     if (element_info[new_element].use_last_ce_value)
10268       CustomValue[x][y] = last_ce_value;
10269
10270     InitField_WithBug1(x, y, FALSE);
10271
10272     new_element = Feld[x][y];   // element may have changed
10273
10274     ResetGfxAnimation(x, y);
10275     ResetRandomAnimationValue(x, y);
10276
10277     TEST_DrawLevelField(x, y);
10278
10279     if (GFX_CRUMBLED(new_element))
10280       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10281   }
10282
10283   // check if element under the player changes from accessible to unaccessible
10284   // (needed for special case of dropping element which then changes)
10285   // (must be checked after creating new element for walkable group elements)
10286   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10287       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10288   {
10289     Bang(x, y);
10290
10291     return;
10292   }
10293
10294   // "ChangeCount" not set yet to allow "entered by player" change one time
10295   if (new_element_is_player)
10296     RelocatePlayer(x, y, new_element);
10297
10298   if (is_change)
10299     ChangeCount[x][y]++;        // count number of changes in the same frame
10300
10301   TestIfBadThingTouchesPlayer(x, y);
10302   TestIfPlayerTouchesCustomElement(x, y);
10303   TestIfElementTouchesCustomElement(x, y);
10304 }
10305
10306 static void CreateField(int x, int y, int element)
10307 {
10308   CreateFieldExt(x, y, element, FALSE);
10309 }
10310
10311 static void CreateElementFromChange(int x, int y, int element)
10312 {
10313   element = GET_VALID_RUNTIME_ELEMENT(element);
10314
10315   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10316   {
10317     int old_element = Feld[x][y];
10318
10319     // prevent changed element from moving in same engine frame
10320     // unless both old and new element can either fall or move
10321     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10322         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10323       Stop[x][y] = TRUE;
10324   }
10325
10326   CreateFieldExt(x, y, element, TRUE);
10327 }
10328
10329 static boolean ChangeElement(int x, int y, int element, int page)
10330 {
10331   struct ElementInfo *ei = &element_info[element];
10332   struct ElementChangeInfo *change = &ei->change_page[page];
10333   int ce_value = CustomValue[x][y];
10334   int ce_score = ei->collect_score;
10335   int target_element;
10336   int old_element = Feld[x][y];
10337
10338   // always use default change event to prevent running into a loop
10339   if (ChangeEvent[x][y] == -1)
10340     ChangeEvent[x][y] = CE_DELAY;
10341
10342   if (ChangeEvent[x][y] == CE_DELAY)
10343   {
10344     // reset actual trigger element, trigger player and action element
10345     change->actual_trigger_element = EL_EMPTY;
10346     change->actual_trigger_player = EL_EMPTY;
10347     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10348     change->actual_trigger_side = CH_SIDE_NONE;
10349     change->actual_trigger_ce_value = 0;
10350     change->actual_trigger_ce_score = 0;
10351   }
10352
10353   // do not change elements more than a specified maximum number of changes
10354   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10355     return FALSE;
10356
10357   ChangeCount[x][y]++;          // count number of changes in the same frame
10358
10359   if (change->explode)
10360   {
10361     Bang(x, y);
10362
10363     return TRUE;
10364   }
10365
10366   if (change->use_target_content)
10367   {
10368     boolean complete_replace = TRUE;
10369     boolean can_replace[3][3];
10370     int xx, yy;
10371
10372     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10373     {
10374       boolean is_empty;
10375       boolean is_walkable;
10376       boolean is_diggable;
10377       boolean is_collectible;
10378       boolean is_removable;
10379       boolean is_destructible;
10380       int ex = x + xx - 1;
10381       int ey = y + yy - 1;
10382       int content_element = change->target_content.e[xx][yy];
10383       int e;
10384
10385       can_replace[xx][yy] = TRUE;
10386
10387       if (ex == x && ey == y)   // do not check changing element itself
10388         continue;
10389
10390       if (content_element == EL_EMPTY_SPACE)
10391       {
10392         can_replace[xx][yy] = FALSE;    // do not replace border with space
10393
10394         continue;
10395       }
10396
10397       if (!IN_LEV_FIELD(ex, ey))
10398       {
10399         can_replace[xx][yy] = FALSE;
10400         complete_replace = FALSE;
10401
10402         continue;
10403       }
10404
10405       e = Feld[ex][ey];
10406
10407       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10408         e = MovingOrBlocked2Element(ex, ey);
10409
10410       is_empty = (IS_FREE(ex, ey) ||
10411                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10412
10413       is_walkable     = (is_empty || IS_WALKABLE(e));
10414       is_diggable     = (is_empty || IS_DIGGABLE(e));
10415       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10416       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10417       is_removable    = (is_diggable || is_collectible);
10418
10419       can_replace[xx][yy] =
10420         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10421           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10422           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10423           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10424           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10425           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10426          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10427
10428       if (!can_replace[xx][yy])
10429         complete_replace = FALSE;
10430     }
10431
10432     if (!change->only_if_complete || complete_replace)
10433     {
10434       boolean something_has_changed = FALSE;
10435
10436       if (change->only_if_complete && change->use_random_replace &&
10437           RND(100) < change->random_percentage)
10438         return FALSE;
10439
10440       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10441       {
10442         int ex = x + xx - 1;
10443         int ey = y + yy - 1;
10444         int content_element;
10445
10446         if (can_replace[xx][yy] && (!change->use_random_replace ||
10447                                     RND(100) < change->random_percentage))
10448         {
10449           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10450             RemoveMovingField(ex, ey);
10451
10452           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10453
10454           content_element = change->target_content.e[xx][yy];
10455           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10456                                               ce_value, ce_score);
10457
10458           CreateElementFromChange(ex, ey, target_element);
10459
10460           something_has_changed = TRUE;
10461
10462           // for symmetry reasons, freeze newly created border elements
10463           if (ex != x || ey != y)
10464             Stop[ex][ey] = TRUE;        // no more moving in this frame
10465         }
10466       }
10467
10468       if (something_has_changed)
10469       {
10470         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10471         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10472       }
10473     }
10474   }
10475   else
10476   {
10477     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10478                                         ce_value, ce_score);
10479
10480     if (element == EL_DIAGONAL_GROWING ||
10481         element == EL_DIAGONAL_SHRINKING)
10482     {
10483       target_element = Store[x][y];
10484
10485       Store[x][y] = EL_EMPTY;
10486     }
10487
10488     CreateElementFromChange(x, y, target_element);
10489
10490     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10491     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10492   }
10493
10494   // this uses direct change before indirect change
10495   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10496
10497   return TRUE;
10498 }
10499
10500 static void HandleElementChange(int x, int y, int page)
10501 {
10502   int element = MovingOrBlocked2Element(x, y);
10503   struct ElementInfo *ei = &element_info[element];
10504   struct ElementChangeInfo *change = &ei->change_page[page];
10505   boolean handle_action_before_change = FALSE;
10506
10507 #ifdef DEBUG
10508   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10509       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10510   {
10511     printf("\n\n");
10512     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10513            x, y, element, element_info[element].token_name);
10514     printf("HandleElementChange(): This should never happen!\n");
10515     printf("\n\n");
10516   }
10517 #endif
10518
10519   // this can happen with classic bombs on walkable, changing elements
10520   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10521   {
10522     return;
10523   }
10524
10525   if (ChangeDelay[x][y] == 0)           // initialize element change
10526   {
10527     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10528
10529     if (change->can_change)
10530     {
10531       // !!! not clear why graphic animation should be reset at all here !!!
10532       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10533       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10534
10535       /*
10536         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10537
10538         When using an animation frame delay of 1 (this only happens with
10539         "sp_zonk.moving.left/right" in the classic graphics), the default
10540         (non-moving) animation shows wrong animation frames (while the
10541         moving animation, like "sp_zonk.moving.left/right", is correct,
10542         so this graphical bug never shows up with the classic graphics).
10543         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10544         be drawn instead of the correct frames 0,1,2,3. This is caused by
10545         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10546         an element change: First when the change delay ("ChangeDelay[][]")
10547         counter has reached zero after decrementing, then a second time in
10548         the next frame (after "GfxFrame[][]" was already incremented) when
10549         "ChangeDelay[][]" is reset to the initial delay value again.
10550
10551         This causes frame 0 to be drawn twice, while the last frame won't
10552         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10553
10554         As some animations may already be cleverly designed around this bug
10555         (at least the "Snake Bite" snake tail animation does this), it cannot
10556         simply be fixed here without breaking such existing animations.
10557         Unfortunately, it cannot easily be detected if a graphics set was
10558         designed "before" or "after" the bug was fixed. As a workaround,
10559         a new graphics set option "game.graphics_engine_version" was added
10560         to be able to specify the game's major release version for which the
10561         graphics set was designed, which can then be used to decide if the
10562         bugfix should be used (version 4 and above) or not (version 3 or
10563         below, or if no version was specified at all, as with old sets).
10564
10565         (The wrong/fixed animation frames can be tested with the test level set
10566         "test_gfxframe" and level "000", which contains a specially prepared
10567         custom element at level position (x/y) == (11/9) which uses the zonk
10568         animation mentioned above. Using "game.graphics_engine_version: 4"
10569         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10570         This can also be seen from the debug output for this test element.)
10571       */
10572
10573       // when a custom element is about to change (for example by change delay),
10574       // do not reset graphic animation when the custom element is moving
10575       if (game.graphics_engine_version < 4 &&
10576           !IS_MOVING(x, y))
10577       {
10578         ResetGfxAnimation(x, y);
10579         ResetRandomAnimationValue(x, y);
10580       }
10581
10582       if (change->pre_change_function)
10583         change->pre_change_function(x, y);
10584     }
10585   }
10586
10587   ChangeDelay[x][y]--;
10588
10589   if (ChangeDelay[x][y] != 0)           // continue element change
10590   {
10591     if (change->can_change)
10592     {
10593       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10594
10595       if (IS_ANIMATED(graphic))
10596         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10597
10598       if (change->change_function)
10599         change->change_function(x, y);
10600     }
10601   }
10602   else                                  // finish element change
10603   {
10604     if (ChangePage[x][y] != -1)         // remember page from delayed change
10605     {
10606       page = ChangePage[x][y];
10607       ChangePage[x][y] = -1;
10608
10609       change = &ei->change_page[page];
10610     }
10611
10612     if (IS_MOVING(x, y))                // never change a running system ;-)
10613     {
10614       ChangeDelay[x][y] = 1;            // try change after next move step
10615       ChangePage[x][y] = page;          // remember page to use for change
10616
10617       return;
10618     }
10619
10620     // special case: set new level random seed before changing element
10621     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10622       handle_action_before_change = TRUE;
10623
10624     if (change->has_action && handle_action_before_change)
10625       ExecuteCustomElementAction(x, y, element, page);
10626
10627     if (change->can_change)
10628     {
10629       if (ChangeElement(x, y, element, page))
10630       {
10631         if (change->post_change_function)
10632           change->post_change_function(x, y);
10633       }
10634     }
10635
10636     if (change->has_action && !handle_action_before_change)
10637       ExecuteCustomElementAction(x, y, element, page);
10638   }
10639 }
10640
10641 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10642                                               int trigger_element,
10643                                               int trigger_event,
10644                                               int trigger_player,
10645                                               int trigger_side,
10646                                               int trigger_page)
10647 {
10648   boolean change_done_any = FALSE;
10649   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10650   int i;
10651
10652   if (!(trigger_events[trigger_element][trigger_event]))
10653     return FALSE;
10654
10655   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10656
10657   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10658   {
10659     int element = EL_CUSTOM_START + i;
10660     boolean change_done = FALSE;
10661     int p;
10662
10663     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10664         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10665       continue;
10666
10667     for (p = 0; p < element_info[element].num_change_pages; p++)
10668     {
10669       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10670
10671       if (change->can_change_or_has_action &&
10672           change->has_event[trigger_event] &&
10673           change->trigger_side & trigger_side &&
10674           change->trigger_player & trigger_player &&
10675           change->trigger_page & trigger_page_bits &&
10676           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10677       {
10678         change->actual_trigger_element = trigger_element;
10679         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10680         change->actual_trigger_player_bits = trigger_player;
10681         change->actual_trigger_side = trigger_side;
10682         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10683         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10684
10685         if ((change->can_change && !change_done) || change->has_action)
10686         {
10687           int x, y;
10688
10689           SCAN_PLAYFIELD(x, y)
10690           {
10691             if (Feld[x][y] == element)
10692             {
10693               if (change->can_change && !change_done)
10694               {
10695                 // if element already changed in this frame, not only prevent
10696                 // another element change (checked in ChangeElement()), but
10697                 // also prevent additional element actions for this element
10698
10699                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10700                     !level.use_action_after_change_bug)
10701                   continue;
10702
10703                 ChangeDelay[x][y] = 1;
10704                 ChangeEvent[x][y] = trigger_event;
10705
10706                 HandleElementChange(x, y, p);
10707               }
10708               else if (change->has_action)
10709               {
10710                 // if element already changed in this frame, not only prevent
10711                 // another element change (checked in ChangeElement()), but
10712                 // also prevent additional element actions for this element
10713
10714                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10715                     !level.use_action_after_change_bug)
10716                   continue;
10717
10718                 ExecuteCustomElementAction(x, y, element, p);
10719                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10720               }
10721             }
10722           }
10723
10724           if (change->can_change)
10725           {
10726             change_done = TRUE;
10727             change_done_any = TRUE;
10728           }
10729         }
10730       }
10731     }
10732   }
10733
10734   RECURSION_LOOP_DETECTION_END();
10735
10736   return change_done_any;
10737 }
10738
10739 static boolean CheckElementChangeExt(int x, int y,
10740                                      int element,
10741                                      int trigger_element,
10742                                      int trigger_event,
10743                                      int trigger_player,
10744                                      int trigger_side)
10745 {
10746   boolean change_done = FALSE;
10747   int p;
10748
10749   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10750       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10751     return FALSE;
10752
10753   if (Feld[x][y] == EL_BLOCKED)
10754   {
10755     Blocked2Moving(x, y, &x, &y);
10756     element = Feld[x][y];
10757   }
10758
10759   // check if element has already changed or is about to change after moving
10760   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10761        Feld[x][y] != element) ||
10762
10763       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10764        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10765         ChangePage[x][y] != -1)))
10766     return FALSE;
10767
10768   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10769
10770   for (p = 0; p < element_info[element].num_change_pages; p++)
10771   {
10772     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10773
10774     /* check trigger element for all events where the element that is checked
10775        for changing interacts with a directly adjacent element -- this is
10776        different to element changes that affect other elements to change on the
10777        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10778     boolean check_trigger_element =
10779       (trigger_event == CE_TOUCHING_X ||
10780        trigger_event == CE_HITTING_X ||
10781        trigger_event == CE_HIT_BY_X ||
10782        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10783
10784     if (change->can_change_or_has_action &&
10785         change->has_event[trigger_event] &&
10786         change->trigger_side & trigger_side &&
10787         change->trigger_player & trigger_player &&
10788         (!check_trigger_element ||
10789          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10790     {
10791       change->actual_trigger_element = trigger_element;
10792       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10793       change->actual_trigger_player_bits = trigger_player;
10794       change->actual_trigger_side = trigger_side;
10795       change->actual_trigger_ce_value = CustomValue[x][y];
10796       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10797
10798       // special case: trigger element not at (x,y) position for some events
10799       if (check_trigger_element)
10800       {
10801         static struct
10802         {
10803           int dx, dy;
10804         } move_xy[] =
10805           {
10806             {  0,  0 },
10807             { -1,  0 },
10808             { +1,  0 },
10809             {  0,  0 },
10810             {  0, -1 },
10811             {  0,  0 }, { 0, 0 }, { 0, 0 },
10812             {  0, +1 }
10813           };
10814
10815         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10816         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10817
10818         change->actual_trigger_ce_value = CustomValue[xx][yy];
10819         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10820       }
10821
10822       if (change->can_change && !change_done)
10823       {
10824         ChangeDelay[x][y] = 1;
10825         ChangeEvent[x][y] = trigger_event;
10826
10827         HandleElementChange(x, y, p);
10828
10829         change_done = TRUE;
10830       }
10831       else if (change->has_action)
10832       {
10833         ExecuteCustomElementAction(x, y, element, p);
10834         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10835       }
10836     }
10837   }
10838
10839   RECURSION_LOOP_DETECTION_END();
10840
10841   return change_done;
10842 }
10843
10844 static void PlayPlayerSound(struct PlayerInfo *player)
10845 {
10846   int jx = player->jx, jy = player->jy;
10847   int sound_element = player->artwork_element;
10848   int last_action = player->last_action_waiting;
10849   int action = player->action_waiting;
10850
10851   if (player->is_waiting)
10852   {
10853     if (action != last_action)
10854       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10855     else
10856       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10857   }
10858   else
10859   {
10860     if (action != last_action)
10861       StopSound(element_info[sound_element].sound[last_action]);
10862
10863     if (last_action == ACTION_SLEEPING)
10864       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10865   }
10866 }
10867
10868 static void PlayAllPlayersSound(void)
10869 {
10870   int i;
10871
10872   for (i = 0; i < MAX_PLAYERS; i++)
10873     if (stored_player[i].active)
10874       PlayPlayerSound(&stored_player[i]);
10875 }
10876
10877 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10878 {
10879   boolean last_waiting = player->is_waiting;
10880   int move_dir = player->MovDir;
10881
10882   player->dir_waiting = move_dir;
10883   player->last_action_waiting = player->action_waiting;
10884
10885   if (is_waiting)
10886   {
10887     if (!last_waiting)          // not waiting -> waiting
10888     {
10889       player->is_waiting = TRUE;
10890
10891       player->frame_counter_bored =
10892         FrameCounter +
10893         game.player_boring_delay_fixed +
10894         GetSimpleRandom(game.player_boring_delay_random);
10895       player->frame_counter_sleeping =
10896         FrameCounter +
10897         game.player_sleeping_delay_fixed +
10898         GetSimpleRandom(game.player_sleeping_delay_random);
10899
10900       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10901     }
10902
10903     if (game.player_sleeping_delay_fixed +
10904         game.player_sleeping_delay_random > 0 &&
10905         player->anim_delay_counter == 0 &&
10906         player->post_delay_counter == 0 &&
10907         FrameCounter >= player->frame_counter_sleeping)
10908       player->is_sleeping = TRUE;
10909     else if (game.player_boring_delay_fixed +
10910              game.player_boring_delay_random > 0 &&
10911              FrameCounter >= player->frame_counter_bored)
10912       player->is_bored = TRUE;
10913
10914     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10915                               player->is_bored ? ACTION_BORING :
10916                               ACTION_WAITING);
10917
10918     if (player->is_sleeping && player->use_murphy)
10919     {
10920       // special case for sleeping Murphy when leaning against non-free tile
10921
10922       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10923           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10924            !IS_MOVING(player->jx - 1, player->jy)))
10925         move_dir = MV_LEFT;
10926       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10927                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10928                 !IS_MOVING(player->jx + 1, player->jy)))
10929         move_dir = MV_RIGHT;
10930       else
10931         player->is_sleeping = FALSE;
10932
10933       player->dir_waiting = move_dir;
10934     }
10935
10936     if (player->is_sleeping)
10937     {
10938       if (player->num_special_action_sleeping > 0)
10939       {
10940         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10941         {
10942           int last_special_action = player->special_action_sleeping;
10943           int num_special_action = player->num_special_action_sleeping;
10944           int special_action =
10945             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10946              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10947              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10948              last_special_action + 1 : ACTION_SLEEPING);
10949           int special_graphic =
10950             el_act_dir2img(player->artwork_element, special_action, move_dir);
10951
10952           player->anim_delay_counter =
10953             graphic_info[special_graphic].anim_delay_fixed +
10954             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10955           player->post_delay_counter =
10956             graphic_info[special_graphic].post_delay_fixed +
10957             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10958
10959           player->special_action_sleeping = special_action;
10960         }
10961
10962         if (player->anim_delay_counter > 0)
10963         {
10964           player->action_waiting = player->special_action_sleeping;
10965           player->anim_delay_counter--;
10966         }
10967         else if (player->post_delay_counter > 0)
10968         {
10969           player->post_delay_counter--;
10970         }
10971       }
10972     }
10973     else if (player->is_bored)
10974     {
10975       if (player->num_special_action_bored > 0)
10976       {
10977         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10978         {
10979           int special_action =
10980             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10981           int special_graphic =
10982             el_act_dir2img(player->artwork_element, special_action, move_dir);
10983
10984           player->anim_delay_counter =
10985             graphic_info[special_graphic].anim_delay_fixed +
10986             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10987           player->post_delay_counter =
10988             graphic_info[special_graphic].post_delay_fixed +
10989             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10990
10991           player->special_action_bored = special_action;
10992         }
10993
10994         if (player->anim_delay_counter > 0)
10995         {
10996           player->action_waiting = player->special_action_bored;
10997           player->anim_delay_counter--;
10998         }
10999         else if (player->post_delay_counter > 0)
11000         {
11001           player->post_delay_counter--;
11002         }
11003       }
11004     }
11005   }
11006   else if (last_waiting)        // waiting -> not waiting
11007   {
11008     player->is_waiting = FALSE;
11009     player->is_bored = FALSE;
11010     player->is_sleeping = FALSE;
11011
11012     player->frame_counter_bored = -1;
11013     player->frame_counter_sleeping = -1;
11014
11015     player->anim_delay_counter = 0;
11016     player->post_delay_counter = 0;
11017
11018     player->dir_waiting = player->MovDir;
11019     player->action_waiting = ACTION_DEFAULT;
11020
11021     player->special_action_bored = ACTION_DEFAULT;
11022     player->special_action_sleeping = ACTION_DEFAULT;
11023   }
11024 }
11025
11026 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11027 {
11028   if ((!player->is_moving  && player->was_moving) ||
11029       (player->MovPos == 0 && player->was_moving) ||
11030       (player->is_snapping && !player->was_snapping) ||
11031       (player->is_dropping && !player->was_dropping))
11032   {
11033     if (!CheckSaveEngineSnapshotToList())
11034       return;
11035
11036     player->was_moving = FALSE;
11037     player->was_snapping = TRUE;
11038     player->was_dropping = TRUE;
11039   }
11040   else
11041   {
11042     if (player->is_moving)
11043       player->was_moving = TRUE;
11044
11045     if (!player->is_snapping)
11046       player->was_snapping = FALSE;
11047
11048     if (!player->is_dropping)
11049       player->was_dropping = FALSE;
11050   }
11051 }
11052
11053 static void CheckSingleStepMode(struct PlayerInfo *player)
11054 {
11055   if (tape.single_step && tape.recording && !tape.pausing)
11056   {
11057     /* as it is called "single step mode", just return to pause mode when the
11058        player stopped moving after one tile (or never starts moving at all) */
11059     if (!player->is_moving &&
11060         !player->is_pushing &&
11061         !player->is_dropping_pressed)
11062       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11063   }
11064
11065   CheckSaveEngineSnapshot(player);
11066 }
11067
11068 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11069 {
11070   int left      = player_action & JOY_LEFT;
11071   int right     = player_action & JOY_RIGHT;
11072   int up        = player_action & JOY_UP;
11073   int down      = player_action & JOY_DOWN;
11074   int button1   = player_action & JOY_BUTTON_1;
11075   int button2   = player_action & JOY_BUTTON_2;
11076   int dx        = (left ? -1 : right ? 1 : 0);
11077   int dy        = (up   ? -1 : down  ? 1 : 0);
11078
11079   if (!player->active || tape.pausing)
11080     return 0;
11081
11082   if (player_action)
11083   {
11084     if (button1)
11085       SnapField(player, dx, dy);
11086     else
11087     {
11088       if (button2)
11089         DropElement(player);
11090
11091       MovePlayer(player, dx, dy);
11092     }
11093
11094     CheckSingleStepMode(player);
11095
11096     SetPlayerWaiting(player, FALSE);
11097
11098     return player_action;
11099   }
11100   else
11101   {
11102     // no actions for this player (no input at player's configured device)
11103
11104     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11105     SnapField(player, 0, 0);
11106     CheckGravityMovementWhenNotMoving(player);
11107
11108     if (player->MovPos == 0)
11109       SetPlayerWaiting(player, TRUE);
11110
11111     if (player->MovPos == 0)    // needed for tape.playing
11112       player->is_moving = FALSE;
11113
11114     player->is_dropping = FALSE;
11115     player->is_dropping_pressed = FALSE;
11116     player->drop_pressed_delay = 0;
11117
11118     CheckSingleStepMode(player);
11119
11120     return 0;
11121   }
11122 }
11123
11124 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11125                                          byte *tape_action)
11126 {
11127   if (!tape.use_mouse)
11128     return;
11129
11130   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11131   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11132   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11133 }
11134
11135 static void SetTapeActionFromMouseAction(byte *tape_action,
11136                                          struct MouseActionInfo *mouse_action)
11137 {
11138   if (!tape.use_mouse)
11139     return;
11140
11141   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11142   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11143   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11144 }
11145
11146 static void CheckLevelSolved(void)
11147 {
11148   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11149   {
11150     if (game_em.level_solved &&
11151         !game_em.game_over)                             // game won
11152     {
11153       LevelSolved();
11154
11155       game_em.game_over = TRUE;
11156
11157       game.all_players_gone = TRUE;
11158     }
11159
11160     if (game_em.game_over)                              // game lost
11161       game.all_players_gone = TRUE;
11162   }
11163   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11164   {
11165     if (game_sp.level_solved &&
11166         !game_sp.game_over)                             // game won
11167     {
11168       LevelSolved();
11169
11170       game_sp.game_over = TRUE;
11171
11172       game.all_players_gone = TRUE;
11173     }
11174
11175     if (game_sp.game_over)                              // game lost
11176       game.all_players_gone = TRUE;
11177   }
11178   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11179   {
11180     if (game_mm.level_solved &&
11181         !game_mm.game_over)                             // game won
11182     {
11183       LevelSolved();
11184
11185       game_mm.game_over = TRUE;
11186
11187       game.all_players_gone = TRUE;
11188     }
11189
11190     if (game_mm.game_over)                              // game lost
11191       game.all_players_gone = TRUE;
11192   }
11193 }
11194
11195 static void CheckLevelTime(void)
11196 {
11197   int i;
11198
11199   if (TimeFrames >= FRAMES_PER_SECOND)
11200   {
11201     TimeFrames = 0;
11202     TapeTime++;
11203
11204     for (i = 0; i < MAX_PLAYERS; i++)
11205     {
11206       struct PlayerInfo *player = &stored_player[i];
11207
11208       if (SHIELD_ON(player))
11209       {
11210         player->shield_normal_time_left--;
11211
11212         if (player->shield_deadly_time_left > 0)
11213           player->shield_deadly_time_left--;
11214       }
11215     }
11216
11217     if (!game.LevelSolved && !level.use_step_counter)
11218     {
11219       TimePlayed++;
11220
11221       if (TimeLeft > 0)
11222       {
11223         TimeLeft--;
11224
11225         if (TimeLeft <= 10 && setup.time_limit)
11226           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11227
11228         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11229            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11230
11231         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11232
11233         if (!TimeLeft && setup.time_limit)
11234         {
11235           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11236             level.native_em_level->lev->killed_out_of_time = TRUE;
11237           else
11238             for (i = 0; i < MAX_PLAYERS; i++)
11239               KillPlayer(&stored_player[i]);
11240         }
11241       }
11242       else if (game.no_time_limit && !game.all_players_gone)
11243       {
11244         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11245       }
11246
11247       level.native_em_level->lev->time =
11248         (game.no_time_limit ? TimePlayed : TimeLeft);
11249     }
11250
11251     if (tape.recording || tape.playing)
11252       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11253   }
11254
11255   if (tape.recording || tape.playing)
11256     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11257
11258   UpdateAndDisplayGameControlValues();
11259 }
11260
11261 void AdvanceFrameAndPlayerCounters(int player_nr)
11262 {
11263   int i;
11264
11265   // advance frame counters (global frame counter and time frame counter)
11266   FrameCounter++;
11267   TimeFrames++;
11268
11269   // advance player counters (counters for move delay, move animation etc.)
11270   for (i = 0; i < MAX_PLAYERS; i++)
11271   {
11272     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11273     int move_delay_value = stored_player[i].move_delay_value;
11274     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11275
11276     if (!advance_player_counters)       // not all players may be affected
11277       continue;
11278
11279     if (move_frames == 0)       // less than one move per game frame
11280     {
11281       int stepsize = TILEX / move_delay_value;
11282       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11283       int count = (stored_player[i].is_moving ?
11284                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11285
11286       if (count % delay == 0)
11287         move_frames = 1;
11288     }
11289
11290     stored_player[i].Frame += move_frames;
11291
11292     if (stored_player[i].MovPos != 0)
11293       stored_player[i].StepFrame += move_frames;
11294
11295     if (stored_player[i].move_delay > 0)
11296       stored_player[i].move_delay--;
11297
11298     // due to bugs in previous versions, counter must count up, not down
11299     if (stored_player[i].push_delay != -1)
11300       stored_player[i].push_delay++;
11301
11302     if (stored_player[i].drop_delay > 0)
11303       stored_player[i].drop_delay--;
11304
11305     if (stored_player[i].is_dropping_pressed)
11306       stored_player[i].drop_pressed_delay++;
11307   }
11308 }
11309
11310 void StartGameActions(boolean init_network_game, boolean record_tape,
11311                       int random_seed)
11312 {
11313   unsigned int new_random_seed = InitRND(random_seed);
11314
11315   if (record_tape)
11316     TapeStartRecording(new_random_seed);
11317
11318   if (init_network_game)
11319   {
11320     SendToServer_LevelFile();
11321     SendToServer_StartPlaying();
11322
11323     return;
11324   }
11325
11326   InitGame();
11327 }
11328
11329 static void GameActionsExt(void)
11330 {
11331 #if 0
11332   static unsigned int game_frame_delay = 0;
11333 #endif
11334   unsigned int game_frame_delay_value;
11335   byte *recorded_player_action;
11336   byte summarized_player_action = 0;
11337   byte tape_action[MAX_PLAYERS];
11338   int i;
11339
11340   // detect endless loops, caused by custom element programming
11341   if (recursion_loop_detected && recursion_loop_depth == 0)
11342   {
11343     char *message = getStringCat3("Internal Error! Element ",
11344                                   EL_NAME(recursion_loop_element),
11345                                   " caused endless loop! Quit the game?");
11346
11347     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11348           EL_NAME(recursion_loop_element));
11349
11350     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11351
11352     recursion_loop_detected = FALSE;    // if game should be continued
11353
11354     free(message);
11355
11356     return;
11357   }
11358
11359   if (game.restart_level)
11360     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11361
11362   CheckLevelSolved();
11363
11364   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11365     GameWon();
11366
11367   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11368     TapeStop();
11369
11370   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11371     return;
11372
11373   game_frame_delay_value =
11374     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11375
11376   if (tape.playing && tape.warp_forward && !tape.pausing)
11377     game_frame_delay_value = 0;
11378
11379   SetVideoFrameDelay(game_frame_delay_value);
11380
11381   // (de)activate virtual buttons depending on current game status
11382   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11383   {
11384     if (game.all_players_gone)  // if no players there to be controlled anymore
11385       SetOverlayActive(FALSE);
11386     else if (!tape.playing)     // if game continues after tape stopped playing
11387       SetOverlayActive(TRUE);
11388   }
11389
11390 #if 0
11391 #if 0
11392   // ---------- main game synchronization point ----------
11393
11394   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11395
11396   printf("::: skip == %d\n", skip);
11397
11398 #else
11399   // ---------- main game synchronization point ----------
11400
11401   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11402 #endif
11403 #endif
11404
11405   if (network_playing && !network_player_action_received)
11406   {
11407     // try to get network player actions in time
11408
11409     // last chance to get network player actions without main loop delay
11410     HandleNetworking();
11411
11412     // game was quit by network peer
11413     if (game_status != GAME_MODE_PLAYING)
11414       return;
11415
11416     // check if network player actions still missing and game still running
11417     if (!network_player_action_received && !checkGameEnded())
11418       return;           // failed to get network player actions in time
11419
11420     // do not yet reset "network_player_action_received" (for tape.pausing)
11421   }
11422
11423   if (tape.pausing)
11424     return;
11425
11426   // at this point we know that we really continue executing the game
11427
11428   network_player_action_received = FALSE;
11429
11430   // when playing tape, read previously recorded player input from tape data
11431   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11432
11433   local_player->effective_mouse_action = local_player->mouse_action;
11434
11435   if (recorded_player_action != NULL)
11436     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11437                                  recorded_player_action);
11438
11439   // TapePlayAction() may return NULL when toggling to "pause before death"
11440   if (tape.pausing)
11441     return;
11442
11443   if (tape.set_centered_player)
11444   {
11445     game.centered_player_nr_next = tape.centered_player_nr_next;
11446     game.set_centered_player = TRUE;
11447   }
11448
11449   for (i = 0; i < MAX_PLAYERS; i++)
11450   {
11451     summarized_player_action |= stored_player[i].action;
11452
11453     if (!network_playing && (game.team_mode || tape.playing))
11454       stored_player[i].effective_action = stored_player[i].action;
11455   }
11456
11457   if (network_playing && !checkGameEnded())
11458     SendToServer_MovePlayer(summarized_player_action);
11459
11460   // summarize all actions at local players mapped input device position
11461   // (this allows using different input devices in single player mode)
11462   if (!network.enabled && !game.team_mode)
11463     stored_player[map_player_action[local_player->index_nr]].effective_action =
11464       summarized_player_action;
11465
11466   if (tape.recording &&
11467       setup.team_mode &&
11468       setup.input_on_focus &&
11469       game.centered_player_nr != -1)
11470   {
11471     for (i = 0; i < MAX_PLAYERS; i++)
11472       stored_player[i].effective_action =
11473         (i == game.centered_player_nr ? summarized_player_action : 0);
11474   }
11475
11476   if (recorded_player_action != NULL)
11477     for (i = 0; i < MAX_PLAYERS; i++)
11478       stored_player[i].effective_action = recorded_player_action[i];
11479
11480   for (i = 0; i < MAX_PLAYERS; i++)
11481   {
11482     tape_action[i] = stored_player[i].effective_action;
11483
11484     /* (this may happen in the RND game engine if a player was not present on
11485        the playfield on level start, but appeared later from a custom element */
11486     if (setup.team_mode &&
11487         tape.recording &&
11488         tape_action[i] &&
11489         !tape.player_participates[i])
11490       tape.player_participates[i] = TRUE;
11491   }
11492
11493   SetTapeActionFromMouseAction(tape_action,
11494                                &local_player->effective_mouse_action);
11495
11496   // only record actions from input devices, but not programmed actions
11497   if (tape.recording)
11498     TapeRecordAction(tape_action);
11499
11500   // remember if game was played (especially after tape stopped playing)
11501   if (!tape.playing && summarized_player_action)
11502     game.GamePlayed = TRUE;
11503
11504 #if USE_NEW_PLAYER_ASSIGNMENTS
11505   // !!! also map player actions in single player mode !!!
11506   // if (game.team_mode)
11507   if (1)
11508   {
11509     byte mapped_action[MAX_PLAYERS];
11510
11511 #if DEBUG_PLAYER_ACTIONS
11512     printf(":::");
11513     for (i = 0; i < MAX_PLAYERS; i++)
11514       printf(" %d, ", stored_player[i].effective_action);
11515 #endif
11516
11517     for (i = 0; i < MAX_PLAYERS; i++)
11518       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11519
11520     for (i = 0; i < MAX_PLAYERS; i++)
11521       stored_player[i].effective_action = mapped_action[i];
11522
11523 #if DEBUG_PLAYER_ACTIONS
11524     printf(" =>");
11525     for (i = 0; i < MAX_PLAYERS; i++)
11526       printf(" %d, ", stored_player[i].effective_action);
11527     printf("\n");
11528 #endif
11529   }
11530 #if DEBUG_PLAYER_ACTIONS
11531   else
11532   {
11533     printf(":::");
11534     for (i = 0; i < MAX_PLAYERS; i++)
11535       printf(" %d, ", stored_player[i].effective_action);
11536     printf("\n");
11537   }
11538 #endif
11539 #endif
11540
11541   for (i = 0; i < MAX_PLAYERS; i++)
11542   {
11543     // allow engine snapshot in case of changed movement attempt
11544     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11545         (stored_player[i].effective_action & KEY_MOTION))
11546       game.snapshot.changed_action = TRUE;
11547
11548     // allow engine snapshot in case of snapping/dropping attempt
11549     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11550         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11551       game.snapshot.changed_action = TRUE;
11552
11553     game.snapshot.last_action[i] = stored_player[i].effective_action;
11554   }
11555
11556   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11557   {
11558     GameActions_EM_Main();
11559   }
11560   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11561   {
11562     GameActions_SP_Main();
11563   }
11564   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11565   {
11566     GameActions_MM_Main();
11567   }
11568   else
11569   {
11570     GameActions_RND_Main();
11571   }
11572
11573   BlitScreenToBitmap(backbuffer);
11574
11575   CheckLevelSolved();
11576   CheckLevelTime();
11577
11578   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11579
11580   if (global.show_frames_per_second)
11581   {
11582     static unsigned int fps_counter = 0;
11583     static int fps_frames = 0;
11584     unsigned int fps_delay_ms = Counter() - fps_counter;
11585
11586     fps_frames++;
11587
11588     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11589     {
11590       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11591
11592       fps_frames = 0;
11593       fps_counter = Counter();
11594
11595       // always draw FPS to screen after FPS value was updated
11596       redraw_mask |= REDRAW_FPS;
11597     }
11598
11599     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11600     if (GetDrawDeactivationMask() == REDRAW_NONE)
11601       redraw_mask |= REDRAW_FPS;
11602   }
11603 }
11604
11605 static void GameActions_CheckSaveEngineSnapshot(void)
11606 {
11607   if (!game.snapshot.save_snapshot)
11608     return;
11609
11610   // clear flag for saving snapshot _before_ saving snapshot
11611   game.snapshot.save_snapshot = FALSE;
11612
11613   SaveEngineSnapshotToList();
11614 }
11615
11616 void GameActions(void)
11617 {
11618   GameActionsExt();
11619
11620   GameActions_CheckSaveEngineSnapshot();
11621 }
11622
11623 void GameActions_EM_Main(void)
11624 {
11625   byte effective_action[MAX_PLAYERS];
11626   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627   int i;
11628
11629   for (i = 0; i < MAX_PLAYERS; i++)
11630     effective_action[i] = stored_player[i].effective_action;
11631
11632   GameActions_EM(effective_action, warp_mode);
11633 }
11634
11635 void GameActions_SP_Main(void)
11636 {
11637   byte effective_action[MAX_PLAYERS];
11638   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11639   int i;
11640
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642     effective_action[i] = stored_player[i].effective_action;
11643
11644   GameActions_SP(effective_action, warp_mode);
11645
11646   for (i = 0; i < MAX_PLAYERS; i++)
11647   {
11648     if (stored_player[i].force_dropping)
11649       stored_player[i].action |= KEY_BUTTON_DROP;
11650
11651     stored_player[i].force_dropping = FALSE;
11652   }
11653 }
11654
11655 void GameActions_MM_Main(void)
11656 {
11657   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11658
11659   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11660 }
11661
11662 void GameActions_RND_Main(void)
11663 {
11664   GameActions_RND();
11665 }
11666
11667 void GameActions_RND(void)
11668 {
11669   int magic_wall_x = 0, magic_wall_y = 0;
11670   int i, x, y, element, graphic, last_gfx_frame;
11671
11672   InitPlayfieldScanModeVars();
11673
11674   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11675   {
11676     SCAN_PLAYFIELD(x, y)
11677     {
11678       ChangeCount[x][y] = 0;
11679       ChangeEvent[x][y] = -1;
11680     }
11681   }
11682
11683   if (game.set_centered_player)
11684   {
11685     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11686
11687     // switching to "all players" only possible if all players fit to screen
11688     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11689     {
11690       game.centered_player_nr_next = game.centered_player_nr;
11691       game.set_centered_player = FALSE;
11692     }
11693
11694     // do not switch focus to non-existing (or non-active) player
11695     if (game.centered_player_nr_next >= 0 &&
11696         !stored_player[game.centered_player_nr_next].active)
11697     {
11698       game.centered_player_nr_next = game.centered_player_nr;
11699       game.set_centered_player = FALSE;
11700     }
11701   }
11702
11703   if (game.set_centered_player &&
11704       ScreenMovPos == 0)        // screen currently aligned at tile position
11705   {
11706     int sx, sy;
11707
11708     if (game.centered_player_nr_next == -1)
11709     {
11710       setScreenCenteredToAllPlayers(&sx, &sy);
11711     }
11712     else
11713     {
11714       sx = stored_player[game.centered_player_nr_next].jx;
11715       sy = stored_player[game.centered_player_nr_next].jy;
11716     }
11717
11718     game.centered_player_nr = game.centered_player_nr_next;
11719     game.set_centered_player = FALSE;
11720
11721     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11722     DrawGameDoorValues();
11723   }
11724
11725   for (i = 0; i < MAX_PLAYERS; i++)
11726   {
11727     int actual_player_action = stored_player[i].effective_action;
11728
11729 #if 1
11730     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11731        - rnd_equinox_tetrachloride 048
11732        - rnd_equinox_tetrachloride_ii 096
11733        - rnd_emanuel_schmieg 002
11734        - doctor_sloan_ww 001, 020
11735     */
11736     if (stored_player[i].MovPos == 0)
11737       CheckGravityMovement(&stored_player[i]);
11738 #endif
11739
11740     // overwrite programmed action with tape action
11741     if (stored_player[i].programmed_action)
11742       actual_player_action = stored_player[i].programmed_action;
11743
11744     PlayerActions(&stored_player[i], actual_player_action);
11745
11746     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11747   }
11748
11749   ScrollScreen(NULL, SCROLL_GO_ON);
11750
11751   /* for backwards compatibility, the following code emulates a fixed bug that
11752      occured when pushing elements (causing elements that just made their last
11753      pushing step to already (if possible) make their first falling step in the
11754      same game frame, which is bad); this code is also needed to use the famous
11755      "spring push bug" which is used in older levels and might be wanted to be
11756      used also in newer levels, but in this case the buggy pushing code is only
11757      affecting the "spring" element and no other elements */
11758
11759   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11760   {
11761     for (i = 0; i < MAX_PLAYERS; i++)
11762     {
11763       struct PlayerInfo *player = &stored_player[i];
11764       int x = player->jx;
11765       int y = player->jy;
11766
11767       if (player->active && player->is_pushing && player->is_moving &&
11768           IS_MOVING(x, y) &&
11769           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11770            Feld[x][y] == EL_SPRING))
11771       {
11772         ContinueMoving(x, y);
11773
11774         // continue moving after pushing (this is actually a bug)
11775         if (!IS_MOVING(x, y))
11776           Stop[x][y] = FALSE;
11777       }
11778     }
11779   }
11780
11781   SCAN_PLAYFIELD(x, y)
11782   {
11783     Last[x][y] = Feld[x][y];
11784
11785     ChangeCount[x][y] = 0;
11786     ChangeEvent[x][y] = -1;
11787
11788     // this must be handled before main playfield loop
11789     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11790     {
11791       MovDelay[x][y]--;
11792       if (MovDelay[x][y] <= 0)
11793         RemoveField(x, y);
11794     }
11795
11796     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11797     {
11798       MovDelay[x][y]--;
11799       if (MovDelay[x][y] <= 0)
11800       {
11801         RemoveField(x, y);
11802         TEST_DrawLevelField(x, y);
11803
11804         TestIfElementTouchesCustomElement(x, y);        // for empty space
11805       }
11806     }
11807
11808 #if DEBUG
11809     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11810     {
11811       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11812       printf("GameActions(): This should never happen!\n");
11813
11814       ChangePage[x][y] = -1;
11815     }
11816 #endif
11817
11818     Stop[x][y] = FALSE;
11819     if (WasJustMoving[x][y] > 0)
11820       WasJustMoving[x][y]--;
11821     if (WasJustFalling[x][y] > 0)
11822       WasJustFalling[x][y]--;
11823     if (CheckCollision[x][y] > 0)
11824       CheckCollision[x][y]--;
11825     if (CheckImpact[x][y] > 0)
11826       CheckImpact[x][y]--;
11827
11828     GfxFrame[x][y]++;
11829
11830     /* reset finished pushing action (not done in ContinueMoving() to allow
11831        continuous pushing animation for elements with zero push delay) */
11832     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11833     {
11834       ResetGfxAnimation(x, y);
11835       TEST_DrawLevelField(x, y);
11836     }
11837
11838 #if DEBUG
11839     if (IS_BLOCKED(x, y))
11840     {
11841       int oldx, oldy;
11842
11843       Blocked2Moving(x, y, &oldx, &oldy);
11844       if (!IS_MOVING(oldx, oldy))
11845       {
11846         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11847         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11848         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11849         printf("GameActions(): This should never happen!\n");
11850       }
11851     }
11852 #endif
11853   }
11854
11855   SCAN_PLAYFIELD(x, y)
11856   {
11857     element = Feld[x][y];
11858     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11859     last_gfx_frame = GfxFrame[x][y];
11860
11861     ResetGfxFrame(x, y);
11862
11863     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11864       DrawLevelGraphicAnimation(x, y, graphic);
11865
11866     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11867         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11868       ResetRandomAnimationValue(x, y);
11869
11870     SetRandomAnimationValue(x, y);
11871
11872     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11873
11874     if (IS_INACTIVE(element))
11875     {
11876       if (IS_ANIMATED(graphic))
11877         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878
11879       continue;
11880     }
11881
11882     // this may take place after moving, so 'element' may have changed
11883     if (IS_CHANGING(x, y) &&
11884         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11885     {
11886       int page = element_info[element].event_page_nr[CE_DELAY];
11887
11888       HandleElementChange(x, y, page);
11889
11890       element = Feld[x][y];
11891       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11892     }
11893
11894     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11895     {
11896       StartMoving(x, y);
11897
11898       element = Feld[x][y];
11899       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11900
11901       if (IS_ANIMATED(graphic) &&
11902           !IS_MOVING(x, y) &&
11903           !Stop[x][y])
11904         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11905
11906       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11907         TEST_DrawTwinkleOnField(x, y);
11908     }
11909     else if (element == EL_ACID)
11910     {
11911       if (!Stop[x][y])
11912         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11913     }
11914     else if ((element == EL_EXIT_OPEN ||
11915               element == EL_EM_EXIT_OPEN ||
11916               element == EL_SP_EXIT_OPEN ||
11917               element == EL_STEEL_EXIT_OPEN ||
11918               element == EL_EM_STEEL_EXIT_OPEN ||
11919               element == EL_SP_TERMINAL ||
11920               element == EL_SP_TERMINAL_ACTIVE ||
11921               element == EL_EXTRA_TIME ||
11922               element == EL_SHIELD_NORMAL ||
11923               element == EL_SHIELD_DEADLY) &&
11924              IS_ANIMATED(graphic))
11925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11926     else if (IS_MOVING(x, y))
11927       ContinueMoving(x, y);
11928     else if (IS_ACTIVE_BOMB(element))
11929       CheckDynamite(x, y);
11930     else if (element == EL_AMOEBA_GROWING)
11931       AmoebeWaechst(x, y);
11932     else if (element == EL_AMOEBA_SHRINKING)
11933       AmoebaDisappearing(x, y);
11934
11935 #if !USE_NEW_AMOEBA_CODE
11936     else if (IS_AMOEBALIVE(element))
11937       AmoebeAbleger(x, y);
11938 #endif
11939
11940     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11941       Life(x, y);
11942     else if (element == EL_EXIT_CLOSED)
11943       CheckExit(x, y);
11944     else if (element == EL_EM_EXIT_CLOSED)
11945       CheckExitEM(x, y);
11946     else if (element == EL_STEEL_EXIT_CLOSED)
11947       CheckExitSteel(x, y);
11948     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11949       CheckExitSteelEM(x, y);
11950     else if (element == EL_SP_EXIT_CLOSED)
11951       CheckExitSP(x, y);
11952     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11953              element == EL_EXPANDABLE_STEELWALL_GROWING)
11954       MauerWaechst(x, y);
11955     else if (element == EL_EXPANDABLE_WALL ||
11956              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11957              element == EL_EXPANDABLE_WALL_VERTICAL ||
11958              element == EL_EXPANDABLE_WALL_ANY ||
11959              element == EL_BD_EXPANDABLE_WALL)
11960       MauerAbleger(x, y);
11961     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11962              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11963              element == EL_EXPANDABLE_STEELWALL_ANY)
11964       MauerAblegerStahl(x, y);
11965     else if (element == EL_FLAMES)
11966       CheckForDragon(x, y);
11967     else if (element == EL_EXPLOSION)
11968       ; // drawing of correct explosion animation is handled separately
11969     else if (element == EL_ELEMENT_SNAPPING ||
11970              element == EL_DIAGONAL_SHRINKING ||
11971              element == EL_DIAGONAL_GROWING)
11972     {
11973       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11974
11975       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11976     }
11977     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11978       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11979
11980     if (IS_BELT_ACTIVE(element))
11981       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11982
11983     if (game.magic_wall_active)
11984     {
11985       int jx = local_player->jx, jy = local_player->jy;
11986
11987       // play the element sound at the position nearest to the player
11988       if ((element == EL_MAGIC_WALL_FULL ||
11989            element == EL_MAGIC_WALL_ACTIVE ||
11990            element == EL_MAGIC_WALL_EMPTYING ||
11991            element == EL_BD_MAGIC_WALL_FULL ||
11992            element == EL_BD_MAGIC_WALL_ACTIVE ||
11993            element == EL_BD_MAGIC_WALL_EMPTYING ||
11994            element == EL_DC_MAGIC_WALL_FULL ||
11995            element == EL_DC_MAGIC_WALL_ACTIVE ||
11996            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11997           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11998       {
11999         magic_wall_x = x;
12000         magic_wall_y = y;
12001       }
12002     }
12003   }
12004
12005 #if USE_NEW_AMOEBA_CODE
12006   // new experimental amoeba growth stuff
12007   if (!(FrameCounter % 8))
12008   {
12009     static unsigned int random = 1684108901;
12010
12011     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12012     {
12013       x = RND(lev_fieldx);
12014       y = RND(lev_fieldy);
12015       element = Feld[x][y];
12016
12017       if (!IS_PLAYER(x,y) &&
12018           (element == EL_EMPTY ||
12019            CAN_GROW_INTO(element) ||
12020            element == EL_QUICKSAND_EMPTY ||
12021            element == EL_QUICKSAND_FAST_EMPTY ||
12022            element == EL_ACID_SPLASH_LEFT ||
12023            element == EL_ACID_SPLASH_RIGHT))
12024       {
12025         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12026             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12027             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12028             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12029           Feld[x][y] = EL_AMOEBA_DROP;
12030       }
12031
12032       random = random * 129 + 1;
12033     }
12034   }
12035 #endif
12036
12037   game.explosions_delayed = FALSE;
12038
12039   SCAN_PLAYFIELD(x, y)
12040   {
12041     element = Feld[x][y];
12042
12043     if (ExplodeField[x][y])
12044       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12045     else if (element == EL_EXPLOSION)
12046       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12047
12048     ExplodeField[x][y] = EX_TYPE_NONE;
12049   }
12050
12051   game.explosions_delayed = TRUE;
12052
12053   if (game.magic_wall_active)
12054   {
12055     if (!(game.magic_wall_time_left % 4))
12056     {
12057       int element = Feld[magic_wall_x][magic_wall_y];
12058
12059       if (element == EL_BD_MAGIC_WALL_FULL ||
12060           element == EL_BD_MAGIC_WALL_ACTIVE ||
12061           element == EL_BD_MAGIC_WALL_EMPTYING)
12062         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12063       else if (element == EL_DC_MAGIC_WALL_FULL ||
12064                element == EL_DC_MAGIC_WALL_ACTIVE ||
12065                element == EL_DC_MAGIC_WALL_EMPTYING)
12066         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12067       else
12068         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12069     }
12070
12071     if (game.magic_wall_time_left > 0)
12072     {
12073       game.magic_wall_time_left--;
12074
12075       if (!game.magic_wall_time_left)
12076       {
12077         SCAN_PLAYFIELD(x, y)
12078         {
12079           element = Feld[x][y];
12080
12081           if (element == EL_MAGIC_WALL_ACTIVE ||
12082               element == EL_MAGIC_WALL_FULL)
12083           {
12084             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12085             TEST_DrawLevelField(x, y);
12086           }
12087           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12088                    element == EL_BD_MAGIC_WALL_FULL)
12089           {
12090             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12091             TEST_DrawLevelField(x, y);
12092           }
12093           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12094                    element == EL_DC_MAGIC_WALL_FULL)
12095           {
12096             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12097             TEST_DrawLevelField(x, y);
12098           }
12099         }
12100
12101         game.magic_wall_active = FALSE;
12102       }
12103     }
12104   }
12105
12106   if (game.light_time_left > 0)
12107   {
12108     game.light_time_left--;
12109
12110     if (game.light_time_left == 0)
12111       RedrawAllLightSwitchesAndInvisibleElements();
12112   }
12113
12114   if (game.timegate_time_left > 0)
12115   {
12116     game.timegate_time_left--;
12117
12118     if (game.timegate_time_left == 0)
12119       CloseAllOpenTimegates();
12120   }
12121
12122   if (game.lenses_time_left > 0)
12123   {
12124     game.lenses_time_left--;
12125
12126     if (game.lenses_time_left == 0)
12127       RedrawAllInvisibleElementsForLenses();
12128   }
12129
12130   if (game.magnify_time_left > 0)
12131   {
12132     game.magnify_time_left--;
12133
12134     if (game.magnify_time_left == 0)
12135       RedrawAllInvisibleElementsForMagnifier();
12136   }
12137
12138   for (i = 0; i < MAX_PLAYERS; i++)
12139   {
12140     struct PlayerInfo *player = &stored_player[i];
12141
12142     if (SHIELD_ON(player))
12143     {
12144       if (player->shield_deadly_time_left)
12145         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12146       else if (player->shield_normal_time_left)
12147         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12148     }
12149   }
12150
12151 #if USE_DELAYED_GFX_REDRAW
12152   SCAN_PLAYFIELD(x, y)
12153   {
12154     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12155     {
12156       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12157          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12158
12159       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12160         DrawLevelField(x, y);
12161
12162       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12163         DrawLevelFieldCrumbled(x, y);
12164
12165       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12166         DrawLevelFieldCrumbledNeighbours(x, y);
12167
12168       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12169         DrawTwinkleOnField(x, y);
12170     }
12171
12172     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12173   }
12174 #endif
12175
12176   DrawAllPlayers();
12177   PlayAllPlayersSound();
12178
12179   if (local_player->show_envelope != 0 && (!local_player->active ||
12180                                            local_player->MovPos == 0))
12181   {
12182     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12183
12184     local_player->show_envelope = 0;
12185   }
12186
12187   // use random number generator in every frame to make it less predictable
12188   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12189     RND(1);
12190 }
12191
12192 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12193 {
12194   int min_x = x, min_y = y, max_x = x, max_y = y;
12195   int i;
12196
12197   for (i = 0; i < MAX_PLAYERS; i++)
12198   {
12199     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12200
12201     if (!stored_player[i].active || &stored_player[i] == player)
12202       continue;
12203
12204     min_x = MIN(min_x, jx);
12205     min_y = MIN(min_y, jy);
12206     max_x = MAX(max_x, jx);
12207     max_y = MAX(max_y, jy);
12208   }
12209
12210   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12211 }
12212
12213 static boolean AllPlayersInVisibleScreen(void)
12214 {
12215   int i;
12216
12217   for (i = 0; i < MAX_PLAYERS; i++)
12218   {
12219     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12220
12221     if (!stored_player[i].active)
12222       continue;
12223
12224     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12225       return FALSE;
12226   }
12227
12228   return TRUE;
12229 }
12230
12231 void ScrollLevel(int dx, int dy)
12232 {
12233   int scroll_offset = 2 * TILEX_VAR;
12234   int x, y;
12235
12236   BlitBitmap(drawto_field, drawto_field,
12237              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12238              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12239              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12240              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12241              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12242              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12243
12244   if (dx != 0)
12245   {
12246     x = (dx == 1 ? BX1 : BX2);
12247     for (y = BY1; y <= BY2; y++)
12248       DrawScreenField(x, y);
12249   }
12250
12251   if (dy != 0)
12252   {
12253     y = (dy == 1 ? BY1 : BY2);
12254     for (x = BX1; x <= BX2; x++)
12255       DrawScreenField(x, y);
12256   }
12257
12258   redraw_mask |= REDRAW_FIELD;
12259 }
12260
12261 static boolean canFallDown(struct PlayerInfo *player)
12262 {
12263   int jx = player->jx, jy = player->jy;
12264
12265   return (IN_LEV_FIELD(jx, jy + 1) &&
12266           (IS_FREE(jx, jy + 1) ||
12267            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12268           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12269           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12270 }
12271
12272 static boolean canPassField(int x, int y, int move_dir)
12273 {
12274   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12275   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12276   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12277   int nextx = x + dx;
12278   int nexty = y + dy;
12279   int element = Feld[x][y];
12280
12281   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12282           !CAN_MOVE(element) &&
12283           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12284           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12285           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12286 }
12287
12288 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12289 {
12290   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12291   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12292   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12293   int newx = x + dx;
12294   int newy = y + dy;
12295
12296   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12297           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12298           (IS_DIGGABLE(Feld[newx][newy]) ||
12299            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12300            canPassField(newx, newy, move_dir)));
12301 }
12302
12303 static void CheckGravityMovement(struct PlayerInfo *player)
12304 {
12305   if (player->gravity && !player->programmed_action)
12306   {
12307     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12308     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12309     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12310     int jx = player->jx, jy = player->jy;
12311     boolean player_is_moving_to_valid_field =
12312       (!player_is_snapping &&
12313        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12314         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12315     boolean player_can_fall_down = canFallDown(player);
12316
12317     if (player_can_fall_down &&
12318         !player_is_moving_to_valid_field)
12319       player->programmed_action = MV_DOWN;
12320   }
12321 }
12322
12323 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12324 {
12325   return CheckGravityMovement(player);
12326
12327   if (player->gravity && !player->programmed_action)
12328   {
12329     int jx = player->jx, jy = player->jy;
12330     boolean field_under_player_is_free =
12331       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12332     boolean player_is_standing_on_valid_field =
12333       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12334        (IS_WALKABLE(Feld[jx][jy]) &&
12335         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12336
12337     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12338       player->programmed_action = MV_DOWN;
12339   }
12340 }
12341
12342 /*
12343   MovePlayerOneStep()
12344   -----------------------------------------------------------------------------
12345   dx, dy:               direction (non-diagonal) to try to move the player to
12346   real_dx, real_dy:     direction as read from input device (can be diagonal)
12347 */
12348
12349 boolean MovePlayerOneStep(struct PlayerInfo *player,
12350                           int dx, int dy, int real_dx, int real_dy)
12351 {
12352   int jx = player->jx, jy = player->jy;
12353   int new_jx = jx + dx, new_jy = jy + dy;
12354   int can_move;
12355   boolean player_can_move = !player->cannot_move;
12356
12357   if (!player->active || (!dx && !dy))
12358     return MP_NO_ACTION;
12359
12360   player->MovDir = (dx < 0 ? MV_LEFT :
12361                     dx > 0 ? MV_RIGHT :
12362                     dy < 0 ? MV_UP :
12363                     dy > 0 ? MV_DOWN :  MV_NONE);
12364
12365   if (!IN_LEV_FIELD(new_jx, new_jy))
12366     return MP_NO_ACTION;
12367
12368   if (!player_can_move)
12369   {
12370     if (player->MovPos == 0)
12371     {
12372       player->is_moving = FALSE;
12373       player->is_digging = FALSE;
12374       player->is_collecting = FALSE;
12375       player->is_snapping = FALSE;
12376       player->is_pushing = FALSE;
12377     }
12378   }
12379
12380   if (!network.enabled && game.centered_player_nr == -1 &&
12381       !AllPlayersInSight(player, new_jx, new_jy))
12382     return MP_NO_ACTION;
12383
12384   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12385   if (can_move != MP_MOVING)
12386     return can_move;
12387
12388   // check if DigField() has caused relocation of the player
12389   if (player->jx != jx || player->jy != jy)
12390     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12391
12392   StorePlayer[jx][jy] = 0;
12393   player->last_jx = jx;
12394   player->last_jy = jy;
12395   player->jx = new_jx;
12396   player->jy = new_jy;
12397   StorePlayer[new_jx][new_jy] = player->element_nr;
12398
12399   if (player->move_delay_value_next != -1)
12400   {
12401     player->move_delay_value = player->move_delay_value_next;
12402     player->move_delay_value_next = -1;
12403   }
12404
12405   player->MovPos =
12406     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12407
12408   player->step_counter++;
12409
12410   PlayerVisit[jx][jy] = FrameCounter;
12411
12412   player->is_moving = TRUE;
12413
12414 #if 1
12415   // should better be called in MovePlayer(), but this breaks some tapes
12416   ScrollPlayer(player, SCROLL_INIT);
12417 #endif
12418
12419   return MP_MOVING;
12420 }
12421
12422 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12423 {
12424   int jx = player->jx, jy = player->jy;
12425   int old_jx = jx, old_jy = jy;
12426   int moved = MP_NO_ACTION;
12427
12428   if (!player->active)
12429     return FALSE;
12430
12431   if (!dx && !dy)
12432   {
12433     if (player->MovPos == 0)
12434     {
12435       player->is_moving = FALSE;
12436       player->is_digging = FALSE;
12437       player->is_collecting = FALSE;
12438       player->is_snapping = FALSE;
12439       player->is_pushing = FALSE;
12440     }
12441
12442     return FALSE;
12443   }
12444
12445   if (player->move_delay > 0)
12446     return FALSE;
12447
12448   player->move_delay = -1;              // set to "uninitialized" value
12449
12450   // store if player is automatically moved to next field
12451   player->is_auto_moving = (player->programmed_action != MV_NONE);
12452
12453   // remove the last programmed player action
12454   player->programmed_action = 0;
12455
12456   if (player->MovPos)
12457   {
12458     // should only happen if pre-1.2 tape recordings are played
12459     // this is only for backward compatibility
12460
12461     int original_move_delay_value = player->move_delay_value;
12462
12463 #if DEBUG
12464     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12465            tape.counter);
12466 #endif
12467
12468     // scroll remaining steps with finest movement resolution
12469     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12470
12471     while (player->MovPos)
12472     {
12473       ScrollPlayer(player, SCROLL_GO_ON);
12474       ScrollScreen(NULL, SCROLL_GO_ON);
12475
12476       AdvanceFrameAndPlayerCounters(player->index_nr);
12477
12478       DrawAllPlayers();
12479       BackToFront_WithFrameDelay(0);
12480     }
12481
12482     player->move_delay_value = original_move_delay_value;
12483   }
12484
12485   player->is_active = FALSE;
12486
12487   if (player->last_move_dir & MV_HORIZONTAL)
12488   {
12489     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12490       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12491   }
12492   else
12493   {
12494     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12495       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12496   }
12497
12498   if (!moved && !player->is_active)
12499   {
12500     player->is_moving = FALSE;
12501     player->is_digging = FALSE;
12502     player->is_collecting = FALSE;
12503     player->is_snapping = FALSE;
12504     player->is_pushing = FALSE;
12505   }
12506
12507   jx = player->jx;
12508   jy = player->jy;
12509
12510   if (moved & MP_MOVING && !ScreenMovPos &&
12511       (player->index_nr == game.centered_player_nr ||
12512        game.centered_player_nr == -1))
12513   {
12514     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12515     int offset = game.scroll_delay_value;
12516
12517     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12518     {
12519       // actual player has left the screen -- scroll in that direction
12520       if (jx != old_jx)         // player has moved horizontally
12521         scroll_x += (jx - old_jx);
12522       else                      // player has moved vertically
12523         scroll_y += (jy - old_jy);
12524     }
12525     else
12526     {
12527       if (jx != old_jx)         // player has moved horizontally
12528       {
12529         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12530             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12531           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12532
12533         // don't scroll over playfield boundaries
12534         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12535           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12536
12537         // don't scroll more than one field at a time
12538         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12539
12540         // don't scroll against the player's moving direction
12541         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12542             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12543           scroll_x = old_scroll_x;
12544       }
12545       else                      // player has moved vertically
12546       {
12547         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12548             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12549           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12550
12551         // don't scroll over playfield boundaries
12552         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12553           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12554
12555         // don't scroll more than one field at a time
12556         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12557
12558         // don't scroll against the player's moving direction
12559         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12560             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12561           scroll_y = old_scroll_y;
12562       }
12563     }
12564
12565     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12566     {
12567       if (!network.enabled && game.centered_player_nr == -1 &&
12568           !AllPlayersInVisibleScreen())
12569       {
12570         scroll_x = old_scroll_x;
12571         scroll_y = old_scroll_y;
12572       }
12573       else
12574       {
12575         ScrollScreen(player, SCROLL_INIT);
12576         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12577       }
12578     }
12579   }
12580
12581   player->StepFrame = 0;
12582
12583   if (moved & MP_MOVING)
12584   {
12585     if (old_jx != jx && old_jy == jy)
12586       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12587     else if (old_jx == jx && old_jy != jy)
12588       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12589
12590     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12591
12592     player->last_move_dir = player->MovDir;
12593     player->is_moving = TRUE;
12594     player->is_snapping = FALSE;
12595     player->is_switching = FALSE;
12596     player->is_dropping = FALSE;
12597     player->is_dropping_pressed = FALSE;
12598     player->drop_pressed_delay = 0;
12599
12600 #if 0
12601     // should better be called here than above, but this breaks some tapes
12602     ScrollPlayer(player, SCROLL_INIT);
12603 #endif
12604   }
12605   else
12606   {
12607     CheckGravityMovementWhenNotMoving(player);
12608
12609     player->is_moving = FALSE;
12610
12611     /* at this point, the player is allowed to move, but cannot move right now
12612        (e.g. because of something blocking the way) -- ensure that the player
12613        is also allowed to move in the next frame (in old versions before 3.1.1,
12614        the player was forced to wait again for eight frames before next try) */
12615
12616     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12617       player->move_delay = 0;   // allow direct movement in the next frame
12618   }
12619
12620   if (player->move_delay == -1)         // not yet initialized by DigField()
12621     player->move_delay = player->move_delay_value;
12622
12623   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12624   {
12625     TestIfPlayerTouchesBadThing(jx, jy);
12626     TestIfPlayerTouchesCustomElement(jx, jy);
12627   }
12628
12629   if (!player->active)
12630     RemovePlayer(player);
12631
12632   return moved;
12633 }
12634
12635 void ScrollPlayer(struct PlayerInfo *player, int mode)
12636 {
12637   int jx = player->jx, jy = player->jy;
12638   int last_jx = player->last_jx, last_jy = player->last_jy;
12639   int move_stepsize = TILEX / player->move_delay_value;
12640
12641   if (!player->active)
12642     return;
12643
12644   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12645     return;
12646
12647   if (mode == SCROLL_INIT)
12648   {
12649     player->actual_frame_counter = FrameCounter;
12650     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12651
12652     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12653         Feld[last_jx][last_jy] == EL_EMPTY)
12654     {
12655       int last_field_block_delay = 0;   // start with no blocking at all
12656       int block_delay_adjustment = player->block_delay_adjustment;
12657
12658       // if player blocks last field, add delay for exactly one move
12659       if (player->block_last_field)
12660       {
12661         last_field_block_delay += player->move_delay_value;
12662
12663         // when blocking enabled, prevent moving up despite gravity
12664         if (player->gravity && player->MovDir == MV_UP)
12665           block_delay_adjustment = -1;
12666       }
12667
12668       // add block delay adjustment (also possible when not blocking)
12669       last_field_block_delay += block_delay_adjustment;
12670
12671       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12672       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12673     }
12674
12675     if (player->MovPos != 0)    // player has not yet reached destination
12676       return;
12677   }
12678   else if (!FrameReached(&player->actual_frame_counter, 1))
12679     return;
12680
12681   if (player->MovPos != 0)
12682   {
12683     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12684     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12685
12686     // before DrawPlayer() to draw correct player graphic for this case
12687     if (player->MovPos == 0)
12688       CheckGravityMovement(player);
12689   }
12690
12691   if (player->MovPos == 0)      // player reached destination field
12692   {
12693     if (player->move_delay_reset_counter > 0)
12694     {
12695       player->move_delay_reset_counter--;
12696
12697       if (player->move_delay_reset_counter == 0)
12698       {
12699         // continue with normal speed after quickly moving through gate
12700         HALVE_PLAYER_SPEED(player);
12701
12702         // be able to make the next move without delay
12703         player->move_delay = 0;
12704       }
12705     }
12706
12707     player->last_jx = jx;
12708     player->last_jy = jy;
12709
12710     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12711         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12712         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12713         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12714         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12715         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12716         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12717         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12718     {
12719       ExitPlayer(player);
12720
12721       if (game.players_still_needed == 0 &&
12722           (game.friends_still_needed == 0 ||
12723            IS_SP_ELEMENT(Feld[jx][jy])))
12724         LevelSolved();
12725     }
12726
12727     // this breaks one level: "machine", level 000
12728     {
12729       int move_direction = player->MovDir;
12730       int enter_side = MV_DIR_OPPOSITE(move_direction);
12731       int leave_side = move_direction;
12732       int old_jx = last_jx;
12733       int old_jy = last_jy;
12734       int old_element = Feld[old_jx][old_jy];
12735       int new_element = Feld[jx][jy];
12736
12737       if (IS_CUSTOM_ELEMENT(old_element))
12738         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12739                                    CE_LEFT_BY_PLAYER,
12740                                    player->index_bit, leave_side);
12741
12742       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12743                                           CE_PLAYER_LEAVES_X,
12744                                           player->index_bit, leave_side);
12745
12746       if (IS_CUSTOM_ELEMENT(new_element))
12747         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12748                                    player->index_bit, enter_side);
12749
12750       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12751                                           CE_PLAYER_ENTERS_X,
12752                                           player->index_bit, enter_side);
12753
12754       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12755                                         CE_MOVE_OF_X, move_direction);
12756     }
12757
12758     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12759     {
12760       TestIfPlayerTouchesBadThing(jx, jy);
12761       TestIfPlayerTouchesCustomElement(jx, jy);
12762
12763       /* needed because pushed element has not yet reached its destination,
12764          so it would trigger a change event at its previous field location */
12765       if (!player->is_pushing)
12766         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12767
12768       if (!player->active)
12769         RemovePlayer(player);
12770     }
12771
12772     if (!game.LevelSolved && level.use_step_counter)
12773     {
12774       int i;
12775
12776       TimePlayed++;
12777
12778       if (TimeLeft > 0)
12779       {
12780         TimeLeft--;
12781
12782         if (TimeLeft <= 10 && setup.time_limit)
12783           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12784
12785         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12786
12787         DisplayGameControlValues();
12788
12789         if (!TimeLeft && setup.time_limit)
12790           for (i = 0; i < MAX_PLAYERS; i++)
12791             KillPlayer(&stored_player[i]);
12792       }
12793       else if (game.no_time_limit && !game.all_players_gone)
12794       {
12795         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12796
12797         DisplayGameControlValues();
12798       }
12799     }
12800
12801     if (tape.single_step && tape.recording && !tape.pausing &&
12802         !player->programmed_action)
12803       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12804
12805     if (!player->programmed_action)
12806       CheckSaveEngineSnapshot(player);
12807   }
12808 }
12809
12810 void ScrollScreen(struct PlayerInfo *player, int mode)
12811 {
12812   static unsigned int screen_frame_counter = 0;
12813
12814   if (mode == SCROLL_INIT)
12815   {
12816     // set scrolling step size according to actual player's moving speed
12817     ScrollStepSize = TILEX / player->move_delay_value;
12818
12819     screen_frame_counter = FrameCounter;
12820     ScreenMovDir = player->MovDir;
12821     ScreenMovPos = player->MovPos;
12822     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12823     return;
12824   }
12825   else if (!FrameReached(&screen_frame_counter, 1))
12826     return;
12827
12828   if (ScreenMovPos)
12829   {
12830     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12831     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12832     redraw_mask |= REDRAW_FIELD;
12833   }
12834   else
12835     ScreenMovDir = MV_NONE;
12836 }
12837
12838 void TestIfPlayerTouchesCustomElement(int x, int y)
12839 {
12840   static int xy[4][2] =
12841   {
12842     { 0, -1 },
12843     { -1, 0 },
12844     { +1, 0 },
12845     { 0, +1 }
12846   };
12847   static int trigger_sides[4][2] =
12848   {
12849     // center side       border side
12850     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12851     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12852     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12853     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12854   };
12855   static int touch_dir[4] =
12856   {
12857     MV_LEFT | MV_RIGHT,
12858     MV_UP   | MV_DOWN,
12859     MV_UP   | MV_DOWN,
12860     MV_LEFT | MV_RIGHT
12861   };
12862   int center_element = Feld[x][y];      // should always be non-moving!
12863   int i;
12864
12865   for (i = 0; i < NUM_DIRECTIONS; i++)
12866   {
12867     int xx = x + xy[i][0];
12868     int yy = y + xy[i][1];
12869     int center_side = trigger_sides[i][0];
12870     int border_side = trigger_sides[i][1];
12871     int border_element;
12872
12873     if (!IN_LEV_FIELD(xx, yy))
12874       continue;
12875
12876     if (IS_PLAYER(x, y))                // player found at center element
12877     {
12878       struct PlayerInfo *player = PLAYERINFO(x, y);
12879
12880       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12881         border_element = Feld[xx][yy];          // may be moving!
12882       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12883         border_element = Feld[xx][yy];
12884       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12885         border_element = MovingOrBlocked2Element(xx, yy);
12886       else
12887         continue;               // center and border element do not touch
12888
12889       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12890                                  player->index_bit, border_side);
12891       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12892                                           CE_PLAYER_TOUCHES_X,
12893                                           player->index_bit, border_side);
12894
12895       {
12896         /* use player element that is initially defined in the level playfield,
12897            not the player element that corresponds to the runtime player number
12898            (example: a level that contains EL_PLAYER_3 as the only player would
12899            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12900         int player_element = PLAYERINFO(x, y)->initial_element;
12901
12902         CheckElementChangeBySide(xx, yy, border_element, player_element,
12903                                  CE_TOUCHING_X, border_side);
12904       }
12905     }
12906     else if (IS_PLAYER(xx, yy))         // player found at border element
12907     {
12908       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12909
12910       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12911       {
12912         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12913           continue;             // center and border element do not touch
12914       }
12915
12916       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12917                                  player->index_bit, center_side);
12918       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12919                                           CE_PLAYER_TOUCHES_X,
12920                                           player->index_bit, center_side);
12921
12922       {
12923         /* use player element that is initially defined in the level playfield,
12924            not the player element that corresponds to the runtime player number
12925            (example: a level that contains EL_PLAYER_3 as the only player would
12926            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12927         int player_element = PLAYERINFO(xx, yy)->initial_element;
12928
12929         CheckElementChangeBySide(x, y, center_element, player_element,
12930                                  CE_TOUCHING_X, center_side);
12931       }
12932
12933       break;
12934     }
12935   }
12936 }
12937
12938 void TestIfElementTouchesCustomElement(int x, int y)
12939 {
12940   static int xy[4][2] =
12941   {
12942     { 0, -1 },
12943     { -1, 0 },
12944     { +1, 0 },
12945     { 0, +1 }
12946   };
12947   static int trigger_sides[4][2] =
12948   {
12949     // center side      border side
12950     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12951     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12952     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12953     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12954   };
12955   static int touch_dir[4] =
12956   {
12957     MV_LEFT | MV_RIGHT,
12958     MV_UP   | MV_DOWN,
12959     MV_UP   | MV_DOWN,
12960     MV_LEFT | MV_RIGHT
12961   };
12962   boolean change_center_element = FALSE;
12963   int center_element = Feld[x][y];      // should always be non-moving!
12964   int border_element_old[NUM_DIRECTIONS];
12965   int i;
12966
12967   for (i = 0; i < NUM_DIRECTIONS; i++)
12968   {
12969     int xx = x + xy[i][0];
12970     int yy = y + xy[i][1];
12971     int border_element;
12972
12973     border_element_old[i] = -1;
12974
12975     if (!IN_LEV_FIELD(xx, yy))
12976       continue;
12977
12978     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12979       border_element = Feld[xx][yy];    // may be moving!
12980     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12981       border_element = Feld[xx][yy];
12982     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12983       border_element = MovingOrBlocked2Element(xx, yy);
12984     else
12985       continue;                 // center and border element do not touch
12986
12987     border_element_old[i] = border_element;
12988   }
12989
12990   for (i = 0; i < NUM_DIRECTIONS; i++)
12991   {
12992     int xx = x + xy[i][0];
12993     int yy = y + xy[i][1];
12994     int center_side = trigger_sides[i][0];
12995     int border_element = border_element_old[i];
12996
12997     if (border_element == -1)
12998       continue;
12999
13000     // check for change of border element
13001     CheckElementChangeBySide(xx, yy, border_element, center_element,
13002                              CE_TOUCHING_X, center_side);
13003
13004     // (center element cannot be player, so we dont have to check this here)
13005   }
13006
13007   for (i = 0; i < NUM_DIRECTIONS; i++)
13008   {
13009     int xx = x + xy[i][0];
13010     int yy = y + xy[i][1];
13011     int border_side = trigger_sides[i][1];
13012     int border_element = border_element_old[i];
13013
13014     if (border_element == -1)
13015       continue;
13016
13017     // check for change of center element (but change it only once)
13018     if (!change_center_element)
13019       change_center_element =
13020         CheckElementChangeBySide(x, y, center_element, border_element,
13021                                  CE_TOUCHING_X, border_side);
13022
13023     if (IS_PLAYER(xx, yy))
13024     {
13025       /* use player element that is initially defined in the level playfield,
13026          not the player element that corresponds to the runtime player number
13027          (example: a level that contains EL_PLAYER_3 as the only player would
13028          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13029       int player_element = PLAYERINFO(xx, yy)->initial_element;
13030
13031       CheckElementChangeBySide(x, y, center_element, player_element,
13032                                CE_TOUCHING_X, border_side);
13033     }
13034   }
13035 }
13036
13037 void TestIfElementHitsCustomElement(int x, int y, int direction)
13038 {
13039   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13040   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13041   int hitx = x + dx, hity = y + dy;
13042   int hitting_element = Feld[x][y];
13043   int touched_element;
13044
13045   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13046     return;
13047
13048   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13049                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13050
13051   if (IN_LEV_FIELD(hitx, hity))
13052   {
13053     int opposite_direction = MV_DIR_OPPOSITE(direction);
13054     int hitting_side = direction;
13055     int touched_side = opposite_direction;
13056     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13057                           MovDir[hitx][hity] != direction ||
13058                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13059
13060     object_hit = TRUE;
13061
13062     if (object_hit)
13063     {
13064       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13065                                CE_HITTING_X, touched_side);
13066
13067       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13068                                CE_HIT_BY_X, hitting_side);
13069
13070       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13071                                CE_HIT_BY_SOMETHING, opposite_direction);
13072
13073       if (IS_PLAYER(hitx, hity))
13074       {
13075         /* use player element that is initially defined in the level playfield,
13076            not the player element that corresponds to the runtime player number
13077            (example: a level that contains EL_PLAYER_3 as the only player would
13078            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13079         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13080
13081         CheckElementChangeBySide(x, y, hitting_element, player_element,
13082                                  CE_HITTING_X, touched_side);
13083       }
13084     }
13085   }
13086
13087   // "hitting something" is also true when hitting the playfield border
13088   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13089                            CE_HITTING_SOMETHING, direction);
13090 }
13091
13092 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13093 {
13094   int i, kill_x = -1, kill_y = -1;
13095
13096   int bad_element = -1;
13097   static int test_xy[4][2] =
13098   {
13099     { 0, -1 },
13100     { -1, 0 },
13101     { +1, 0 },
13102     { 0, +1 }
13103   };
13104   static int test_dir[4] =
13105   {
13106     MV_UP,
13107     MV_LEFT,
13108     MV_RIGHT,
13109     MV_DOWN
13110   };
13111
13112   for (i = 0; i < NUM_DIRECTIONS; i++)
13113   {
13114     int test_x, test_y, test_move_dir, test_element;
13115
13116     test_x = good_x + test_xy[i][0];
13117     test_y = good_y + test_xy[i][1];
13118
13119     if (!IN_LEV_FIELD(test_x, test_y))
13120       continue;
13121
13122     test_move_dir =
13123       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13124
13125     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13126
13127     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13128        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13129     */
13130     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13131         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13132     {
13133       kill_x = test_x;
13134       kill_y = test_y;
13135       bad_element = test_element;
13136
13137       break;
13138     }
13139   }
13140
13141   if (kill_x != -1 || kill_y != -1)
13142   {
13143     if (IS_PLAYER(good_x, good_y))
13144     {
13145       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13146
13147       if (player->shield_deadly_time_left > 0 &&
13148           !IS_INDESTRUCTIBLE(bad_element))
13149         Bang(kill_x, kill_y);
13150       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13151         KillPlayer(player);
13152     }
13153     else
13154       Bang(good_x, good_y);
13155   }
13156 }
13157
13158 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13159 {
13160   int i, kill_x = -1, kill_y = -1;
13161   int bad_element = Feld[bad_x][bad_y];
13162   static int test_xy[4][2] =
13163   {
13164     { 0, -1 },
13165     { -1, 0 },
13166     { +1, 0 },
13167     { 0, +1 }
13168   };
13169   static int touch_dir[4] =
13170   {
13171     MV_LEFT | MV_RIGHT,
13172     MV_UP   | MV_DOWN,
13173     MV_UP   | MV_DOWN,
13174     MV_LEFT | MV_RIGHT
13175   };
13176   static int test_dir[4] =
13177   {
13178     MV_UP,
13179     MV_LEFT,
13180     MV_RIGHT,
13181     MV_DOWN
13182   };
13183
13184   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13185     return;
13186
13187   for (i = 0; i < NUM_DIRECTIONS; i++)
13188   {
13189     int test_x, test_y, test_move_dir, test_element;
13190
13191     test_x = bad_x + test_xy[i][0];
13192     test_y = bad_y + test_xy[i][1];
13193
13194     if (!IN_LEV_FIELD(test_x, test_y))
13195       continue;
13196
13197     test_move_dir =
13198       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13199
13200     test_element = Feld[test_x][test_y];
13201
13202     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13203        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13204     */
13205     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13206         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13207     {
13208       // good thing is player or penguin that does not move away
13209       if (IS_PLAYER(test_x, test_y))
13210       {
13211         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13212
13213         if (bad_element == EL_ROBOT && player->is_moving)
13214           continue;     // robot does not kill player if he is moving
13215
13216         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13217         {
13218           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13219             continue;           // center and border element do not touch
13220         }
13221
13222         kill_x = test_x;
13223         kill_y = test_y;
13224
13225         break;
13226       }
13227       else if (test_element == EL_PENGUIN)
13228       {
13229         kill_x = test_x;
13230         kill_y = test_y;
13231
13232         break;
13233       }
13234     }
13235   }
13236
13237   if (kill_x != -1 || kill_y != -1)
13238   {
13239     if (IS_PLAYER(kill_x, kill_y))
13240     {
13241       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13242
13243       if (player->shield_deadly_time_left > 0 &&
13244           !IS_INDESTRUCTIBLE(bad_element))
13245         Bang(bad_x, bad_y);
13246       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13247         KillPlayer(player);
13248     }
13249     else
13250       Bang(kill_x, kill_y);
13251   }
13252 }
13253
13254 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13255 {
13256   int bad_element = Feld[bad_x][bad_y];
13257   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13258   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13259   int test_x = bad_x + dx, test_y = bad_y + dy;
13260   int test_move_dir, test_element;
13261   int kill_x = -1, kill_y = -1;
13262
13263   if (!IN_LEV_FIELD(test_x, test_y))
13264     return;
13265
13266   test_move_dir =
13267     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13268
13269   test_element = Feld[test_x][test_y];
13270
13271   if (test_move_dir != bad_move_dir)
13272   {
13273     // good thing can be player or penguin that does not move away
13274     if (IS_PLAYER(test_x, test_y))
13275     {
13276       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13277
13278       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13279          player as being hit when he is moving towards the bad thing, because
13280          the "get hit by" condition would be lost after the player stops) */
13281       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13282         return;         // player moves away from bad thing
13283
13284       kill_x = test_x;
13285       kill_y = test_y;
13286     }
13287     else if (test_element == EL_PENGUIN)
13288     {
13289       kill_x = test_x;
13290       kill_y = test_y;
13291     }
13292   }
13293
13294   if (kill_x != -1 || kill_y != -1)
13295   {
13296     if (IS_PLAYER(kill_x, kill_y))
13297     {
13298       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13299
13300       if (player->shield_deadly_time_left > 0 &&
13301           !IS_INDESTRUCTIBLE(bad_element))
13302         Bang(bad_x, bad_y);
13303       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13304         KillPlayer(player);
13305     }
13306     else
13307       Bang(kill_x, kill_y);
13308   }
13309 }
13310
13311 void TestIfPlayerTouchesBadThing(int x, int y)
13312 {
13313   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13314 }
13315
13316 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13317 {
13318   TestIfGoodThingHitsBadThing(x, y, move_dir);
13319 }
13320
13321 void TestIfBadThingTouchesPlayer(int x, int y)
13322 {
13323   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13324 }
13325
13326 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13327 {
13328   TestIfBadThingHitsGoodThing(x, y, move_dir);
13329 }
13330
13331 void TestIfFriendTouchesBadThing(int x, int y)
13332 {
13333   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13334 }
13335
13336 void TestIfBadThingTouchesFriend(int x, int y)
13337 {
13338   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13339 }
13340
13341 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13342 {
13343   int i, kill_x = bad_x, kill_y = bad_y;
13344   static int xy[4][2] =
13345   {
13346     { 0, -1 },
13347     { -1, 0 },
13348     { +1, 0 },
13349     { 0, +1 }
13350   };
13351
13352   for (i = 0; i < NUM_DIRECTIONS; i++)
13353   {
13354     int x, y, element;
13355
13356     x = bad_x + xy[i][0];
13357     y = bad_y + xy[i][1];
13358     if (!IN_LEV_FIELD(x, y))
13359       continue;
13360
13361     element = Feld[x][y];
13362     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13363         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13364     {
13365       kill_x = x;
13366       kill_y = y;
13367       break;
13368     }
13369   }
13370
13371   if (kill_x != bad_x || kill_y != bad_y)
13372     Bang(bad_x, bad_y);
13373 }
13374
13375 void KillPlayer(struct PlayerInfo *player)
13376 {
13377   int jx = player->jx, jy = player->jy;
13378
13379   if (!player->active)
13380     return;
13381
13382 #if 0
13383   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13384          player->killed, player->active, player->reanimated);
13385 #endif
13386
13387   /* the following code was introduced to prevent an infinite loop when calling
13388      -> Bang()
13389      -> CheckTriggeredElementChangeExt()
13390      -> ExecuteCustomElementAction()
13391      -> KillPlayer()
13392      -> (infinitely repeating the above sequence of function calls)
13393      which occurs when killing the player while having a CE with the setting
13394      "kill player X when explosion of <player X>"; the solution using a new
13395      field "player->killed" was chosen for backwards compatibility, although
13396      clever use of the fields "player->active" etc. would probably also work */
13397 #if 1
13398   if (player->killed)
13399     return;
13400 #endif
13401
13402   player->killed = TRUE;
13403
13404   // remove accessible field at the player's position
13405   Feld[jx][jy] = EL_EMPTY;
13406
13407   // deactivate shield (else Bang()/Explode() would not work right)
13408   player->shield_normal_time_left = 0;
13409   player->shield_deadly_time_left = 0;
13410
13411 #if 0
13412   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13413          player->killed, player->active, player->reanimated);
13414 #endif
13415
13416   Bang(jx, jy);
13417
13418 #if 0
13419   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13420          player->killed, player->active, player->reanimated);
13421 #endif
13422
13423   if (player->reanimated)       // killed player may have been reanimated
13424     player->killed = player->reanimated = FALSE;
13425   else
13426     BuryPlayer(player);
13427 }
13428
13429 static void KillPlayerUnlessEnemyProtected(int x, int y)
13430 {
13431   if (!PLAYER_ENEMY_PROTECTED(x, y))
13432     KillPlayer(PLAYERINFO(x, y));
13433 }
13434
13435 static void KillPlayerUnlessExplosionProtected(int x, int y)
13436 {
13437   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13438     KillPlayer(PLAYERINFO(x, y));
13439 }
13440
13441 void BuryPlayer(struct PlayerInfo *player)
13442 {
13443   int jx = player->jx, jy = player->jy;
13444
13445   if (!player->active)
13446     return;
13447
13448   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13449   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13450
13451   RemovePlayer(player);
13452
13453   player->buried = TRUE;
13454
13455   if (game.all_players_gone)
13456     game.GameOver = TRUE;
13457 }
13458
13459 void RemovePlayer(struct PlayerInfo *player)
13460 {
13461   int jx = player->jx, jy = player->jy;
13462   int i, found = FALSE;
13463
13464   player->present = FALSE;
13465   player->active = FALSE;
13466
13467   // required for some CE actions (even if the player is not active anymore)
13468   player->MovPos = 0;
13469
13470   if (!ExplodeField[jx][jy])
13471     StorePlayer[jx][jy] = 0;
13472
13473   if (player->is_moving)
13474     TEST_DrawLevelField(player->last_jx, player->last_jy);
13475
13476   for (i = 0; i < MAX_PLAYERS; i++)
13477     if (stored_player[i].active)
13478       found = TRUE;
13479
13480   if (!found)
13481   {
13482     game.all_players_gone = TRUE;
13483     game.GameOver = TRUE;
13484   }
13485
13486   game.exit_x = game.robot_wheel_x = jx;
13487   game.exit_y = game.robot_wheel_y = jy;
13488 }
13489
13490 void ExitPlayer(struct PlayerInfo *player)
13491 {
13492   DrawPlayer(player);   // needed here only to cleanup last field
13493   RemovePlayer(player);
13494
13495   if (game.players_still_needed > 0)
13496     game.players_still_needed--;
13497 }
13498
13499 static void setFieldForSnapping(int x, int y, int element, int direction)
13500 {
13501   struct ElementInfo *ei = &element_info[element];
13502   int direction_bit = MV_DIR_TO_BIT(direction);
13503   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13504   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13505                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13506
13507   Feld[x][y] = EL_ELEMENT_SNAPPING;
13508   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13509
13510   ResetGfxAnimation(x, y);
13511
13512   GfxElement[x][y] = element;
13513   GfxAction[x][y] = action;
13514   GfxDir[x][y] = direction;
13515   GfxFrame[x][y] = -1;
13516 }
13517
13518 /*
13519   =============================================================================
13520   checkDiagonalPushing()
13521   -----------------------------------------------------------------------------
13522   check if diagonal input device direction results in pushing of object
13523   (by checking if the alternative direction is walkable, diggable, ...)
13524   =============================================================================
13525 */
13526
13527 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13528                                     int x, int y, int real_dx, int real_dy)
13529 {
13530   int jx, jy, dx, dy, xx, yy;
13531
13532   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13533     return TRUE;
13534
13535   // diagonal direction: check alternative direction
13536   jx = player->jx;
13537   jy = player->jy;
13538   dx = x - jx;
13539   dy = y - jy;
13540   xx = jx + (dx == 0 ? real_dx : 0);
13541   yy = jy + (dy == 0 ? real_dy : 0);
13542
13543   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13544 }
13545
13546 /*
13547   =============================================================================
13548   DigField()
13549   -----------------------------------------------------------------------------
13550   x, y:                 field next to player (non-diagonal) to try to dig to
13551   real_dx, real_dy:     direction as read from input device (can be diagonal)
13552   =============================================================================
13553 */
13554
13555 static int DigField(struct PlayerInfo *player,
13556                     int oldx, int oldy, int x, int y,
13557                     int real_dx, int real_dy, int mode)
13558 {
13559   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13560   boolean player_was_pushing = player->is_pushing;
13561   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13562   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13563   int jx = oldx, jy = oldy;
13564   int dx = x - jx, dy = y - jy;
13565   int nextx = x + dx, nexty = y + dy;
13566   int move_direction = (dx == -1 ? MV_LEFT  :
13567                         dx == +1 ? MV_RIGHT :
13568                         dy == -1 ? MV_UP    :
13569                         dy == +1 ? MV_DOWN  : MV_NONE);
13570   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13571   int dig_side = MV_DIR_OPPOSITE(move_direction);
13572   int old_element = Feld[jx][jy];
13573   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13574   int collect_count;
13575
13576   if (is_player)                // function can also be called by EL_PENGUIN
13577   {
13578     if (player->MovPos == 0)
13579     {
13580       player->is_digging = FALSE;
13581       player->is_collecting = FALSE;
13582     }
13583
13584     if (player->MovPos == 0)    // last pushing move finished
13585       player->is_pushing = FALSE;
13586
13587     if (mode == DF_NO_PUSH)     // player just stopped pushing
13588     {
13589       player->is_switching = FALSE;
13590       player->push_delay = -1;
13591
13592       return MP_NO_ACTION;
13593     }
13594   }
13595
13596   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13597     old_element = Back[jx][jy];
13598
13599   // in case of element dropped at player position, check background
13600   else if (Back[jx][jy] != EL_EMPTY &&
13601            game.engine_version >= VERSION_IDENT(2,2,0,0))
13602     old_element = Back[jx][jy];
13603
13604   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13605     return MP_NO_ACTION;        // field has no opening in this direction
13606
13607   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13608     return MP_NO_ACTION;        // field has no opening in this direction
13609
13610   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13611   {
13612     SplashAcid(x, y);
13613
13614     Feld[jx][jy] = player->artwork_element;
13615     InitMovingField(jx, jy, MV_DOWN);
13616     Store[jx][jy] = EL_ACID;
13617     ContinueMoving(jx, jy);
13618     BuryPlayer(player);
13619
13620     return MP_DONT_RUN_INTO;
13621   }
13622
13623   if (player_can_move && DONT_RUN_INTO(element))
13624   {
13625     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13626
13627     return MP_DONT_RUN_INTO;
13628   }
13629
13630   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13631     return MP_NO_ACTION;
13632
13633   collect_count = element_info[element].collect_count_initial;
13634
13635   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13636     return MP_NO_ACTION;
13637
13638   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13639     player_can_move = player_can_move_or_snap;
13640
13641   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13642       game.engine_version >= VERSION_IDENT(2,2,0,0))
13643   {
13644     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13645                                player->index_bit, dig_side);
13646     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13647                                         player->index_bit, dig_side);
13648
13649     if (element == EL_DC_LANDMINE)
13650       Bang(x, y);
13651
13652     if (Feld[x][y] != element)          // field changed by snapping
13653       return MP_ACTION;
13654
13655     return MP_NO_ACTION;
13656   }
13657
13658   if (player->gravity && is_player && !player->is_auto_moving &&
13659       canFallDown(player) && move_direction != MV_DOWN &&
13660       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13661     return MP_NO_ACTION;        // player cannot walk here due to gravity
13662
13663   if (player_can_move &&
13664       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13665   {
13666     int sound_element = SND_ELEMENT(element);
13667     int sound_action = ACTION_WALKING;
13668
13669     if (IS_RND_GATE(element))
13670     {
13671       if (!player->key[RND_GATE_NR(element)])
13672         return MP_NO_ACTION;
13673     }
13674     else if (IS_RND_GATE_GRAY(element))
13675     {
13676       if (!player->key[RND_GATE_GRAY_NR(element)])
13677         return MP_NO_ACTION;
13678     }
13679     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13680     {
13681       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13682         return MP_NO_ACTION;
13683     }
13684     else if (element == EL_EXIT_OPEN ||
13685              element == EL_EM_EXIT_OPEN ||
13686              element == EL_EM_EXIT_OPENING ||
13687              element == EL_STEEL_EXIT_OPEN ||
13688              element == EL_EM_STEEL_EXIT_OPEN ||
13689              element == EL_EM_STEEL_EXIT_OPENING ||
13690              element == EL_SP_EXIT_OPEN ||
13691              element == EL_SP_EXIT_OPENING)
13692     {
13693       sound_action = ACTION_PASSING;    // player is passing exit
13694     }
13695     else if (element == EL_EMPTY)
13696     {
13697       sound_action = ACTION_MOVING;             // nothing to walk on
13698     }
13699
13700     // play sound from background or player, whatever is available
13701     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13702       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13703     else
13704       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13705   }
13706   else if (player_can_move &&
13707            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13708   {
13709     if (!ACCESS_FROM(element, opposite_direction))
13710       return MP_NO_ACTION;      // field not accessible from this direction
13711
13712     if (CAN_MOVE(element))      // only fixed elements can be passed!
13713       return MP_NO_ACTION;
13714
13715     if (IS_EM_GATE(element))
13716     {
13717       if (!player->key[EM_GATE_NR(element)])
13718         return MP_NO_ACTION;
13719     }
13720     else if (IS_EM_GATE_GRAY(element))
13721     {
13722       if (!player->key[EM_GATE_GRAY_NR(element)])
13723         return MP_NO_ACTION;
13724     }
13725     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13726     {
13727       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13728         return MP_NO_ACTION;
13729     }
13730     else if (IS_EMC_GATE(element))
13731     {
13732       if (!player->key[EMC_GATE_NR(element)])
13733         return MP_NO_ACTION;
13734     }
13735     else if (IS_EMC_GATE_GRAY(element))
13736     {
13737       if (!player->key[EMC_GATE_GRAY_NR(element)])
13738         return MP_NO_ACTION;
13739     }
13740     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13741     {
13742       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13743         return MP_NO_ACTION;
13744     }
13745     else if (element == EL_DC_GATE_WHITE ||
13746              element == EL_DC_GATE_WHITE_GRAY ||
13747              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13748     {
13749       if (player->num_white_keys == 0)
13750         return MP_NO_ACTION;
13751
13752       player->num_white_keys--;
13753     }
13754     else if (IS_SP_PORT(element))
13755     {
13756       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13757           element == EL_SP_GRAVITY_PORT_RIGHT ||
13758           element == EL_SP_GRAVITY_PORT_UP ||
13759           element == EL_SP_GRAVITY_PORT_DOWN)
13760         player->gravity = !player->gravity;
13761       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13762                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13763                element == EL_SP_GRAVITY_ON_PORT_UP ||
13764                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13765         player->gravity = TRUE;
13766       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13767                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13768                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13769                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13770         player->gravity = FALSE;
13771     }
13772
13773     // automatically move to the next field with double speed
13774     player->programmed_action = move_direction;
13775
13776     if (player->move_delay_reset_counter == 0)
13777     {
13778       player->move_delay_reset_counter = 2;     // two double speed steps
13779
13780       DOUBLE_PLAYER_SPEED(player);
13781     }
13782
13783     PlayLevelSoundAction(x, y, ACTION_PASSING);
13784   }
13785   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13786   {
13787     RemoveField(x, y);
13788
13789     if (mode != DF_SNAP)
13790     {
13791       GfxElement[x][y] = GFX_ELEMENT(element);
13792       player->is_digging = TRUE;
13793     }
13794
13795     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13796
13797     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13798                                         player->index_bit, dig_side);
13799
13800     if (mode == DF_SNAP)
13801     {
13802       if (level.block_snap_field)
13803         setFieldForSnapping(x, y, element, move_direction);
13804       else
13805         TestIfElementTouchesCustomElement(x, y);        // for empty space
13806
13807       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13808                                           player->index_bit, dig_side);
13809     }
13810   }
13811   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13812   {
13813     RemoveField(x, y);
13814
13815     if (is_player && mode != DF_SNAP)
13816     {
13817       GfxElement[x][y] = element;
13818       player->is_collecting = TRUE;
13819     }
13820
13821     if (element == EL_SPEED_PILL)
13822     {
13823       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13824     }
13825     else if (element == EL_EXTRA_TIME && level.time > 0)
13826     {
13827       TimeLeft += level.extra_time;
13828
13829       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13830
13831       DisplayGameControlValues();
13832     }
13833     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13834     {
13835       player->shield_normal_time_left += level.shield_normal_time;
13836       if (element == EL_SHIELD_DEADLY)
13837         player->shield_deadly_time_left += level.shield_deadly_time;
13838     }
13839     else if (element == EL_DYNAMITE ||
13840              element == EL_EM_DYNAMITE ||
13841              element == EL_SP_DISK_RED)
13842     {
13843       if (player->inventory_size < MAX_INVENTORY_SIZE)
13844         player->inventory_element[player->inventory_size++] = element;
13845
13846       DrawGameDoorValues();
13847     }
13848     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13849     {
13850       player->dynabomb_count++;
13851       player->dynabombs_left++;
13852     }
13853     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13854     {
13855       player->dynabomb_size++;
13856     }
13857     else if (element == EL_DYNABOMB_INCREASE_POWER)
13858     {
13859       player->dynabomb_xl = TRUE;
13860     }
13861     else if (IS_KEY(element))
13862     {
13863       player->key[KEY_NR(element)] = TRUE;
13864
13865       DrawGameDoorValues();
13866     }
13867     else if (element == EL_DC_KEY_WHITE)
13868     {
13869       player->num_white_keys++;
13870
13871       // display white keys?
13872       // DrawGameDoorValues();
13873     }
13874     else if (IS_ENVELOPE(element))
13875     {
13876       player->show_envelope = element;
13877     }
13878     else if (element == EL_EMC_LENSES)
13879     {
13880       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13881
13882       RedrawAllInvisibleElementsForLenses();
13883     }
13884     else if (element == EL_EMC_MAGNIFIER)
13885     {
13886       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13887
13888       RedrawAllInvisibleElementsForMagnifier();
13889     }
13890     else if (IS_DROPPABLE(element) ||
13891              IS_THROWABLE(element))     // can be collected and dropped
13892     {
13893       int i;
13894
13895       if (collect_count == 0)
13896         player->inventory_infinite_element = element;
13897       else
13898         for (i = 0; i < collect_count; i++)
13899           if (player->inventory_size < MAX_INVENTORY_SIZE)
13900             player->inventory_element[player->inventory_size++] = element;
13901
13902       DrawGameDoorValues();
13903     }
13904     else if (collect_count > 0)
13905     {
13906       game.gems_still_needed -= collect_count;
13907       if (game.gems_still_needed < 0)
13908         game.gems_still_needed = 0;
13909
13910       game.snapshot.collected_item = TRUE;
13911
13912       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13913
13914       DisplayGameControlValues();
13915     }
13916
13917     RaiseScoreElement(element);
13918     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13919
13920     if (is_player)
13921       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13922                                           player->index_bit, dig_side);
13923
13924     if (mode == DF_SNAP)
13925     {
13926       if (level.block_snap_field)
13927         setFieldForSnapping(x, y, element, move_direction);
13928       else
13929         TestIfElementTouchesCustomElement(x, y);        // for empty space
13930
13931       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13932                                           player->index_bit, dig_side);
13933     }
13934   }
13935   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13936   {
13937     if (mode == DF_SNAP && element != EL_BD_ROCK)
13938       return MP_NO_ACTION;
13939
13940     if (CAN_FALL(element) && dy)
13941       return MP_NO_ACTION;
13942
13943     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13944         !(element == EL_SPRING && level.use_spring_bug))
13945       return MP_NO_ACTION;
13946
13947     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13948         ((move_direction & MV_VERTICAL &&
13949           ((element_info[element].move_pattern & MV_LEFT &&
13950             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13951            (element_info[element].move_pattern & MV_RIGHT &&
13952             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13953          (move_direction & MV_HORIZONTAL &&
13954           ((element_info[element].move_pattern & MV_UP &&
13955             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13956            (element_info[element].move_pattern & MV_DOWN &&
13957             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13958       return MP_NO_ACTION;
13959
13960     // do not push elements already moving away faster than player
13961     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13962         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13963       return MP_NO_ACTION;
13964
13965     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13966     {
13967       if (player->push_delay_value == -1 || !player_was_pushing)
13968         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13969     }
13970     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13971     {
13972       if (player->push_delay_value == -1)
13973         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13974     }
13975     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13976     {
13977       if (!player->is_pushing)
13978         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13979     }
13980
13981     player->is_pushing = TRUE;
13982     player->is_active = TRUE;
13983
13984     if (!(IN_LEV_FIELD(nextx, nexty) &&
13985           (IS_FREE(nextx, nexty) ||
13986            (IS_SB_ELEMENT(element) &&
13987             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13988            (IS_CUSTOM_ELEMENT(element) &&
13989             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13990       return MP_NO_ACTION;
13991
13992     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13993       return MP_NO_ACTION;
13994
13995     if (player->push_delay == -1)       // new pushing; restart delay
13996       player->push_delay = 0;
13997
13998     if (player->push_delay < player->push_delay_value &&
13999         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14000         element != EL_SPRING && element != EL_BALLOON)
14001     {
14002       // make sure that there is no move delay before next try to push
14003       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14004         player->move_delay = 0;
14005
14006       return MP_NO_ACTION;
14007     }
14008
14009     if (IS_CUSTOM_ELEMENT(element) &&
14010         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14011     {
14012       if (!DigFieldByCE(nextx, nexty, element))
14013         return MP_NO_ACTION;
14014     }
14015
14016     if (IS_SB_ELEMENT(element))
14017     {
14018       boolean sokoban_task_solved = FALSE;
14019
14020       if (element == EL_SOKOBAN_FIELD_FULL)
14021       {
14022         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14023
14024         IncrementSokobanFieldsNeeded();
14025         IncrementSokobanObjectsNeeded();
14026       }
14027
14028       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14029       {
14030         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14031
14032         DecrementSokobanFieldsNeeded();
14033         DecrementSokobanObjectsNeeded();
14034
14035         // sokoban object was pushed from empty field to sokoban field
14036         if (Back[x][y] == EL_EMPTY)
14037           sokoban_task_solved = TRUE;
14038       }
14039
14040       Feld[x][y] = EL_SOKOBAN_OBJECT;
14041
14042       if (Back[x][y] == Back[nextx][nexty])
14043         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14044       else if (Back[x][y] != 0)
14045         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14046                                     ACTION_EMPTYING);
14047       else
14048         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14049                                     ACTION_FILLING);
14050
14051       if (sokoban_task_solved &&
14052           game.sokoban_fields_still_needed == 0 &&
14053           game.sokoban_objects_still_needed == 0 &&
14054           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14055       {
14056         game.players_still_needed = 0;
14057
14058         LevelSolved();
14059
14060         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14061       }
14062     }
14063     else
14064       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14065
14066     InitMovingField(x, y, move_direction);
14067     GfxAction[x][y] = ACTION_PUSHING;
14068
14069     if (mode == DF_SNAP)
14070       ContinueMoving(x, y);
14071     else
14072       MovPos[x][y] = (dx != 0 ? dx : dy);
14073
14074     Pushed[x][y] = TRUE;
14075     Pushed[nextx][nexty] = TRUE;
14076
14077     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14078       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14079     else
14080       player->push_delay_value = -1;    // get new value later
14081
14082     // check for element change _after_ element has been pushed
14083     if (game.use_change_when_pushing_bug)
14084     {
14085       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14086                                  player->index_bit, dig_side);
14087       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14088                                           player->index_bit, dig_side);
14089     }
14090   }
14091   else if (IS_SWITCHABLE(element))
14092   {
14093     if (PLAYER_SWITCHING(player, x, y))
14094     {
14095       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14096                                           player->index_bit, dig_side);
14097
14098       return MP_ACTION;
14099     }
14100
14101     player->is_switching = TRUE;
14102     player->switch_x = x;
14103     player->switch_y = y;
14104
14105     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14106
14107     if (element == EL_ROBOT_WHEEL)
14108     {
14109       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14110
14111       game.robot_wheel_x = x;
14112       game.robot_wheel_y = y;
14113       game.robot_wheel_active = TRUE;
14114
14115       TEST_DrawLevelField(x, y);
14116     }
14117     else if (element == EL_SP_TERMINAL)
14118     {
14119       int xx, yy;
14120
14121       SCAN_PLAYFIELD(xx, yy)
14122       {
14123         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14124         {
14125           Bang(xx, yy);
14126         }
14127         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14128         {
14129           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14130
14131           ResetGfxAnimation(xx, yy);
14132           TEST_DrawLevelField(xx, yy);
14133         }
14134       }
14135     }
14136     else if (IS_BELT_SWITCH(element))
14137     {
14138       ToggleBeltSwitch(x, y);
14139     }
14140     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14141              element == EL_SWITCHGATE_SWITCH_DOWN ||
14142              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14143              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14144     {
14145       ToggleSwitchgateSwitch(x, y);
14146     }
14147     else if (element == EL_LIGHT_SWITCH ||
14148              element == EL_LIGHT_SWITCH_ACTIVE)
14149     {
14150       ToggleLightSwitch(x, y);
14151     }
14152     else if (element == EL_TIMEGATE_SWITCH ||
14153              element == EL_DC_TIMEGATE_SWITCH)
14154     {
14155       ActivateTimegateSwitch(x, y);
14156     }
14157     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14158              element == EL_BALLOON_SWITCH_RIGHT ||
14159              element == EL_BALLOON_SWITCH_UP    ||
14160              element == EL_BALLOON_SWITCH_DOWN  ||
14161              element == EL_BALLOON_SWITCH_NONE  ||
14162              element == EL_BALLOON_SWITCH_ANY)
14163     {
14164       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14165                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14166                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14167                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14168                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14169                              move_direction);
14170     }
14171     else if (element == EL_LAMP)
14172     {
14173       Feld[x][y] = EL_LAMP_ACTIVE;
14174       game.lights_still_needed--;
14175
14176       ResetGfxAnimation(x, y);
14177       TEST_DrawLevelField(x, y);
14178     }
14179     else if (element == EL_TIME_ORB_FULL)
14180     {
14181       Feld[x][y] = EL_TIME_ORB_EMPTY;
14182
14183       if (level.time > 0 || level.use_time_orb_bug)
14184       {
14185         TimeLeft += level.time_orb_time;
14186         game.no_time_limit = FALSE;
14187
14188         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14189
14190         DisplayGameControlValues();
14191       }
14192
14193       ResetGfxAnimation(x, y);
14194       TEST_DrawLevelField(x, y);
14195     }
14196     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14197              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14198     {
14199       int xx, yy;
14200
14201       game.ball_state = !game.ball_state;
14202
14203       SCAN_PLAYFIELD(xx, yy)
14204       {
14205         int e = Feld[xx][yy];
14206
14207         if (game.ball_state)
14208         {
14209           if (e == EL_EMC_MAGIC_BALL)
14210             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14211           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14212             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14213         }
14214         else
14215         {
14216           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14217             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14218           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14219             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14220         }
14221       }
14222     }
14223
14224     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14225                                         player->index_bit, dig_side);
14226
14227     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14228                                         player->index_bit, dig_side);
14229
14230     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14231                                         player->index_bit, dig_side);
14232
14233     return MP_ACTION;
14234   }
14235   else
14236   {
14237     if (!PLAYER_SWITCHING(player, x, y))
14238     {
14239       player->is_switching = TRUE;
14240       player->switch_x = x;
14241       player->switch_y = y;
14242
14243       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14244                                  player->index_bit, dig_side);
14245       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14246                                           player->index_bit, dig_side);
14247
14248       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14249                                  player->index_bit, dig_side);
14250       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14251                                           player->index_bit, dig_side);
14252     }
14253
14254     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14255                                player->index_bit, dig_side);
14256     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14257                                         player->index_bit, dig_side);
14258
14259     return MP_NO_ACTION;
14260   }
14261
14262   player->push_delay = -1;
14263
14264   if (is_player)                // function can also be called by EL_PENGUIN
14265   {
14266     if (Feld[x][y] != element)          // really digged/collected something
14267     {
14268       player->is_collecting = !player->is_digging;
14269       player->is_active = TRUE;
14270     }
14271   }
14272
14273   return MP_MOVING;
14274 }
14275
14276 static boolean DigFieldByCE(int x, int y, int digging_element)
14277 {
14278   int element = Feld[x][y];
14279
14280   if (!IS_FREE(x, y))
14281   {
14282     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14283                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14284                   ACTION_BREAKING);
14285
14286     // no element can dig solid indestructible elements
14287     if (IS_INDESTRUCTIBLE(element) &&
14288         !IS_DIGGABLE(element) &&
14289         !IS_COLLECTIBLE(element))
14290       return FALSE;
14291
14292     if (AmoebaNr[x][y] &&
14293         (element == EL_AMOEBA_FULL ||
14294          element == EL_BD_AMOEBA ||
14295          element == EL_AMOEBA_GROWING))
14296     {
14297       AmoebaCnt[AmoebaNr[x][y]]--;
14298       AmoebaCnt2[AmoebaNr[x][y]]--;
14299     }
14300
14301     if (IS_MOVING(x, y))
14302       RemoveMovingField(x, y);
14303     else
14304     {
14305       RemoveField(x, y);
14306       TEST_DrawLevelField(x, y);
14307     }
14308
14309     // if digged element was about to explode, prevent the explosion
14310     ExplodeField[x][y] = EX_TYPE_NONE;
14311
14312     PlayLevelSoundAction(x, y, action);
14313   }
14314
14315   Store[x][y] = EL_EMPTY;
14316
14317   // this makes it possible to leave the removed element again
14318   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14319     Store[x][y] = element;
14320
14321   return TRUE;
14322 }
14323
14324 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14325 {
14326   int jx = player->jx, jy = player->jy;
14327   int x = jx + dx, y = jy + dy;
14328   int snap_direction = (dx == -1 ? MV_LEFT  :
14329                         dx == +1 ? MV_RIGHT :
14330                         dy == -1 ? MV_UP    :
14331                         dy == +1 ? MV_DOWN  : MV_NONE);
14332   boolean can_continue_snapping = (level.continuous_snapping &&
14333                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14334
14335   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14336     return FALSE;
14337
14338   if (!player->active || !IN_LEV_FIELD(x, y))
14339     return FALSE;
14340
14341   if (dx && dy)
14342     return FALSE;
14343
14344   if (!dx && !dy)
14345   {
14346     if (player->MovPos == 0)
14347       player->is_pushing = FALSE;
14348
14349     player->is_snapping = FALSE;
14350
14351     if (player->MovPos == 0)
14352     {
14353       player->is_moving = FALSE;
14354       player->is_digging = FALSE;
14355       player->is_collecting = FALSE;
14356     }
14357
14358     return FALSE;
14359   }
14360
14361   // prevent snapping with already pressed snap key when not allowed
14362   if (player->is_snapping && !can_continue_snapping)
14363     return FALSE;
14364
14365   player->MovDir = snap_direction;
14366
14367   if (player->MovPos == 0)
14368   {
14369     player->is_moving = FALSE;
14370     player->is_digging = FALSE;
14371     player->is_collecting = FALSE;
14372   }
14373
14374   player->is_dropping = FALSE;
14375   player->is_dropping_pressed = FALSE;
14376   player->drop_pressed_delay = 0;
14377
14378   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14379     return FALSE;
14380
14381   player->is_snapping = TRUE;
14382   player->is_active = TRUE;
14383
14384   if (player->MovPos == 0)
14385   {
14386     player->is_moving = FALSE;
14387     player->is_digging = FALSE;
14388     player->is_collecting = FALSE;
14389   }
14390
14391   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14392     TEST_DrawLevelField(player->last_jx, player->last_jy);
14393
14394   TEST_DrawLevelField(x, y);
14395
14396   return TRUE;
14397 }
14398
14399 static boolean DropElement(struct PlayerInfo *player)
14400 {
14401   int old_element, new_element;
14402   int dropx = player->jx, dropy = player->jy;
14403   int drop_direction = player->MovDir;
14404   int drop_side = drop_direction;
14405   int drop_element = get_next_dropped_element(player);
14406
14407   /* do not drop an element on top of another element; when holding drop key
14408      pressed without moving, dropped element must move away before the next
14409      element can be dropped (this is especially important if the next element
14410      is dynamite, which can be placed on background for historical reasons) */
14411   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14412     return MP_ACTION;
14413
14414   if (IS_THROWABLE(drop_element))
14415   {
14416     dropx += GET_DX_FROM_DIR(drop_direction);
14417     dropy += GET_DY_FROM_DIR(drop_direction);
14418
14419     if (!IN_LEV_FIELD(dropx, dropy))
14420       return FALSE;
14421   }
14422
14423   old_element = Feld[dropx][dropy];     // old element at dropping position
14424   new_element = drop_element;           // default: no change when dropping
14425
14426   // check if player is active, not moving and ready to drop
14427   if (!player->active || player->MovPos || player->drop_delay > 0)
14428     return FALSE;
14429
14430   // check if player has anything that can be dropped
14431   if (new_element == EL_UNDEFINED)
14432     return FALSE;
14433
14434   // only set if player has anything that can be dropped
14435   player->is_dropping_pressed = TRUE;
14436
14437   // check if drop key was pressed long enough for EM style dynamite
14438   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14439     return FALSE;
14440
14441   // check if anything can be dropped at the current position
14442   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14443     return FALSE;
14444
14445   // collected custom elements can only be dropped on empty fields
14446   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14447     return FALSE;
14448
14449   if (old_element != EL_EMPTY)
14450     Back[dropx][dropy] = old_element;   // store old element on this field
14451
14452   ResetGfxAnimation(dropx, dropy);
14453   ResetRandomAnimationValue(dropx, dropy);
14454
14455   if (player->inventory_size > 0 ||
14456       player->inventory_infinite_element != EL_UNDEFINED)
14457   {
14458     if (player->inventory_size > 0)
14459     {
14460       player->inventory_size--;
14461
14462       DrawGameDoorValues();
14463
14464       if (new_element == EL_DYNAMITE)
14465         new_element = EL_DYNAMITE_ACTIVE;
14466       else if (new_element == EL_EM_DYNAMITE)
14467         new_element = EL_EM_DYNAMITE_ACTIVE;
14468       else if (new_element == EL_SP_DISK_RED)
14469         new_element = EL_SP_DISK_RED_ACTIVE;
14470     }
14471
14472     Feld[dropx][dropy] = new_element;
14473
14474     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14475       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14476                           el2img(Feld[dropx][dropy]), 0);
14477
14478     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14479
14480     // needed if previous element just changed to "empty" in the last frame
14481     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14482
14483     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14484                                player->index_bit, drop_side);
14485     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14486                                         CE_PLAYER_DROPS_X,
14487                                         player->index_bit, drop_side);
14488
14489     TestIfElementTouchesCustomElement(dropx, dropy);
14490   }
14491   else          // player is dropping a dyna bomb
14492   {
14493     player->dynabombs_left--;
14494
14495     Feld[dropx][dropy] = new_element;
14496
14497     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14498       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14499                           el2img(Feld[dropx][dropy]), 0);
14500
14501     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14502   }
14503
14504   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14505     InitField_WithBug1(dropx, dropy, FALSE);
14506
14507   new_element = Feld[dropx][dropy];     // element might have changed
14508
14509   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14510       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14511   {
14512     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14513       MovDir[dropx][dropy] = drop_direction;
14514
14515     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14516
14517     // do not cause impact style collision by dropping elements that can fall
14518     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14519   }
14520
14521   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14522   player->is_dropping = TRUE;
14523
14524   player->drop_pressed_delay = 0;
14525   player->is_dropping_pressed = FALSE;
14526
14527   player->drop_x = dropx;
14528   player->drop_y = dropy;
14529
14530   return TRUE;
14531 }
14532
14533 // ----------------------------------------------------------------------------
14534 // game sound playing functions
14535 // ----------------------------------------------------------------------------
14536
14537 static int *loop_sound_frame = NULL;
14538 static int *loop_sound_volume = NULL;
14539
14540 void InitPlayLevelSound(void)
14541 {
14542   int num_sounds = getSoundListSize();
14543
14544   checked_free(loop_sound_frame);
14545   checked_free(loop_sound_volume);
14546
14547   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14548   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14549 }
14550
14551 static void PlayLevelSound(int x, int y, int nr)
14552 {
14553   int sx = SCREENX(x), sy = SCREENY(y);
14554   int volume, stereo_position;
14555   int max_distance = 8;
14556   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14557
14558   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14559       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14560     return;
14561
14562   if (!IN_LEV_FIELD(x, y) ||
14563       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14564       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14565     return;
14566
14567   volume = SOUND_MAX_VOLUME;
14568
14569   if (!IN_SCR_FIELD(sx, sy))
14570   {
14571     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14572     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14573
14574     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14575   }
14576
14577   stereo_position = (SOUND_MAX_LEFT +
14578                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14579                      (SCR_FIELDX + 2 * max_distance));
14580
14581   if (IS_LOOP_SOUND(nr))
14582   {
14583     /* This assures that quieter loop sounds do not overwrite louder ones,
14584        while restarting sound volume comparison with each new game frame. */
14585
14586     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14587       return;
14588
14589     loop_sound_volume[nr] = volume;
14590     loop_sound_frame[nr] = FrameCounter;
14591   }
14592
14593   PlaySoundExt(nr, volume, stereo_position, type);
14594 }
14595
14596 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14597 {
14598   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14599                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14600                  y < LEVELY(BY1) ? LEVELY(BY1) :
14601                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14602                  sound_action);
14603 }
14604
14605 static void PlayLevelSoundAction(int x, int y, int action)
14606 {
14607   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14608 }
14609
14610 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14611 {
14612   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14613
14614   if (sound_effect != SND_UNDEFINED)
14615     PlayLevelSound(x, y, sound_effect);
14616 }
14617
14618 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14619                                               int action)
14620 {
14621   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14622
14623   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14624     PlayLevelSound(x, y, sound_effect);
14625 }
14626
14627 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14628 {
14629   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14630
14631   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14632     PlayLevelSound(x, y, sound_effect);
14633 }
14634
14635 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14636 {
14637   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14638
14639   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14640     StopSound(sound_effect);
14641 }
14642
14643 static int getLevelMusicNr(void)
14644 {
14645   if (levelset.music[level_nr] != MUS_UNDEFINED)
14646     return levelset.music[level_nr];            // from config file
14647   else
14648     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14649 }
14650
14651 static void FadeLevelSounds(void)
14652 {
14653   FadeSounds();
14654 }
14655
14656 static void FadeLevelMusic(void)
14657 {
14658   int music_nr = getLevelMusicNr();
14659   char *curr_music = getCurrentlyPlayingMusicFilename();
14660   char *next_music = getMusicInfoEntryFilename(music_nr);
14661
14662   if (!strEqual(curr_music, next_music))
14663     FadeMusic();
14664 }
14665
14666 void FadeLevelSoundsAndMusic(void)
14667 {
14668   FadeLevelSounds();
14669   FadeLevelMusic();
14670 }
14671
14672 static void PlayLevelMusic(void)
14673 {
14674   int music_nr = getLevelMusicNr();
14675   char *curr_music = getCurrentlyPlayingMusicFilename();
14676   char *next_music = getMusicInfoEntryFilename(music_nr);
14677
14678   if (!strEqual(curr_music, next_music))
14679     PlayMusicLoop(music_nr);
14680 }
14681
14682 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14683 {
14684   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14685   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14686   int x = xx - 1 - offset;
14687   int y = yy - 1 - offset;
14688
14689   switch (sample)
14690   {
14691     case SAMPLE_blank:
14692       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14693       break;
14694
14695     case SAMPLE_roll:
14696       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14697       break;
14698
14699     case SAMPLE_stone:
14700       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14701       break;
14702
14703     case SAMPLE_nut:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14705       break;
14706
14707     case SAMPLE_crack:
14708       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14709       break;
14710
14711     case SAMPLE_bug:
14712       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14713       break;
14714
14715     case SAMPLE_tank:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14717       break;
14718
14719     case SAMPLE_android_clone:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14721       break;
14722
14723     case SAMPLE_android_move:
14724       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14725       break;
14726
14727     case SAMPLE_spring:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14729       break;
14730
14731     case SAMPLE_slurp:
14732       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14733       break;
14734
14735     case SAMPLE_eater:
14736       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14737       break;
14738
14739     case SAMPLE_eater_eat:
14740       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14741       break;
14742
14743     case SAMPLE_alien:
14744       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14745       break;
14746
14747     case SAMPLE_collect:
14748       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14749       break;
14750
14751     case SAMPLE_diamond:
14752       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14753       break;
14754
14755     case SAMPLE_squash:
14756       // !!! CHECK THIS !!!
14757 #if 1
14758       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14759 #else
14760       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14761 #endif
14762       break;
14763
14764     case SAMPLE_wonderfall:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14766       break;
14767
14768     case SAMPLE_drip:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14770       break;
14771
14772     case SAMPLE_push:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14774       break;
14775
14776     case SAMPLE_dirt:
14777       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14778       break;
14779
14780     case SAMPLE_acid:
14781       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14782       break;
14783
14784     case SAMPLE_ball:
14785       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14786       break;
14787
14788     case SAMPLE_grow:
14789       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14790       break;
14791
14792     case SAMPLE_wonder:
14793       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14794       break;
14795
14796     case SAMPLE_door:
14797       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14798       break;
14799
14800     case SAMPLE_exit_open:
14801       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14802       break;
14803
14804     case SAMPLE_exit_leave:
14805       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14806       break;
14807
14808     case SAMPLE_dynamite:
14809       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14810       break;
14811
14812     case SAMPLE_tick:
14813       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14814       break;
14815
14816     case SAMPLE_press:
14817       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14818       break;
14819
14820     case SAMPLE_wheel:
14821       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14822       break;
14823
14824     case SAMPLE_boom:
14825       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14826       break;
14827
14828     case SAMPLE_die:
14829       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14830       break;
14831
14832     case SAMPLE_time:
14833       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14834       break;
14835
14836     default:
14837       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14838       break;
14839   }
14840 }
14841
14842 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14843 {
14844   int element = map_element_SP_to_RND(element_sp);
14845   int action = map_action_SP_to_RND(action_sp);
14846   int offset = (setup.sp_show_border_elements ? 0 : 1);
14847   int x = xx - offset;
14848   int y = yy - offset;
14849
14850   PlayLevelSoundElementAction(x, y, element, action);
14851 }
14852
14853 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14854 {
14855   int element = map_element_MM_to_RND(element_mm);
14856   int action = map_action_MM_to_RND(action_mm);
14857   int offset = 0;
14858   int x = xx - offset;
14859   int y = yy - offset;
14860
14861   if (!IS_MM_ELEMENT(element))
14862     element = EL_MM_DEFAULT;
14863
14864   PlayLevelSoundElementAction(x, y, element, action);
14865 }
14866
14867 void PlaySound_MM(int sound_mm)
14868 {
14869   int sound = map_sound_MM_to_RND(sound_mm);
14870
14871   if (sound == SND_UNDEFINED)
14872     return;
14873
14874   PlaySound(sound);
14875 }
14876
14877 void PlaySoundLoop_MM(int sound_mm)
14878 {
14879   int sound = map_sound_MM_to_RND(sound_mm);
14880
14881   if (sound == SND_UNDEFINED)
14882     return;
14883
14884   PlaySoundLoop(sound);
14885 }
14886
14887 void StopSound_MM(int sound_mm)
14888 {
14889   int sound = map_sound_MM_to_RND(sound_mm);
14890
14891   if (sound == SND_UNDEFINED)
14892     return;
14893
14894   StopSound(sound);
14895 }
14896
14897 void RaiseScore(int value)
14898 {
14899   game.score += value;
14900
14901   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14902
14903   DisplayGameControlValues();
14904 }
14905
14906 void RaiseScoreElement(int element)
14907 {
14908   switch (element)
14909   {
14910     case EL_EMERALD:
14911     case EL_BD_DIAMOND:
14912     case EL_EMERALD_YELLOW:
14913     case EL_EMERALD_RED:
14914     case EL_EMERALD_PURPLE:
14915     case EL_SP_INFOTRON:
14916       RaiseScore(level.score[SC_EMERALD]);
14917       break;
14918     case EL_DIAMOND:
14919       RaiseScore(level.score[SC_DIAMOND]);
14920       break;
14921     case EL_CRYSTAL:
14922       RaiseScore(level.score[SC_CRYSTAL]);
14923       break;
14924     case EL_PEARL:
14925       RaiseScore(level.score[SC_PEARL]);
14926       break;
14927     case EL_BUG:
14928     case EL_BD_BUTTERFLY:
14929     case EL_SP_ELECTRON:
14930       RaiseScore(level.score[SC_BUG]);
14931       break;
14932     case EL_SPACESHIP:
14933     case EL_BD_FIREFLY:
14934     case EL_SP_SNIKSNAK:
14935       RaiseScore(level.score[SC_SPACESHIP]);
14936       break;
14937     case EL_YAMYAM:
14938     case EL_DARK_YAMYAM:
14939       RaiseScore(level.score[SC_YAMYAM]);
14940       break;
14941     case EL_ROBOT:
14942       RaiseScore(level.score[SC_ROBOT]);
14943       break;
14944     case EL_PACMAN:
14945       RaiseScore(level.score[SC_PACMAN]);
14946       break;
14947     case EL_NUT:
14948       RaiseScore(level.score[SC_NUT]);
14949       break;
14950     case EL_DYNAMITE:
14951     case EL_EM_DYNAMITE:
14952     case EL_SP_DISK_RED:
14953     case EL_DYNABOMB_INCREASE_NUMBER:
14954     case EL_DYNABOMB_INCREASE_SIZE:
14955     case EL_DYNABOMB_INCREASE_POWER:
14956       RaiseScore(level.score[SC_DYNAMITE]);
14957       break;
14958     case EL_SHIELD_NORMAL:
14959     case EL_SHIELD_DEADLY:
14960       RaiseScore(level.score[SC_SHIELD]);
14961       break;
14962     case EL_EXTRA_TIME:
14963       RaiseScore(level.extra_time_score);
14964       break;
14965     case EL_KEY_1:
14966     case EL_KEY_2:
14967     case EL_KEY_3:
14968     case EL_KEY_4:
14969     case EL_EM_KEY_1:
14970     case EL_EM_KEY_2:
14971     case EL_EM_KEY_3:
14972     case EL_EM_KEY_4:
14973     case EL_EMC_KEY_5:
14974     case EL_EMC_KEY_6:
14975     case EL_EMC_KEY_7:
14976     case EL_EMC_KEY_8:
14977     case EL_DC_KEY_WHITE:
14978       RaiseScore(level.score[SC_KEY]);
14979       break;
14980     default:
14981       RaiseScore(element_info[element].collect_score);
14982       break;
14983   }
14984 }
14985
14986 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14987 {
14988   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14989   {
14990     // closing door required in case of envelope style request dialogs
14991     if (!skip_request)
14992     {
14993       // prevent short reactivation of overlay buttons while closing door
14994       SetOverlayActive(FALSE);
14995
14996       CloseDoor(DOOR_CLOSE_1);
14997     }
14998
14999     if (network.enabled)
15000       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15001     else
15002     {
15003       if (quick_quit)
15004         FadeSkipNextFadeIn();
15005
15006       SetGameStatus(GAME_MODE_MAIN);
15007
15008       DrawMainMenu();
15009     }
15010   }
15011   else          // continue playing the game
15012   {
15013     if (tape.playing && tape.deactivate_display)
15014       TapeDeactivateDisplayOff(TRUE);
15015
15016     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15017
15018     if (tape.playing && tape.deactivate_display)
15019       TapeDeactivateDisplayOn();
15020   }
15021 }
15022
15023 void RequestQuitGame(boolean ask_if_really_quit)
15024 {
15025   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15026   boolean skip_request = game.all_players_gone || quick_quit;
15027
15028   RequestQuitGameExt(skip_request, quick_quit,
15029                      "Do you really want to quit the game?");
15030 }
15031
15032 void RequestRestartGame(char *message)
15033 {
15034   game.restart_game_message = NULL;
15035
15036   boolean has_started_game = hasStartedNetworkGame();
15037   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15038
15039   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15040   {
15041     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15042   }
15043   else
15044   {
15045     SetGameStatus(GAME_MODE_MAIN);
15046
15047     DrawMainMenu();
15048   }
15049 }
15050
15051 void CheckGameOver(void)
15052 {
15053   static boolean last_game_over = FALSE;
15054   static int game_over_delay = 0;
15055   int game_over_delay_value = 50;
15056   boolean game_over = checkGameFailed();
15057
15058   // do not handle game over if request dialog is already active
15059   if (game.request_active)
15060     return;
15061
15062   // do not ask to play again if game was never actually played
15063   if (!game.GamePlayed)
15064     return;
15065
15066   if (!game_over)
15067   {
15068     last_game_over = FALSE;
15069     game_over_delay = game_over_delay_value;
15070
15071     return;
15072   }
15073
15074   if (game_over_delay > 0)
15075   {
15076     game_over_delay--;
15077
15078     return;
15079   }
15080
15081   if (last_game_over != game_over)
15082     game.restart_game_message = (hasStartedNetworkGame() ?
15083                                  "Game over! Play it again?" :
15084                                  "Game over!");
15085
15086   last_game_over = game_over;
15087 }
15088
15089 boolean checkGameSolved(void)
15090 {
15091   // set for all game engines if level was solved
15092   return game.LevelSolved_GameEnd;
15093 }
15094
15095 boolean checkGameFailed(void)
15096 {
15097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15098     return (game_em.game_over && !game_em.level_solved);
15099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15100     return (game_sp.game_over && !game_sp.level_solved);
15101   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15102     return (game_mm.game_over && !game_mm.level_solved);
15103   else                          // GAME_ENGINE_TYPE_RND
15104     return (game.GameOver && !game.LevelSolved);
15105 }
15106
15107 boolean checkGameEnded(void)
15108 {
15109   return (checkGameSolved() || checkGameFailed());
15110 }
15111
15112
15113 // ----------------------------------------------------------------------------
15114 // random generator functions
15115 // ----------------------------------------------------------------------------
15116
15117 unsigned int InitEngineRandom_RND(int seed)
15118 {
15119   game.num_random_calls = 0;
15120
15121   return InitEngineRandom(seed);
15122 }
15123
15124 unsigned int RND(int max)
15125 {
15126   if (max > 0)
15127   {
15128     game.num_random_calls++;
15129
15130     return GetEngineRandom(max);
15131   }
15132
15133   return 0;
15134 }
15135
15136
15137 // ----------------------------------------------------------------------------
15138 // game engine snapshot handling functions
15139 // ----------------------------------------------------------------------------
15140
15141 struct EngineSnapshotInfo
15142 {
15143   // runtime values for custom element collect score
15144   int collect_score[NUM_CUSTOM_ELEMENTS];
15145
15146   // runtime values for group element choice position
15147   int choice_pos[NUM_GROUP_ELEMENTS];
15148
15149   // runtime values for belt position animations
15150   int belt_graphic[4][NUM_BELT_PARTS];
15151   int belt_anim_mode[4][NUM_BELT_PARTS];
15152 };
15153
15154 static struct EngineSnapshotInfo engine_snapshot_rnd;
15155 static char *snapshot_level_identifier = NULL;
15156 static int snapshot_level_nr = -1;
15157
15158 static void SaveEngineSnapshotValues_RND(void)
15159 {
15160   static int belt_base_active_element[4] =
15161   {
15162     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15163     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15164     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15165     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15166   };
15167   int i, j;
15168
15169   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15170   {
15171     int element = EL_CUSTOM_START + i;
15172
15173     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15174   }
15175
15176   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15177   {
15178     int element = EL_GROUP_START + i;
15179
15180     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15181   }
15182
15183   for (i = 0; i < 4; i++)
15184   {
15185     for (j = 0; j < NUM_BELT_PARTS; j++)
15186     {
15187       int element = belt_base_active_element[i] + j;
15188       int graphic = el2img(element);
15189       int anim_mode = graphic_info[graphic].anim_mode;
15190
15191       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15192       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15193     }
15194   }
15195 }
15196
15197 static void LoadEngineSnapshotValues_RND(void)
15198 {
15199   unsigned int num_random_calls = game.num_random_calls;
15200   int i, j;
15201
15202   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15203   {
15204     int element = EL_CUSTOM_START + i;
15205
15206     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15207   }
15208
15209   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15210   {
15211     int element = EL_GROUP_START + i;
15212
15213     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15214   }
15215
15216   for (i = 0; i < 4; i++)
15217   {
15218     for (j = 0; j < NUM_BELT_PARTS; j++)
15219     {
15220       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15221       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15222
15223       graphic_info[graphic].anim_mode = anim_mode;
15224     }
15225   }
15226
15227   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15228   {
15229     InitRND(tape.random_seed);
15230     for (i = 0; i < num_random_calls; i++)
15231       RND(1);
15232   }
15233
15234   if (game.num_random_calls != num_random_calls)
15235   {
15236     Error(ERR_INFO, "number of random calls out of sync");
15237     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15238     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15239     Error(ERR_EXIT, "this should not happen -- please debug");
15240   }
15241 }
15242
15243 void FreeEngineSnapshotSingle(void)
15244 {
15245   FreeSnapshotSingle();
15246
15247   setString(&snapshot_level_identifier, NULL);
15248   snapshot_level_nr = -1;
15249 }
15250
15251 void FreeEngineSnapshotList(void)
15252 {
15253   FreeSnapshotList();
15254 }
15255
15256 static ListNode *SaveEngineSnapshotBuffers(void)
15257 {
15258   ListNode *buffers = NULL;
15259
15260   // copy some special values to a structure better suited for the snapshot
15261
15262   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15263     SaveEngineSnapshotValues_RND();
15264   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15265     SaveEngineSnapshotValues_EM();
15266   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15267     SaveEngineSnapshotValues_SP(&buffers);
15268   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15269     SaveEngineSnapshotValues_MM(&buffers);
15270
15271   // save values stored in special snapshot structure
15272
15273   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15274     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15275   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15276     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15277   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15278     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15279   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15280     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15281
15282   // save further RND engine values
15283
15284   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15285   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15287
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15289   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15293
15294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15295   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15296   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15297
15298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15299
15300   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15301   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15302
15303   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15304   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15305   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15306   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15307   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15308   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15309   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15312   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15314   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15318   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15320   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15321
15322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15323   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15324
15325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15326   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15327   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15328
15329   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15330   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15331
15332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15333   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15337
15338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15340
15341 #if 0
15342   ListNode *node = engine_snapshot_list_rnd;
15343   int num_bytes = 0;
15344
15345   while (node != NULL)
15346   {
15347     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15348
15349     node = node->next;
15350   }
15351
15352   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15353 #endif
15354
15355   return buffers;
15356 }
15357
15358 void SaveEngineSnapshotSingle(void)
15359 {
15360   ListNode *buffers = SaveEngineSnapshotBuffers();
15361
15362   // finally save all snapshot buffers to single snapshot
15363   SaveSnapshotSingle(buffers);
15364
15365   // save level identification information
15366   setString(&snapshot_level_identifier, leveldir_current->identifier);
15367   snapshot_level_nr = level_nr;
15368 }
15369
15370 boolean CheckSaveEngineSnapshotToList(void)
15371 {
15372   boolean save_snapshot =
15373     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15374      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15375       game.snapshot.changed_action) ||
15376      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15377       game.snapshot.collected_item));
15378
15379   game.snapshot.changed_action = FALSE;
15380   game.snapshot.collected_item = FALSE;
15381   game.snapshot.save_snapshot = save_snapshot;
15382
15383   return save_snapshot;
15384 }
15385
15386 void SaveEngineSnapshotToList(void)
15387 {
15388   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15389       tape.quick_resume)
15390     return;
15391
15392   ListNode *buffers = SaveEngineSnapshotBuffers();
15393
15394   // finally save all snapshot buffers to snapshot list
15395   SaveSnapshotToList(buffers);
15396 }
15397
15398 void SaveEngineSnapshotToListInitial(void)
15399 {
15400   FreeEngineSnapshotList();
15401
15402   SaveEngineSnapshotToList();
15403 }
15404
15405 static void LoadEngineSnapshotValues(void)
15406 {
15407   // restore special values from snapshot structure
15408
15409   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15410     LoadEngineSnapshotValues_RND();
15411   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15412     LoadEngineSnapshotValues_EM();
15413   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15414     LoadEngineSnapshotValues_SP();
15415   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15416     LoadEngineSnapshotValues_MM();
15417 }
15418
15419 void LoadEngineSnapshotSingle(void)
15420 {
15421   LoadSnapshotSingle();
15422
15423   LoadEngineSnapshotValues();
15424 }
15425
15426 static void LoadEngineSnapshot_Undo(int steps)
15427 {
15428   LoadSnapshotFromList_Older(steps);
15429
15430   LoadEngineSnapshotValues();
15431 }
15432
15433 static void LoadEngineSnapshot_Redo(int steps)
15434 {
15435   LoadSnapshotFromList_Newer(steps);
15436
15437   LoadEngineSnapshotValues();
15438 }
15439
15440 boolean CheckEngineSnapshotSingle(void)
15441 {
15442   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15443           snapshot_level_nr == level_nr);
15444 }
15445
15446 boolean CheckEngineSnapshotList(void)
15447 {
15448   return CheckSnapshotList();
15449 }
15450
15451
15452 // ---------- new game button stuff -------------------------------------------
15453
15454 static struct
15455 {
15456   int graphic;
15457   struct XY *pos;
15458   int gadget_id;
15459   boolean *setup_value;
15460   boolean allowed_on_tape;
15461   char *infotext;
15462 } gamebutton_info[NUM_GAME_BUTTONS] =
15463 {
15464   {
15465     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15466     GAME_CTRL_ID_STOP,                          NULL,
15467     TRUE,                                       "stop game"
15468   },
15469   {
15470     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15471     GAME_CTRL_ID_PAUSE,                         NULL,
15472     TRUE,                                       "pause game"
15473   },
15474   {
15475     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15476     GAME_CTRL_ID_PLAY,                          NULL,
15477     TRUE,                                       "play game"
15478   },
15479   {
15480     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15481     GAME_CTRL_ID_UNDO,                          NULL,
15482     TRUE,                                       "undo step"
15483   },
15484   {
15485     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15486     GAME_CTRL_ID_REDO,                          NULL,
15487     TRUE,                                       "redo step"
15488   },
15489   {
15490     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15491     GAME_CTRL_ID_SAVE,                          NULL,
15492     TRUE,                                       "save game"
15493   },
15494   {
15495     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15496     GAME_CTRL_ID_PAUSE2,                        NULL,
15497     TRUE,                                       "pause game"
15498   },
15499   {
15500     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15501     GAME_CTRL_ID_LOAD,                          NULL,
15502     TRUE,                                       "load game"
15503   },
15504   {
15505     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15506     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15507     FALSE,                                      "stop game"
15508   },
15509   {
15510     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15511     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15512     FALSE,                                      "pause game"
15513   },
15514   {
15515     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15516     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15517     FALSE,                                      "play game"
15518   },
15519   {
15520     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15521     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15522     TRUE,                                       "background music on/off"
15523   },
15524   {
15525     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15526     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15527     TRUE,                                       "sound loops on/off"
15528   },
15529   {
15530     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15531     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15532     TRUE,                                       "normal sounds on/off"
15533   },
15534   {
15535     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15536     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15537     FALSE,                                      "background music on/off"
15538   },
15539   {
15540     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15541     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15542     FALSE,                                      "sound loops on/off"
15543   },
15544   {
15545     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15546     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15547     FALSE,                                      "normal sounds on/off"
15548   }
15549 };
15550
15551 void CreateGameButtons(void)
15552 {
15553   int i;
15554
15555   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15556   {
15557     int graphic = gamebutton_info[i].graphic;
15558     struct GraphicInfo *gfx = &graphic_info[graphic];
15559     struct XY *pos = gamebutton_info[i].pos;
15560     struct GadgetInfo *gi;
15561     int button_type;
15562     boolean checked;
15563     unsigned int event_mask;
15564     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15565     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15566     int base_x = (on_tape ? VX : DX);
15567     int base_y = (on_tape ? VY : DY);
15568     int gd_x   = gfx->src_x;
15569     int gd_y   = gfx->src_y;
15570     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15571     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15572     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15573     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15574     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15575     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15576     int id = i;
15577
15578     if (gfx->bitmap == NULL)
15579     {
15580       game_gadget[id] = NULL;
15581
15582       continue;
15583     }
15584
15585     if (id == GAME_CTRL_ID_STOP ||
15586         id == GAME_CTRL_ID_PANEL_STOP ||
15587         id == GAME_CTRL_ID_PLAY ||
15588         id == GAME_CTRL_ID_PANEL_PLAY ||
15589         id == GAME_CTRL_ID_SAVE ||
15590         id == GAME_CTRL_ID_LOAD)
15591     {
15592       button_type = GD_TYPE_NORMAL_BUTTON;
15593       checked = FALSE;
15594       event_mask = GD_EVENT_RELEASED;
15595     }
15596     else if (id == GAME_CTRL_ID_UNDO ||
15597              id == GAME_CTRL_ID_REDO)
15598     {
15599       button_type = GD_TYPE_NORMAL_BUTTON;
15600       checked = FALSE;
15601       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15602     }
15603     else
15604     {
15605       button_type = GD_TYPE_CHECK_BUTTON;
15606       checked = (gamebutton_info[i].setup_value != NULL ?
15607                  *gamebutton_info[i].setup_value : FALSE);
15608       event_mask = GD_EVENT_PRESSED;
15609     }
15610
15611     gi = CreateGadget(GDI_CUSTOM_ID, id,
15612                       GDI_IMAGE_ID, graphic,
15613                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15614                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15615                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15616                       GDI_WIDTH, gfx->width,
15617                       GDI_HEIGHT, gfx->height,
15618                       GDI_TYPE, button_type,
15619                       GDI_STATE, GD_BUTTON_UNPRESSED,
15620                       GDI_CHECKED, checked,
15621                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15622                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15623                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15624                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15625                       GDI_DIRECT_DRAW, FALSE,
15626                       GDI_EVENT_MASK, event_mask,
15627                       GDI_CALLBACK_ACTION, HandleGameButtons,
15628                       GDI_END);
15629
15630     if (gi == NULL)
15631       Error(ERR_EXIT, "cannot create gadget");
15632
15633     game_gadget[id] = gi;
15634   }
15635 }
15636
15637 void FreeGameButtons(void)
15638 {
15639   int i;
15640
15641   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15642     FreeGadget(game_gadget[i]);
15643 }
15644
15645 static void UnmapGameButtonsAtSamePosition(int id)
15646 {
15647   int i;
15648
15649   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15650     if (i != id &&
15651         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15652         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15653       UnmapGadget(game_gadget[i]);
15654 }
15655
15656 static void UnmapGameButtonsAtSamePosition_All(void)
15657 {
15658   if (setup.show_snapshot_buttons)
15659   {
15660     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15661     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15662     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15663   }
15664   else
15665   {
15666     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15667     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15668     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15669
15670     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15671     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15672     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15673   }
15674 }
15675
15676 static void MapGameButtonsAtSamePosition(int id)
15677 {
15678   int i;
15679
15680   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15681     if (i != id &&
15682         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15683         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15684       MapGadget(game_gadget[i]);
15685
15686   UnmapGameButtonsAtSamePosition_All();
15687 }
15688
15689 void MapUndoRedoButtons(void)
15690 {
15691   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15692   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15693
15694   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15695   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15696
15697   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15698 }
15699
15700 void UnmapUndoRedoButtons(void)
15701 {
15702   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15703   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15704
15705   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15706   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15707
15708   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15709 }
15710
15711 static void MapGameButtonsExt(boolean on_tape)
15712 {
15713   int i;
15714
15715   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15716     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15717         i != GAME_CTRL_ID_UNDO &&
15718         i != GAME_CTRL_ID_REDO)
15719       MapGadget(game_gadget[i]);
15720
15721   UnmapGameButtonsAtSamePosition_All();
15722
15723   RedrawGameButtons();
15724 }
15725
15726 static void UnmapGameButtonsExt(boolean on_tape)
15727 {
15728   int i;
15729
15730   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15731     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15732       UnmapGadget(game_gadget[i]);
15733 }
15734
15735 static void RedrawGameButtonsExt(boolean on_tape)
15736 {
15737   int i;
15738
15739   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15740     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15741       RedrawGadget(game_gadget[i]);
15742
15743   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15744   redraw_mask &= ~REDRAW_ALL;
15745 }
15746
15747 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15748 {
15749   if (gi == NULL)
15750     return;
15751
15752   gi->checked = state;
15753 }
15754
15755 static void RedrawSoundButtonGadget(int id)
15756 {
15757   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15758              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15759              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15760              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15761              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15762              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15763              id);
15764
15765   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15766   RedrawGadget(game_gadget[id2]);
15767 }
15768
15769 void MapGameButtons(void)
15770 {
15771   MapGameButtonsExt(FALSE);
15772 }
15773
15774 void UnmapGameButtons(void)
15775 {
15776   UnmapGameButtonsExt(FALSE);
15777 }
15778
15779 void RedrawGameButtons(void)
15780 {
15781   RedrawGameButtonsExt(FALSE);
15782 }
15783
15784 void MapGameButtonsOnTape(void)
15785 {
15786   MapGameButtonsExt(TRUE);
15787 }
15788
15789 void UnmapGameButtonsOnTape(void)
15790 {
15791   UnmapGameButtonsExt(TRUE);
15792 }
15793
15794 void RedrawGameButtonsOnTape(void)
15795 {
15796   RedrawGameButtonsExt(TRUE);
15797 }
15798
15799 static void GameUndoRedoExt(void)
15800 {
15801   ClearPlayerAction();
15802
15803   tape.pausing = TRUE;
15804
15805   RedrawPlayfield();
15806   UpdateAndDisplayGameControlValues();
15807
15808   DrawCompleteVideoDisplay();
15809   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15810   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15811   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15812
15813   BackToFront();
15814 }
15815
15816 static void GameUndo(int steps)
15817 {
15818   if (!CheckEngineSnapshotList())
15819     return;
15820
15821   LoadEngineSnapshot_Undo(steps);
15822
15823   GameUndoRedoExt();
15824 }
15825
15826 static void GameRedo(int steps)
15827 {
15828   if (!CheckEngineSnapshotList())
15829     return;
15830
15831   LoadEngineSnapshot_Redo(steps);
15832
15833   GameUndoRedoExt();
15834 }
15835
15836 static void HandleGameButtonsExt(int id, int button)
15837 {
15838   static boolean game_undo_executed = FALSE;
15839   int steps = BUTTON_STEPSIZE(button);
15840   boolean handle_game_buttons =
15841     (game_status == GAME_MODE_PLAYING ||
15842      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15843
15844   if (!handle_game_buttons)
15845     return;
15846
15847   switch (id)
15848   {
15849     case GAME_CTRL_ID_STOP:
15850     case GAME_CTRL_ID_PANEL_STOP:
15851       if (game_status == GAME_MODE_MAIN)
15852         break;
15853
15854       if (tape.playing)
15855         TapeStop();
15856       else
15857         RequestQuitGame(TRUE);
15858
15859       break;
15860
15861     case GAME_CTRL_ID_PAUSE:
15862     case GAME_CTRL_ID_PAUSE2:
15863     case GAME_CTRL_ID_PANEL_PAUSE:
15864       if (network.enabled && game_status == GAME_MODE_PLAYING)
15865       {
15866         if (tape.pausing)
15867           SendToServer_ContinuePlaying();
15868         else
15869           SendToServer_PausePlaying();
15870       }
15871       else
15872         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15873
15874       game_undo_executed = FALSE;
15875
15876       break;
15877
15878     case GAME_CTRL_ID_PLAY:
15879     case GAME_CTRL_ID_PANEL_PLAY:
15880       if (game_status == GAME_MODE_MAIN)
15881       {
15882         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15883       }
15884       else if (tape.pausing)
15885       {
15886         if (network.enabled)
15887           SendToServer_ContinuePlaying();
15888         else
15889           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15890       }
15891       break;
15892
15893     case GAME_CTRL_ID_UNDO:
15894       // Important: When using "save snapshot when collecting an item" mode,
15895       // load last (current) snapshot for first "undo" after pressing "pause"
15896       // (else the last-but-one snapshot would be loaded, because the snapshot
15897       // pointer already points to the last snapshot when pressing "pause",
15898       // which is fine for "every step/move" mode, but not for "every collect")
15899       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15900           !game_undo_executed)
15901         steps--;
15902
15903       game_undo_executed = TRUE;
15904
15905       GameUndo(steps);
15906       break;
15907
15908     case GAME_CTRL_ID_REDO:
15909       GameRedo(steps);
15910       break;
15911
15912     case GAME_CTRL_ID_SAVE:
15913       TapeQuickSave();
15914       break;
15915
15916     case GAME_CTRL_ID_LOAD:
15917       TapeQuickLoad();
15918       break;
15919
15920     case SOUND_CTRL_ID_MUSIC:
15921     case SOUND_CTRL_ID_PANEL_MUSIC:
15922       if (setup.sound_music)
15923       { 
15924         setup.sound_music = FALSE;
15925
15926         FadeMusic();
15927       }
15928       else if (audio.music_available)
15929       { 
15930         setup.sound = setup.sound_music = TRUE;
15931
15932         SetAudioMode(setup.sound);
15933
15934         if (game_status == GAME_MODE_PLAYING)
15935           PlayLevelMusic();
15936       }
15937
15938       RedrawSoundButtonGadget(id);
15939
15940       break;
15941
15942     case SOUND_CTRL_ID_LOOPS:
15943     case SOUND_CTRL_ID_PANEL_LOOPS:
15944       if (setup.sound_loops)
15945         setup.sound_loops = FALSE;
15946       else if (audio.loops_available)
15947       {
15948         setup.sound = setup.sound_loops = TRUE;
15949
15950         SetAudioMode(setup.sound);
15951       }
15952
15953       RedrawSoundButtonGadget(id);
15954
15955       break;
15956
15957     case SOUND_CTRL_ID_SIMPLE:
15958     case SOUND_CTRL_ID_PANEL_SIMPLE:
15959       if (setup.sound_simple)
15960         setup.sound_simple = FALSE;
15961       else if (audio.sound_available)
15962       {
15963         setup.sound = setup.sound_simple = TRUE;
15964
15965         SetAudioMode(setup.sound);
15966       }
15967
15968       RedrawSoundButtonGadget(id);
15969
15970       break;
15971
15972     default:
15973       break;
15974   }
15975 }
15976
15977 static void HandleGameButtons(struct GadgetInfo *gi)
15978 {
15979   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15980 }
15981
15982 void HandleSoundButtonKeys(Key key)
15983 {
15984   if (key == setup.shortcut.sound_simple)
15985     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15986   else if (key == setup.shortcut.sound_loops)
15987     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15988   else if (key == setup.shortcut.sound_music)
15989     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15990 }