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